diff --git a/assets/zip_asset_store.cc b/assets/zip_asset_store.cc index e3ebefb730abe..8b8d93e3c0be9 100644 --- a/assets/zip_asset_store.cc +++ b/assets/zip_asset_store.cc @@ -10,9 +10,9 @@ #include #include +#include "flutter/glue/trace_event.h" #include "lib/fxl/files/eintr_wrapper.h" #include "lib/fxl/files/unique_fd.h" -#include "flutter/glue/trace_event.h" #include "lib/zip/unique_unzipper.h" namespace blink { diff --git a/content_handler/app.cc b/content_handler/app.cc index 1c7c2a560f63b..ea89615508b9b 100644 --- a/content_handler/app.cc +++ b/content_handler/app.cc @@ -10,10 +10,10 @@ #include "flutter/common/settings.h" #include "flutter/common/threads.h" #include "flutter/sky/engine/platform/fonts/fuchsia/FontCacheFuchsia.h" +#include "lib/fsl/tasks/message_loop.h" #include "lib/fxl/macros.h" #include "lib/fxl/tasks/task_runner.h" #include "lib/icu_data/cpp/icu_data.h" -#include "lib/fsl/tasks/message_loop.h" namespace flutter_runner { namespace { diff --git a/content_handler/app.h b/content_handler/app.h index 45b656232718f..cd2af2adffa57 100644 --- a/content_handler/app.h +++ b/content_handler/app.h @@ -8,12 +8,12 @@ #include #include +#include "flutter/content_handler/application_controller_impl.h" #include "lib/app/cpp/application_context.h" #include "lib/app/fidl/application_runner.fidl.h" -#include "flutter/content_handler/application_controller_impl.h" +#include "lib/fsl/threading/thread.h" #include "lib/fxl/macros.h" #include "lib/fxl/synchronization/waitable_event.h" -#include "lib/fsl/threading/thread.h" namespace flutter_runner { diff --git a/content_handler/application_controller_impl.cc b/content_handler/application_controller_impl.cc index 8201b6b8d5856..8492d7aa1428f 100644 --- a/content_handler/application_controller_impl.cc +++ b/content_handler/application_controller_impl.cc @@ -9,11 +9,11 @@ #include #include -#include "lib/app/cpp/connect.h" #include "flutter/content_handler/app.h" #include "flutter/content_handler/runtime_holder.h" -#include "lib/fxl/logging.h" +#include "lib/app/cpp/connect.h" #include "lib/fsl/vmo/vector.h" +#include "lib/fxl/logging.h" namespace flutter_runner { @@ -52,8 +52,8 @@ ApplicationControllerImpl::ApplicationControllerImpl( service_provider_bridge_.AddService( [this](fidl::InterfaceRequest request) { - view_provider_bindings_.AddBinding(this, std::move(request)); - }); + view_provider_bindings_.AddBinding(this, std::move(request)); + }); app::ServiceProviderPtr service_provider; auto request = service_provider.NewRequest(); @@ -67,8 +67,7 @@ ApplicationControllerImpl::ApplicationControllerImpl( url_ = startup_info->launch_info->url; runtime_holder_.reset(new RuntimeHolder()); runtime_holder_->Init( - mxio_ns, - app::ApplicationContext::CreateFrom(std::move(startup_info)), + mxio_ns, app::ApplicationContext::CreateFrom(std::move(startup_info)), std::move(request), std::move(bundle)); } diff --git a/content_handler/application_controller_impl.h b/content_handler/application_controller_impl.h index 4c5ba1a0639ad..e50291294d421 100644 --- a/content_handler/application_controller_impl.h +++ b/content_handler/application_controller_impl.h @@ -9,16 +9,16 @@ #include -#include "lib/svc/cpp/service_provider_bridge.h" +#include "dart/runtime/include/dart_api.h" #include "lib/app/fidl/application_controller.fidl.h" #include "lib/app/fidl/application_runner.fidl.h" #include "lib/app/fidl/service_provider.fidl.h" -#include "lib/ui/views/fidl/view_provider.fidl.h" -#include "dart/runtime/include/dart_api.h" #include "lib/fidl/cpp/bindings/binding.h" #include "lib/fidl/cpp/bindings/binding_set.h" #include "lib/fxl/macros.h" #include "lib/fxl/synchronization/waitable_event.h" +#include "lib/svc/cpp/service_provider_bridge.h" +#include "lib/ui/views/fidl/view_provider.fidl.h" namespace flutter_runner { class App; diff --git a/content_handler/rasterizer.h b/content_handler/rasterizer.h index 40589244beef2..4420b6960af2b 100644 --- a/content_handler/rasterizer.h +++ b/content_handler/rasterizer.h @@ -7,10 +7,10 @@ #include -#include "lib/ui/scenic/fidl/session.fidl.h" #include "flutter/flow/layers/layer_tree.h" #include "lib/fxl/functional/closure.h" #include "lib/fxl/macros.h" +#include "lib/ui/scenic/fidl/session.fidl.h" #include "magenta/system/ulib/mx/include/mx/eventpair.h" namespace flutter_runner { diff --git a/content_handler/runtime_holder.cc b/content_handler/runtime_holder.cc index afae008d5c135..96ce84316ce2f 100644 --- a/content_handler/runtime_holder.cc +++ b/content_handler/runtime_holder.cc @@ -9,7 +9,7 @@ #include #include -#include "lib/app/cpp/connect.h" +#include "dart-pkg/zircon/sdk_ext/handle.h" #include "dart/runtime/include/dart_api.h" #include "flutter/assets/zip_asset_store.h" #include "flutter/common/threads.h" @@ -21,11 +21,11 @@ #include "flutter/runtime/dart_controller.h" #include "flutter/runtime/dart_init.h" #include "flutter/runtime/runtime_init.h" -#include "dart-pkg/zircon/sdk_ext/handle.h" +#include "lib/app/cpp/connect.h" +#include "lib/fsl/vmo/vector.h" #include "lib/fxl/functional/make_copyable.h" #include "lib/fxl/logging.h" #include "lib/fxl/time/time_delta.h" -#include "lib/fsl/vmo/vector.h" #include "lib/zip/create_unzipper.h" #include "third_party/rapidjson/rapidjson/document.h" #include "third_party/rapidjson/rapidjson/stringbuffer.h" @@ -371,8 +371,8 @@ void RuntimeHolder::InitDartIoInternal() { Dart_Handle namespace_args[1]; namespace_args[0] = Dart_NewInteger(reinterpret_cast(namespc_)); DART_CHECK_VALID(namespace_args[0]); - DART_CHECK_VALID(Dart_Invoke( - namespace_type, ToDart("_setupNamespace"), 1, namespace_args)); + DART_CHECK_VALID(Dart_Invoke(namespace_type, ToDart("_setupNamespace"), 1, + namespace_args)); // Disable dart:io exit() Dart_Handle embedder_config_type = @@ -385,8 +385,8 @@ void RuntimeHolder::InitDartIoInternal() { void RuntimeHolder::InitFuchsia() { fidl::InterfaceHandle environment; context_->ConnectToEnvironmentService(environment.NewRequest()); - fuchsia::dart::Initialize(std::move(environment), std::move(outgoing_services_)); - + fuchsia::dart::Initialize(std::move(environment), + std::move(outgoing_services_)); } void RuntimeHolder::InitMozartInternal() { diff --git a/content_handler/runtime_holder.h b/content_handler/runtime_holder.h index f1f59e2f48da1..cc7523cf6d032 100644 --- a/content_handler/runtime_holder.h +++ b/content_handler/runtime_holder.h @@ -10,13 +10,6 @@ #include -#include "lib/app/cpp/application_context.h" -#include "lib/app/fidl/application_environment.fidl.h" -#include "lib/app/fidl/service_provider.fidl.h" -#include "lib/ui/flutter/sdk_ext/src/natives.h" -#include "lib/ui/input/fidl/input_connection.fidl.h" -#include "lib/ui/input/fidl/text_input.fidl.h" -#include "lib/ui/views/fidl/view_manager.fidl.h" #include "dart-pkg/fuchsia/sdk_ext/fuchsia.h" #include "flutter/assets/unzipper_provider.h" #include "flutter/assets/zip_asset_store.h" @@ -24,10 +17,17 @@ #include "flutter/lib/ui/window/viewport_metrics.h" #include "flutter/runtime/runtime_controller.h" #include "flutter/runtime/runtime_delegate.h" +#include "lib/app/cpp/application_context.h" +#include "lib/app/fidl/application_environment.fidl.h" +#include "lib/app/fidl/service_provider.fidl.h" #include "lib/fidl/cpp/bindings/binding.h" #include "lib/fxl/functional/closure.h" #include "lib/fxl/macros.h" #include "lib/fxl/memory/weak_ptr.h" +#include "lib/ui/flutter/sdk_ext/src/natives.h" +#include "lib/ui/input/fidl/input_connection.fidl.h" +#include "lib/ui/input/fidl/text_input.fidl.h" +#include "lib/ui/views/fidl/view_manager.fidl.h" namespace flutter_runner { class Rasterizer; diff --git a/content_handler/service_protocol_hooks.cc b/content_handler/service_protocol_hooks.cc index 7eea44c46624c..947f2a226e615 100644 --- a/content_handler/service_protocol_hooks.cc +++ b/content_handler/service_protocol_hooks.cc @@ -49,16 +49,15 @@ void ServiceProtocolHooks::RegisterHooks(bool running_precompiled_code) { nullptr); } - const char* ServiceProtocolHooks::kListViewsExtensionName = "_flutter.listViews"; bool ServiceProtocolHooks::ListViews(const char* method, - const char** param_keys, - const char** param_values, - intptr_t num_params, - void* user_data, - const char** json_object) { + const char** param_keys, + const char** param_values, + intptr_t num_params, + void* user_data, + const char** json_object) { // Ask the App for the list of platform views. This will run a task on // the UI thread before returning. App& app = App::Shared(); diff --git a/content_handler/session_connection.h b/content_handler/session_connection.h index 85d97191fbed8..1042539b97a16 100644 --- a/content_handler/session_connection.h +++ b/content_handler/session_connection.h @@ -5,8 +5,6 @@ #ifndef FLUTTER_CONTENT_HANDLER_SESSION_CONNECTION_H_ #define FLUTTER_CONTENT_HANDLER_SESSION_CONNECTION_H_ -#include "lib/ui/scenic/client/resources.h" -#include "lib/ui/scenic/client/session.h" #include "flutter/common/threads.h" #include "flutter/content_handler/vulkan_surface_producer.h" #include "flutter/flow/compositor_context.h" @@ -14,6 +12,8 @@ #include "lib/fidl/cpp/bindings/interface_handle.h" #include "lib/fxl/functional/closure.h" #include "lib/fxl/macros.h" +#include "lib/ui/scenic/client/resources.h" +#include "lib/ui/scenic/client/session.h" #include "magenta/system/ulib/mx/include/mx/eventpair.h" namespace flutter_runner { diff --git a/content_handler/vulkan_surface.cc b/content_handler/vulkan_surface.cc index 27e02c2d8817d..6ef98cd4fc529 100644 --- a/content_handler/vulkan_surface.cc +++ b/content_handler/vulkan_surface.cc @@ -76,8 +76,8 @@ GrBackendSemaphore VulkanSurface::GetAcquireSemaphore() const { return gr_semaphore; } -vulkan::VulkanHandle -VulkanSurface::SemaphoreFromEvent(const mx::event &event) const { +vulkan::VulkanHandle VulkanSurface::SemaphoreFromEvent( + const mx::event& event) const { VkResult result; VkSemaphore semaphore; @@ -187,7 +187,7 @@ bool VulkanSurface::AllocateDeviceMemory(sk_sp context, vk_.GetImageMemoryRequirements(backend_context_->fDevice, // vk_image_, // &memory_reqs // - ); + ); uint32_t memory_type = 0; for (; memory_type < 32; memory_type++) { @@ -261,18 +261,19 @@ bool VulkanSurface::SetupSkiaSurface(sk_sp context, .fLevelCount = image_create_info.mipLevels, }; - GrBackendRenderTarget sk_render_target(size.width(), size.height(), 0, 0, image_info); + GrBackendRenderTarget sk_render_target(size.width(), size.height(), 0, 0, + image_info); SkSurfaceProps sk_surface_props( SkSurfaceProps::InitType::kLegacyFontHost_InitType); auto sk_surface = - SkSurface::MakeFromBackendRenderTarget(context.get(), // - sk_render_target, // - kTopLeft_GrSurfaceOrigin, // - nullptr, // - &sk_surface_props // - ); + SkSurface::MakeFromBackendRenderTarget(context.get(), // + sk_render_target, // + kTopLeft_GrSurfaceOrigin, // + nullptr, // + &sk_surface_props // + ); if (!sk_surface || sk_surface->getCanvas() == nullptr) { return false; @@ -289,7 +290,7 @@ bool VulkanSurface::PushSessionImageSetupOps(scenic_lib::Session* session, } scenic_lib::Memory memory(session, std::move(exported_vmo), - scenic::MemoryType::VK_DEVICE_MEMORY); + scenic::MemoryType::VK_DEVICE_MEMORY); auto image_info = scenic::ImageInfo::New(); image_info->width = sk_surface_->width(); diff --git a/content_handler/vulkan_surface.h b/content_handler/vulkan_surface.h index 10a6b692a23fe..453fb58f91cf2 100644 --- a/content_handler/vulkan_surface.h +++ b/content_handler/vulkan_surface.h @@ -5,13 +5,13 @@ #pragma once #include -#include "lib/ui/scenic/client/resources.h" #include "flutter/flow/scene_update_context.h" #include "flutter/vulkan/vulkan_handle.h" #include "flutter/vulkan/vulkan_proc_table.h" -#include "lib/fxl/macros.h" #include "lib/fsl/tasks/message_loop.h" #include "lib/fsl/tasks/message_loop_handler.h" +#include "lib/fxl/macros.h" +#include "lib/ui/scenic/client/resources.h" #include "mx/event.h" #include "mx/vmo.h" #include "third_party/skia/include/core/SkSurface.h" @@ -55,7 +55,7 @@ class VulkanSurface : public flow::SceneUpdateContext::SurfaceProducerSurface, // VkSemaphore) GrBackendSemaphore GetAcquireSemaphore() const; -private: + private: vulkan::VulkanProcTable& vk_; sk_sp backend_context_; scenic_lib::Session* session_; @@ -92,8 +92,8 @@ class VulkanSurface : public flow::SceneUpdateContext::SurfaceProducerSurface, void Reset(); - vulkan::VulkanHandle - SemaphoreFromEvent(const mx::event &event) const; + vulkan::VulkanHandle SemaphoreFromEvent( + const mx::event& event) const; FXL_DISALLOW_COPY_AND_ASSIGN(VulkanSurface); }; diff --git a/content_handler/vulkan_surface_pool.cc b/content_handler/vulkan_surface_pool.cc index 81bdd4104b095..a96eac2f782e4 100644 --- a/content_handler/vulkan_surface_pool.cc +++ b/content_handler/vulkan_surface_pool.cc @@ -168,7 +168,7 @@ void VulkanSurfacePool::TraceStats() { "SkiaCacheResources", skia_resources, // "SkiaCacheBytes", skia_bytes, // "SkiaCachePurgeable", skia_cache_purgeable // - ); + ); // Reset per present/frame stats. trace_surfaces_created_ = 0; diff --git a/content_handler/vulkan_surface_producer.cc b/content_handler/vulkan_surface_producer.cc index b9da6ce33a120..a0ca3b76fa1ec 100644 --- a/content_handler/vulkan_surface_producer.cc +++ b/content_handler/vulkan_surface_producer.cc @@ -3,13 +3,13 @@ // found in the LICENSE file. #include "flutter/content_handler/vulkan_surface_producer.h" +#include +#include +#include #include "third_party/skia/include/gpu/GrBackendSemaphore.h" #include "third_party/skia/include/gpu/GrContext.h" #include "third_party/skia/include/gpu/vk/GrVkTypes.h" #include "third_party/skia/src/gpu/vk/GrVkUtil.h" -#include -#include -#include namespace flutter_runner { @@ -33,8 +33,7 @@ VulkanSurfaceProducer::~VulkanSurfaceProducer() { FXL_DCHECK(wait_result == VK_SUCCESS); }; -bool VulkanSurfaceProducer::Initialize( - scenic_lib::Session* mozart_session) { +bool VulkanSurfaceProducer::Initialize(scenic_lib::Session* mozart_session) { vk_ = fxl::MakeRefCounted(); std::vector extensions = { @@ -119,11 +118,10 @@ void VulkanSurfaceProducer::OnSurfacesPresented( std::vector< std::unique_ptr> surfaces) { - std::vector semaphores; semaphores.reserve(surfaces.size()); - for (auto &surface : surfaces) { - auto vk_surface = static_cast(surface.get()); + for (auto& surface : surfaces) { + auto vk_surface = static_cast(surface.get()); semaphores.push_back(vk_surface->GetAcquireSemaphore()); } diff --git a/content_handler/vulkan_surface_producer.h b/content_handler/vulkan_surface_producer.h index e238de54fb025..da1e1718071da 100644 --- a/content_handler/vulkan_surface_producer.h +++ b/content_handler/vulkan_surface_producer.h @@ -5,16 +5,16 @@ #ifndef FLUTTER_CONTENT_HANDLER_VULKAN_SURFACE_PRODUCER_H_ #define FLUTTER_CONTENT_HANDLER_VULKAN_SURFACE_PRODUCER_H_ -#include "lib/ui/scenic/client/resources.h" -#include "lib/ui/scenic/client/session.h" #include "flutter/content_handler/vulkan_surface.h" #include "flutter/content_handler/vulkan_surface_pool.h" #include "flutter/flow/scene_update_context.h" #include "flutter/vulkan/vulkan_application.h" #include "flutter/vulkan/vulkan_device.h" #include "flutter/vulkan/vulkan_proc_table.h" -#include "lib/fxl/macros.h" #include "lib/fsl/tasks/message_loop.h" +#include "lib/fxl/macros.h" +#include "lib/ui/scenic/client/resources.h" +#include "lib/ui/scenic/client/session.h" #include "third_party/skia/include/gpu/vk/GrVkBackendContext.h" namespace flutter_runner { diff --git a/flow/export_node.h b/flow/export_node.h index aa8b55f49ee1b..a8ccc7dfdae3c 100644 --- a/flow/export_node.h +++ b/flow/export_node.h @@ -9,14 +9,14 @@ #include -#include "lib/ui/scenic/client/resources.h" -#include "flutter/flow/scene_update_context.h" #include "dart-pkg/zircon/sdk_ext/handle.h" +#include "flutter/flow/scene_update_context.h" #include "lib/fxl/build_config.h" #include "lib/fxl/macros.h" #include "lib/fxl/memory/ref_counted.h" #include "lib/fxl/synchronization/mutex.h" #include "lib/fxl/synchronization/thread_annotations.h" +#include "lib/ui/scenic/client/resources.h" #include "third_party/skia/include/core/SkPoint.h" namespace flow { diff --git a/flow/instrumentation.cc b/flow/instrumentation.cc index 4dcd18aa9fed9..d8952f9ec32a1 100644 --- a/flow/instrumentation.cc +++ b/flow/instrumentation.cc @@ -87,8 +87,7 @@ void Stopwatch::Visualize(SkCanvas& canvas, const SkRect& rect) const { const double sample_margin_unit_width = sample_unit_width / 6.0; const double sample_margin_width = width * sample_margin_unit_width; path.moveTo(x, bottom); - path.lineTo(x, y + - height * (1.0 - UnitHeight(laps_[0].ToMillisecondsF(), + path.lineTo(x, y + height * (1.0 - UnitHeight(laps_[0].ToMillisecondsF(), max_unit_interval))); double unit_x; double unit_next_x = 0.0; @@ -96,16 +95,14 @@ void Stopwatch::Visualize(SkCanvas& canvas, const SkRect& rect) const { unit_x = unit_next_x; unit_next_x = (static_cast(i + 1) / kMaxSamples); const double sample_y = - y + - height * - (1.0 - UnitHeight(laps_[i].ToMillisecondsF(), max_unit_interval)); + y + height * (1.0 - UnitHeight(laps_[i].ToMillisecondsF(), + max_unit_interval)); path.lineTo(x + width * unit_x + sample_margin_width, sample_y); path.lineTo(x + width * unit_next_x - sample_margin_width, sample_y); } path.lineTo( right, - y + - height * (1.0 - UnitHeight(laps_[kMaxSamples - 1].ToMillisecondsF(), + y + height * (1.0 - UnitHeight(laps_[kMaxSamples - 1].ToMillisecondsF(), max_unit_interval))); path.lineTo(right, bottom); path.close(); diff --git a/flow/layers/clip_path_layer.cc b/flow/layers/clip_path_layer.cc index eedebd7cae16c..35afdb9509c78 100644 --- a/flow/layers/clip_path_layer.cc +++ b/flow/layers/clip_path_layer.cc @@ -34,9 +34,9 @@ void ClipPathLayer::UpdateScene(SceneUpdateContext& context) { // Treating the shape as a rectangle for now. auto bounds = clip_path_.getBounds(); scenic_lib::Rectangle shape(context.session(), // session - bounds.width(), // width - bounds.height() // height - ); + bounds.width(), // width + bounds.height() // height + ); SceneUpdateContext::Clip clip(context, shape, bounds); UpdateSceneChildren(context); diff --git a/flow/layers/clip_rect_layer.cc b/flow/layers/clip_rect_layer.cc index e0aa00bf7ff14..20ffd55e37e2c 100644 --- a/flow/layers/clip_rect_layer.cc +++ b/flow/layers/clip_rect_layer.cc @@ -25,9 +25,9 @@ void ClipRectLayer::UpdateScene(SceneUpdateContext& context) { FXL_DCHECK(needs_system_composite()); scenic_lib::Rectangle shape(context.session(), // session - clip_rect_.width(), // width - clip_rect_.height() // height - ); + clip_rect_.width(), // width + clip_rect_.height() // height + ); SceneUpdateContext::Clip clip(context, shape, clip_rect_); UpdateSceneChildren(context); diff --git a/flow/layers/clip_rrect_layer.cc b/flow/layers/clip_rrect_layer.cc index 913af8df19050..6128dc9ab9434 100644 --- a/flow/layers/clip_rrect_layer.cc +++ b/flow/layers/clip_rrect_layer.cc @@ -34,7 +34,7 @@ void ClipRRectLayer::UpdateScene(SceneUpdateContext& context) { clip_rrect_.radii(SkRRect::kLowerRight_Corner) .x(), // bottom_right_radius clip_rrect_.radii(SkRRect::kLowerLeft_Corner).x() // bottom_left_radius - ); + ); SceneUpdateContext::Clip clip(context, shape, clip_rrect_.getBounds()); UpdateSceneChildren(context); diff --git a/flow/layers/layer.h b/flow/layers/layer.h index ffab33ef0ad83..51c2a6672920d 100644 --- a/flow/layers/layer.h +++ b/flow/layers/layer.h @@ -25,9 +25,9 @@ #if defined(OS_FUCHSIA) -#include "lib/ui/scenic/client/resources.h" //nogncheck -#include "lib/ui/scenic/client/session.h" //nogncheck -#include "flutter/flow/scene_update_context.h" //nogncheck +#include "flutter/flow/scene_update_context.h" //nogncheck +#include "lib/ui/scenic/client/resources.h" //nogncheck +#include "lib/ui/scenic/client/session.h" //nogncheck #endif // defined(OS_FUCHSIA) diff --git a/flow/layers/layer_tree.cc b/flow/layers/layer_tree.cc index 000a841d543e9..1596046c64d0b 100644 --- a/flow/layers/layer_tree.cc +++ b/flow/layers/layer_tree.cc @@ -32,7 +32,9 @@ void LayerTree::Preroll(CompositorContext::ScopedFrame& frame, checkerboard_raster_cache_images_); Layer::PrerollContext context = { ignore_raster_cache ? nullptr : &frame.context().raster_cache(), - frame.gr_context(), color_space, SkRect::MakeEmpty(), + frame.gr_context(), + color_space, + SkRect::MakeEmpty(), }; root_layer_->Preroll(&context, SkMatrix::I()); diff --git a/flow/layers/picture_layer.cc b/flow/layers/picture_layer.cc index e2bcb03c44001..05dbbec1654bf 100644 --- a/flow/layers/picture_layer.cc +++ b/flow/layers/picture_layer.cc @@ -46,7 +46,7 @@ void PictureLayer::Paint(PaintContext& context) { raster_cache_result_.destination_rect(), // destination nullptr, // paint SkCanvas::kStrict_SrcRectConstraint // source constraint - ); + ); } else { context.canvas.drawPicture(picture_.get()); } diff --git a/flow/paint_utils.cc b/flow/paint_utils.cc index a64f7aa666841..a9e2bb45cada5 100644 --- a/flow/paint_utils.cc +++ b/flow/paint_utils.cc @@ -12,9 +12,7 @@ namespace flow { namespace { -sk_sp CreateCheckerboardShader(SkColor c1, - SkColor c2, - int size) { +sk_sp CreateCheckerboardShader(SkColor c1, SkColor c2, int size) { SkBitmap bm; bm.allocN32Pixels(2 * size, 2 * size); bm.eraseColor(c1); @@ -26,10 +24,7 @@ sk_sp CreateCheckerboardShader(SkColor c1, } // anonymous namespace -void DrawCheckerboard(SkCanvas* canvas, - SkColor c1, - SkColor c2, - int size) { +void DrawCheckerboard(SkCanvas* canvas, SkColor c1, SkColor c2, int size) { SkPaint paint; paint.setShader(CreateCheckerboardShader(c1, c2, size)); canvas->drawPaint(paint); diff --git a/flow/raster_cache.cc b/flow/raster_cache.cc index 4d0efa286a727..7a89a368d2b6c 100644 --- a/flow/raster_cache.cc +++ b/flow/raster_cache.cc @@ -82,11 +82,11 @@ RasterCacheResult RasterizePicture(SkPicture* picture, SkRect::MakeWH(std::fabs(logical_rect.width() * scale.x()), std::fabs(logical_rect.height() * scale.y())); - const SkImageInfo image_info = - SkImageInfo::MakeN32Premul(std::ceil(physical_rect.width()), // physical width - std::ceil(physical_rect.height()), // physical height - sk_ref_sp(dst_color_space) // colorspace - ); + const SkImageInfo image_info = SkImageInfo::MakeN32Premul( + std::ceil(physical_rect.width()), // physical width + std::ceil(physical_rect.height()), // physical height + sk_ref_sp(dst_color_space) // colorspace + ); sk_sp surface = context diff --git a/flow/raster_cache_unittests.cc b/flow/raster_cache_unittests.cc index b361edf67ec35..cadf168e77f0d 100644 --- a/flow/raster_cache_unittests.cc +++ b/flow/raster_cache_unittests.cc @@ -33,14 +33,14 @@ TEST(RasterCache, ThresholdIsRespected) { sk_sp image; sk_sp srgb = SkColorSpace::MakeSRGB(); - ASSERT_FALSE( - cache.GetPrerolledImage(NULL, picture.get(), matrix, srgb.get(), true, false)); // 1 + ASSERT_FALSE(cache.GetPrerolledImage(NULL, picture.get(), matrix, srgb.get(), + true, false)); // 1 cache.SweepAfterFrame(); - ASSERT_FALSE( - cache.GetPrerolledImage(NULL, picture.get(), matrix, srgb.get(), true, false)); // 2 + ASSERT_FALSE(cache.GetPrerolledImage(NULL, picture.get(), matrix, srgb.get(), + true, false)); // 2 cache.SweepAfterFrame(); - ASSERT_TRUE( - cache.GetPrerolledImage(NULL, picture.get(), matrix, srgb.get(), true, false)); // 3 + ASSERT_TRUE(cache.GetPrerolledImage(NULL, picture.get(), matrix, srgb.get(), + true, false)); // 3 cache.SweepAfterFrame(); } @@ -55,14 +55,14 @@ TEST(RasterCache, ThresholdIsRespectedWhenZero) { sk_sp image; sk_sp srgb = SkColorSpace::MakeSRGB(); - ASSERT_FALSE( - cache.GetPrerolledImage(NULL, picture.get(), matrix, srgb.get(), true, false)); // 1 + ASSERT_FALSE(cache.GetPrerolledImage(NULL, picture.get(), matrix, srgb.get(), + true, false)); // 1 cache.SweepAfterFrame(); - ASSERT_FALSE( - cache.GetPrerolledImage(NULL, picture.get(), matrix, srgb.get(), true, false)); // 2 + ASSERT_FALSE(cache.GetPrerolledImage(NULL, picture.get(), matrix, srgb.get(), + true, false)); // 2 cache.SweepAfterFrame(); - ASSERT_FALSE( - cache.GetPrerolledImage(NULL, picture.get(), matrix, srgb.get(), true, false)); // 3 + ASSERT_FALSE(cache.GetPrerolledImage(NULL, picture.get(), matrix, srgb.get(), + true, false)); // 3 cache.SweepAfterFrame(); } @@ -77,19 +77,19 @@ TEST(RasterCache, SweepsRemoveUnusedFrames) { sk_sp image; sk_sp srgb = SkColorSpace::MakeSRGB(); - ASSERT_FALSE( - cache.GetPrerolledImage(NULL, picture.get(), matrix, srgb.get(), true, false)); // 1 + ASSERT_FALSE(cache.GetPrerolledImage(NULL, picture.get(), matrix, srgb.get(), + true, false)); // 1 cache.SweepAfterFrame(); - ASSERT_FALSE( - cache.GetPrerolledImage(NULL, picture.get(), matrix, srgb.get(), true, false)); // 2 + ASSERT_FALSE(cache.GetPrerolledImage(NULL, picture.get(), matrix, srgb.get(), + true, false)); // 2 cache.SweepAfterFrame(); - ASSERT_TRUE( - cache.GetPrerolledImage(NULL, picture.get(), matrix, srgb.get(), true, false)); // 3 + ASSERT_TRUE(cache.GetPrerolledImage(NULL, picture.get(), matrix, srgb.get(), + true, false)); // 3 cache.SweepAfterFrame(); - ASSERT_TRUE( - cache.GetPrerolledImage(NULL, picture.get(), matrix, srgb.get(), true, false)); // 4 + ASSERT_TRUE(cache.GetPrerolledImage(NULL, picture.get(), matrix, srgb.get(), + true, false)); // 4 cache.SweepAfterFrame(); cache.SweepAfterFrame(); // Extra frame without a preroll image access. - ASSERT_FALSE( - cache.GetPrerolledImage(NULL, picture.get(), matrix, srgb.get(), true, false)); // 5 + ASSERT_FALSE(cache.GetPrerolledImage(NULL, picture.get(), matrix, srgb.get(), + true, false)); // 5 } diff --git a/flow/scene_update_context.cc b/flow/scene_update_context.cc index e26e0a7709b3f..dc42a3bf3e41d 100644 --- a/flow/scene_update_context.cc +++ b/flow/scene_update_context.cc @@ -72,7 +72,7 @@ void SceneUpdateContext::CreateFrame(scenic_lib::EntityNode& entity_node, rrect.radii(SkRRect::kUpperRight_Corner).x(), // top_right_radius rrect.radii(SkRRect::kLowerRight_Corner).x(), // bottom_right_radius rrect.radii(SkRRect::kLowerLeft_Corner).x() // bottom_left_radius - ); + ); scenic_lib::ShapeNode shape_node(session_); shape_node.SetShape(shape); shape_node.SetTranslation(shape_bounds.width() * 0.5f + shape_bounds.left(), @@ -102,7 +102,7 @@ void SceneUpdateContext::CreateFrame(scenic_lib::EntityNode& entity_node, SetShapeColor(shape_node, color); scenic_lib::Rectangle inner_shape(session_, inner_bounds.width(), - inner_bounds.height()); + inner_bounds.height()); scenic_lib::ShapeNode inner_node(session_); inner_node.SetShape(inner_shape); inner_node.SetTranslation(inner_bounds.width() * 0.5f + inner_bounds.left(), @@ -255,12 +255,12 @@ SceneUpdateContext::Transform::Transform(SceneUpdateContext& context, entity_node().SetTranslation(decomposition.translation().x(), // decomposition.translation().y(), // decomposition.translation().z() // - ); + ); entity_node().SetScale(decomposition.scale().x(), // decomposition.scale().y(), // decomposition.scale().z() // - ); + ); context.top_scale_x_ *= decomposition.scale().x(); context.top_scale_y_ *= decomposition.scale().y(); @@ -268,7 +268,7 @@ SceneUpdateContext::Transform::Transform(SceneUpdateContext& context, decomposition.rotation().fData[1], // decomposition.rotation().fData[2], // decomposition.rotation().fData[3] // - ); + ); } } } diff --git a/flow/scene_update_context.h b/flow/scene_update_context.h index 918bfebc47a84..4bba913f52adc 100644 --- a/flow/scene_update_context.h +++ b/flow/scene_update_context.h @@ -8,11 +8,11 @@ #include #include -#include "lib/ui/scenic/client/resources.h" #include "flutter/flow/compositor_context.h" #include "lib/fxl/build_config.h" #include "lib/fxl/logging.h" #include "lib/fxl/macros.h" +#include "lib/ui/scenic/client/resources.h" #include "third_party/skia/include/core/SkRect.h" #include "third_party/skia/include/core/SkSurface.h" @@ -167,12 +167,11 @@ class SceneUpdateContext { const SkRect& paint_bounds, std::vector paint_layers); void SetShapeColor(scenic_lib::ShapeNode& shape_node, SkColor color); - scenic_lib::Image* GenerateImageIfNeeded( - SkColor color, - SkScalar scale_x, - SkScalar scale_y, - const SkRect& paint_bounds, - std::vector paint_layers); + scenic_lib::Image* GenerateImageIfNeeded(SkColor color, + SkScalar scale_x, + SkScalar scale_y, + const SkRect& paint_bounds, + std::vector paint_layers); Entity* top_entity_ = nullptr; float top_scale_x_ = 1.f; diff --git a/fml/platform/android/message_loop_android.cc b/fml/platform/android/message_loop_android.cc index 7a83e22509449..0004d1e0429db 100644 --- a/fml/platform/android/message_loop_android.cc +++ b/fml/platform/android/message_loop_android.cc @@ -51,7 +51,7 @@ MessageLoopAndroid::MessageLoopAndroid() kWakeEvents, // events read_event_fd, // callback this // baton - ); + ); FXL_CHECK(add_result == 1); } @@ -70,7 +70,7 @@ void MessageLoopAndroid::Run() { nullptr, // out fd, nullptr, // out events, nullptr // out data - ); + ); if (result == ALOOPER_POLL_TIMEOUT || result == ALOOPER_POLL_ERROR) { // This handles the case where the loop is terminated using ALooper APIs. running_ = false; diff --git a/fml/platform/linux/paths_linux.cc b/fml/platform/linux/paths_linux.cc index cca676e4d04bf..4a8da450efd9d 100644 --- a/fml/platform/linux/paths_linux.cc +++ b/fml/platform/linux/paths_linux.cc @@ -18,7 +18,8 @@ std::pair GetExecutableDirectoryPath() { if (read_size == -1) { return {false, ""}; } - return {true, files::GetDirectoryName(std::string{path, static_cast(read_size)})}; + return {true, files::GetDirectoryName( + std::string{path, static_cast(read_size)})}; } } // namespace paths diff --git a/fml/thread.cc b/fml/thread.cc index 7043556675383..a1500bfef46a4 100644 --- a/fml/thread.cc +++ b/fml/thread.cc @@ -57,10 +57,10 @@ void Thread::Join() { // a MSDN article: http://msdn2.microsoft.com/en-us/library/xcb2z8hs.aspx const DWORD kVCThreadNameException = 0x406D1388; typedef struct tagTHREADNAME_INFO { - DWORD dwType; // Must be 0x1000. - LPCSTR szName; // Pointer to name (in user addr space). + DWORD dwType; // Must be 0x1000. + LPCSTR szName; // Pointer to name (in user addr space). DWORD dwThreadID; // Thread ID (-1=caller thread). - DWORD dwFlags; // Reserved for future use, must be zero. + DWORD dwFlags; // Reserved for future use, must be zero. } THREADNAME_INFO; #endif @@ -79,9 +79,9 @@ void Thread::SetCurrentThreadName(const std::string& name) { info.dwThreadID = GetCurrentThreadId(); info.dwFlags = 0; __try { - RaiseException(kVCThreadNameException, 0, sizeof(info)/sizeof(DWORD), + RaiseException(kVCThreadNameException, 0, sizeof(info) / sizeof(DWORD), reinterpret_cast(&info)); - } __except(EXCEPTION_CONTINUE_EXECUTION) { + } __except (EXCEPTION_CONTINUE_EXECUTION) { } #else #error Unsupported Platform diff --git a/fml/trace_event.cc b/fml/trace_event.cc index 096b2e6c70e3b..cd30d18527375 100644 --- a/fml/trace_event.cc +++ b/fml/trace_event.cc @@ -17,7 +17,7 @@ void TraceEvent0(TraceArg category_group, TraceArg name) { 0, // argument_count nullptr, // argument_names nullptr // argument_values - ); + ); } void TraceEvent1(TraceArg category_group, @@ -33,7 +33,7 @@ void TraceEvent1(TraceArg category_group, 1, // argument_count arg_names, // argument_names arg_values // argument_values - ); + ); } void TraceEvent2(TraceArg category_group, @@ -51,7 +51,7 @@ void TraceEvent2(TraceArg category_group, 2, // argument_count arg_names, // argument_names arg_values // argument_values - ); + ); } void TraceEventEnd(TraceArg name) { @@ -62,7 +62,7 @@ void TraceEventEnd(TraceArg name) { 0, // argument_count nullptr, // argument_names nullptr // argument_values - ); + ); } void TraceEventAsyncBegin0(TraceArg category_group, @@ -75,7 +75,7 @@ void TraceEventAsyncBegin0(TraceArg category_group, 0, // argument_count nullptr, // argument_names nullptr // argument_values - ); + ); } void TraceEventAsyncEnd0(TraceArg category_group, @@ -88,7 +88,7 @@ void TraceEventAsyncEnd0(TraceArg category_group, 0, // argument_count nullptr, // argument_names nullptr // argument_values - ); + ); } void TraceEventAsyncBegin1(TraceArg category_group, @@ -105,7 +105,7 @@ void TraceEventAsyncBegin1(TraceArg category_group, 1, // argument_count arg_names, // argument_names arg_values // argument_values - ); + ); } void TraceEventAsyncEnd1(TraceArg category_group, @@ -122,7 +122,7 @@ void TraceEventAsyncEnd1(TraceArg category_group, 1, // argument_count arg_names, // argument_names arg_values // argument_values - ); + ); } void TraceEventInstant0(TraceArg category_group, TraceArg name) { @@ -133,7 +133,7 @@ void TraceEventInstant0(TraceArg category_group, TraceArg name) { 0, // argument_count nullptr, // argument_names nullptr // argument_values - ); + ); } void TraceEventFlowBegin0(TraceArg category_group, @@ -146,7 +146,7 @@ void TraceEventFlowBegin0(TraceArg category_group, 0, // argument_count nullptr, // argument_names nullptr // argument_values - ); + ); } void TraceEventFlowStep0(TraceArg category_group, @@ -159,7 +159,7 @@ void TraceEventFlowStep0(TraceArg category_group, 0, // argument_count nullptr, // argument_names nullptr // argument_values - ); + ); } void TraceEventFlowEnd0(TraceArg category_group, TraceArg name, TraceIDArg id) { @@ -170,7 +170,7 @@ void TraceEventFlowEnd0(TraceArg category_group, TraceArg name, TraceIDArg id) { 0, // argument_count nullptr, // argument_names nullptr // argument_values - ); + ); } } // namespace tracing diff --git a/lib/ui/compositing/scene.cc b/lib/ui/compositing/scene.cc index 52d26a897cada..513d89f102e03 100644 --- a/lib/ui/compositing/scene.cc +++ b/lib/ui/compositing/scene.cc @@ -23,10 +23,9 @@ fxl::RefPtr Scene::create(std::unique_ptr rootLayer, uint32_t rasterizerTracingThreshold, bool checkerboardRasterCacheImages, bool checkerboardOffscreenLayers) { - return fxl::MakeRefCounted(std::move(rootLayer), - rasterizerTracingThreshold, - checkerboardRasterCacheImages, - checkerboardOffscreenLayers); + return fxl::MakeRefCounted( + std::move(rootLayer), rasterizerTracingThreshold, + checkerboardRasterCacheImages, checkerboardOffscreenLayers); } Scene::Scene(std::unique_ptr rootLayer, @@ -38,8 +37,7 @@ Scene::Scene(std::unique_ptr rootLayer, m_layerTree->set_rasterizer_tracing_threshold(rasterizerTracingThreshold); m_layerTree->set_checkerboard_raster_cache_images( checkerboardRasterCacheImages); - m_layerTree->set_checkerboard_offscreen_layers( - checkerboardOffscreenLayers); + m_layerTree->set_checkerboard_offscreen_layers(checkerboardOffscreenLayers); } Scene::~Scene() {} diff --git a/lib/ui/compositing/scene_builder.cc b/lib/ui/compositing/scene_builder.cc index cf9087d0e4997..d1e3e61cc03fc 100644 --- a/lib/ui/compositing/scene_builder.cc +++ b/lib/ui/compositing/scene_builder.cc @@ -16,9 +16,9 @@ #include "flutter/flow/layers/picture_layer.h" #include "flutter/flow/layers/shader_mask_layer.h" #include "flutter/flow/layers/transform_layer.h" -#include "flutter/lib/ui/ui_dart_state.h" #include "flutter/lib/ui/painting/matrix.h" #include "flutter/lib/ui/painting/shader.h" +#include "flutter/lib/ui/ui_dart_state.h" #include "flutter/lib/ui/window/window.h" #include "lib/fxl/build_config.h" #include "lib/tonic/converter/dart_converter.h" @@ -165,7 +165,8 @@ void SceneBuilder::pushPhysicalModel(const RRect& rrect, if (!cullRect.intersect(rrect.sk_rrect.rect(), m_cullRects.top())) cullRect = SkRect::MakeEmpty(); - SkScalar dpr = UIDartState::Current()->window()->viewport_metrics().device_pixel_ratio; + SkScalar dpr = + UIDartState::Current()->window()->viewport_metrics().device_pixel_ratio; std::unique_ptr layer( new flow::PhysicalModelLayer()); diff --git a/lib/ui/dart_runtime_hooks.cc b/lib/ui/dart_runtime_hooks.cc index d0b3742e2335c..15663132c296d 100644 --- a/lib/ui/dart_runtime_hooks.cc +++ b/lib/ui/dart_runtime_hooks.cc @@ -27,8 +27,8 @@ #include #elif defined(OS_IOS) extern "C" { - // Cannot import the syslog.h header directly because of macro collision. - extern void syslog(int, const char*, ...); +// Cannot import the syslog.h header directly because of macro collision. +extern void syslog(int, const char*, ...); } #endif diff --git a/lib/ui/dart_runtime_hooks.h b/lib/ui/dart_runtime_hooks.h index 2f88ef901a409..506665c1e6f12 100644 --- a/lib/ui/dart_runtime_hooks.h +++ b/lib/ui/dart_runtime_hooks.h @@ -6,8 +6,8 @@ #define FLUTTER_LIB_UI_DART_RUNTIME_HOOKS_H_ #include "dart/runtime/include/dart_api.h" -#include "lib/tonic/dart_library_natives.h" #include "lib/fxl/macros.h" +#include "lib/tonic/dart_library_natives.h" namespace blink { diff --git a/lib/ui/painting/canvas.cc b/lib/ui/painting/canvas.cc index 8dec3a7cf69c1..929e545715488 100644 --- a/lib/ui/painting/canvas.cc +++ b/lib/ui/painting/canvas.cc @@ -7,9 +7,9 @@ #include #include "flutter/flow/layers/physical_model_layer.h" -#include "flutter/lib/ui/ui_dart_state.h" #include "flutter/lib/ui/painting/image.h" #include "flutter/lib/ui/painting/matrix.h" +#include "flutter/lib/ui/ui_dart_state.h" #include "flutter/lib/ui/window/window.h" #include "lib/tonic/converter/dart_converter.h" #include "lib/tonic/dart_args.h" @@ -75,8 +75,9 @@ fxl::RefPtr Canvas::Create(PictureRecorder* recorder, double right, double bottom) { if (!recorder) - Dart_ThrowException(ToDart("Canvas constructor called with non-genuine PictureRecorder.")); - FXL_DCHECK(!recorder->isRecording()); // verified by Dart code + Dart_ThrowException( + ToDart("Canvas constructor called with non-genuine PictureRecorder.")); + FXL_DCHECK(!recorder->isRecording()); // verified by Dart code fxl::RefPtr canvas = fxl::MakeRefCounted( recorder->BeginRecording(SkRect::MakeLTRB(left, top, right, bottom))); recorder->set_canvas(canvas); @@ -157,7 +158,8 @@ void Canvas::transform(const tonic::Float64List& matrix4) { void Canvas::clipRect(double left, double top, double right, double bottom) { if (!canvas_) return; - canvas_->clipRect(SkRect::MakeLTRB(left, top, right, bottom), SkClipOp::kIntersect, true); + canvas_->clipRect(SkRect::MakeLTRB(left, top, right, bottom), + SkClipOp::kIntersect, true); } void Canvas::clipRRect(const RRect& rrect) { @@ -170,7 +172,8 @@ void Canvas::clipPath(const CanvasPath* path) { if (!canvas_) return; if (!path) - Dart_ThrowException(ToDart("Canvas.clipPath called with non-genuine Path.")); + Dart_ThrowException( + ToDart("Canvas.clipPath called with non-genuine Path.")); canvas_->clipPath(path->path(), true); } @@ -268,7 +271,8 @@ void Canvas::drawPath(const CanvasPath* path, if (!canvas_) return; if (!path) - Dart_ThrowException(ToDart("Canvas.drawPath called with non-genuine Path.")); + Dart_ThrowException( + ToDart("Canvas.drawPath called with non-genuine Path.")); canvas_->drawPath(path->path(), *paint.paint()); } @@ -280,7 +284,8 @@ void Canvas::drawImage(const CanvasImage* image, if (!canvas_) return; if (!image) - Dart_ThrowException(ToDart("Canvas.drawImage called with non-genuine Image.")); + Dart_ThrowException( + ToDart("Canvas.drawImage called with non-genuine Image.")); canvas_->drawImage(image->image(), x, y, paint.paint()); } @@ -298,7 +303,8 @@ void Canvas::drawImageRect(const CanvasImage* image, if (!canvas_) return; if (!image) - Dart_ThrowException(ToDart("Canvas.drawImageRect called with non-genuine Image.")); + Dart_ThrowException( + ToDart("Canvas.drawImageRect called with non-genuine Image.")); SkRect src = SkRect::MakeLTRB(src_left, src_top, src_right, src_bottom); SkRect dst = SkRect::MakeLTRB(dst_left, dst_top, dst_right, dst_bottom); canvas_->drawImageRect(image->image(), src, dst, paint.paint(), @@ -319,7 +325,8 @@ void Canvas::drawImageNine(const CanvasImage* image, if (!canvas_) return; if (!image) - Dart_ThrowException(ToDart("Canvas.drawImageNine called with non-genuine Image.")); + Dart_ThrowException( + ToDart("Canvas.drawImageNine called with non-genuine Image.")); SkRect center = SkRect::MakeLTRB(center_left, center_top, center_right, center_bottom); SkIRect icenter; @@ -332,7 +339,8 @@ void Canvas::drawPicture(Picture* picture) { if (!canvas_) return; if (!picture) - Dart_ThrowException(ToDart("Canvas.drawPicture called with non-genuine Picture.")); + Dart_ThrowException( + ToDart("Canvas.drawPicture called with non-genuine Picture.")); canvas_->drawPicture(picture->picture().get()); } @@ -359,11 +367,10 @@ void Canvas::drawVertices(const Vertices* vertices, if (!canvas_) return; if (!vertices) - Dart_ThrowException(ToDart("Canvas.drawVertices called with non-genuine Vertices.")); + Dart_ThrowException( + ToDart("Canvas.drawVertices called with non-genuine Vertices.")); - canvas_->drawVertices(vertices->vertices(), - blend_mode, - *paint.paint()); + canvas_->drawVertices(vertices->vertices(), blend_mode, *paint.paint()); } void Canvas::drawAtlas(const Paint& paint, @@ -377,7 +384,9 @@ void Canvas::drawAtlas(const Paint& paint, if (!canvas_) return; if (!atlas) - Dart_ThrowException(ToDart("Canvas.drawAtlas or Canvas.drawRawAtlas called with non-genuine Image.")); + Dart_ThrowException( + ToDart("Canvas.drawAtlas or Canvas.drawRawAtlas called with " + "non-genuine Image.")); sk_sp skImage = atlas->image(); @@ -400,14 +409,12 @@ void Canvas::drawShadow(const CanvasPath* path, double elevation, bool transparentOccluder) { if (!path) - Dart_ThrowException(ToDart("Canvas.drawShader called with non-genuine Path.")); - SkScalar dpr = UIDartState::Current()->window()->viewport_metrics().device_pixel_ratio; - flow::PhysicalModelLayer::DrawShadow(canvas_, - path->path(), - color, - elevation, - transparentOccluder, - dpr); + Dart_ThrowException( + ToDart("Canvas.drawShader called with non-genuine Path.")); + SkScalar dpr = + UIDartState::Current()->window()->viewport_metrics().device_pixel_ratio; + flow::PhysicalModelLayer::DrawShadow(canvas_, path->path(), color, elevation, + transparentOccluder, dpr); } void Canvas::Clear() { diff --git a/lib/ui/painting/gradient.cc b/lib/ui/painting/gradient.cc index e0d19584ea56d..7035df81b6804 100644 --- a/lib/ui/painting/gradient.cc +++ b/lib/ui/painting/gradient.cc @@ -4,9 +4,9 @@ #include "flutter/lib/ui/painting/gradient.h" +#include "lib/tonic/converter/dart_converter.h" #include "lib/tonic/dart_args.h" #include "lib/tonic/dart_binding_macros.h" -#include "lib/tonic/converter/dart_converter.h" #include "lib/tonic/dart_library_natives.h" namespace blink { diff --git a/lib/ui/painting/gradient.h b/lib/ui/painting/gradient.h index 063b10673d780..788552cee1f8a 100644 --- a/lib/ui/painting/gradient.h +++ b/lib/ui/painting/gradient.h @@ -13,7 +13,7 @@ namespace tonic { class DartLibraryNatives; -} // namspace tonic +} // namespace tonic namespace blink { diff --git a/lib/ui/painting/image.cc b/lib/ui/painting/image.cc index f94f3ff216040..0b0127cd0c6c2 100644 --- a/lib/ui/painting/image.cc +++ b/lib/ui/painting/image.cc @@ -6,9 +6,9 @@ #include "flutter/common/threads.h" #include "flutter/lib/ui/painting/utils.h" +#include "lib/tonic/converter/dart_converter.h" #include "lib/tonic/dart_args.h" #include "lib/tonic/dart_binding_macros.h" -#include "lib/tonic/converter/dart_converter.h" #include "lib/tonic/dart_library_natives.h" namespace blink { diff --git a/lib/ui/painting/image_filter.cc b/lib/ui/painting/image_filter.cc index 13bb0c9be39a9..4965b6aa0385d 100644 --- a/lib/ui/painting/image_filter.cc +++ b/lib/ui/painting/image_filter.cc @@ -4,9 +4,9 @@ #include "flutter/lib/ui/painting/image_filter.h" +#include "lib/tonic/converter/dart_converter.h" #include "lib/tonic/dart_args.h" #include "lib/tonic/dart_binding_macros.h" -#include "lib/tonic/converter/dart_converter.h" #include "lib/tonic/dart_library_natives.h" #include "third_party/skia/include/effects/SkBlurImageFilter.h" #include "third_party/skia/include/effects/SkImageSource.h" diff --git a/lib/ui/painting/image_shader.cc b/lib/ui/painting/image_shader.cc index 504c1471f7a46..0fabd134c737c 100644 --- a/lib/ui/painting/image_shader.cc +++ b/lib/ui/painting/image_shader.cc @@ -4,9 +4,9 @@ #include "flutter/lib/ui/painting/image_shader.h" +#include "lib/tonic/converter/dart_converter.h" #include "lib/tonic/dart_args.h" #include "lib/tonic/dart_binding_macros.h" -#include "lib/tonic/converter/dart_converter.h" #include "lib/tonic/dart_library_natives.h" using tonic::ToDart; @@ -38,7 +38,8 @@ void ImageShader::initWithImage(CanvasImage* image, SkShader::TileMode tmy, const tonic::Float64List& matrix4) { if (!image) - Dart_ThrowException(ToDart("ImageShader constructor called with non-genuine Image.")); + Dart_ThrowException( + ToDart("ImageShader constructor called with non-genuine Image.")); SkMatrix sk_matrix = ToSkMatrix(matrix4); set_shader(image->image()->makeShader(tmx, tmy, &sk_matrix)); } diff --git a/lib/ui/painting/mask_filter.cc b/lib/ui/painting/mask_filter.cc index 13d43fb1844bb..1dfcfaa4a146a 100644 --- a/lib/ui/painting/mask_filter.cc +++ b/lib/ui/painting/mask_filter.cc @@ -6,9 +6,9 @@ #include "flutter/common/threads.h" #include "flutter/lib/ui/painting/utils.h" +#include "lib/tonic/converter/dart_converter.h" #include "lib/tonic/dart_args.h" #include "lib/tonic/dart_binding_macros.h" -#include "lib/tonic/converter/dart_converter.h" #include "lib/tonic/dart_library_natives.h" #include "third_party/skia/include/effects/SkBlurMaskFilter.h" diff --git a/lib/ui/painting/paint.cc b/lib/ui/painting/paint.cc index 430e6ffafc98b..81506956831df 100644 --- a/lib/ui/painting/paint.cc +++ b/lib/ui/painting/paint.cc @@ -97,8 +97,7 @@ Paint DartConverter::FromArguments(Dart_NativeArguments args, uint32_t encoded_blend_mode = uint_data[kBlendModeIndex]; if (encoded_blend_mode) { - uint32_t blend_mode = - encoded_blend_mode ^ kBlendModeDefault; + uint32_t blend_mode = encoded_blend_mode ^ kBlendModeDefault; paint.setBlendMode(static_cast(blend_mode)); } diff --git a/lib/ui/painting/path.cc b/lib/ui/painting/path.cc index 0353ebeb8d0cf..e4638e7439f2c 100644 --- a/lib/ui/painting/path.cc +++ b/lib/ui/painting/path.cc @@ -7,9 +7,9 @@ #include #include "flutter/lib/ui/painting/matrix.h" +#include "lib/tonic/converter/dart_converter.h" #include "lib/tonic/dart_args.h" #include "lib/tonic/dart_binding_macros.h" -#include "lib/tonic/converter/dart_converter.h" #include "lib/tonic/dart_library_natives.h" using tonic::ToDart; @@ -174,7 +174,8 @@ void CanvasPath::addPath(CanvasPath* path, double dx, double dy) { void CanvasPath::extendWithPath(CanvasPath* path, double dx, double dy) { if (!path) - Dart_ThrowException(ToDart("Path.extendWithPath called with non-genuine Path.")); + Dart_ThrowException( + ToDart("Path.extendWithPath called with non-genuine Path.")); path_.addPath(path->path(), dx, dy, SkPath::kExtend_AddPathMode); } @@ -196,8 +197,7 @@ fxl::RefPtr CanvasPath::shift(double dx, double dy) { return path; } -fxl::RefPtr CanvasPath::transform( - tonic::Float64List& matrix4) { +fxl::RefPtr CanvasPath::transform(tonic::Float64List& matrix4) { fxl::RefPtr path = CanvasPath::Create(); path_.transform(ToSkMatrix(matrix4), &path->path_); matrix4.Release(); diff --git a/lib/ui/painting/picture.cc b/lib/ui/painting/picture.cc index ace8387c2f919..89ddbf5f41bb0 100644 --- a/lib/ui/painting/picture.cc +++ b/lib/ui/painting/picture.cc @@ -38,9 +38,9 @@ Picture::~Picture() { fxl::RefPtr Picture::toImage(int width, int height) { fxl::RefPtr image = CanvasImage::Create(); // TODO(abarth): We should pass in an SkColorSpace at some point. - image->set_image( - SkImage::MakeFromPicture(picture_, SkISize::Make(width, height), nullptr, - nullptr, SkImage::BitDepth::kU8, SkColorSpace::MakeSRGB())); + image->set_image(SkImage::MakeFromPicture( + picture_, SkISize::Make(width, height), nullptr, nullptr, + SkImage::BitDepth::kU8, SkColorSpace::MakeSRGB())); return image; } diff --git a/lib/ui/painting/picture_recorder.cc b/lib/ui/painting/picture_recorder.cc index f3365e283cb66..adc73c0043920 100644 --- a/lib/ui/painting/picture_recorder.cc +++ b/lib/ui/painting/picture_recorder.cc @@ -6,9 +6,9 @@ #include "flutter/lib/ui/painting/canvas.h" #include "flutter/lib/ui/painting/picture.h" +#include "lib/tonic/converter/dart_converter.h" #include "lib/tonic/dart_args.h" #include "lib/tonic/dart_binding_macros.h" -#include "lib/tonic/converter/dart_converter.h" #include "lib/tonic/dart_library_natives.h" namespace blink { diff --git a/lib/ui/painting/rrect.cc b/lib/ui/painting/rrect.cc index cfc1d0daaef38..3f1b6134ca3f5 100644 --- a/lib/ui/painting/rrect.cc +++ b/lib/ui/painting/rrect.cc @@ -4,8 +4,8 @@ #include "flutter/lib/ui/painting/rrect.h" -#include "lib/tonic/logging/dart_error.h" #include "lib/fxl/logging.h" +#include "lib/tonic/logging/dart_error.h" #include "lib/tonic/typed_data/float32_list.h" using namespace blink; diff --git a/lib/ui/painting/shader.cc b/lib/ui/painting/shader.cc index a36238fd49c82..d7d8ccf20aefc 100644 --- a/lib/ui/painting/shader.cc +++ b/lib/ui/painting/shader.cc @@ -11,8 +11,7 @@ namespace blink { IMPLEMENT_WRAPPERTYPEINFO(ui, Shader); -Shader::Shader(sk_sp shader) : shader_(shader) { -} +Shader::Shader(sk_sp shader) : shader_(shader) {} Shader::~Shader() { // Skia objects must be deleted on the IO thread so that any associated GL @@ -20,4 +19,4 @@ Shader::~Shader() { SkiaUnrefOnIOThread(&shader_); } -} // namespace blink +} // namespace blink diff --git a/lib/ui/painting/utils.cc b/lib/ui/painting/utils.cc index 3666d0b98861b..6bffd565d7c3e 100644 --- a/lib/ui/painting/utils.cc +++ b/lib/ui/painting/utils.cc @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "flutter/common/threads.h" #include "flutter/lib/ui/painting/utils.h" +#include "flutter/common/threads.h" namespace blink { @@ -13,8 +13,7 @@ constexpr fxl::TimeDelta kDrainDelay = fxl::TimeDelta::FromMilliseconds(250); } // anonymous namespace -SkiaUnrefQueue::SkiaUnrefQueue() - : drain_pending_(false) {} +SkiaUnrefQueue::SkiaUnrefQueue() : drain_pending_(false) {} SkiaUnrefQueue SkiaUnrefQueue::instance_; @@ -27,8 +26,7 @@ void SkiaUnrefQueue::Unref(SkRefCnt* object) { objects_.push_back(object); if (!drain_pending_) { drain_pending_ = true; - Threads::IO()->PostDelayedTask([this] { Drain(); }, - kDrainDelay); + Threads::IO()->PostDelayedTask([this] { Drain(); }, kDrainDelay); } } diff --git a/lib/ui/painting/utils.h b/lib/ui/painting/utils.h index 0aab9b7e4c70e..6802171337b3c 100644 --- a/lib/ui/painting/utils.h +++ b/lib/ui/painting/utils.h @@ -27,11 +27,12 @@ class SkiaUnrefQueue { bool drain_pending_ FXL_GUARDED_BY(mutex_); }; -template void SkiaUnrefOnIOThread(sk_sp* sp) { +template +void SkiaUnrefOnIOThread(sk_sp* sp) { T* object = sp->release(); if (object) { SkiaUnrefQueue::Get().Unref(object); } } -} // namespace blink +} // namespace blink diff --git a/lib/ui/painting/vertices.cc b/lib/ui/painting/vertices.cc index 65ba1372684b3..0ad5144251daf 100644 --- a/lib/ui/painting/vertices.cc +++ b/lib/ui/painting/vertices.cc @@ -11,14 +11,13 @@ namespace blink { namespace { -void DecodePoints(const tonic::Float32List& coords, - SkPoint* points) { +void DecodePoints(const tonic::Float32List& coords, SkPoint* points) { for (int i = 0; i < coords.num_elements(); i += 2) points[i / 2] = SkPoint::Make(coords[i], coords[i + 1]); } -template void DecodeInts(const tonic::Int32List& ints, - T* out) { +template +void DecodeInts(const tonic::Int32List& ints, T* out) { for (int i = 0; i < ints.num_elements(); i++) out[i] = ints[i]; } @@ -31,8 +30,7 @@ static void Vertices_constructor(Dart_NativeArguments args) { IMPLEMENT_WRAPPERTYPEINFO(ui, Vertices); -#define FOR_EACH_BINDING(V) \ - V(Vertices, init) +#define FOR_EACH_BINDING(V) V(Vertices, init) FOR_EACH_BINDING(DART_NATIVE_CALLBACK) @@ -60,10 +58,8 @@ void Vertices::init(SkVertices::VertexMode vertex_mode, if (colors.data()) builderFlags |= SkVertices::kHasColors_BuilderFlag; - SkVertices::Builder builder(vertex_mode, - positions.num_elements() / 2, - indices.num_elements(), - builderFlags); + SkVertices::Builder builder(vertex_mode, positions.num_elements() / 2, + indices.num_elements(), builderFlags); if (positions.data()) DecodePoints(positions, builder.positions()); if (texture_coordinates.data()) diff --git a/lib/ui/painting/vertices.h b/lib/ui/painting/vertices.h index f3deda7ef742e..47b5133ae6a8c 100644 --- a/lib/ui/painting/vertices.h +++ b/lib/ui/painting/vertices.h @@ -28,12 +28,11 @@ class Vertices : public fxl::RefCountedThreadSafe, static fxl::RefPtr Create(); - void init( - SkVertices::VertexMode vertex_mode, - const tonic::Float32List& positions, - const tonic::Float32List& texture_coordinates, - const tonic::Int32List& colors, - const tonic::Int32List& indices); + void init(SkVertices::VertexMode vertex_mode, + const tonic::Float32List& positions, + const tonic::Float32List& texture_coordinates, + const tonic::Int32List& colors, + const tonic::Int32List& indices); const sk_sp& vertices() const { return vertices_; } diff --git a/lib/ui/text/font_collection.cc b/lib/ui/text/font_collection.cc index c12284d7d9eb3..cab0796786299 100644 --- a/lib/ui/text/font_collection.cc +++ b/lib/ui/text/font_collection.cc @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include #include "flutter/lib/ui/text/font_collection.h" +#include #include "third_party/rapidjson/rapidjson/document.h" #include "third_party/rapidjson/rapidjson/rapidjson.h" #include "third_party/skia/include/core/SkStream.h" diff --git a/lib/ui/text/paragraph_builder.cc b/lib/ui/text/paragraph_builder.cc index ec50efb0776f5..a86fcc931599a 100644 --- a/lib/ui/text/paragraph_builder.cc +++ b/lib/ui/text/paragraph_builder.cc @@ -111,7 +111,7 @@ PassRefPtr decodeParagraphStyle(RenderStyle* parentStyle, double fontSize, double lineHeight, const std::u16string& ellipsis) { - FXL_DCHECK(encoded.num_elements() == 6); // also update text.dart + FXL_DCHECK(encoded.num_elements() == 6); // also update text.dart RefPtr style = RenderStyle::create(); style->inheritFrom(parentStyle); @@ -123,7 +123,8 @@ PassRefPtr decodeParagraphStyle(RenderStyle* parentStyle, style->setTextAlign(static_cast(encoded[psTextAlignIndex])); if (mask & psTextDirectionMask) - style->setDirection(static_cast(encoded[psTextDirectionIndex])); + style->setDirection( + static_cast(encoded[psTextDirectionIndex])); if (mask & (psFontWeightMask | psFontStyleMask | psFontFamilyMask | psFontSizeMask)) { diff --git a/lib/ui/ui_dart_state.cc b/lib/ui/ui_dart_state.cc index ac38e9ff0c015..da1821641ebd6 100644 --- a/lib/ui/ui_dart_state.cc +++ b/lib/ui/ui_dart_state.cc @@ -49,7 +49,6 @@ void UIDartState::set_font_selector(PassRefPtr selector) { font_selector_ = selector; } - PassRefPtr UIDartState::font_selector() { return font_selector_; } diff --git a/lib/ui/window/platform_message_response_dart.cc b/lib/ui/window/platform_message_response_dart.cc index 1e80040cccd10..adddae83653d7 100644 --- a/lib/ui/window/platform_message_response_dart.cc +++ b/lib/ui/window/platform_message_response_dart.cc @@ -38,8 +38,8 @@ void PlatformMessageResponseDart::Complete(std::vector data) { return; tonic::DartState::Scope scope(dart_state); - Dart_Handle byte_buffer = Dart_NewTypedData( - Dart_TypedData_kByteData, data.size()); + Dart_Handle byte_buffer = + Dart_NewTypedData(Dart_TypedData_kByteData, data.size()); DART_CHECK_VALID(byte_buffer); void* buffer; @@ -60,8 +60,8 @@ void PlatformMessageResponseDart::CompleteEmpty() { return; FXL_DCHECK(!is_complete_); is_complete_ = true; - Threads::UI()->PostTask(fxl::MakeCopyable( - [ callback = std::move(callback_) ]() mutable { + Threads::UI()->PostTask( + fxl::MakeCopyable([callback = std::move(callback_)]() mutable { tonic::DartState* dart_state = callback.dart_state().get(); if (!dart_state) return; diff --git a/lib/ui/window/window.cc b/lib/ui/window/window.cc index a156c4764e460..54ac6895590ed 100644 --- a/lib/ui/window/window.cc +++ b/lib/ui/window/window.cc @@ -40,7 +40,8 @@ Dart_Handle ToByteData(const std::vector& buffer) { } void DefaultRouteName(Dart_NativeArguments args) { - std::string routeName = UIDartState::Current()->window()->client()->DefaultRouteName(); + std::string routeName = + UIDartState::Current()->window()->client()->DefaultRouteName(); Dart_SetReturnValue(args, StdStringToDart(routeName)); } @@ -155,11 +156,11 @@ void Window::UpdateLocale(const std::string& language_code, return; tonic::DartState::Scope scope(dart_state); - DartInvokeField( - library_.value(), "_updateLocale", - { - StdStringToDart(language_code), StdStringToDart(country_code), - }); + DartInvokeField(library_.value(), "_updateLocale", + { + StdStringToDart(language_code), + StdStringToDart(country_code), + }); } void Window::UpdateSemanticsEnabled(bool enabled) { @@ -177,9 +178,8 @@ void Window::DispatchPlatformMessage(fxl::RefPtr message) { if (!dart_state) return; tonic::DartState::Scope scope(dart_state); - Dart_Handle data_handle = (message->hasData()) - ? ToByteData(message->data()) - : Dart_Null(); + Dart_Handle data_handle = + (message->hasData()) ? ToByteData(message->data()) : Dart_Null(); if (Dart_IsError(data_handle)) return; diff --git a/runtime/dart_controller.cc b/runtime/dart_controller.cc index 1452f9ebba8a2..517ad70bec1f3 100644 --- a/runtime/dart_controller.cc +++ b/runtime/dart_controller.cc @@ -44,8 +44,8 @@ std::string ResolvePath(std::string path) { } // namespace -DartController::DartController() : ui_dart_state_(nullptr), - platform_kernel_bytes(nullptr) {} +DartController::DartController() + : ui_dart_state_(nullptr), platform_kernel_bytes(nullptr) {} DartController::~DartController() { if (ui_dart_state_) { @@ -99,11 +99,11 @@ bool DartController::SendStartMessage(Dart_Handle root_library) { } static void CopyVectorBytes(const std::vector& vector, - uint8_t*& bytes) { + uint8_t*& bytes) { if (bytes) { free(bytes); } - bytes = (uint8_t*) malloc(vector.size()); + bytes = (uint8_t*)malloc(vector.size()); memcpy(bytes, vector.data(), vector.size()); } @@ -115,8 +115,8 @@ tonic::DartErrorHandleType DartController::RunFromKernel( uint8_t* kernel_bytes = nullptr; CopyVectorBytes(kernel, kernel_bytes); - Dart_Handle result = Dart_LoadKernel( - Dart_ReadKernelBinary(kernel_bytes, kernel.size())); + Dart_Handle result = + Dart_LoadKernel(Dart_ReadKernelBinary(kernel_bytes, kernel.size())); LogIfError(result); tonic::DartErrorHandleType error = tonic::GetErrorHandleType(result); if (SendStartMessage(Dart_RootLibrary())) { @@ -136,7 +136,8 @@ tonic::DartErrorHandleType DartController::RunFromPrecompiledSnapshot() { } tonic::DartErrorHandleType DartController::RunFromScriptSnapshot( - const uint8_t* buffer, size_t size) { + const uint8_t* buffer, + size_t size) { tonic::DartState::Scope scope(dart_state()); Dart_Handle result = Dart_LoadScriptFromSnapshot(buffer, size); LogIfError(result); @@ -148,7 +149,8 @@ tonic::DartErrorHandleType DartController::RunFromScriptSnapshot( } tonic::DartErrorHandleType DartController::RunFromSource( - const std::string& main, const std::string& packages) { + const std::string& main, + const std::string& packages) { tonic::DartState::Scope scope(dart_state()); tonic::FileLoader& loader = dart_state()->file_loader(); if (!packages.empty() && !loader.LoadPackagesMap(ResolvePath(packages))) @@ -162,11 +164,12 @@ tonic::DartErrorHandleType DartController::RunFromSource( return error; } -void DartController::CreateIsolateFor(const std::string& script_uri, - const uint8_t* isolate_snapshot_data, - const uint8_t* isolate_snapshot_instr, - const std::vector& platform_kernel, - std::unique_ptr state) { +void DartController::CreateIsolateFor( + const std::string& script_uri, + const uint8_t* isolate_snapshot_data, + const uint8_t* isolate_snapshot_instr, + const std::vector& platform_kernel, + std::unique_ptr state) { char* error = nullptr; Dart_Isolate isolate; @@ -179,13 +182,13 @@ void DartController::CreateIsolateFor(const std::string& script_uri, isolate = Dart_CreateIsolateFromKernel( script_uri.c_str(), "main", Dart_ReadKernelBinary(platform_kernel_bytes, platform_kernel.size()), - nullptr /* flags */, - static_cast(state.get()), &error); + nullptr /* flags */, static_cast(state.get()), + &error); } else { - isolate = Dart_CreateIsolate( - script_uri.c_str(), "main", isolate_snapshot_data, - isolate_snapshot_instr, nullptr, - static_cast(state.get()), &error); + isolate = + Dart_CreateIsolate(script_uri.c_str(), "main", isolate_snapshot_data, + isolate_snapshot_instr, nullptr, + static_cast(state.get()), &error); } FXL_CHECK(isolate) << error; ui_dart_state_ = state.release(); diff --git a/runtime/dart_controller.h b/runtime/dart_controller.h index 16c0ace08d458..0145d30089439 100644 --- a/runtime/dart_controller.h +++ b/runtime/dart_controller.h @@ -49,6 +49,6 @@ class DartController { FXL_DISALLOW_COPY_AND_ASSIGN(DartController); }; -} +} // namespace blink #endif // FLUTTER_RUNTIME_DART_CONTROLLER_H_ diff --git a/runtime/dart_init.cc b/runtime/dart_init.cc index f11d78e5b2ded..e458e1210bf5a 100644 --- a/runtime/dart_init.cc +++ b/runtime/dart_init.cc @@ -73,11 +73,8 @@ namespace { // Arguments passed to the Dart VM in all configurations. static const char* kDartLanguageArgs[] = { - "--enable_mirrors=false", - "--background_compilation", - "--await_is_keyword", - "--assert_initializer", - "--causal_async_stacks", + "--enable_mirrors=false", "--background_compilation", "--await_is_keyword", + "--assert_initializer", "--causal_async_stacks", }; static const char* kDartPrecompilationArgs[] = { @@ -110,8 +107,8 @@ static const char* kDartEndlessTraceBufferArgs[]{ }; static const char* kDartFuchsiaTraceArgs[] FXL_ALLOW_UNUSED_TYPE = { - "--systrace_timeline", - "--timeline_streams=VM,Isolate,Compiler,Dart,GC", + "--systrace_timeline", + "--timeline_streams=VM,Isolate,Compiler,Dart,GC", }; constexpr char kFileUriPrefix[] = "file://"; @@ -186,7 +183,7 @@ Dart_Isolate ServiceIsolateCreateCallback(const char* script_uri, #if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_RELEASE // No VM-service in release mode. return nullptr; -#else // FLUTTER_RUNTIME_MODE +#else // FLUTTER_RUNTIME_MODE tonic::DartState* dart_state = new tonic::DartState(); Dart_Isolate isolate = Dart_CreateIsolate( script_uri, "main", g_default_isolate_snapshot_data, @@ -227,7 +224,6 @@ Dart_Isolate ServiceIsolateCreateCallback(const char* script_uri, #endif // FLUTTER_RUNTIME_MODE } - Dart_Isolate IsolateCreateCallback(const char* script_uri, const char* main, const char* package_root, @@ -279,11 +275,14 @@ Dart_Isolate IsolateCreateCallback(const char* script_uri, UIDartState* parent_dart_state = static_cast(callback_data); UIDartState* dart_state = parent_dart_state->CreateForChildIsolate(); - Dart_Isolate isolate = kernel_platform != nullptr - ? Dart_CreateIsolateFromKernel(script_uri, main, kernel_platform, - nullptr /* flags */, dart_state, error) - : Dart_CreateIsolate(script_uri, main, g_default_isolate_snapshot_data, - g_default_isolate_snapshot_instructions, nullptr, dart_state, error); + Dart_Isolate isolate = + kernel_platform != nullptr + ? Dart_CreateIsolateFromKernel(script_uri, main, kernel_platform, + nullptr /* flags */, dart_state, error) + : Dart_CreateIsolate(script_uri, main, + g_default_isolate_snapshot_data, + g_default_isolate_snapshot_instructions, nullptr, + dart_state, error); FXL_CHECK(isolate) << error; dart_state->SetIsolate(isolate); FXL_CHECK(!LogIfError( @@ -302,8 +301,8 @@ Dart_Isolate IsolateCreateCallback(const char* script_uri, if (!kernel_data.empty()) { // We are running kernel code. - FXL_CHECK(!LogIfError(Dart_LoadKernel(Dart_ReadKernelBinary(kernel_data.data(), - kernel_data.size())))); + FXL_CHECK(!LogIfError(Dart_LoadKernel( + Dart_ReadKernelBinary(kernel_data.data(), kernel_data.size())))); } else if (!snapshot_data.empty()) { // We are running from a script snapshot. FXL_CHECK(!LogIfError(Dart_LoadScriptFromSnapshot(snapshot_data.data(), @@ -573,7 +572,7 @@ void InitDartVM(const uint8_t* vm_snapshot_data, 0, // argument_count nullptr, // argument_names nullptr // argument_values - ); + ); } } diff --git a/runtime/dart_init.h b/runtime/dart_init.h index 64a18fb80e3b7..5886e470f99bd 100644 --- a/runtime/dart_init.h +++ b/runtime/dart_init.h @@ -6,8 +6,8 @@ #define FLUTTER_RUNTIME_DART_INIT_H_ #include "dart/runtime/include/dart_api.h" -#include "lib/fxl/functional/closure.h" #include "lib/fxl/build_config.h" +#include "lib/fxl/functional/closure.h" #include #include diff --git a/runtime/dart_service_isolate.cc b/runtime/dart_service_isolate.cc index 9a6184f8800a9..16fae732be8f0 100644 --- a/runtime/dart_service_isolate.cc +++ b/runtime/dart_service_isolate.cc @@ -33,7 +33,7 @@ namespace flutter { namespace runtime { extern ResourcesEntry __flutter_embedded_service_isolate_resources_[]; } -} +} // namespace flutter namespace blink { namespace { @@ -66,7 +66,8 @@ void DartServiceIsolate::TriggerResourceLoad(Dart_NativeArguments args) { void DartServiceIsolate::NotifyServerState(Dart_NativeArguments args) { Dart_Handle exception = nullptr; - std::string uri = tonic::DartConverter::FromArguments(args, 0, exception); + std::string uri = + tonic::DartConverter::FromArguments(args, 0, exception); if (!exception) { observatory_uri_ = uri; } diff --git a/runtime/platform_impl.h b/runtime/platform_impl.h index 5c9b02b69acca..280f72e3213eb 100644 --- a/runtime/platform_impl.h +++ b/runtime/platform_impl.h @@ -5,8 +5,8 @@ #ifndef FLUTTER_RUNTIME_PLATFORM_IMPL_H_ #define FLUTTER_RUNTIME_PLATFORM_IMPL_H_ -#include "lib/fxl/macros.h" #include "flutter/sky/engine/public/platform/Platform.h" +#include "lib/fxl/macros.h" namespace blink { diff --git a/runtime/runtime_controller.cc b/runtime/runtime_controller.cc index e388b36142f57..64457934b1019 100644 --- a/runtime/runtime_controller.cc +++ b/runtime/runtime_controller.cc @@ -27,7 +27,8 @@ RuntimeController::RuntimeController(RuntimeDelegate* client) RuntimeController::~RuntimeController() {} void RuntimeController::CreateDartController( - const std::string& script_uri, const uint8_t* isolate_snapshot_data, + const std::string& script_uri, + const uint8_t* isolate_snapshot_data, const uint8_t* isolate_snapshot_instr, const std::vector& platform_kernel) { FXL_DCHECK(!dart_controller_); diff --git a/runtime/test_font_selector.cc b/runtime/test_font_selector.cc index 90907157bbed7..0f15e7f77f406 100644 --- a/runtime/test_font_selector.cc +++ b/runtime/test_font_selector.cc @@ -27,13 +27,12 @@ PassRefPtr TestFontSelector::getFontData( bool syntheticBold = (fontDescription.weight() >= FontWeight600 || fontDescription.isSyntheticBold()); - bool syntheticItalic = (fontDescription.style() || - fontDescription.isSyntheticItalic()); - FontPlatformData platform_data(test_typeface_, "Ahem", - fontDescription.effectiveFontSize(), - syntheticBold, syntheticItalic, - fontDescription.orientation(), - fontDescription.useSubpixelPositioning()); + bool syntheticItalic = + (fontDescription.style() || fontDescription.isSyntheticItalic()); + FontPlatformData platform_data( + test_typeface_, "Ahem", fontDescription.effectiveFontSize(), + syntheticBold, syntheticItalic, fontDescription.orientation(), + fontDescription.useSubpixelPositioning()); return SimpleFontData::create(platform_data, CustomFontData::create()); } diff --git a/shell/common/animator.cc b/shell/common/animator.cc index b671d98ce5733..fd2cfef2aa005 100644 --- a/shell/common/animator.cc +++ b/shell/common/animator.cc @@ -4,10 +4,10 @@ #include "flutter/shell/common/animator.h" +#include "dart/runtime/include/dart_tools_api.h" #include "flutter/common/threads.h" #include "flutter/fml/trace_event.h" #include "lib/fxl/time/stopwatch.h" -#include "dart/runtime/include/dart_tools_api.h" namespace shell { diff --git a/shell/common/diagnostic/diagnostic_server.cc b/shell/common/diagnostic/diagnostic_server.cc index 64ae0738e60ed..a064e516cee7d 100644 --- a/shell/common/diagnostic/diagnostic_server.cc +++ b/shell/common/diagnostic/diagnostic_server.cc @@ -23,13 +23,13 @@ namespace flutter { namespace runtime { extern ResourcesEntry __sky_embedder_diagnostic_server_resources_[]; } -} +} // namespace flutter namespace shell { -using tonic::DartLibraryNatives; using blink::EmbedderResources; using tonic::DartInvokeField; +using tonic::DartLibraryNatives; using tonic::LogIfError; using tonic::ToDart; diff --git a/shell/common/engine.h b/shell/common/engine.h index 1198b2494369d..1baf245cb0697 100644 --- a/shell/common/engine.h +++ b/shell/common/engine.h @@ -85,7 +85,8 @@ class Engine : public blink::RuntimeDelegate { void StartAnimatorIfPossible(); void ConfigureAssetBundle(const std::string& path); - void ConfigureRuntime(const std::string& script_uri, + void ConfigureRuntime( + const std::string& script_uri, const std::vector& platform_kernel = std::vector()); bool HandleLifecyclePlatformMessage(blink::PlatformMessage* message); diff --git a/shell/common/platform_view.h b/shell/common/platform_view.h index 33c6a4ca1ad84..67176a9f72806 100644 --- a/shell/common/platform_view.h +++ b/shell/common/platform_view.h @@ -83,8 +83,7 @@ class PlatformView : public std::enable_shared_from_this { std::unique_ptr vsync_waiter_; SkISize size_; -private: - + private: FXL_DISALLOW_COPY_AND_ASSIGN(PlatformView); }; diff --git a/shell/common/platform_view_service_protocol.cc b/shell/common/platform_view_service_protocol.cc index da63869aa5390..02c15487a0037 100644 --- a/shell/common/platform_view_service_protocol.cc +++ b/shell/common/platform_view_service_protocol.cc @@ -326,11 +326,11 @@ const char* PlatformViewServiceProtocol::kFlushUIThreadTasksExtensionName = // It should be invoked from the VM Service and and blocks it until UI thread // tasks are processed. bool PlatformViewServiceProtocol::FlushUIThreadTasks(const char* method, - const char** param_keys, - const char** param_values, - intptr_t num_params, - void* user_data, - const char** json_object) { + const char** param_keys, + const char** param_values, + intptr_t num_params, + void* user_data, + const char** json_object) { fxl::AutoResetWaitableEvent latch; blink::Threads::UI()->PostTask([&latch]() { // This task is empty because we just need to synchronize this RPC with the diff --git a/shell/common/vsync_waiter_fallback.cc b/shell/common/vsync_waiter_fallback.cc index d5338cb9e3416..01c86cdedc6aa 100644 --- a/shell/common/vsync_waiter_fallback.cc +++ b/shell/common/vsync_waiter_fallback.cc @@ -4,8 +4,8 @@ #include "flutter/shell/common/vsync_waiter_fallback.h" -#include "lib/fxl/logging.h" #include "flutter/common/threads.h" +#include "lib/fxl/logging.h" namespace shell { namespace { diff --git a/shell/platform/android/android_context_gl.cc b/shell/platform/android/android_context_gl.cc index ce6642a52f9a2..45b5ee9a8d6a0 100644 --- a/shell/platform/android/android_context_gl.cc +++ b/shell/platform/android/android_context_gl.cc @@ -128,13 +128,9 @@ bool AndroidContextGL::CreateWindowSurface( window_ = std::move(window); EGLDisplay display = environment_->Display(); - const EGLint srgb_attribs[] = { - EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR, - EGL_NONE - }; - const EGLint default_attribs[] = { - EGL_NONE - }; + const EGLint srgb_attribs[] = {EGL_GL_COLORSPACE_KHR, + EGL_GL_COLORSPACE_SRGB_KHR, EGL_NONE}; + const EGLint default_attribs[] = {EGL_NONE}; const EGLint* attribs = default_attribs; if (srgb_support_) { @@ -154,17 +150,14 @@ bool AndroidContextGL::CreatePBufferSurface() { EGLDisplay display = environment_->Display(); - const EGLint srgb_attribs[] = { - EGL_WIDTH, 1, - EGL_HEIGHT, 1, - EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR, - EGL_NONE - }; - const EGLint default_attribs[] = { - EGL_WIDTH, 1, - EGL_HEIGHT, 1, - EGL_NONE - }; + const EGLint srgb_attribs[] = {EGL_WIDTH, + 1, + EGL_HEIGHT, + 1, + EGL_GL_COLORSPACE_KHR, + EGL_GL_COLORSPACE_SRGB_KHR, + EGL_NONE}; + const EGLint default_attribs[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE}; const EGLint* attribs = default_attribs; if (srgb_support_) { @@ -231,12 +224,14 @@ AndroidContextGL::AndroidContextGL(fxl::RefPtr env, AndroidContextGL::~AndroidContextGL() { if (!TeardownContext(environment_->Display(), context_)) { - FXL_LOG(ERROR) << "Could not tear down the EGL context. Possible resource leak."; + FXL_LOG(ERROR) + << "Could not tear down the EGL context. Possible resource leak."; LogLastEGLError(); } if (!TeardownSurface(environment_->Display(), surface_)) { - FXL_LOG(ERROR) << "Could not tear down the EGL surface. Possible resource leak."; + FXL_LOG(ERROR) + << "Could not tear down the EGL surface. Possible resource leak."; LogLastEGLError(); } } diff --git a/shell/platform/android/android_context_gl.h b/shell/platform/android/android_context_gl.h index d9672603e3b84..89f02f52cf048 100644 --- a/shell/platform/android/android_context_gl.h +++ b/shell/platform/android/android_context_gl.h @@ -17,7 +17,6 @@ namespace shell { class AndroidContextGL : public fxl::RefCountedThreadSafe { public: - bool CreateWindowSurface(fxl::RefPtr window); bool CreatePBufferSurface(); diff --git a/shell/platform/android/android_surface_software.cc b/shell/platform/android/android_surface_software.cc index 5c07aed74ff6c..5b916b0ec3881 100644 --- a/shell/platform/android/android_surface_software.cc +++ b/shell/platform/android/android_surface_software.cc @@ -34,8 +34,7 @@ bool GetSkColorType(int32_t buffer_format, SkColorType* color_type) { } // anonymous namespace AndroidSurfaceSoftware::AndroidSurfaceSoftware() - : AndroidSurface(), - target_color_type_(kRGBA_8888_SkColorType) {} + : AndroidSurface(), target_color_type_(kRGBA_8888_SkColorType) {} AndroidSurfaceSoftware::~AndroidSurfaceSoftware() = default; @@ -102,20 +101,19 @@ bool AndroidSurfaceSoftware::PresentBackingStore( SkColorType color_type; if (GetSkColorType(native_buffer.format, &color_type)) { - SkImageInfo native_image_info = SkImageInfo::Make( - native_buffer.width, native_buffer.height, color_type, kPremul_SkAlphaType); + SkImageInfo native_image_info = + SkImageInfo::Make(native_buffer.width, native_buffer.height, color_type, + kPremul_SkAlphaType); std::unique_ptr canvas = SkCanvas::MakeRasterDirect( - native_image_info, - native_buffer.bits, + native_image_info, native_buffer.bits, native_buffer.stride * SkColorTypeBytesPerPixel(color_type)); if (canvas) { SkBitmap bitmap; if (bitmap.installPixels(pixmap)) { canvas->drawBitmapRect( - bitmap, - SkRect::MakeIWH(native_buffer.width, native_buffer.height), + bitmap, SkRect::MakeIWH(native_buffer.width, native_buffer.height), nullptr); } } diff --git a/shell/platform/android/platform_view_android.cc b/shell/platform/android/platform_view_android.cc index 29bd09d748b77..478894e54268f 100644 --- a/shell/platform/android/platform_view_android.cc +++ b/shell/platform/android/platform_view_android.cc @@ -115,19 +115,19 @@ static std::unique_ptr InitializePlatformSurface() { } if (auto surface = InitializePlatformSurfaceSoftware()) { - FXL_DLOG(INFO) << "Software surface initialized."; - return surface; + FXL_DLOG(INFO) << "Software surface initialized."; + return surface; } - FXL_CHECK(false) << "Could not initialize either the Vulkan, OpenGL, or Software" - "surface backends. Flutter requires a GPU to render."; + FXL_CHECK(false) + << "Could not initialize either the Vulkan, OpenGL, or Software" + "surface backends. Flutter requires a GPU to render."; return nullptr; } PlatformViewAndroid::PlatformViewAndroid() : PlatformView(std::make_unique(nullptr)), - android_surface_(InitializePlatformSurface()) { -} + android_surface_(InitializePlatformSurface()) {} PlatformViewAndroid::~PlatformViewAndroid() = default; @@ -141,14 +141,13 @@ void PlatformViewAndroid::Attach() { PostAddToShellTask(); - rasterizer_->AddNextFrameCallback( - [this]() { - JNIEnv* env = fml::jni::AttachCurrentThread(); - fml::jni::ScopedJavaLocalRef view = flutter_view_.get(env); - if (!view.is_null()) { - FlutterViewOnFirstFrame(env, view.obj()); - } - }); + rasterizer_->AddNextFrameCallback([this]() { + JNIEnv* env = fml::jni::AttachCurrentThread(); + fml::jni::ScopedJavaLocalRef view = flutter_view_.get(env); + if (!view.is_null()) { + FlutterViewOnFirstFrame(env, view.obj()); + } + }); } void PlatformViewAndroid::Detach() { diff --git a/shell/platform/android/platform_view_android.h b/shell/platform/android/platform_view_android.h index 133bfd9c9e0e3..c6d90e569c948 100644 --- a/shell/platform/android/platform_view_android.h +++ b/shell/platform/android/platform_view_android.h @@ -102,7 +102,6 @@ class PlatformViewAndroid : public PlatformView { android_surface_->SetFlutterView(flutter_view); } - private: const std::unique_ptr android_surface_; fml::jni::JavaObjectWeakGlobalRef flutter_view_; diff --git a/shell/platform/android/platform_view_android_jni.cc b/shell/platform/android/platform_view_android_jni.cc index 5c0211d0aa4b3..1335d0a471cb2 100644 --- a/shell/platform/android/platform_view_android_jni.cc +++ b/shell/platform/android/platform_view_android_jni.cc @@ -11,7 +11,8 @@ #include "lib/fxl/arraysize.h" #include "lib/fxl/logging.h" -#define PLATFORM_VIEW (*reinterpret_cast*>(platform_view)) +#define PLATFORM_VIEW \ + (*reinterpret_cast*>(platform_view)) namespace shell { @@ -107,7 +108,7 @@ static void RunBundleAndSnapshot(JNIEnv* env, return PLATFORM_VIEW->RunBundleAndSnapshot( fml::jni::JavaStringToString(env, bundlePath), // fml::jni::JavaStringToString(env, snapshotOverride) // - ); + ); } void RunBundleAndSource(JNIEnv* env, diff --git a/shell/platform/android/vsync_waiter_android.cc b/shell/platform/android/vsync_waiter_android.cc index ab18b4cc75ab9..7df245530f8bd 100644 --- a/shell/platform/android/vsync_waiter_android.cc +++ b/shell/platform/android/vsync_waiter_android.cc @@ -41,12 +41,13 @@ void VsyncWaiterAndroid::OnVsync(int64_t frameTimeNanos, int64_t frameTargetTimeNanos) { Callback callback = std::move(callback_); callback_ = Callback(); - blink::Threads::UI()->PostTask([callback, frameTimeNanos, frameTargetTimeNanos] { - callback(fxl::TimePoint::FromEpochDelta( - fxl::TimeDelta::FromNanoseconds(frameTimeNanos)), - fxl::TimePoint::FromEpochDelta( - fxl::TimeDelta::FromNanoseconds(frameTargetTimeNanos))); - }); + blink::Threads::UI()->PostTask( + [callback, frameTimeNanos, frameTargetTimeNanos] { + callback(fxl::TimePoint::FromEpochDelta( + fxl::TimeDelta::FromNanoseconds(frameTimeNanos)), + fxl::TimePoint::FromEpochDelta( + fxl::TimeDelta::FromNanoseconds(frameTargetTimeNanos))); + }); } static void OnNativeVsync(JNIEnv* env, diff --git a/shell/platform/darwin/desktop/vsync_waiter_mac.h b/shell/platform/darwin/desktop/vsync_waiter_mac.h index d7226011c1965..15f551f212901 100644 --- a/shell/platform/darwin/desktop/vsync_waiter_mac.h +++ b/shell/platform/darwin/desktop/vsync_waiter_mac.h @@ -5,8 +5,8 @@ #ifndef FLUTTER_SHELL_PLATFORM_DARWIN_DESKTOP_VSYNC_WAITER_MAC_H_ #define FLUTTER_SHELL_PLATFORM_DARWIN_DESKTOP_VSYNC_WAITER_MAC_H_ -#include "lib/fxl/macros.h" #include "flutter/shell/common/vsync_waiter.h" +#include "lib/fxl/macros.h" namespace shell { diff --git a/shell/platform/darwin/ios/framework/Headers/Flutter.h b/shell/platform/darwin/ios/framework/Headers/Flutter.h index f21d3f7075686..a47e239bd8424 100644 --- a/shell/platform/darwin/ios/framework/Headers/Flutter.h +++ b/shell/platform/darwin/ios/framework/Headers/Flutter.h @@ -11,8 +11,8 @@ #include "FlutterCodecs.h" #include "FlutterDartProject.h" #include "FlutterMacros.h" +#include "FlutterNavigationController.h" #include "FlutterPlugin.h" #include "FlutterViewController.h" -#include "FlutterNavigationController.h" #endif // FLUTTER_FLUTTER_H_ diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterAppDelegate.h b/shell/platform/darwin/ios/framework/Headers/FlutterAppDelegate.h index 7084a02a534ed..a2ad15742f983 100644 --- a/shell/platform/darwin/ios/framework/Headers/FlutterAppDelegate.h +++ b/shell/platform/darwin/ios/framework/Headers/FlutterAppDelegate.h @@ -24,8 +24,7 @@ * code as necessary from FlutterAppDelegate.mm. */ FLUTTER_EXPORT -@interface FlutterAppDelegate - : UIResponder +@interface FlutterAppDelegate : UIResponder @property(strong, nonatomic) UIWindow* window; @@ -33,7 +32,7 @@ FLUTTER_EXPORT // typically a FlutterViewController, for plugin interop. // // Defaults to window's rootViewController. -- (NSObject*) binaryMessenger; +- (NSObject*)binaryMessenger; @end diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterNavigationController.h b/shell/platform/darwin/ios/framework/Headers/FlutterNavigationController.h index 207ab3c8b1121..1ff0911bd685d 100644 --- a/shell/platform/darwin/ios/framework/Headers/FlutterNavigationController.h +++ b/shell/platform/darwin/ios/framework/Headers/FlutterNavigationController.h @@ -4,6 +4,6 @@ #import -@interface FlutterNavigationController: UINavigationController +@interface FlutterNavigationController : UINavigationController @end diff --git a/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm b/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm index ba011a89ad9cf..ad52df61ecc9a 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm @@ -160,9 +160,7 @@ - (BOOL)application:(UIApplication*)application options:(NSDictionary*)options { for (id plugin in _pluginDelegates) { if ([plugin respondsToSelector:_cmd]) { - if ([plugin application:application - openURL:url - options:options]) { + if ([plugin application:application openURL:url options:options]) { return YES; } } diff --git a/shell/platform/darwin/ios/framework/Source/FlutterCodecs.mm b/shell/platform/darwin/ios/framework/Source/FlutterCodecs.mm index 04b6079024039..e49ec86aadff1 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterCodecs.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterCodecs.mm @@ -63,7 +63,7 @@ - (NSData*)encode:(id)message { } else { // NSJSONSerialization does not support top-level simple values. // We encode as singleton array, then extract the relevant bytes. - encoding = [NSJSONSerialization dataWithJSONObject:@[message] options:0 error:nil]; + encoding = [NSJSONSerialization dataWithJSONObject:@[ message ] options:0 error:nil]; if (encoding) { encoding = [encoding subdataWithRange:NSMakeRange(1, encoding.length - 2)]; } @@ -96,7 +96,7 @@ - (id)decode:(NSData*)message { decoded = [NSJSONSerialization JSONObjectWithData:message options:0 error:nil]; } NSAssert(decoded, @"Invalid JSON message, decoding failed"); - return isSimpleValue ? ((NSArray*) decoded)[0] : decoded; + return isSimpleValue ? ((NSArray*)decoded)[0] : decoded; } @end @@ -117,7 +117,7 @@ - (NSData*)encodeMethodCall:(FlutterMethodCall*)call { } - (NSData*)encodeSuccessEnvelope:(id)result { - return [[FlutterJSONMessageCodec sharedInstance] encode:@[[self wrapNil:result]]]; + return [[FlutterJSONMessageCodec sharedInstance] encode:@[ [self wrapNil:result] ]]; } - (NSData*)encodeErrorEnvelope:(FlutterError*)error { diff --git a/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm b/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm index ca056955c0203..a3230beb32ea2 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm @@ -182,11 +182,11 @@ - (void)launchInEngine:(shell::Engine*)engine if (_vmTypeRequirement != embedderVMType) { NSString* message = - [NSString stringWithFormat:@"Could not load the project because of differing project type. " - @"The project can run in '%@' but the embedder is configured as " - @"'%@'", - NSStringFromVMType(_vmTypeRequirement), - NSStringFromVMType(embedderVMType)]; + [NSString stringWithFormat: + @"Could not load the project because of differing project type. " + @"The project can run in '%@' but the embedder is configured as " + @"'%@'", + NSStringFromVMType(_vmTypeRequirement), NSStringFromVMType(embedderVMType)]; result(NO, message); return; } @@ -218,9 +218,10 @@ - (void)runFromPrecompiledSourceInEngine:(shell::Engine*)engine result:(LaunchRe NSString* path = [self pathForFLXFromBundle:_precompiledDartBundle]; if (path.length == 0) { - NSString* message = [NSString stringWithFormat:@"Could not find the 'app.flx' archive in " - @"the precompiled Dart bundle with ID '%@'", - _precompiledDartBundle.bundleIdentifier]; + NSString* message = [NSString stringWithFormat: + @"Could not find the 'app.flx' archive in " + @"the precompiled Dart bundle with ID '%@'", + _precompiledDartBundle.bundleIdentifier]; result(NO, message); return; } diff --git a/shell/platform/darwin/ios/framework/Source/FlutterNavigationController.mm b/shell/platform/darwin/ios/framework/Source/FlutterNavigationController.mm index 4c0fc1918929a..2ca2978a548f1 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterNavigationController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterNavigationController.mm @@ -6,7 +6,7 @@ @implementation FlutterNavigationController -- (void) viewWillAppear:(BOOL)animated { +- (void)viewWillAppear:(BOOL)animated { [self setNavigationBarHidden:YES]; [super viewWillAppear:animated]; } diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.mm index 9fdcf63347109..c9b3ff9433d40 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.mm @@ -156,7 +156,7 @@ - (void)popSystemNavigator { // in the navigation hierarchy. UIViewController* viewController = [UIApplication sharedApplication].keyWindow.rootViewController; if ([viewController isKindOfClass:[UINavigationController class]]) { - [((UINavigationController*)viewController) popViewControllerAnimated:NO]; + [((UINavigationController*)viewController) popViewControllerAnimated:NO]; } } @@ -170,7 +170,6 @@ - (NSDictionary*)getClipboardData:(NSString*)format { return nil; } - - (void)setClipboardData:(NSDictionary*)data { UIPasteboard* pasteboard = [UIPasteboard generalPasteboard]; pasteboard.string = data[@"text"]; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index abda71d42d49c..ca76e8832734b 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -127,8 +127,8 @@ - (void)performCommonViewControllerInitialization { _orientationPreferences = UIInterfaceOrientationMaskAll; _statusBarStyle = UIStatusBarStyleDefault; - _platformView = std::make_shared( - reinterpret_cast(self.view.layer)); + _platformView = + std::make_shared(reinterpret_cast(self.view.layer)); _platformView->Attach( // First frame callback. @@ -136,11 +136,13 @@ - (void)performCommonViewControllerInitialization { TRACE_EVENT0("flutter", "First Frame"); if (_launchView) { [UIView animateWithDuration:0.2 - animations:^{ _launchView.get().alpha = 0; } - completion:^(BOOL finished){ - [_launchView.get() removeFromSuperview]; - _launchView.reset(); - }]; + animations:^{ + _launchView.get().alpha = 0; + } + completion:^(BOOL finished) { + [_launchView.get() removeFromSuperview]; + _launchView.reset(); + }]; } }); _platformView->SetupResourceContextOnIOThread(); @@ -298,8 +300,8 @@ - (void)loadView { [[[NSBundle mainBundle] infoDictionary] objectForKey:@"UILaunchStoryboardName"]; if (launchStoryboardName && !self.isBeingPresented && !self.isMovingToParentViewController) { UIViewController* launchViewController = - [[UIStoryboard storyboardWithName:launchStoryboardName - bundle:nil] instantiateInitialViewController]; + [[UIStoryboard storyboardWithName:launchStoryboardName bundle:nil] + instantiateInitialViewController]; _launchView.reset([launchViewController.view retain]); _launchView.get().frame = self.view.bounds; _launchView.get().autoresizingMask = diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.h b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.h index 2a914ec87600b..574662d5f4881 100644 --- a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.h +++ b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.h @@ -55,7 +55,8 @@ class AccessibilityBridge final { private: SemanticsObject* GetOrCreateObject(int32_t id); - void VisitObjectsRecursivelyAndRemove(SemanticsObject* object, NSMutableArray* doomed_uids); + void VisitObjectsRecursivelyAndRemove(SemanticsObject* object, + NSMutableArray* doomed_uids); void ReleaseObjects(std::unordered_map& objects); UIView* view_; diff --git a/shell/platform/darwin/ios/framework/Source/flutter_codecs_unittest.mm b/shell/platform/darwin/ios/framework/Source/flutter_codecs_unittest.mm index cc311b10baf5a..b94793d39e812 100644 --- a/shell/platform/darwin/ios/framework/Source/flutter_codecs_unittest.mm +++ b/shell/platform/darwin/ios/framework/Source/flutter_codecs_unittest.mm @@ -48,7 +48,7 @@ } TEST(FlutterJSONCodec, CanEncodeAndDecodeArray) { - NSArray* value = @[ [NSNull null], @"hello", @3.14, @47, @{ @"a" : @"nested" } ]; + NSArray* value = @[ [NSNull null], @"hello", @3.14, @47, @{@"a" : @"nested"} ]; FlutterJSONMessageCodec* codec = [FlutterJSONMessageCodec sharedInstance]; NSData* encoded = [codec encode:value]; NSArray* decoded = [codec decode:encoded]; diff --git a/shell/platform/darwin/ios/framework/Source/platform_message_router.h b/shell/platform/darwin/ios/framework/Source/platform_message_router.h index 423d462b7150d..cfa91c04771c3 100644 --- a/shell/platform/darwin/ios/framework/Source/platform_message_router.h +++ b/shell/platform/darwin/ios/framework/Source/platform_message_router.h @@ -20,10 +20,12 @@ class PlatformMessageRouter { void HandlePlatformMessage(fxl::RefPtr message); - void SetMessageHandler(const std::string& channel, FlutterBinaryMessageHandler handler); + void SetMessageHandler(const std::string& channel, + FlutterBinaryMessageHandler handler); private: - std::unordered_map message_handlers_; + std::unordered_map + message_handlers_; FXL_DISALLOW_COPY_AND_ASSIGN(PlatformMessageRouter); }; diff --git a/shell/platform/darwin/ios/ios_gl_context.h b/shell/platform/darwin/ios/ios_gl_context.h index 091d9eb82e2ef..263031ca91d0f 100644 --- a/shell/platform/darwin/ios/ios_gl_context.h +++ b/shell/platform/darwin/ios/ios_gl_context.h @@ -34,9 +34,7 @@ class IOSGLContext { bool ResourceMakeCurrent(); - sk_sp ColorSpace() const { - return color_space_; - } + sk_sp ColorSpace() const { return color_space_; } private: fml::scoped_nsobject layer_; diff --git a/shell/platform/darwin/ios/ios_gl_context.mm b/shell/platform/darwin/ios/ios_gl_context.mm index 0d5b7a0180691..dc018a0eb5067 100644 --- a/shell/platform/darwin/ios/ios_gl_context.mm +++ b/shell/platform/darwin/ios/ios_gl_context.mm @@ -106,19 +106,16 @@ // should use iOS APIs to perform the final correction step based on the // device properties. Ex: We can indicate that we have rendered in P3, and // the framework will do the final adjustment for us. - NSOperatingSystemVersion version = [[NSProcessInfo processInfo] - operatingSystemVersion]; + NSOperatingSystemVersion version = [[NSProcessInfo processInfo] operatingSystemVersion]; color_space_ = SkColorSpace::MakeSRGB(); if (version.majorVersion >= 10) { - UIDisplayGamut displayGamut = - [UIScreen mainScreen].traitCollection.displayGamut; + UIDisplayGamut displayGamut = [UIScreen mainScreen].traitCollection.displayGamut; switch (displayGamut) { case UIDisplayGamutP3: // Should we consider using more than 8-bits of precision given that // P3 specifies a wider range of colors? - color_space_ = SkColorSpace::MakeRGB( - SkColorSpace::kSRGB_RenderTargetGamma, - SkColorSpace::kDCIP3_D65_Gamut); + color_space_ = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, + SkColorSpace::kDCIP3_D65_Gamut); break; default: break; @@ -154,7 +151,8 @@ bool IOSGLContext::PresentRenderBuffer() const { const GLenum discards[] = { - GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT, + GL_DEPTH_ATTACHMENT, + GL_STENCIL_ATTACHMENT, }; glDiscardFramebufferEXT(GL_FRAMEBUFFER, sizeof(discards) / sizeof(GLenum), discards); diff --git a/shell/platform/darwin/ios/ios_surface_software.mm b/shell/platform/darwin/ios/ios_surface_software.mm index be830484cc8e3..06dc2c0bec34d 100644 --- a/shell/platform/darwin/ios/ios_surface_software.mm +++ b/shell/platform/darwin/ios/ios_surface_software.mm @@ -63,8 +63,7 @@ return sk_surface_; } - SkImageInfo info = SkImageInfo::MakeS32(size.fWidth, size.fHeight, - kPremul_SkAlphaType); + SkImageInfo info = SkImageInfo::MakeS32(size.fWidth, size.fHeight, kPremul_SkAlphaType); sk_surface_ = SkSurface::MakeRaster(info, nullptr); return sk_surface_; } diff --git a/shell/platform/darwin/ios/platform_view_ios.mm b/shell/platform/darwin/ios/platform_view_ios.mm index b4d574d0dc645..56c22b5ce3f39 100644 --- a/shell/platform/darwin/ios/platform_view_ios.mm +++ b/shell/platform/darwin/ios/platform_view_ios.mm @@ -20,8 +20,7 @@ PlatformViewIOS::PlatformViewIOS(CALayer* layer) : PlatformView(std::make_unique(std::make_unique())), ios_surface_(IOSSurface::Create(surface_config_, layer)), - weak_factory_(this) { -} + weak_factory_(this) {} PlatformViewIOS::~PlatformViewIOS() = default; diff --git a/shell/testing/platform_view_test.cc b/shell/testing/platform_view_test.cc index 14d731d9e1046..04fb1943d740b 100644 --- a/shell/testing/platform_view_test.cc +++ b/shell/testing/platform_view_test.cc @@ -10,8 +10,7 @@ namespace shell { PlatformViewTest::PlatformViewTest() - : PlatformView(std::unique_ptr(new NullRasterizer())) { -} + : PlatformView(std::unique_ptr(new NullRasterizer())) {} void PlatformViewTest::Attach() { CreateEngine(); diff --git a/shell/testing/test_runner.cc b/shell/testing/test_runner.cc index 43ff4821ec493..838b04120c773 100644 --- a/shell/testing/test_runner.cc +++ b/shell/testing/test_runner.cc @@ -13,12 +13,13 @@ namespace shell { -TestRunner::TestRunner() : platform_view_(std::make_shared()) { +TestRunner::TestRunner() + : platform_view_(std::make_shared()) { platform_view_->Attach(); blink::ViewportMetrics metrics; metrics.device_pixel_ratio = 3.0; - metrics.physical_width = 2400; // 800 at 3x resolution - metrics.physical_height = 1800; // 600 at 3x resolution + metrics.physical_width = 2400; // 800 at 3x resolution + metrics.physical_height = 1800; // 600 at 3x resolution blink::Threads::UI()->PostTask( [ engine = platform_view_->engine().GetWeakPtr(), metrics ] { diff --git a/sky/engine/core/Init.cpp b/sky/engine/core/Init.cpp index 7371c79ed75d6..7ddd99626802e 100644 --- a/sky/engine/core/Init.cpp +++ b/sky/engine/core/Init.cpp @@ -36,23 +36,21 @@ namespace blink { -void CoreInitializer::init() -{ - ASSERT(!m_isInited); - m_isInited = true; +void CoreInitializer::init() { + ASSERT(!m_isInited); + m_isInited = true; - // It would make logical sense to do this in WTF::initialize() but there are - // ordering dependencies, e.g. about "xmlns". - WTF::StringStatics::init(); + // It would make logical sense to do this in WTF::initialize() but there are + // ordering dependencies, e.g. about "xmlns". + WTF::StringStatics::init(); - Partitions::init(); + Partitions::init(); - StringImpl::freezeStaticStrings(); + StringImpl::freezeStaticStrings(); } -void CoreInitializer::shutdown() -{ - Partitions::shutdown(); +void CoreInitializer::shutdown() { + Partitions::shutdown(); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/core/Init.h b/sky/engine/core/Init.h index 9a744b919147e..f92f519d9d5e0 100644 --- a/sky/engine/core/Init.h +++ b/sky/engine/core/Init.h @@ -34,20 +34,20 @@ namespace blink { class CoreInitializer { -public: - CoreInitializer() : m_isInited(false) { } - // Should be called by clients before trying to create Frames. - virtual void init(); + public: + CoreInitializer() : m_isInited(false) {} + // Should be called by clients before trying to create Frames. + virtual void init(); - // FIXME: Why is this function static? - static void shutdown(); + // FIXME: Why is this function static? + static void shutdown(); -protected: - bool isInitialized() const { return m_isInited; } + protected: + bool isInitialized() const { return m_isInited; } - bool m_isInited; + bool m_isInited; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_INIT_H_ diff --git a/sky/engine/core/editing/CompositionUnderline.h b/sky/engine/core/editing/CompositionUnderline.h index 516a0cd215771..de6b8228f426d 100644 --- a/sky/engine/core/editing/CompositionUnderline.h +++ b/sky/engine/core/editing/CompositionUnderline.h @@ -31,35 +31,35 @@ namespace blink { struct CompositionUnderline { - CompositionUnderline() - : startOffset(0) - , endOffset(0) - , color(Color::transparent) - , thick(false) - , backgroundColor(Color::transparent) { } + CompositionUnderline() + : startOffset(0), + endOffset(0), + color(Color::transparent), + thick(false), + backgroundColor(Color::transparent) {} - // FIXME(huangs): remove this constructor. - CompositionUnderline(unsigned s, unsigned e, const Color& c, bool t) - : startOffset(s) - , endOffset(e) - , color(c) - , thick(t) - , backgroundColor(Color::transparent) { } + // FIXME(huangs): remove this constructor. + CompositionUnderline(unsigned s, unsigned e, const Color& c, bool t) + : startOffset(s), + endOffset(e), + color(c), + thick(t), + backgroundColor(Color::transparent) {} - CompositionUnderline(unsigned s, unsigned e, const Color& c, bool t, const Color& bc) - : startOffset(s) - , endOffset(e) - , color(c) - , thick(t) - , backgroundColor(bc) { } + CompositionUnderline(unsigned s, + unsigned e, + const Color& c, + bool t, + const Color& bc) + : startOffset(s), endOffset(e), color(c), thick(t), backgroundColor(bc) {} - unsigned startOffset; - unsigned endOffset; - Color color; - bool thick; - Color backgroundColor; + unsigned startOffset; + unsigned endOffset; + Color color; + bool thick; + Color backgroundColor; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_EDITING_COMPOSITIONUNDERLINE_H_ diff --git a/sky/engine/core/editing/CompositionUnderlineRangeFilter.cpp b/sky/engine/core/editing/CompositionUnderlineRangeFilter.cpp index 803899f907b50..46fe582a029d9 100644 --- a/sky/engine/core/editing/CompositionUnderlineRangeFilter.cpp +++ b/sky/engine/core/editing/CompositionUnderlineRangeFilter.cpp @@ -6,33 +6,35 @@ namespace blink { -CompositionUnderlineRangeFilter::CompositionUnderlineRangeFilter(const Vector& underlines, size_t indexLo, size_t indexHi) - : m_underlines(underlines) - , m_indexLo(indexLo) - , m_indexHi(indexHi) - , m_theEnd(this, kNotFound) { } +CompositionUnderlineRangeFilter::CompositionUnderlineRangeFilter( + const Vector& underlines, + size_t indexLo, + size_t indexHi) + : m_underlines(underlines), + m_indexLo(indexLo), + m_indexHi(indexHi), + m_theEnd(this, kNotFound) {} -size_t CompositionUnderlineRangeFilter::seekValidIndex(size_t index) -{ - if (index == kNotFound) - return kNotFound; +size_t CompositionUnderlineRangeFilter::seekValidIndex(size_t index) { + if (index == kNotFound) + return kNotFound; - size_t numUnderlines = m_underlines.size(); - while (index < numUnderlines) { - const CompositionUnderline& underline = m_underlines[index]; + size_t numUnderlines = m_underlines.size(); + while (index < numUnderlines) { + const CompositionUnderline& underline = m_underlines[index]; - if (underline.endOffset <= m_indexLo) { - // |underline| lies before the query range: keep on looking. - ++index; - } else if (underline.startOffset <= m_indexHi) { - // |underline| intersects with the query range: valid, so return. - return index; - } else { - // |underline| is completely after the query range: bail. - break; - } + if (underline.endOffset <= m_indexLo) { + // |underline| lies before the query range: keep on looking. + ++index; + } else if (underline.startOffset <= m_indexHi) { + // |underline| intersects with the query range: valid, so return. + return index; + } else { + // |underline| is completely after the query range: bail. + break; } - return kNotFound; + } + return kNotFound; } -} // namespace blink +} // namespace blink diff --git a/sky/engine/core/editing/CompositionUnderlineRangeFilter.h b/sky/engine/core/editing/CompositionUnderlineRangeFilter.h index f58817db80449..01215cd292fb4 100644 --- a/sky/engine/core/editing/CompositionUnderlineRangeFilter.h +++ b/sky/engine/core/editing/CompositionUnderlineRangeFilter.h @@ -15,64 +15,63 @@ namespace blink { // underlines, visiting only elements that intersect with specified *inclusive* // range [indexLo, indexHi]. class CompositionUnderlineRangeFilter { - WTF_MAKE_NONCOPYABLE(CompositionUnderlineRangeFilter); -public: - class ConstIterator { - public: - ConstIterator(): m_filter(nullptr), m_index(0) { } - const CompositionUnderline& operator*() - { - ASSERT(m_index != kNotFound); - return m_filter->m_underlines[m_index]; - } - ConstIterator& operator++() - { - if (m_index != kNotFound) - m_index = m_filter->seekValidIndex(m_index + 1); - return *this; - } - const CompositionUnderline* operator->() - { - ASSERT(m_index != kNotFound); - return &m_filter->m_underlines[m_index]; - } - bool operator==(const ConstIterator& other) - { - return other.m_index == m_index && other.m_filter == m_filter; - } - bool operator!=(const ConstIterator& other) { return !operator==(other); } + WTF_MAKE_NONCOPYABLE(CompositionUnderlineRangeFilter); - private: - friend class CompositionUnderlineRangeFilter; + public: + class ConstIterator { + public: + ConstIterator() : m_filter(nullptr), m_index(0) {} + const CompositionUnderline& operator*() { + ASSERT(m_index != kNotFound); + return m_filter->m_underlines[m_index]; + } + ConstIterator& operator++() { + if (m_index != kNotFound) + m_index = m_filter->seekValidIndex(m_index + 1); + return *this; + } + const CompositionUnderline* operator->() { + ASSERT(m_index != kNotFound); + return &m_filter->m_underlines[m_index]; + } + bool operator==(const ConstIterator& other) { + return other.m_index == m_index && other.m_filter == m_filter; + } + bool operator!=(const ConstIterator& other) { return !operator==(other); } - ConstIterator(CompositionUnderlineRangeFilter* filter, size_t index) - : m_filter(filter) - , m_index(index) { } - CompositionUnderlineRangeFilter* m_filter; - size_t m_index; - }; + private: + friend class CompositionUnderlineRangeFilter; - CompositionUnderlineRangeFilter(const Vector& underlines, size_t indexLo, size_t indexHi); + ConstIterator(CompositionUnderlineRangeFilter* filter, size_t index) + : m_filter(filter), m_index(index) {} + CompositionUnderlineRangeFilter* m_filter; + size_t m_index; + }; - ConstIterator begin() { return ConstIterator(this, seekValidIndex(0)); } - const ConstIterator& end() { return m_theEnd; } + CompositionUnderlineRangeFilter( + const Vector& underlines, + size_t indexLo, + size_t indexHi); -private: - friend class ConstIterator; + ConstIterator begin() { return ConstIterator(this, seekValidIndex(0)); } + const ConstIterator& end() { return m_theEnd; } - // Returns |index| if |m_underlines[index]| intersects with range - // [m_indexLo, m_indexHi]. Otherwise returns the index of the next - // intersecting interval, or END if there are none left. - size_t seekValidIndex(size_t index); + private: + friend class ConstIterator; - // Assume that elements of |m_underlines| are sorted by |.startOffset|. - const Vector& m_underlines; - // The "query range" is the inclusive range [m_indexLo, m_indexHi]. - const size_t m_indexLo; - const size_t m_indexHi; - const ConstIterator m_theEnd; + // Returns |index| if |m_underlines[index]| intersects with range + // [m_indexLo, m_indexHi]. Otherwise returns the index of the next + // intersecting interval, or END if there are none left. + size_t seekValidIndex(size_t index); + + // Assume that elements of |m_underlines| are sorted by |.startOffset|. + const Vector& m_underlines; + // The "query range" is the inclusive range [m_indexLo, m_indexHi]. + const size_t m_indexLo; + const size_t m_indexHi; + const ConstIterator m_theEnd; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_EDITING_COMPOSITIONUNDERLINERANGEFILTER_H_ diff --git a/sky/engine/core/editing/PositionWithAffinity.cpp b/sky/engine/core/editing/PositionWithAffinity.cpp index 9e82c4664b4ef..f4cb5c76c6172 100644 --- a/sky/engine/core/editing/PositionWithAffinity.cpp +++ b/sky/engine/core/editing/PositionWithAffinity.cpp @@ -6,15 +6,11 @@ namespace blink { -PositionWithAffinity::PositionWithAffinity(RenderObject* renderer, int offset, EAffinity affinity) - : m_renderer(renderer) - , m_offset(offset) - , m_affinity(affinity) -{ -} +PositionWithAffinity::PositionWithAffinity(RenderObject* renderer, + int offset, + EAffinity affinity) + : m_renderer(renderer), m_offset(offset), m_affinity(affinity) {} -PositionWithAffinity::~PositionWithAffinity() -{ -} +PositionWithAffinity::~PositionWithAffinity() {} -} // namespace blink +} // namespace blink diff --git a/sky/engine/core/editing/PositionWithAffinity.h b/sky/engine/core/editing/PositionWithAffinity.h index 2d7ca88b97edc..1a4d251cdd79b 100644 --- a/sky/engine/core/editing/PositionWithAffinity.h +++ b/sky/engine/core/editing/PositionWithAffinity.h @@ -25,20 +25,22 @@ enum EAffinity { UPSTREAM, DOWNSTREAM }; #define VP_UPSTREAM_IF_POSSIBLE UPSTREAM class PositionWithAffinity { -public: - PositionWithAffinity(RenderObject* renderer, int offset, EAffinity = DOWNSTREAM); - ~PositionWithAffinity(); - - RenderObject* renderer() const { return m_renderer; } - int offset() const { return m_offset; } - EAffinity affinity() const { return m_affinity; } - -private: - RenderObject* m_renderer; - int m_offset; - EAffinity m_affinity; + public: + PositionWithAffinity(RenderObject* renderer, + int offset, + EAffinity = DOWNSTREAM); + ~PositionWithAffinity(); + + RenderObject* renderer() const { return m_renderer; } + int offset() const { return m_offset; } + EAffinity affinity() const { return m_affinity; } + + private: + RenderObject* m_renderer; + int m_offset; + EAffinity m_affinity; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_EDITING_POSITIONWITHAFFINITY_H_ diff --git a/sky/engine/core/rendering/BidiRun.h b/sky/engine/core/rendering/BidiRun.h index 55f0f18ade2cf..37ee047ed16ed 100644 --- a/sky/engine/core/rendering/BidiRun.h +++ b/sky/engine/core/rendering/BidiRun.h @@ -34,24 +34,27 @@ class BidiContext; class InlineBox; struct BidiRun : BidiCharacterRun { - BidiRun(int start, int stop, RenderObject* object, BidiContext* context, WTF::Unicode::Direction dir) - : BidiCharacterRun(start, stop, context, dir) - , m_object(object) - , m_box(0) - { - // Stored in base class to save space. - m_hasHyphen = false; - m_hasAddedEllipsis = false; - } - - BidiRun* next() { return static_cast(m_next); } - RenderObject* object() { return m_object; } - -public: - RenderObject* m_object; - InlineBox* m_box; + BidiRun(int start, + int stop, + RenderObject* object, + BidiContext* context, + WTF::Unicode::Direction dir) + : BidiCharacterRun(start, stop, context, dir), + m_object(object), + m_box(0) { + // Stored in base class to save space. + m_hasHyphen = false; + m_hasAddedEllipsis = false; + } + + BidiRun* next() { return static_cast(m_next); } + RenderObject* object() { return m_object; } + + public: + RenderObject* m_object; + InlineBox* m_box; }; -} +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_BIDIRUN_H_ diff --git a/sky/engine/core/rendering/BidiRunForLine.cpp b/sky/engine/core/rendering/BidiRunForLine.cpp index ff24b68b5f91c..3dc4cc4b09bf2 100644 --- a/sky/engine/core/rendering/BidiRunForLine.cpp +++ b/sky/engine/core/rendering/BidiRunForLine.cpp @@ -30,180 +30,179 @@ namespace blink { using namespace WTF::Unicode; static RenderObject* firstRenderObjectForDirectionalityDetermination( - RenderObject* root, RenderObject* current = 0) -{ - RenderObject* next = current; - while (current) { - if (isIsolated(current->style()->unicodeBidi()) - && (current->isRenderInline() || current->isRenderBlock())) { - if (current != root) - current = 0; - else - current = next; - break; - } + RenderObject* root, + RenderObject* current = 0) { + RenderObject* next = current; + while (current) { + if (isIsolated(current->style()->unicodeBidi()) && + (current->isRenderInline() || current->isRenderBlock())) { + if (current != root) + current = 0; + else + current = next; + break; + } + current = current->parent(); + } + + if (!current) + current = root->slowFirstChild(); + + while (current) { + next = 0; + if (isIteratorTarget(current) && + !(current->isText() && + toRenderText(current)->isAllCollapsibleWhitespace())) + break; + + if (!isIteratorTarget(current) && + !isIsolated(current->style()->unicodeBidi())) + next = current->slowFirstChild(); + + if (!next) { + while (current && current != root) { + next = current->nextSibling(); + if (next) + break; current = current->parent(); + } } - if (!current) - current = root->slowFirstChild(); - - while (current) { - next = 0; - if (isIteratorTarget(current) && !(current->isText() - && toRenderText(current)->isAllCollapsibleWhitespace())) - break; + if (!next) + break; - if (!isIteratorTarget(current) - && !isIsolated(current->style()->unicodeBidi())) - next = current->slowFirstChild(); + current = next; + } - if (!next) { - while (current && current != root) { - next = current->nextSibling(); - if (next) - break; - current = current->parent(); - } - } - - if (!next) - break; - - current = next; - } - - return current; + return current; } TextDirection determinePlaintextDirectionality(RenderObject* root, - RenderObject* current, unsigned pos) -{ - InlineIterator iter(root, - firstRenderObjectForDirectionalityDetermination(root, current), pos); - InlineBidiResolver observer; - observer.setStatus(BidiStatus(root->style()->direction(), - isOverride(root->style()->unicodeBidi()))); - observer.setPositionIgnoringNestedIsolates(iter); - return observer.determineParagraphDirectionality(); + RenderObject* current, + unsigned pos) { + InlineIterator iter( + root, firstRenderObjectForDirectionalityDetermination(root, current), + pos); + InlineBidiResolver observer; + observer.setStatus(BidiStatus(root->style()->direction(), + isOverride(root->style()->unicodeBidi()))); + observer.setPositionIgnoringNestedIsolates(iter); + return observer.determineParagraphDirectionality(); } // FIXME: This should be a BidiStatus constructor or create method. static inline BidiStatus statusWithDirection(TextDirection textDirection, - bool isOverride) -{ - WTF::Unicode::Direction direction = textDirection == LTR - ? LeftToRight - : RightToLeft; - RefPtr context = BidiContext::create( - textDirection == LTR ? 0 : 1, direction, isOverride, FromStyleOrDOM); - - // This copies BidiStatus and may churn the ref on BidiContext. - // I doubt it matters. - return BidiStatus(direction, direction, direction, context.release()); + bool isOverride) { + WTF::Unicode::Direction direction = + textDirection == LTR ? LeftToRight : RightToLeft; + RefPtr context = BidiContext::create( + textDirection == LTR ? 0 : 1, direction, isOverride, FromStyleOrDOM); + + // This copies BidiStatus and may churn the ref on BidiContext. + // I doubt it matters. + return BidiStatus(direction, direction, direction, context.release()); } static inline void setupResolverToResumeInIsolate(InlineBidiResolver& resolver, - RenderObject* root, RenderObject* startObject) -{ - if (root != startObject) { - RenderObject* parent = startObject->parent(); - setupResolverToResumeInIsolate(resolver, root, parent); - notifyObserverEnteredObject(&resolver, startObject); - } + RenderObject* root, + RenderObject* startObject) { + if (root != startObject) { + RenderObject* parent = startObject->parent(); + setupResolverToResumeInIsolate(resolver, root, parent); + notifyObserverEnteredObject(&resolver, startObject); + } } -static void restoreIsolatedMidpointStates(InlineBidiResolver& topResolver, - InlineBidiResolver& isolatedResolver) -{ - while (!isolatedResolver.isolatedRuns().isEmpty()) { - BidiRun* run = isolatedResolver.isolatedRuns().last(); - isolatedResolver.isolatedRuns().removeLast(); - topResolver.setMidpointStateForIsolatedRun(run, - isolatedResolver.midpointStateForIsolatedRun(run)); - } +static void restoreIsolatedMidpointStates( + InlineBidiResolver& topResolver, + InlineBidiResolver& isolatedResolver) { + while (!isolatedResolver.isolatedRuns().isEmpty()) { + BidiRun* run = isolatedResolver.isolatedRuns().last(); + isolatedResolver.isolatedRuns().removeLast(); + topResolver.setMidpointStateForIsolatedRun( + run, isolatedResolver.midpointStateForIsolatedRun(run)); + } } void constructBidiRunsForLine(InlineBidiResolver& topResolver, - BidiRunList& bidiRuns, const InlineIterator& endOfLine, - VisualDirectionOverride override, bool previousLineBrokeCleanly, - bool isNewUBAParagraph) -{ - // FIXME: We should pass a BidiRunList into createBidiRunsForLine instead - // of the resolver owning the runs. - ASSERT(&topResolver.runs() == &bidiRuns); - ASSERT(topResolver.position() != endOfLine); - RenderObject* currentRoot = topResolver.position().root(); - topResolver.createBidiRunsForLine(endOfLine, override, - previousLineBrokeCleanly); - - while (!topResolver.isolatedRuns().isEmpty()) { - // It does not matter which order we resolve the runs as long as we - // resolve them all. - BidiRun* isolatedRun = topResolver.isolatedRuns().last(); - topResolver.isolatedRuns().removeLast(); - - RenderObject* startObj = isolatedRun->object(); - - // Only inlines make sense with unicode-bidi: isolate (blocks are - // already isolated). - // FIXME: Because enterIsolate is not passed a RenderObject, we have to - // crawl up the tree to see which parent inline is the isolate. We could - // change enterIsolate to take a RenderObject and do this logic there, - // but that would be a layering violation for BidiResolver (which knows - // nothing about RenderObject). - RenderInline* isolatedInline = toRenderInline( - highestContainingIsolateWithinRoot(startObj, currentRoot)); - ASSERT(isolatedInline); - - InlineBidiResolver isolatedResolver; - LineMidpointState& isolatedLineMidpointState = - isolatedResolver.midpointState(); - isolatedLineMidpointState = topResolver.midpointStateForIsolatedRun( - isolatedRun); - EUnicodeBidi unicodeBidi = isolatedInline->style()->unicodeBidi(); - TextDirection direction; - if (unicodeBidi == Plaintext) { - direction = determinePlaintextDirectionality(isolatedInline, - isNewUBAParagraph ? startObj : 0); - } else { - ASSERT(unicodeBidi == Isolate || unicodeBidi == IsolateOverride); - direction = isolatedInline->style()->direction(); - } - isolatedResolver.setStatus(statusWithDirection(direction, - isOverride(unicodeBidi))); - - setupResolverToResumeInIsolate(isolatedResolver, isolatedInline, - startObj); - - // The starting position is the beginning of the first run within the - // isolate that was identified during the earlier call to - // createBidiRunsForLine. This can be but is not necessarily the first - // run within the isolate. - InlineIterator iter = InlineIterator(isolatedInline, startObj, - isolatedRun->m_start); - isolatedResolver.setPositionIgnoringNestedIsolates(iter); - // We stop at the next end of line; we may re-enter this isolate in the - // next call to constructBidiRuns(). - // FIXME: What should end and previousLineBrokeCleanly be? - // rniwa says previousLineBrokeCleanly is just a WinIE hack and could - // always be false here? - isolatedResolver.createBidiRunsForLine(endOfLine, NoVisualOverride, - previousLineBrokeCleanly); - - ASSERT(isolatedResolver.runs().runCount()); - if (isolatedResolver.runs().runCount()) - bidiRuns.replaceRunWithRuns(isolatedRun, isolatedResolver.runs()); - - // If we encountered any nested isolate runs, just move them - // to the top resolver's list for later processing. - if (!isolatedResolver.isolatedRuns().isEmpty()) { - topResolver.isolatedRuns().appendVector( - isolatedResolver.isolatedRuns()); - currentRoot = isolatedInline; - restoreIsolatedMidpointStates(topResolver, isolatedResolver); - } + BidiRunList& bidiRuns, + const InlineIterator& endOfLine, + VisualDirectionOverride override, + bool previousLineBrokeCleanly, + bool isNewUBAParagraph) { + // FIXME: We should pass a BidiRunList into createBidiRunsForLine instead + // of the resolver owning the runs. + ASSERT(&topResolver.runs() == &bidiRuns); + ASSERT(topResolver.position() != endOfLine); + RenderObject* currentRoot = topResolver.position().root(); + topResolver.createBidiRunsForLine(endOfLine, override, + previousLineBrokeCleanly); + + while (!topResolver.isolatedRuns().isEmpty()) { + // It does not matter which order we resolve the runs as long as we + // resolve them all. + BidiRun* isolatedRun = topResolver.isolatedRuns().last(); + topResolver.isolatedRuns().removeLast(); + + RenderObject* startObj = isolatedRun->object(); + + // Only inlines make sense with unicode-bidi: isolate (blocks are + // already isolated). + // FIXME: Because enterIsolate is not passed a RenderObject, we have to + // crawl up the tree to see which parent inline is the isolate. We could + // change enterIsolate to take a RenderObject and do this logic there, + // but that would be a layering violation for BidiResolver (which knows + // nothing about RenderObject). + RenderInline* isolatedInline = toRenderInline( + highestContainingIsolateWithinRoot(startObj, currentRoot)); + ASSERT(isolatedInline); + + InlineBidiResolver isolatedResolver; + LineMidpointState& isolatedLineMidpointState = + isolatedResolver.midpointState(); + isolatedLineMidpointState = + topResolver.midpointStateForIsolatedRun(isolatedRun); + EUnicodeBidi unicodeBidi = isolatedInline->style()->unicodeBidi(); + TextDirection direction; + if (unicodeBidi == Plaintext) { + direction = determinePlaintextDirectionality( + isolatedInline, isNewUBAParagraph ? startObj : 0); + } else { + ASSERT(unicodeBidi == Isolate || unicodeBidi == IsolateOverride); + direction = isolatedInline->style()->direction(); + } + isolatedResolver.setStatus( + statusWithDirection(direction, isOverride(unicodeBidi))); + + setupResolverToResumeInIsolate(isolatedResolver, isolatedInline, startObj); + + // The starting position is the beginning of the first run within the + // isolate that was identified during the earlier call to + // createBidiRunsForLine. This can be but is not necessarily the first + // run within the isolate. + InlineIterator iter = + InlineIterator(isolatedInline, startObj, isolatedRun->m_start); + isolatedResolver.setPositionIgnoringNestedIsolates(iter); + // We stop at the next end of line; we may re-enter this isolate in the + // next call to constructBidiRuns(). + // FIXME: What should end and previousLineBrokeCleanly be? + // rniwa says previousLineBrokeCleanly is just a WinIE hack and could + // always be false here? + isolatedResolver.createBidiRunsForLine(endOfLine, NoVisualOverride, + previousLineBrokeCleanly); + + ASSERT(isolatedResolver.runs().runCount()); + if (isolatedResolver.runs().runCount()) + bidiRuns.replaceRunWithRuns(isolatedRun, isolatedResolver.runs()); + + // If we encountered any nested isolate runs, just move them + // to the top resolver's list for later processing. + if (!isolatedResolver.isolatedRuns().isEmpty()) { + topResolver.isolatedRuns().appendVector(isolatedResolver.isolatedRuns()); + currentRoot = isolatedInline; + restoreIsolatedMidpointStates(topResolver, isolatedResolver); } + } } -} // namespace blink +} // namespace blink diff --git a/sky/engine/core/rendering/BidiRunForLine.h b/sky/engine/core/rendering/BidiRunForLine.h index 3c3674d12acae..c039643562741 100644 --- a/sky/engine/core/rendering/BidiRunForLine.h +++ b/sky/engine/core/rendering/BidiRunForLine.h @@ -30,12 +30,16 @@ namespace blink { TextDirection determinePlaintextDirectionality(RenderObject* root, - RenderObject* current = 0, unsigned pos = 0); + RenderObject* current = 0, + unsigned pos = 0); -void constructBidiRunsForLine(InlineBidiResolver&, BidiRunList&, - const InlineIterator& endOfLine, VisualDirectionOverride, - bool previousLineBrokeCleanly, bool isNewUBAParagraph); +void constructBidiRunsForLine(InlineBidiResolver&, + BidiRunList&, + const InlineIterator& endOfLine, + VisualDirectionOverride, + bool previousLineBrokeCleanly, + bool isNewUBAParagraph); -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_BIDIRUNFORLINE_H_ diff --git a/sky/engine/core/rendering/ClipPathOperation.h b/sky/engine/core/rendering/ClipPathOperation.h index 1807f07947971..f8f36d34a5404 100644 --- a/sky/engine/core/rendering/ClipPathOperation.h +++ b/sky/engine/core/rendering/ClipPathOperation.h @@ -39,29 +39,25 @@ namespace blink { class ClipPathOperation : public RefCounted { -public: - enum OperationType { - REFERENCE, - SHAPE - }; + public: + enum OperationType { REFERENCE, SHAPE }; - virtual ~ClipPathOperation() { } + virtual ~ClipPathOperation() {} - virtual bool operator==(const ClipPathOperation&) const = 0; - bool operator!=(const ClipPathOperation& o) const { return !(*this == o); } + virtual bool operator==(const ClipPathOperation&) const = 0; + bool operator!=(const ClipPathOperation& o) const { return !(*this == o); } - OperationType type() const { return m_type; } - bool isSameType(const ClipPathOperation& o) const { return o.type() == m_type; } + OperationType type() const { return m_type; } + bool isSameType(const ClipPathOperation& o) const { + return o.type() == m_type; + } -protected: - ClipPathOperation(OperationType type) - : m_type(type) - { - } + protected: + ClipPathOperation(OperationType type) : m_type(type) {} - OperationType m_type; + OperationType m_type; }; -} +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_CLIPPATHOPERATION_H_ diff --git a/sky/engine/core/rendering/ClipRect.cpp b/sky/engine/core/rendering/ClipRect.cpp index d0a562b5a7913..34284dcd9fbd4 100644 --- a/sky/engine/core/rendering/ClipRect.cpp +++ b/sky/engine/core/rendering/ClipRect.cpp @@ -1,5 +1,6 @@ /* - * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. + * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -29,9 +30,8 @@ namespace blink { -bool ClipRect::intersects(const HitTestLocation& hitTestLocation) const -{ - return hitTestLocation.intersects(m_rect); +bool ClipRect::intersects(const HitTestLocation& hitTestLocation) const { + return hitTestLocation.intersects(m_rect); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/core/rendering/ClipRect.h b/sky/engine/core/rendering/ClipRect.h index efc86d07983c9..397d6da306df3 100644 --- a/sky/engine/core/rendering/ClipRect.h +++ b/sky/engine/core/rendering/ClipRect.h @@ -34,53 +34,54 @@ class RenderLayer; class HitTestLocation; class ClipRect { -public: - ClipRect() - : m_hasRadius(false) - { } + public: + ClipRect() : m_hasRadius(false) {} - ClipRect(const LayoutRect& rect) - : m_rect(rect) - , m_hasRadius(false) - { } + ClipRect(const LayoutRect& rect) : m_rect(rect), m_hasRadius(false) {} - const LayoutRect& rect() const { return m_rect; } - void setRect(const LayoutRect& rect) { m_rect = rect; } + const LayoutRect& rect() const { return m_rect; } + void setRect(const LayoutRect& rect) { m_rect = rect; } - bool hasRadius() const { return m_hasRadius; } - void setHasRadius(bool hasRadius) { m_hasRadius = hasRadius; } + bool hasRadius() const { return m_hasRadius; } + void setHasRadius(bool hasRadius) { m_hasRadius = hasRadius; } - bool operator==(const ClipRect& other) const { return rect() == other.rect() && hasRadius() == other.hasRadius(); } - bool operator!=(const ClipRect& other) const { return rect() != other.rect() || hasRadius() != other.hasRadius(); } - bool operator!=(const LayoutRect& otherRect) const { return rect() != otherRect; } + bool operator==(const ClipRect& other) const { + return rect() == other.rect() && hasRadius() == other.hasRadius(); + } + bool operator!=(const ClipRect& other) const { + return rect() != other.rect() || hasRadius() != other.hasRadius(); + } + bool operator!=(const LayoutRect& otherRect) const { + return rect() != otherRect; + } - void intersect(const LayoutRect& other) { m_rect.intersect(other); } - void intersect(const ClipRect& other) - { - m_rect.intersect(other.rect()); - if (other.hasRadius()) - m_hasRadius = true; - } - void move(LayoutUnit x, LayoutUnit y) { m_rect.move(x, y); } - void move(const LayoutSize& size) { m_rect.move(size); } - void moveBy(const LayoutPoint& point) { m_rect.moveBy(point); } + void intersect(const LayoutRect& other) { m_rect.intersect(other); } + void intersect(const ClipRect& other) { + m_rect.intersect(other.rect()); + if (other.hasRadius()) + m_hasRadius = true; + } + void move(LayoutUnit x, LayoutUnit y) { m_rect.move(x, y); } + void move(const LayoutSize& size) { m_rect.move(size); } + void moveBy(const LayoutPoint& point) { m_rect.moveBy(point); } - bool isEmpty() const { return m_rect.isEmpty(); } - bool intersects(const LayoutRect& rect) const { return m_rect.intersects(rect); } - bool intersects(const HitTestLocation&) const; + bool isEmpty() const { return m_rect.isEmpty(); } + bool intersects(const LayoutRect& rect) const { + return m_rect.intersects(rect); + } + bool intersects(const HitTestLocation&) const; -private: - LayoutRect m_rect; - bool m_hasRadius; + private: + LayoutRect m_rect; + bool m_hasRadius; }; -inline ClipRect intersection(const ClipRect& a, const ClipRect& b) -{ - ClipRect c = a; - c.intersect(b); - return c; +inline ClipRect intersection(const ClipRect& a, const ClipRect& b) { + ClipRect c = a; + c.intersect(b); + return c; } -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_CLIPRECT_H_ diff --git a/sky/engine/core/rendering/ClipRects.h b/sky/engine/core/rendering/ClipRects.h index 6b4fcda94c781..9f96d74986725 100644 --- a/sky/engine/core/rendering/ClipRects.h +++ b/sky/engine/core/rendering/ClipRects.h @@ -31,85 +31,66 @@ namespace blink { class ClipRects { - WTF_MAKE_FAST_ALLOCATED; -public: - static PassRefPtr create() - { - return adoptRef(new ClipRects); - } - - static PassRefPtr create(const ClipRects& other) - { - return adoptRef(new ClipRects(other)); - } - - ClipRects() - : m_refCnt(1) - , m_fixed(0) - { - } - - void reset(const LayoutRect& r) - { - m_overflowClipRect = r; - m_posClipRect = r; - m_fixed = 0; - } - - const ClipRect& overflowClipRect() const { return m_overflowClipRect; } - void setOverflowClipRect(const ClipRect& r) { m_overflowClipRect = r; } - - const ClipRect& posClipRect() const { return m_posClipRect; } - void setPosClipRect(const ClipRect& r) { m_posClipRect = r; } - - bool fixed() const { return static_cast(m_fixed); } - void setFixed(bool fixed) { m_fixed = fixed ? 1 : 0; } - - void ref() { m_refCnt++; } - void deref() - { - if (!--m_refCnt) - delete this; - } - - bool operator==(const ClipRects& other) const - { - return m_overflowClipRect == other.overflowClipRect() - && m_posClipRect == other.posClipRect() - && fixed() == other.fixed(); - } - - ClipRects& operator=(const ClipRects& other) - { - m_overflowClipRect = other.overflowClipRect(); - m_posClipRect = other.posClipRect(); - m_fixed = other.fixed(); - return *this; - } - -private: - ClipRects(const LayoutRect& r) - : m_overflowClipRect(r) - , m_posClipRect(r) - , m_refCnt(1) - , m_fixed(0) - { - } - - ClipRects(const ClipRects& other) - : m_overflowClipRect(other.overflowClipRect()) - , m_posClipRect(other.posClipRect()) - , m_refCnt(1) - , m_fixed(other.fixed()) - { - } - - ClipRect m_overflowClipRect; - ClipRect m_posClipRect; - unsigned m_refCnt : 31; - unsigned m_fixed : 1; + WTF_MAKE_FAST_ALLOCATED; + + public: + static PassRefPtr create() { return adoptRef(new ClipRects); } + + static PassRefPtr create(const ClipRects& other) { + return adoptRef(new ClipRects(other)); + } + + ClipRects() : m_refCnt(1), m_fixed(0) {} + + void reset(const LayoutRect& r) { + m_overflowClipRect = r; + m_posClipRect = r; + m_fixed = 0; + } + + const ClipRect& overflowClipRect() const { return m_overflowClipRect; } + void setOverflowClipRect(const ClipRect& r) { m_overflowClipRect = r; } + + const ClipRect& posClipRect() const { return m_posClipRect; } + void setPosClipRect(const ClipRect& r) { m_posClipRect = r; } + + bool fixed() const { return static_cast(m_fixed); } + void setFixed(bool fixed) { m_fixed = fixed ? 1 : 0; } + + void ref() { m_refCnt++; } + void deref() { + if (!--m_refCnt) + delete this; + } + + bool operator==(const ClipRects& other) const { + return m_overflowClipRect == other.overflowClipRect() && + m_posClipRect == other.posClipRect() && fixed() == other.fixed(); + } + + ClipRects& operator=(const ClipRects& other) { + m_overflowClipRect = other.overflowClipRect(); + m_posClipRect = other.posClipRect(); + m_fixed = other.fixed(); + return *this; + } + + private: + ClipRects(const LayoutRect& r) + : m_overflowClipRect(r), m_posClipRect(r), m_refCnt(1), m_fixed(0) {} + + ClipRects(const ClipRects& other) + : m_overflowClipRect(other.overflowClipRect()), + m_posClipRect(other.posClipRect()), + m_refCnt(1), + m_fixed(other.fixed()) {} + + ClipRect m_overflowClipRect; + ClipRect m_posClipRect; + unsigned m_refCnt : 31; + unsigned m_fixed : 1; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_CLIPRECTS_H_ diff --git a/sky/engine/core/rendering/ClipRectsCache.h b/sky/engine/core/rendering/ClipRectsCache.h index 5553d5249ce8d..6a0294b03d151 100644 --- a/sky/engine/core/rendering/ClipRectsCache.h +++ b/sky/engine/core/rendering/ClipRectsCache.h @@ -10,45 +10,42 @@ namespace blink { enum ClipRectsCacheSlot { - // Relative to the ancestor treated as the root (e.g. transformed layer). Used for hit testing. - RootRelativeClipRects, + // Relative to the ancestor treated as the root (e.g. transformed layer). Used + // for hit testing. + RootRelativeClipRects, - // Relative to painting ancestor. Used for painting. - PaintingClipRects, + // Relative to painting ancestor. Used for painting. + PaintingClipRects, - NumberOfClipRectsCacheSlots, - UncachedClipRects, + NumberOfClipRectsCacheSlots, + UncachedClipRects, }; class ClipRectsCache { - WTF_MAKE_FAST_ALLOCATED; -public: - struct Entry { - Entry() - : root(0) - { - } - - const RenderLayer* root; - RefPtr clipRects; - }; - - Entry& get(ClipRectsCacheSlot slot) - { - ASSERT(slot < NumberOfClipRectsCacheSlots); - return m_entries[slot]; - } - - void clear(ClipRectsCacheSlot slot) - { - ASSERT(slot < NumberOfClipRectsCacheSlots); - m_entries[slot] = Entry(); - } - -private: - Entry m_entries[NumberOfClipRectsCacheSlots]; + WTF_MAKE_FAST_ALLOCATED; + + public: + struct Entry { + Entry() : root(0) {} + + const RenderLayer* root; + RefPtr clipRects; + }; + + Entry& get(ClipRectsCacheSlot slot) { + ASSERT(slot < NumberOfClipRectsCacheSlots); + return m_entries[slot]; + } + + void clear(ClipRectsCacheSlot slot) { + ASSERT(slot < NumberOfClipRectsCacheSlots); + m_entries[slot] = Entry(); + } + + private: + Entry m_entries[NumberOfClipRectsCacheSlots]; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_CLIPRECTSCACHE_H_ diff --git a/sky/engine/core/rendering/GapRects.h b/sky/engine/core/rendering/GapRects.h index 21b96636dbf2e..d1dd69aae377a 100644 --- a/sky/engine/core/rendering/GapRects.h +++ b/sky/engine/core/rendering/GapRects.h @@ -27,36 +27,39 @@ namespace blink { - struct GapRects { - const LayoutRect& left() const { return m_left; } - const LayoutRect& center() const { return m_center; } - const LayoutRect& right() const { return m_right; } - - void uniteLeft(const LayoutRect& r) { m_left.unite(r); } - void uniteCenter(const LayoutRect& r) { m_center.unite(r); } - void uniteRight(const LayoutRect& r) { m_right.unite(r); } - void unite(const GapRects& o) { uniteLeft(o.left()); uniteCenter(o.center()); uniteRight(o.right()); } - - operator LayoutRect() const - { - LayoutRect result = m_left; - result.unite(m_center); - result.unite(m_right); - return result; - } - - bool operator==(const GapRects& other) - { - return m_left == other.left() && m_center == other.center() && m_right == other.right(); - } - bool operator!=(const GapRects& other) { return !(*this == other); } - - private: - LayoutRect m_left; - LayoutRect m_center; - LayoutRect m_right; - }; - -} // namespace blink +struct GapRects { + const LayoutRect& left() const { return m_left; } + const LayoutRect& center() const { return m_center; } + const LayoutRect& right() const { return m_right; } + + void uniteLeft(const LayoutRect& r) { m_left.unite(r); } + void uniteCenter(const LayoutRect& r) { m_center.unite(r); } + void uniteRight(const LayoutRect& r) { m_right.unite(r); } + void unite(const GapRects& o) { + uniteLeft(o.left()); + uniteCenter(o.center()); + uniteRight(o.right()); + } + + operator LayoutRect() const { + LayoutRect result = m_left; + result.unite(m_center); + result.unite(m_right); + return result; + } + + bool operator==(const GapRects& other) { + return m_left == other.left() && m_center == other.center() && + m_right == other.right(); + } + bool operator!=(const GapRects& other) { return !(*this == other); } + + private: + LayoutRect m_left; + LayoutRect m_center; + LayoutRect m_right; +}; + +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_GAPRECTS_H_ diff --git a/sky/engine/core/rendering/HitTestLocation.cpp b/sky/engine/core/rendering/HitTestLocation.cpp index d7e49018f298a..b19d0a6886dba 100644 --- a/sky/engine/core/rendering/HitTestLocation.cpp +++ b/sky/engine/core/rendering/HitTestLocation.cpp @@ -17,156 +17,149 @@ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * -*/ + */ #include "flutter/sky/engine/core/rendering/HitTestLocation.h" namespace blink { HitTestLocation::HitTestLocation() - : m_isRectBased(false) - , m_isRectilinear(true) -{ -} + : m_isRectBased(false), m_isRectilinear(true) {} HitTestLocation::HitTestLocation(const LayoutPoint& point) - : m_point(point) - , m_boundingBox(rectForPoint(point, 0, 0, 0, 0)) - , m_transformedPoint(point) - , m_transformedRect(m_boundingBox) - , m_isRectBased(false) - , m_isRectilinear(true) -{ -} + : m_point(point), + m_boundingBox(rectForPoint(point, 0, 0, 0, 0)), + m_transformedPoint(point), + m_transformedRect(m_boundingBox), + m_isRectBased(false), + m_isRectilinear(true) {} HitTestLocation::HitTestLocation(const FloatPoint& point) - : m_point(flooredLayoutPoint(point)) - , m_boundingBox(rectForPoint(m_point, 0, 0, 0, 0)) - , m_transformedPoint(point) - , m_transformedRect(m_boundingBox) - , m_isRectBased(false) - , m_isRectilinear(true) -{ -} + : m_point(flooredLayoutPoint(point)), + m_boundingBox(rectForPoint(m_point, 0, 0, 0, 0)), + m_transformedPoint(point), + m_transformedRect(m_boundingBox), + m_isRectBased(false), + m_isRectilinear(true) {} HitTestLocation::HitTestLocation(const FloatPoint& point, const FloatQuad& quad) - : m_transformedPoint(point) - , m_transformedRect(quad) - , m_isRectBased(true) -{ - m_point = flooredLayoutPoint(point); - m_boundingBox = enclosingIntRect(quad.boundingBox()); - m_isRectilinear = quad.isRectilinear(); -} - -HitTestLocation::HitTestLocation(const LayoutPoint& centerPoint, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding) - : m_point(centerPoint) - , m_boundingBox(rectForPoint(centerPoint, topPadding, rightPadding, bottomPadding, leftPadding)) - , m_transformedPoint(centerPoint) - , m_isRectBased(topPadding || rightPadding || bottomPadding || leftPadding) - , m_isRectilinear(true) -{ - m_transformedRect = FloatQuad(m_boundingBox); -} - -HitTestLocation::HitTestLocation(const HitTestLocation& other, const LayoutSize& offset) - : m_point(other.m_point) - , m_boundingBox(other.m_boundingBox) - , m_transformedPoint(other.m_transformedPoint) - , m_transformedRect(other.m_transformedRect) - , m_isRectBased(other.m_isRectBased) - , m_isRectilinear(other.m_isRectilinear) -{ - move(offset); + : m_transformedPoint(point), m_transformedRect(quad), m_isRectBased(true) { + m_point = flooredLayoutPoint(point); + m_boundingBox = enclosingIntRect(quad.boundingBox()); + m_isRectilinear = quad.isRectilinear(); +} + +HitTestLocation::HitTestLocation(const LayoutPoint& centerPoint, + unsigned topPadding, + unsigned rightPadding, + unsigned bottomPadding, + unsigned leftPadding) + : m_point(centerPoint), + m_boundingBox(rectForPoint(centerPoint, + topPadding, + rightPadding, + bottomPadding, + leftPadding)), + m_transformedPoint(centerPoint), + m_isRectBased(topPadding || rightPadding || bottomPadding || leftPadding), + m_isRectilinear(true) { + m_transformedRect = FloatQuad(m_boundingBox); +} + +HitTestLocation::HitTestLocation(const HitTestLocation& other, + const LayoutSize& offset) + : m_point(other.m_point), + m_boundingBox(other.m_boundingBox), + m_transformedPoint(other.m_transformedPoint), + m_transformedRect(other.m_transformedRect), + m_isRectBased(other.m_isRectBased), + m_isRectilinear(other.m_isRectilinear) { + move(offset); } HitTestLocation::HitTestLocation(const HitTestLocation& other) - : m_point(other.m_point) - , m_boundingBox(other.m_boundingBox) - , m_transformedPoint(other.m_transformedPoint) - , m_transformedRect(other.m_transformedRect) - , m_isRectBased(other.m_isRectBased) - , m_isRectilinear(other.m_isRectilinear) -{ -} + : m_point(other.m_point), + m_boundingBox(other.m_boundingBox), + m_transformedPoint(other.m_transformedPoint), + m_transformedRect(other.m_transformedRect), + m_isRectBased(other.m_isRectBased), + m_isRectilinear(other.m_isRectilinear) {} -HitTestLocation::~HitTestLocation() -{ -} +HitTestLocation::~HitTestLocation() {} -HitTestLocation& HitTestLocation::operator=(const HitTestLocation& other) -{ - m_point = other.m_point; - m_boundingBox = other.m_boundingBox; - m_transformedPoint = other.m_transformedPoint; - m_transformedRect = other.m_transformedRect; - m_isRectBased = other.m_isRectBased; - m_isRectilinear = other.m_isRectilinear; +HitTestLocation& HitTestLocation::operator=(const HitTestLocation& other) { + m_point = other.m_point; + m_boundingBox = other.m_boundingBox; + m_transformedPoint = other.m_transformedPoint; + m_transformedRect = other.m_transformedRect; + m_isRectBased = other.m_isRectBased; + m_isRectilinear = other.m_isRectilinear; - return *this; + return *this; } -void HitTestLocation::move(const LayoutSize& offset) -{ - m_point.move(offset); - m_transformedPoint.move(offset); - m_transformedRect.move(offset); - m_boundingBox = enclosingIntRect(m_transformedRect.boundingBox()); +void HitTestLocation::move(const LayoutSize& offset) { + m_point.move(offset); + m_transformedPoint.move(offset); + m_transformedRect.move(offset); + m_boundingBox = enclosingIntRect(m_transformedRect.boundingBox()); } -template -bool HitTestLocation::intersectsRect(const RectType& rect) const -{ - // FIXME: When the hit test is not rect based we should use rect.contains(m_point). - // That does change some corner case tests though. +template +bool HitTestLocation::intersectsRect(const RectType& rect) const { + // FIXME: When the hit test is not rect based we should use + // rect.contains(m_point). That does change some corner case tests though. - // First check if rect even intersects our bounding box. - if (!rect.intersects(m_boundingBox)) - return false; + // First check if rect even intersects our bounding box. + if (!rect.intersects(m_boundingBox)) + return false; - // If the transformed rect is rectilinear the bounding box intersection was accurate. - if (m_isRectilinear) - return true; + // If the transformed rect is rectilinear the bounding box intersection was + // accurate. + if (m_isRectilinear) + return true; - // If rect fully contains our bounding box, we are also sure of an intersection. - if (rect.contains(m_boundingBox)) - return true; + // If rect fully contains our bounding box, we are also sure of an + // intersection. + if (rect.contains(m_boundingBox)) + return true; - // Otherwise we need to do a slower quad based intersection test. - return m_transformedRect.intersectsRect(rect); + // Otherwise we need to do a slower quad based intersection test. + return m_transformedRect.intersectsRect(rect); } -bool HitTestLocation::intersects(const LayoutRect& rect) const -{ - return intersectsRect(rect); +bool HitTestLocation::intersects(const LayoutRect& rect) const { + return intersectsRect(rect); } -bool HitTestLocation::intersects(const FloatRect& rect) const -{ - return intersectsRect(rect); +bool HitTestLocation::intersects(const FloatRect& rect) const { + return intersectsRect(rect); } -bool HitTestLocation::intersects(const RoundedRect& rect) const -{ - return rect.intersectsQuad(m_transformedRect); +bool HitTestLocation::intersects(const RoundedRect& rect) const { + return rect.intersectsQuad(m_transformedRect); } -bool HitTestLocation::containsPoint(const FloatPoint& point) const -{ - return m_transformedRect.containsPoint(point); +bool HitTestLocation::containsPoint(const FloatPoint& point) const { + return m_transformedRect.containsPoint(point); } -IntRect HitTestLocation::rectForPoint(const LayoutPoint& point, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding) -{ - IntPoint actualPoint(flooredIntPoint(point)); - actualPoint -= IntSize(leftPadding, topPadding); +IntRect HitTestLocation::rectForPoint(const LayoutPoint& point, + unsigned topPadding, + unsigned rightPadding, + unsigned bottomPadding, + unsigned leftPadding) { + IntPoint actualPoint(flooredIntPoint(point)); + actualPoint -= IntSize(leftPadding, topPadding); - IntSize actualPadding(leftPadding + rightPadding, topPadding + bottomPadding); - // As IntRect is left inclusive and right exclusive (seeing IntRect::contains(x, y)), adding "1". - // FIXME: Remove this once non-rect based hit-detection stops using IntRect:intersects. - actualPadding += IntSize(1, 1); + IntSize actualPadding(leftPadding + rightPadding, topPadding + bottomPadding); + // As IntRect is left inclusive and right exclusive (seeing + // IntRect::contains(x, y)), adding "1". + // FIXME: Remove this once non-rect based hit-detection stops using + // IntRect:intersects. + actualPadding += IntSize(1, 1); - return IntRect(actualPoint, actualPadding); + return IntRect(actualPoint, actualPadding); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/core/rendering/HitTestLocation.h b/sky/engine/core/rendering/HitTestLocation.h index 5a10f162422d4..91f8d892a871f 100644 --- a/sky/engine/core/rendering/HitTestLocation.h +++ b/sky/engine/core/rendering/HitTestLocation.h @@ -17,7 +17,7 @@ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * -*/ + */ #ifndef SKY_ENGINE_CORE_RENDERING_HITTESTLOCATION_H_ #define SKY_ENGINE_CORE_RENDERING_HITTESTLOCATION_H_ @@ -34,57 +34,68 @@ namespace blink { class HitTestLocation { -public: - - HitTestLocation(); - HitTestLocation(const LayoutPoint&); - HitTestLocation(const FloatPoint&); - HitTestLocation(const FloatPoint&, const FloatQuad&); - // Pass non-zero padding values to perform a rect-based hit test. - HitTestLocation(const LayoutPoint& centerPoint, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding); - HitTestLocation(const HitTestLocation&, const LayoutSize& offset); - HitTestLocation(const HitTestLocation&); - ~HitTestLocation(); - HitTestLocation& operator=(const HitTestLocation&); - - const LayoutPoint& point() const { return m_point; } - IntPoint roundedPoint() const { return roundedIntPoint(m_point); } - - // Rect-based hit test related methods. - bool isRectBasedTest() const { return m_isRectBased; } - bool isRectilinear() const { return m_isRectilinear; } - IntRect boundingBox() const { return m_boundingBox; } - - static IntRect rectForPoint(const LayoutPoint&, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding); - int topPadding() const { return roundedPoint().y() - m_boundingBox.y(); } - int rightPadding() const { return m_boundingBox.maxX() - roundedPoint().x() - 1; } - int bottomPadding() const { return m_boundingBox.maxY() - roundedPoint().y() - 1; } - int leftPadding() const { return roundedPoint().x() - m_boundingBox.x(); } - - bool intersects(const LayoutRect&) const; - bool intersects(const FloatRect&) const; - bool intersects(const RoundedRect&) const; - bool containsPoint(const FloatPoint&) const; - - const FloatPoint& transformedPoint() const { return m_transformedPoint; } - const FloatQuad& transformedRect() const { return m_transformedRect; } - -private: - template - bool intersectsRect(const RectType&) const; - void move(const LayoutSize& offset); - - // This is cached forms of the more accurate point and area below. - LayoutPoint m_point; - IntRect m_boundingBox; - - FloatPoint m_transformedPoint; - FloatQuad m_transformedRect; - - bool m_isRectBased; - bool m_isRectilinear; + public: + HitTestLocation(); + HitTestLocation(const LayoutPoint&); + HitTestLocation(const FloatPoint&); + HitTestLocation(const FloatPoint&, const FloatQuad&); + // Pass non-zero padding values to perform a rect-based hit test. + HitTestLocation(const LayoutPoint& centerPoint, + unsigned topPadding, + unsigned rightPadding, + unsigned bottomPadding, + unsigned leftPadding); + HitTestLocation(const HitTestLocation&, const LayoutSize& offset); + HitTestLocation(const HitTestLocation&); + ~HitTestLocation(); + HitTestLocation& operator=(const HitTestLocation&); + + const LayoutPoint& point() const { return m_point; } + IntPoint roundedPoint() const { return roundedIntPoint(m_point); } + + // Rect-based hit test related methods. + bool isRectBasedTest() const { return m_isRectBased; } + bool isRectilinear() const { return m_isRectilinear; } + IntRect boundingBox() const { return m_boundingBox; } + + static IntRect rectForPoint(const LayoutPoint&, + unsigned topPadding, + unsigned rightPadding, + unsigned bottomPadding, + unsigned leftPadding); + int topPadding() const { return roundedPoint().y() - m_boundingBox.y(); } + int rightPadding() const { + return m_boundingBox.maxX() - roundedPoint().x() - 1; + } + int bottomPadding() const { + return m_boundingBox.maxY() - roundedPoint().y() - 1; + } + int leftPadding() const { return roundedPoint().x() - m_boundingBox.x(); } + + bool intersects(const LayoutRect&) const; + bool intersects(const FloatRect&) const; + bool intersects(const RoundedRect&) const; + bool containsPoint(const FloatPoint&) const; + + const FloatPoint& transformedPoint() const { return m_transformedPoint; } + const FloatQuad& transformedRect() const { return m_transformedRect; } + + private: + template + bool intersectsRect(const RectType&) const; + void move(const LayoutSize& offset); + + // This is cached forms of the more accurate point and area below. + LayoutPoint m_point; + IntRect m_boundingBox; + + FloatPoint m_transformedPoint; + FloatQuad m_transformedRect; + + bool m_isRectBased; + bool m_isRectilinear; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_HITTESTLOCATION_H_ diff --git a/sky/engine/core/rendering/HitTestRequest.h b/sky/engine/core/rendering/HitTestRequest.h index ea820575f811e..d98e9829e7cb4 100644 --- a/sky/engine/core/rendering/HitTestRequest.h +++ b/sky/engine/core/rendering/HitTestRequest.h @@ -18,7 +18,7 @@ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * -*/ + */ #ifndef SKY_ENGINE_CORE_RENDERING_HITTESTREQUEST_H_ #define SKY_ENGINE_CORE_RENDERING_HITTESTREQUEST_H_ @@ -26,38 +26,37 @@ namespace blink { class HitTestRequest { -public: - enum RequestType { - ReadOnly = 1 << 1, - Active = 1 << 2, - Move = 1 << 3, - Release = 1 << 4, - IgnoreClipping = 1 << 5, - SVGClipContent = 1 << 6, - IgnorePointerEventsNone = 1 << 8, - }; - - typedef unsigned HitTestRequestType; - - HitTestRequest(HitTestRequestType requestType) - : m_requestType(requestType) - { - } - - bool readOnly() const { return m_requestType & ReadOnly; } - bool active() const { return m_requestType & Active; } - bool move() const { return m_requestType & Move; } - bool release() const { return m_requestType & Release; } - bool ignoreClipping() const { return m_requestType & IgnoreClipping; } - bool svgClipContent() const { return m_requestType & SVGClipContent; } - bool ignorePointerEventsNone() const { return m_requestType & IgnorePointerEventsNone; } - - HitTestRequestType type() const { return m_requestType; } - -private: - HitTestRequestType m_requestType; + public: + enum RequestType { + ReadOnly = 1 << 1, + Active = 1 << 2, + Move = 1 << 3, + Release = 1 << 4, + IgnoreClipping = 1 << 5, + SVGClipContent = 1 << 6, + IgnorePointerEventsNone = 1 << 8, + }; + + typedef unsigned HitTestRequestType; + + HitTestRequest(HitTestRequestType requestType) : m_requestType(requestType) {} + + bool readOnly() const { return m_requestType & ReadOnly; } + bool active() const { return m_requestType & Active; } + bool move() const { return m_requestType & Move; } + bool release() const { return m_requestType & Release; } + bool ignoreClipping() const { return m_requestType & IgnoreClipping; } + bool svgClipContent() const { return m_requestType & SVGClipContent; } + bool ignorePointerEventsNone() const { + return m_requestType & IgnorePointerEventsNone; + } + + HitTestRequestType type() const { return m_requestType; } + + private: + HitTestRequestType m_requestType; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_HITTESTREQUEST_H_ diff --git a/sky/engine/core/rendering/HitTestResult.cpp b/sky/engine/core/rendering/HitTestResult.cpp index e377d891bbde4..599e36ec983b3 100644 --- a/sky/engine/core/rendering/HitTestResult.cpp +++ b/sky/engine/core/rendering/HitTestResult.cpp @@ -17,7 +17,7 @@ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * -*/ + */ #include "flutter/sky/engine/core/rendering/HitTestResult.h" @@ -26,82 +26,69 @@ namespace blink { -HitTestResult::HitTestResult() -{ -} +HitTestResult::HitTestResult() {} HitTestResult::HitTestResult(const LayoutPoint& point) - : m_hitTestLocation(point) - , m_pointInInnerNodeFrame(point) -{ -} - -HitTestResult::HitTestResult(const LayoutPoint& centerPoint, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding) - : m_hitTestLocation(centerPoint, topPadding, rightPadding, bottomPadding, leftPadding) - , m_pointInInnerNodeFrame(centerPoint) -{ -} + : m_hitTestLocation(point), m_pointInInnerNodeFrame(point) {} + +HitTestResult::HitTestResult(const LayoutPoint& centerPoint, + unsigned topPadding, + unsigned rightPadding, + unsigned bottomPadding, + unsigned leftPadding) + : m_hitTestLocation(centerPoint, + topPadding, + rightPadding, + bottomPadding, + leftPadding), + m_pointInInnerNodeFrame(centerPoint) {} HitTestResult::HitTestResult(const HitTestLocation& other) - : m_hitTestLocation(other) - , m_pointInInnerNodeFrame(m_hitTestLocation.point()) -{ -} + : m_hitTestLocation(other), + m_pointInInnerNodeFrame(m_hitTestLocation.point()) {} HitTestResult::HitTestResult(const HitTestResult& other) - : m_hitTestLocation(other.m_hitTestLocation) - , m_localPoint(other.localPoint()) -{ -} + : m_hitTestLocation(other.m_hitTestLocation), + m_localPoint(other.localPoint()) {} -HitTestResult::~HitTestResult() -{ -} +HitTestResult::~HitTestResult() {} -HitTestResult& HitTestResult::operator=(const HitTestResult& other) -{ - m_hitTestLocation = other.m_hitTestLocation; - m_pointInInnerNodeFrame = other.m_pointInInnerNodeFrame; - m_localPoint = other.localPoint(); - return *this; +HitTestResult& HitTestResult::operator=(const HitTestResult& other) { + m_hitTestLocation = other.m_hitTestLocation; + m_pointInInnerNodeFrame = other.m_pointInInnerNodeFrame; + m_localPoint = other.localPoint(); + return *this; } -RenderObject* HitTestResult::renderer() const -{ - return 0; +RenderObject* HitTestResult::renderer() const { + return 0; } -bool HitTestResult::isSelected() const -{ - return false; +bool HitTestResult::isSelected() const { + return false; } -Image* HitTestResult::image() const -{ - return 0; +Image* HitTestResult::image() const { + return 0; } -IntRect HitTestResult::imageRect() const -{ - return IntRect(); +IntRect HitTestResult::imageRect() const { + return IntRect(); } -bool HitTestResult::isMisspelled() const -{ - return false; +bool HitTestResult::isMisspelled() const { + return false; } -// FIXME: This function needs a better name and may belong in a different class. It's not -// really isContentEditable(); it's more like needsEditingContextMenu(). In many ways, this -// function would make more sense in the ContextMenu class, except that WebElementDictionary -// hooks into it. Anyway, we should architect this better. -bool HitTestResult::isContentEditable() const -{ - return false; +// FIXME: This function needs a better name and may belong in a different class. +// It's not really isContentEditable(); it's more like +// needsEditingContextMenu(). In many ways, this function would make more sense +// in the ContextMenu class, except that WebElementDictionary hooks into it. +// Anyway, we should architect this better. +bool HitTestResult::isContentEditable() const { + return false; } -void HitTestResult::append(const HitTestResult& other) -{ -} +void HitTestResult::append(const HitTestResult& other) {} -} // namespace blink +} // namespace blink diff --git a/sky/engine/core/rendering/HitTestResult.h b/sky/engine/core/rendering/HitTestResult.h index 02ecfea1e131f..3b05e45f18686 100644 --- a/sky/engine/core/rendering/HitTestResult.h +++ b/sky/engine/core/rendering/HitTestResult.h @@ -17,7 +17,7 @@ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * -*/ + */ #ifndef SKY_ENGINE_CORE_RENDERING_HITTESTRESULT_H_ #define SKY_ENGINE_CORE_RENDERING_HITTESTRESULT_H_ @@ -40,52 +40,64 @@ class Image; class RenderObject; class HitTestResult { - DISALLOW_ALLOCATION(); -public: - HitTestResult(); - HitTestResult(const LayoutPoint&); - // Pass non-negative padding values to perform a rect-based hit test. - HitTestResult(const LayoutPoint& centerPoint, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding); - HitTestResult(const HitTestLocation&); - HitTestResult(const HitTestResult&); - ~HitTestResult(); - HitTestResult& operator=(const HitTestResult&); - - bool isOverWidget() const { return m_isOverWidget; } - - // Forwarded from HitTestLocation - bool isRectBasedTest() const { return m_hitTestLocation.isRectBasedTest(); } - - // The hit-tested point in the coordinates of the main frame. - const LayoutPoint& pointInMainFrame() const { return m_hitTestLocation.point(); } - IntPoint roundedPointInMainFrame() const { return roundedIntPoint(pointInMainFrame()); } - - // The hit-tested point in the coordinates of the inner node. - const LayoutPoint& localPoint() const { return m_localPoint; } - void setLocalPoint(const LayoutPoint& p) { m_localPoint = p; } - - RenderObject* renderer() const; - - const HitTestLocation& hitTestLocation() const { return m_hitTestLocation; } - - bool isSelected() const; - Image* image() const; - IntRect imageRect() const; - bool isMisspelled() const; - bool isContentEditable() const; - - void append(const HitTestResult&); - -private: - HitTestLocation m_hitTestLocation; - - // FIXME: Nothing changes this to a value different from m_hitTestLocation! - LayoutPoint m_pointInInnerNodeFrame; // The hit-tested point in innerNode frame coordinates. - LayoutPoint m_localPoint; // A point in the local coordinate space of m_innerNonSharedNode's renderer. Allows us to efficiently - // determine where inside the renderer we hit on subsequent operations. - bool m_isOverWidget; // Returns true if we are over a widget. + DISALLOW_ALLOCATION(); + + public: + HitTestResult(); + HitTestResult(const LayoutPoint&); + // Pass non-negative padding values to perform a rect-based hit test. + HitTestResult(const LayoutPoint& centerPoint, + unsigned topPadding, + unsigned rightPadding, + unsigned bottomPadding, + unsigned leftPadding); + HitTestResult(const HitTestLocation&); + HitTestResult(const HitTestResult&); + ~HitTestResult(); + HitTestResult& operator=(const HitTestResult&); + + bool isOverWidget() const { return m_isOverWidget; } + + // Forwarded from HitTestLocation + bool isRectBasedTest() const { return m_hitTestLocation.isRectBasedTest(); } + + // The hit-tested point in the coordinates of the main frame. + const LayoutPoint& pointInMainFrame() const { + return m_hitTestLocation.point(); + } + IntPoint roundedPointInMainFrame() const { + return roundedIntPoint(pointInMainFrame()); + } + + // The hit-tested point in the coordinates of the inner node. + const LayoutPoint& localPoint() const { return m_localPoint; } + void setLocalPoint(const LayoutPoint& p) { m_localPoint = p; } + + RenderObject* renderer() const; + + const HitTestLocation& hitTestLocation() const { return m_hitTestLocation; } + + bool isSelected() const; + Image* image() const; + IntRect imageRect() const; + bool isMisspelled() const; + bool isContentEditable() const; + + void append(const HitTestResult&); + + private: + HitTestLocation m_hitTestLocation; + + // FIXME: Nothing changes this to a value different from m_hitTestLocation! + LayoutPoint m_pointInInnerNodeFrame; // The hit-tested point in innerNode + // frame coordinates. + LayoutPoint m_localPoint; // A point in the local coordinate space of + // m_innerNonSharedNode's renderer. Allows us to + // efficiently determine where inside the renderer + // we hit on subsequent operations. + bool m_isOverWidget; // Returns true if we are over a widget. }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_HITTESTRESULT_H_ diff --git a/sky/engine/core/rendering/HitTestingTransformState.cpp b/sky/engine/core/rendering/HitTestingTransformState.cpp index bc6f2282a69f3..42c3cd0f8cf59 100644 --- a/sky/engine/core/rendering/HitTestingTransformState.cpp +++ b/sky/engine/core/rendering/HitTestingTransformState.cpp @@ -30,58 +30,56 @@ namespace blink { -void HitTestingTransformState::translate(int x, int y, TransformAccumulation accumulate) -{ - m_accumulatedTransform.translate(x, y); - if (accumulate == FlattenTransform) - flattenWithTransform(m_accumulatedTransform); +void HitTestingTransformState::translate(int x, + int y, + TransformAccumulation accumulate) { + m_accumulatedTransform.translate(x, y); + if (accumulate == FlattenTransform) + flattenWithTransform(m_accumulatedTransform); - m_accumulatingTransform = accumulate == AccumulateTransform; + m_accumulatingTransform = accumulate == AccumulateTransform; } -void HitTestingTransformState::applyTransform(const TransformationMatrix& transformFromContainer, TransformAccumulation accumulate) -{ - m_accumulatedTransform.multiply(transformFromContainer); - if (accumulate == FlattenTransform) - flattenWithTransform(m_accumulatedTransform); +void HitTestingTransformState::applyTransform( + const TransformationMatrix& transformFromContainer, + TransformAccumulation accumulate) { + m_accumulatedTransform.multiply(transformFromContainer); + if (accumulate == FlattenTransform) + flattenWithTransform(m_accumulatedTransform); - m_accumulatingTransform = accumulate == AccumulateTransform; + m_accumulatingTransform = accumulate == AccumulateTransform; } -void HitTestingTransformState::flatten() -{ - flattenWithTransform(m_accumulatedTransform); +void HitTestingTransformState::flatten() { + flattenWithTransform(m_accumulatedTransform); } -void HitTestingTransformState::flattenWithTransform(const TransformationMatrix& t) -{ - TransformationMatrix inverseTransform = t.inverse(); - m_lastPlanarPoint = inverseTransform.projectPoint(m_lastPlanarPoint); - m_lastPlanarQuad = inverseTransform.projectQuad(m_lastPlanarQuad); - m_lastPlanarArea = inverseTransform.projectQuad(m_lastPlanarArea); +void HitTestingTransformState::flattenWithTransform( + const TransformationMatrix& t) { + TransformationMatrix inverseTransform = t.inverse(); + m_lastPlanarPoint = inverseTransform.projectPoint(m_lastPlanarPoint); + m_lastPlanarQuad = inverseTransform.projectQuad(m_lastPlanarQuad); + m_lastPlanarArea = inverseTransform.projectQuad(m_lastPlanarArea); - m_accumulatedTransform.makeIdentity(); - m_accumulatingTransform = false; + m_accumulatedTransform.makeIdentity(); + m_accumulatingTransform = false; } -FloatPoint HitTestingTransformState::mappedPoint() const -{ - return m_accumulatedTransform.inverse().projectPoint(m_lastPlanarPoint); +FloatPoint HitTestingTransformState::mappedPoint() const { + return m_accumulatedTransform.inverse().projectPoint(m_lastPlanarPoint); } -FloatQuad HitTestingTransformState::mappedQuad() const -{ - return m_accumulatedTransform.inverse().projectQuad(m_lastPlanarQuad); +FloatQuad HitTestingTransformState::mappedQuad() const { + return m_accumulatedTransform.inverse().projectQuad(m_lastPlanarQuad); } -FloatQuad HitTestingTransformState::mappedArea() const -{ - return m_accumulatedTransform.inverse().projectQuad(m_lastPlanarArea); +FloatQuad HitTestingTransformState::mappedArea() const { + return m_accumulatedTransform.inverse().projectQuad(m_lastPlanarArea); } -LayoutRect HitTestingTransformState::boundsOfMappedArea() const -{ - return m_accumulatedTransform.inverse().clampedBoundsOfProjectedQuad(m_lastPlanarArea); +LayoutRect HitTestingTransformState::boundsOfMappedArea() const { + return m_accumulatedTransform.inverse().clampedBoundsOfProjectedQuad( + m_lastPlanarArea); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/core/rendering/HitTestingTransformState.h b/sky/engine/core/rendering/HitTestingTransformState.h index d4eaf51d7a3fc..89c5d7dbd9049 100644 --- a/sky/engine/core/rendering/HitTestingTransformState.h +++ b/sky/engine/core/rendering/HitTestingTransformState.h @@ -36,60 +36,61 @@ namespace blink { -// FIXME: Now that TransformState lazily creates its TransformationMatrix it takes up less space. -// So there's really no need for a ref counted version. So This class should be removed and replaced -// with TransformState. There are some minor differences (like the way translate() works slightly -// differently than move()) so care has to be taken when this is done. +// FIXME: Now that TransformState lazily creates its TransformationMatrix it +// takes up less space. So there's really no need for a ref counted version. So +// This class should be removed and replaced with TransformState. There are some +// minor differences (like the way translate() works slightly differently than +// move()) so care has to be taken when this is done. class HitTestingTransformState : public RefCounted { -public: - static PassRefPtr create(const FloatPoint& p, const FloatQuad& quad, const FloatQuad& area) - { - return adoptRef(new HitTestingTransformState(p, quad, area)); - } + public: + static PassRefPtr create(const FloatPoint& p, + const FloatQuad& quad, + const FloatQuad& area) { + return adoptRef(new HitTestingTransformState(p, quad, area)); + } - static PassRefPtr create(const HitTestingTransformState& other) - { - return adoptRef(new HitTestingTransformState(other)); - } + static PassRefPtr create( + const HitTestingTransformState& other) { + return adoptRef(new HitTestingTransformState(other)); + } - enum TransformAccumulation { FlattenTransform, AccumulateTransform }; - void translate(int x, int y, TransformAccumulation); - void applyTransform(const TransformationMatrix& transformFromContainer, TransformAccumulation); + enum TransformAccumulation { FlattenTransform, AccumulateTransform }; + void translate(int x, int y, TransformAccumulation); + void applyTransform(const TransformationMatrix& transformFromContainer, + TransformAccumulation); - FloatPoint mappedPoint() const; - FloatQuad mappedQuad() const; - FloatQuad mappedArea() const; - LayoutRect boundsOfMappedArea() const; - void flatten(); + FloatPoint mappedPoint() const; + FloatQuad mappedQuad() const; + FloatQuad mappedArea() const; + LayoutRect boundsOfMappedArea() const; + void flatten(); - FloatPoint m_lastPlanarPoint; - FloatQuad m_lastPlanarQuad; - FloatQuad m_lastPlanarArea; - TransformationMatrix m_accumulatedTransform; - bool m_accumulatingTransform; + FloatPoint m_lastPlanarPoint; + FloatQuad m_lastPlanarQuad; + FloatQuad m_lastPlanarArea; + TransformationMatrix m_accumulatedTransform; + bool m_accumulatingTransform; -private: - HitTestingTransformState(const FloatPoint& p, const FloatQuad& quad, const FloatQuad& area) - : m_lastPlanarPoint(p) - , m_lastPlanarQuad(quad) - , m_lastPlanarArea(area) - , m_accumulatingTransform(false) - { - } + private: + HitTestingTransformState(const FloatPoint& p, + const FloatQuad& quad, + const FloatQuad& area) + : m_lastPlanarPoint(p), + m_lastPlanarQuad(quad), + m_lastPlanarArea(area), + m_accumulatingTransform(false) {} - HitTestingTransformState(const HitTestingTransformState& other) - : RefCounted() - , m_lastPlanarPoint(other.m_lastPlanarPoint) - , m_lastPlanarQuad(other.m_lastPlanarQuad) - , m_lastPlanarArea(other.m_lastPlanarArea) - , m_accumulatedTransform(other.m_accumulatedTransform) - , m_accumulatingTransform(other.m_accumulatingTransform) - { - } + HitTestingTransformState(const HitTestingTransformState& other) + : RefCounted(), + m_lastPlanarPoint(other.m_lastPlanarPoint), + m_lastPlanarQuad(other.m_lastPlanarQuad), + m_lastPlanarArea(other.m_lastPlanarArea), + m_accumulatedTransform(other.m_accumulatedTransform), + m_accumulatingTransform(other.m_accumulatingTransform) {} - void flattenWithTransform(const TransformationMatrix&); + void flattenWithTransform(const TransformationMatrix&); }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_HITTESTINGTRANSFORMSTATE_H_ diff --git a/sky/engine/core/rendering/InlineBox.cpp b/sky/engine/core/rendering/InlineBox.cpp index b464cc345a231..13b761d641ead 100644 --- a/sky/engine/core/rendering/InlineBox.cpp +++ b/sky/engine/core/rendering/InlineBox.cpp @@ -21,8 +21,8 @@ #include "flutter/sky/engine/core/rendering/InlineFlowBox.h" #include "flutter/sky/engine/core/rendering/PaintInfo.h" -#include "flutter/sky/engine/core/rendering/RenderParagraph.h" #include "flutter/sky/engine/core/rendering/RenderObjectInlines.h" +#include "flutter/sky/engine/core/rendering/RenderParagraph.h" #include "flutter/sky/engine/core/rendering/RootInlineBox.h" #include "flutter/sky/engine/platform/Partitions.h" #include "flutter/sky/engine/platform/fonts/FontMetrics.h" @@ -34,275 +34,264 @@ namespace blink { struct SameSizeAsInlineBox { - virtual ~SameSizeAsInlineBox() { } - void* a[4]; - FloatPoint b; - float c; - uint32_t d : 32; + virtual ~SameSizeAsInlineBox() {} + void* a[4]; + FloatPoint b; + float c; + uint32_t d : 32; #if ENABLE(ASSERT) - bool f; + bool f; #endif }; -COMPILE_ASSERT(sizeof(InlineBox) == sizeof(SameSizeAsInlineBox), InlineBox_size_guard); +COMPILE_ASSERT(sizeof(InlineBox) == sizeof(SameSizeAsInlineBox), + InlineBox_size_guard); #if ENABLE(ASSERT) -InlineBox::~InlineBox() -{ - if (!m_hasBadParent && m_parent) - m_parent->setHasBadChildList(); +InlineBox::~InlineBox() { + if (!m_hasBadParent && m_parent) + m_parent->setHasBadChildList(); } #endif -void InlineBox::remove(MarkLineBoxes markLineBoxes) -{ - if (parent()) - parent()->removeChild(this, markLineBoxes); +void InlineBox::remove(MarkLineBoxes markLineBoxes) { + if (parent()) + parent()->removeChild(this, markLineBoxes); } -void* InlineBox::operator new(size_t sz) -{ - return partitionAlloc(Partitions::getRenderingPartition(), sz); +void* InlineBox::operator new(size_t sz) { + return partitionAlloc(Partitions::getRenderingPartition(), sz); } -void InlineBox::operator delete(void* ptr) -{ - partitionFree(ptr); +void InlineBox::operator delete(void* ptr) { + partitionFree(ptr); } #ifndef NDEBUG -const char* InlineBox::boxName() const -{ - return "InlineBox"; +const char* InlineBox::boxName() const { + return "InlineBox"; } -void InlineBox::showTreeForThis() const -{ - renderer().showTreeForThis(); +void InlineBox::showTreeForThis() const { + renderer().showTreeForThis(); } -void InlineBox::showLineTreeForThis() const -{ - renderer().containingBlock()->showLineTreeAndMark(this, "*"); +void InlineBox::showLineTreeForThis() const { + renderer().containingBlock()->showLineTreeAndMark(this, "*"); } -void InlineBox::showLineTreeAndMark(const InlineBox* markedBox1, const char* markedLabel1, const InlineBox* markedBox2, const char* markedLabel2, const RenderObject* obj, int depth) const -{ - int printedCharacters = 0; - if (this == markedBox1) - printedCharacters += fprintf(stderr, "%s", markedLabel1); - if (this == markedBox2) - printedCharacters += fprintf(stderr, "%s", markedLabel2); - if (&renderer() == obj) - printedCharacters += fprintf(stderr, "*"); - for (; printedCharacters < depth * 2; printedCharacters++) - fputc(' ', stderr); +void InlineBox::showLineTreeAndMark(const InlineBox* markedBox1, + const char* markedLabel1, + const InlineBox* markedBox2, + const char* markedLabel2, + const RenderObject* obj, + int depth) const { + int printedCharacters = 0; + if (this == markedBox1) + printedCharacters += fprintf(stderr, "%s", markedLabel1); + if (this == markedBox2) + printedCharacters += fprintf(stderr, "%s", markedLabel2); + if (&renderer() == obj) + printedCharacters += fprintf(stderr, "*"); + for (; printedCharacters < depth * 2; printedCharacters++) + fputc(' ', stderr); - showBox(printedCharacters); + showBox(printedCharacters); } -void InlineBox::showBox(int printedCharacters) const -{ - printedCharacters += fprintf(stderr, "%s\t%p", boxName(), this); - for (; printedCharacters < showTreeCharacterOffset; printedCharacters++) - fputc(' ', stderr); - fprintf(stderr, "\t%s %p {pos=%g,%g size=%g,%g} baseline=%i/%i\n", - renderer().renderName(), &renderer(), x(), y(), width(), height(), - baselinePosition(AlphabeticBaseline), - baselinePosition(IdeographicBaseline)); +void InlineBox::showBox(int printedCharacters) const { + printedCharacters += fprintf(stderr, "%s\t%p", boxName(), this); + for (; printedCharacters < showTreeCharacterOffset; printedCharacters++) + fputc(' ', stderr); + fprintf(stderr, "\t%s %p {pos=%g,%g size=%g,%g} baseline=%i/%i\n", + renderer().renderName(), &renderer(), x(), y(), width(), height(), + baselinePosition(AlphabeticBaseline), + baselinePosition(IdeographicBaseline)); } #endif -float InlineBox::logicalHeight() const -{ - if (hasVirtualLogicalHeight()) - return virtualLogicalHeight(); +float InlineBox::logicalHeight() const { + if (hasVirtualLogicalHeight()) + return virtualLogicalHeight(); - if (renderer().isText()) - return m_bitfields.isText() ? renderer().style(isFirstLineStyle())->fontMetrics().height() : 0; - if (renderer().isBox() && parent()) - return toRenderBox(renderer()).height().toFloat(); + if (renderer().isText()) + return m_bitfields.isText() + ? renderer().style(isFirstLineStyle())->fontMetrics().height() + : 0; + if (renderer().isBox() && parent()) + return toRenderBox(renderer()).height().toFloat(); - ASSERT(isInlineFlowBox()); - RenderBoxModelObject* flowObject = boxModelObject(); - const FontMetrics& fontMetrics = renderer().style(isFirstLineStyle())->fontMetrics(); - float result = fontMetrics.height(); - if (parent()) - result += flowObject->borderAndPaddingLogicalHeight(); - return result; + ASSERT(isInlineFlowBox()); + RenderBoxModelObject* flowObject = boxModelObject(); + const FontMetrics& fontMetrics = + renderer().style(isFirstLineStyle())->fontMetrics(); + float result = fontMetrics.height(); + if (parent()) + result += flowObject->borderAndPaddingLogicalHeight(); + return result; } -int InlineBox::baselinePosition(FontBaseline baselineType) const -{ - return boxModelObject()->baselinePosition(baselineType, m_bitfields.firstLine(), HorizontalLine, PositionOnContainingLine); +int InlineBox::baselinePosition(FontBaseline baselineType) const { + return boxModelObject()->baselinePosition( + baselineType, m_bitfields.firstLine(), HorizontalLine, + PositionOnContainingLine); } -LayoutUnit InlineBox::lineHeight() const -{ - return boxModelObject()->lineHeight(m_bitfields.firstLine(), HorizontalLine, PositionOnContainingLine); +LayoutUnit InlineBox::lineHeight() const { + return boxModelObject()->lineHeight(m_bitfields.firstLine(), HorizontalLine, + PositionOnContainingLine); } -int InlineBox::caretMinOffset() const -{ - return renderer().caretMinOffset(); +int InlineBox::caretMinOffset() const { + return renderer().caretMinOffset(); } -int InlineBox::caretMaxOffset() const -{ - return renderer().caretMaxOffset(); +int InlineBox::caretMaxOffset() const { + return renderer().caretMaxOffset(); } -void InlineBox::dirtyLineBoxes() -{ - markDirty(); - for (InlineFlowBox* curr = parent(); curr && !curr->isDirty(); curr = curr->parent()) - curr->markDirty(); +void InlineBox::dirtyLineBoxes() { + markDirty(); + for (InlineFlowBox* curr = parent(); curr && !curr->isDirty(); + curr = curr->parent()) + curr->markDirty(); } -void InlineBox::deleteLine() -{ - if (!m_bitfields.extracted() && renderer().isBox()) - toRenderBox(renderer()).setInlineBoxWrapper(0); - destroy(); +void InlineBox::deleteLine() { + if (!m_bitfields.extracted() && renderer().isBox()) + toRenderBox(renderer()).setInlineBoxWrapper(0); + destroy(); } -void InlineBox::extractLine() -{ - m_bitfields.setExtracted(true); - if (renderer().isBox()) - toRenderBox(renderer()).setInlineBoxWrapper(0); +void InlineBox::extractLine() { + m_bitfields.setExtracted(true); + if (renderer().isBox()) + toRenderBox(renderer()).setInlineBoxWrapper(0); } -void InlineBox::attachLine() -{ - m_bitfields.setExtracted(false); - if (renderer().isBox()) - toRenderBox(renderer()).setInlineBoxWrapper(this); +void InlineBox::attachLine() { + m_bitfields.setExtracted(false); + if (renderer().isBox()) + toRenderBox(renderer()).setInlineBoxWrapper(this); } -void InlineBox::adjustPosition(float dx, float dy) -{ - m_topLeft.move(dx, dy); +void InlineBox::adjustPosition(float dx, float dy) { + m_topLeft.move(dx, dy); - if (renderer().isReplaced()) - toRenderBox(renderer()).move(dx, dy); + if (renderer().isReplaced()) + toRenderBox(renderer()).move(dx, dy); } -void InlineBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit /* lineTop */, LayoutUnit /*lineBottom*/, Vector& layers) -{ - renderer().paint(paintInfo, paintOffset, layers); +void InlineBox::paint(PaintInfo& paintInfo, + const LayoutPoint& paintOffset, + LayoutUnit /* lineTop */, + LayoutUnit /*lineBottom*/, + Vector& layers) { + renderer().paint(paintInfo, paintOffset, layers); } -bool InlineBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, LayoutUnit /* lineTop */, LayoutUnit /*lineBottom*/) -{ - // Hit test all phases of replaced elements atomically, as though the replaced element established its - // own stacking context. (See Appendix E.2, section 6.4 on inline block/table elements in the CSS2.1 - // specification.) - LayoutPoint childPoint = accumulatedOffset; - return renderer().hitTest(request, result, locationInContainer, childPoint); +bool InlineBox::nodeAtPoint(const HitTestRequest& request, + HitTestResult& result, + const HitTestLocation& locationInContainer, + const LayoutPoint& accumulatedOffset, + LayoutUnit /* lineTop */, + LayoutUnit /*lineBottom*/) { + // Hit test all phases of replaced elements atomically, as though the replaced + // element established its own stacking context. (See Appendix E.2, + // section 6.4 on inline block/table elements in the CSS2.1 specification.) + LayoutPoint childPoint = accumulatedOffset; + return renderer().hitTest(request, result, locationInContainer, childPoint); } -const RootInlineBox& InlineBox::root() const -{ - if (m_parent) - return m_parent->root(); - ASSERT(isRootInlineBox()); - return static_cast(*this); +const RootInlineBox& InlineBox::root() const { + if (m_parent) + return m_parent->root(); + ASSERT(isRootInlineBox()); + return static_cast(*this); } -RootInlineBox& InlineBox::root() -{ - if (m_parent) - return m_parent->root(); - ASSERT(isRootInlineBox()); - return static_cast(*this); +RootInlineBox& InlineBox::root() { + if (m_parent) + return m_parent->root(); + ASSERT(isRootInlineBox()); + return static_cast(*this); } -bool InlineBox::nextOnLineExists() const -{ - if (!m_bitfields.determinedIfNextOnLineExists()) { - m_bitfields.setDeterminedIfNextOnLineExists(true); +bool InlineBox::nextOnLineExists() const { + if (!m_bitfields.determinedIfNextOnLineExists()) { + m_bitfields.setDeterminedIfNextOnLineExists(true); - if (!parent()) - m_bitfields.setNextOnLineExists(false); - else if (nextOnLine()) - m_bitfields.setNextOnLineExists(true); - else - m_bitfields.setNextOnLineExists(parent()->nextOnLineExists()); - } - return m_bitfields.nextOnLineExists(); + if (!parent()) + m_bitfields.setNextOnLineExists(false); + else if (nextOnLine()) + m_bitfields.setNextOnLineExists(true); + else + m_bitfields.setNextOnLineExists(parent()->nextOnLineExists()); + } + return m_bitfields.nextOnLineExists(); } -InlineBox* InlineBox::nextLeafChild() const -{ - InlineBox* leaf = 0; - for (InlineBox* box = nextOnLine(); box && !leaf; box = box->nextOnLine()) - leaf = box->isLeaf() ? box : toInlineFlowBox(box)->firstLeafChild(); - if (!leaf && parent()) - leaf = parent()->nextLeafChild(); - return leaf; +InlineBox* InlineBox::nextLeafChild() const { + InlineBox* leaf = 0; + for (InlineBox* box = nextOnLine(); box && !leaf; box = box->nextOnLine()) + leaf = box->isLeaf() ? box : toInlineFlowBox(box)->firstLeafChild(); + if (!leaf && parent()) + leaf = parent()->nextLeafChild(); + return leaf; } -InlineBox* InlineBox::prevLeafChild() const -{ - InlineBox* leaf = 0; - for (InlineBox* box = prevOnLine(); box && !leaf; box = box->prevOnLine()) - leaf = box->isLeaf() ? box : toInlineFlowBox(box)->lastLeafChild(); - if (!leaf && parent()) - leaf = parent()->prevLeafChild(); - return leaf; +InlineBox* InlineBox::prevLeafChild() const { + InlineBox* leaf = 0; + for (InlineBox* box = prevOnLine(); box && !leaf; box = box->prevOnLine()) + leaf = box->isLeaf() ? box : toInlineFlowBox(box)->lastLeafChild(); + if (!leaf && parent()) + leaf = parent()->prevLeafChild(); + return leaf; } -InlineBox* InlineBox::nextLeafChildIgnoringLineBreak() const -{ - InlineBox* leaf = nextLeafChild(); - if (leaf && leaf->isLineBreak()) - return 0; - return leaf; +InlineBox* InlineBox::nextLeafChildIgnoringLineBreak() const { + InlineBox* leaf = nextLeafChild(); + if (leaf && leaf->isLineBreak()) + return 0; + return leaf; } -InlineBox* InlineBox::prevLeafChildIgnoringLineBreak() const -{ - InlineBox* leaf = prevLeafChild(); - if (leaf && leaf->isLineBreak()) - return 0; - return leaf; +InlineBox* InlineBox::prevLeafChildIgnoringLineBreak() const { + InlineBox* leaf = prevLeafChild(); + if (leaf && leaf->isLineBreak()) + return 0; + return leaf; } -RenderObject::SelectionState InlineBox::selectionState() -{ - return renderer().selectionState(); +RenderObject::SelectionState InlineBox::selectionState() { + return renderer().selectionState(); } -void InlineBox::clearKnownToHaveNoOverflow() -{ - m_bitfields.setKnownToHaveNoOverflow(false); - if (parent() && parent()->knownToHaveNoOverflow()) - parent()->clearKnownToHaveNoOverflow(); +void InlineBox::clearKnownToHaveNoOverflow() { + m_bitfields.setKnownToHaveNoOverflow(false); + if (parent() && parent()->knownToHaveNoOverflow()) + parent()->clearKnownToHaveNoOverflow(); } -FloatPoint InlineBox::locationIncludingFlipping() -{ - // FIXME(sky): remove - return FloatPoint(x(), y()); +FloatPoint InlineBox::locationIncludingFlipping() { + // FIXME(sky): remove + return FloatPoint(x(), y()); } -} // namespace blink +} // namespace blink #ifndef NDEBUG -void showTree(const blink::InlineBox* b) -{ - if (b) - b->showTreeForThis(); +void showTree(const blink::InlineBox* b) { + if (b) + b->showTreeForThis(); } -void showLineTree(const blink::InlineBox* b) -{ - if (b) - b->showLineTreeForThis(); +void showLineTree(const blink::InlineBox* b) { + if (b) + b->showLineTreeForThis(); } #endif diff --git a/sky/engine/core/rendering/InlineBox.h b/sky/engine/core/rendering/InlineBox.h index 6aa9ad070cb87..bc939a9f09cff 100644 --- a/sky/engine/core/rendering/InlineBox.h +++ b/sky/engine/core/rendering/InlineBox.h @@ -1,5 +1,6 @@ /* - * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2009, 2010, 2011 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2009, 2010, 2011 Apple Inc. All + * rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -36,382 +37,458 @@ enum MarkLineBoxes { MarkLineBoxesDirty, DontMarkLineBoxes }; // InlineBox represents a rectangle that occurs on a line. It corresponds to // some RenderObject (i.e., it represents a portion of that RenderObject). class InlineBox { - WTF_MAKE_NONCOPYABLE(InlineBox); -public: - InlineBox(RenderObject& obj) - : m_next(0) - , m_prev(0) - , m_parent(0) - , m_renderer(obj) - , m_logicalWidth(0) + WTF_MAKE_NONCOPYABLE(InlineBox); + + public: + InlineBox(RenderObject& obj) + : m_next(0), + m_prev(0), + m_parent(0), + m_renderer(obj), + m_logicalWidth(0) #if ENABLE(ASSERT) - , m_hasBadParent(false) + , + m_hasBadParent(false) #endif - { - } - - InlineBox(RenderObject& obj, FloatPoint topLeft, float logicalWidth, bool firstLine, bool constructed, - bool dirty, bool extracted, InlineBox* next, InlineBox* prev, InlineFlowBox* parent) - : m_next(next) - , m_prev(prev) - , m_parent(parent) - , m_renderer(obj) - , m_topLeft(topLeft) - , m_logicalWidth(logicalWidth) - , m_bitfields(firstLine, constructed, dirty, extracted) + { + } + + InlineBox(RenderObject& obj, + FloatPoint topLeft, + float logicalWidth, + bool firstLine, + bool constructed, + bool dirty, + bool extracted, + InlineBox* next, + InlineBox* prev, + InlineFlowBox* parent) + : m_next(next), + m_prev(prev), + m_parent(parent), + m_renderer(obj), + m_topLeft(topLeft), + m_logicalWidth(logicalWidth), + m_bitfields(firstLine, constructed, dirty, extracted) #if ENABLE(ASSERT) - , m_hasBadParent(false) + , + m_hasBadParent(false) #endif - { - } - - virtual ~InlineBox(); - - virtual void destroy() { delete this; } - - virtual void deleteLine(); - virtual void extractLine(); - virtual void attachLine(); - - virtual bool isLineBreak() const { return false; } - - virtual void adjustPosition(float dx, float dy); - void adjustLogicalPosition(float deltaLogicalLeft, float deltaLogicalTop) - { - // FIXME(sky): Remove - adjustPosition(deltaLogicalLeft, deltaLogicalTop); - } - void adjustLineDirectionPosition(float delta) - { - adjustPosition(delta, 0); - } - void adjustBlockDirectionPosition(float delta) - { - adjustPosition(0, delta); - } - - virtual void paint(PaintInfo&, const LayoutPoint&, LayoutUnit lineTop, LayoutUnit lineBottom, Vector& layers); - virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, LayoutUnit lineTop, LayoutUnit lineBottom); - - // InlineBoxes are allocated out of the rendering partition. - void* operator new(size_t); - void operator delete(void*); + { + } + + virtual ~InlineBox(); + + virtual void destroy() { delete this; } + + virtual void deleteLine(); + virtual void extractLine(); + virtual void attachLine(); + + virtual bool isLineBreak() const { return false; } + + virtual void adjustPosition(float dx, float dy); + void adjustLogicalPosition(float deltaLogicalLeft, float deltaLogicalTop) { + // FIXME(sky): Remove + adjustPosition(deltaLogicalLeft, deltaLogicalTop); + } + void adjustLineDirectionPosition(float delta) { adjustPosition(delta, 0); } + void adjustBlockDirectionPosition(float delta) { adjustPosition(0, delta); } + + virtual void paint(PaintInfo&, + const LayoutPoint&, + LayoutUnit lineTop, + LayoutUnit lineBottom, + Vector& layers); + virtual bool nodeAtPoint(const HitTestRequest&, + HitTestResult&, + const HitTestLocation& locationInContainer, + const LayoutPoint& accumulatedOffset, + LayoutUnit lineTop, + LayoutUnit lineBottom); + + // InlineBoxes are allocated out of the rendering partition. + void* operator new(size_t); + void operator delete(void*); #ifndef NDEBUG - void showTreeForThis() const; - void showLineTreeForThis() const; - - virtual void showBox(int = 0) const; - virtual void showLineTreeAndMark(const InlineBox* = 0, const char* = 0, const InlineBox* = 0, const char* = 0, const RenderObject* = 0, int = 0) const; - virtual const char* boxName() const; + void showTreeForThis() const; + void showLineTreeForThis() const; + + virtual void showBox(int = 0) const; + virtual void showLineTreeAndMark(const InlineBox* = 0, + const char* = 0, + const InlineBox* = 0, + const char* = 0, + const RenderObject* = 0, + int = 0) const; + virtual const char* boxName() const; #endif - bool isText() const { return m_bitfields.isText(); } - void setIsText(bool isText) { m_bitfields.setIsText(isText); } + bool isText() const { return m_bitfields.isText(); } + void setIsText(bool isText) { m_bitfields.setIsText(isText); } + + virtual bool isInlineFlowBox() const { return false; } + virtual bool isInlineTextBox() const { return false; } + virtual bool isRootInlineBox() const { return false; } + + virtual bool isSVGInlineTextBox() const { return false; } + virtual bool isSVGInlineFlowBox() const { return false; } + virtual bool isSVGRootInlineBox() const { return false; } + + bool hasVirtualLogicalHeight() const { + return m_bitfields.hasVirtualLogicalHeight(); + } + void setHasVirtualLogicalHeight() { + m_bitfields.setHasVirtualLogicalHeight(true); + } + virtual float virtualLogicalHeight() const { + ASSERT_NOT_REACHED(); + return 0; + } + + virtual FloatRect calculateBoundaries() const { + ASSERT_NOT_REACHED(); + return FloatRect(); + } + + bool isConstructed() { return m_bitfields.constructed(); } + virtual void setConstructed() { m_bitfields.setConstructed(true); } + + void setExtracted(bool extracted = true) { + m_bitfields.setExtracted(extracted); + } + + void setFirstLineStyleBit(bool firstLine) { + m_bitfields.setFirstLine(firstLine); + } + bool isFirstLineStyle() const { return m_bitfields.firstLine(); } + + void remove(MarkLineBoxes = MarkLineBoxesDirty); + + InlineBox* nextOnLine() const { return m_next; } + InlineBox* prevOnLine() const { return m_prev; } + void setNextOnLine(InlineBox* next) { + ASSERT(m_parent || !next); + m_next = next; + } + void setPrevOnLine(InlineBox* prev) { + ASSERT(m_parent || !prev); + m_prev = prev; + } + bool nextOnLineExists() const; + + virtual bool isLeaf() const { return true; } + + InlineBox* nextLeafChild() const; + InlineBox* prevLeafChild() const; + + // Helper functions for editing and hit-testing code. + // FIXME: These two functions should be moved to RenderedPosition once the + // code to convert between Position and inline box, offset pair is moved to + // RenderedPosition. + InlineBox* nextLeafChildIgnoringLineBreak() const; + InlineBox* prevLeafChildIgnoringLineBreak() const; + + RenderObject& renderer() const { return m_renderer; } + + InlineFlowBox* parent() const { + ASSERT(!m_hasBadParent); + return m_parent; + } + void setParent(InlineFlowBox* par) { m_parent = par; } + + const RootInlineBox& root() const; + RootInlineBox& root(); + + // x() is the left side of the box in the containing block's coordinate + // system. + void setX(float x) { m_topLeft.setX(x); } + float x() const { return m_topLeft.x(); } + float left() const { return m_topLeft.x(); } + + // y() is the top side of the box in the containing block's coordinate system. + void setY(float y) { m_topLeft.setY(y); } + float y() const { return m_topLeft.y(); } + float top() const { return m_topLeft.y(); } + + const FloatPoint& topLeft() const { return m_topLeft; } + + float width() const { return logicalWidth(); } + float height() const { + return hasVirtualLogicalHeight() ? virtualLogicalHeight() : logicalHeight(); + } + FloatSize size() const { return FloatSize(width(), height()); } + float right() const { return left() + width(); } + float bottom() const { return top() + height(); } + + // The logicalLeft position is the left edge of the line box in a horizontal + // line and the top edge in a vertical line. + float logicalLeft() const { return m_topLeft.x(); } + float logicalRight() const { return logicalLeft() + logicalWidth(); } + void setLogicalLeft(float left) { + // FIXME(sky): Remove + setX(left); + } + int pixelSnappedLogicalLeft() const { return logicalLeft(); } + int pixelSnappedLogicalRight() const { return ceilf(logicalRight()); } + int pixelSnappedLogicalTop() const { return logicalTop(); } + int pixelSnappedLogicalBottom() const { return ceilf(logicalBottom()); } + + // The logicalTop[ position is the top edge of the line box in a horizontal + // line and the left edge in a vertical line. + float logicalTop() const { return m_topLeft.y(); } + float logicalBottom() const { return logicalTop() + logicalHeight(); } + void setLogicalTop(float top) { setY(top); } + + // The logical width is our extent in the line's overall inline direction, + // i.e., width for horizontal text and height for vertical text. + void setLogicalWidth(float w) { m_logicalWidth = w; } + float logicalWidth() const { return m_logicalWidth; } + + // The logical height is our extent in the block flow direction, i.e., height + // for horizontal text and width for vertical text. + float logicalHeight() const; + + FloatRect logicalFrameRect() const { + return FloatRect(m_topLeft.x(), m_topLeft.y(), m_logicalWidth, + logicalHeight()); + } + + virtual int baselinePosition(FontBaseline baselineType) const; + virtual LayoutUnit lineHeight() const; + + virtual int caretMinOffset() const; + virtual int caretMaxOffset() const; + + unsigned char bidiLevel() const { return m_bitfields.bidiEmbeddingLevel(); } + void setBidiLevel(unsigned char level) { + m_bitfields.setBidiEmbeddingLevel(level); + } + TextDirection direction() const { return bidiLevel() % 2 ? RTL : LTR; } + bool isLeftToRightDirection() const { return direction() == LTR; } + int caretLeftmostOffset() const { + return isLeftToRightDirection() ? caretMinOffset() : caretMaxOffset(); + } + int caretRightmostOffset() const { + return isLeftToRightDirection() ? caretMaxOffset() : caretMinOffset(); + } + + virtual void clearTruncation() {} + + bool isDirty() const { return m_bitfields.dirty(); } + virtual void markDirty() { m_bitfields.setDirty(true); } + + virtual void dirtyLineBoxes(); + + virtual RenderObject::SelectionState selectionState(); - virtual bool isInlineFlowBox() const { return false; } - virtual bool isInlineTextBox() const { return false; } - virtual bool isRootInlineBox() const { return false; } +#if ENABLE(ASSERT) + void setHasBadParent(); +#endif - virtual bool isSVGInlineTextBox() const { return false; } - virtual bool isSVGInlineFlowBox() const { return false; } - virtual bool isSVGRootInlineBox() const { return false; } + int expansion() const { return m_bitfields.expansion(); } - bool hasVirtualLogicalHeight() const { return m_bitfields.hasVirtualLogicalHeight(); } - void setHasVirtualLogicalHeight() { m_bitfields.setHasVirtualLogicalHeight(true); } - virtual float virtualLogicalHeight() const - { - ASSERT_NOT_REACHED(); - return 0; - } + bool visibleToHitTestRequest(const HitTestRequest& request) const { + return renderer().visibleToHitTestRequest(request); + } - virtual FloatRect calculateBoundaries() const - { - ASSERT_NOT_REACHED(); - return FloatRect(); - } + EVerticalAlign verticalAlign() const { + return renderer().style(m_bitfields.firstLine())->verticalAlign(); + } - bool isConstructed() { return m_bitfields.constructed(); } - virtual void setConstructed() { m_bitfields.setConstructed(true); } + // Use with caution! The type is not checked! + RenderBoxModelObject* boxModelObject() const { + if (!renderer().isText()) + return toRenderBoxModelObject(&renderer()); + return 0; + } - void setExtracted(bool extracted = true) { m_bitfields.setExtracted(extracted); } + FloatPoint locationIncludingFlipping(); - void setFirstLineStyleBit(bool firstLine) { m_bitfields.setFirstLine(firstLine); } - bool isFirstLineStyle() const { return m_bitfields.firstLine(); } + bool knownToHaveNoOverflow() const { + return m_bitfields.knownToHaveNoOverflow(); + } + void clearKnownToHaveNoOverflow(); - void remove(MarkLineBoxes = MarkLineBoxesDirty); + bool dirOverride() const { return m_bitfields.dirOverride(); } + void setDirOverride(bool dirOverride) { + m_bitfields.setDirOverride(dirOverride); + } - InlineBox* nextOnLine() const { return m_next; } - InlineBox* prevOnLine() const { return m_prev; } - void setNextOnLine(InlineBox* next) - { - ASSERT(m_parent || !next); - m_next = next; - } - void setPrevOnLine(InlineBox* prev) - { - ASSERT(m_parent || !prev); - m_prev = prev; +#define ADD_BOOLEAN_BITFIELD(name, Name) \ + private: \ + unsigned m_##name : 1; \ + \ + public: \ + bool name() const { return m_##name; } \ + void set##Name(bool name) { m_##name = name; } + + class InlineBoxBitfields { + public: + InlineBoxBitfields(bool firstLine = false, + bool constructed = false, + bool dirty = false, + bool extracted = false) + : m_firstLine(firstLine), + m_constructed(constructed), + m_bidiEmbeddingLevel(0), + m_dirty(dirty), + m_extracted(extracted), + m_hasVirtualLogicalHeight(false), + m_endsWithBreak(false), + m_hasSelectedChildrenOrCanHaveLeadingExpansion(false), + m_knownToHaveNoOverflow(true), + m_hasEllipsisBoxOrHyphen(false), + m_dirOverride(false), + m_isText(false), + m_hasAddedEllipsis(false), + m_determinedIfNextOnLineExists(false), + m_nextOnLineExists(false), + m_expansion(0) {} + + // Some of these bits are actually for subclasses and moved here to compact + // the structures. for this class + ADD_BOOLEAN_BITFIELD(firstLine, FirstLine); + ADD_BOOLEAN_BITFIELD(constructed, Constructed); + + private: + unsigned + m_bidiEmbeddingLevel : 6; // The maximium bidi level is 62: + // http://unicode.org/reports/tr9/#Explicit_Levels_and_Directions + + public: + unsigned char bidiEmbeddingLevel() const { return m_bidiEmbeddingLevel; } + void setBidiEmbeddingLevel(unsigned char bidiEmbeddingLevel) { + m_bidiEmbeddingLevel = bidiEmbeddingLevel; } - bool nextOnLineExists() const; - - virtual bool isLeaf() const { return true; } - - InlineBox* nextLeafChild() const; - InlineBox* prevLeafChild() const; - // Helper functions for editing and hit-testing code. - // FIXME: These two functions should be moved to RenderedPosition once the code to convert between - // Position and inline box, offset pair is moved to RenderedPosition. - InlineBox* nextLeafChildIgnoringLineBreak() const; - InlineBox* prevLeafChildIgnoringLineBreak() const; - - RenderObject& renderer() const { return m_renderer; } - - InlineFlowBox* parent() const - { - ASSERT(!m_hasBadParent); - return m_parent; - } - void setParent(InlineFlowBox* par) { m_parent = par; } - - const RootInlineBox& root() const; - RootInlineBox& root(); - - // x() is the left side of the box in the containing block's coordinate system. - void setX(float x) { m_topLeft.setX(x); } - float x() const { return m_topLeft.x(); } - float left() const { return m_topLeft.x(); } - - // y() is the top side of the box in the containing block's coordinate system. - void setY(float y) { m_topLeft.setY(y); } - float y() const { return m_topLeft.y(); } - float top() const { return m_topLeft.y(); } - - const FloatPoint& topLeft() const { return m_topLeft; } - - float width() const { return logicalWidth(); } - float height() const { return hasVirtualLogicalHeight() ? virtualLogicalHeight() : logicalHeight(); } - FloatSize size() const { return FloatSize(width(), height()); } - float right() const { return left() + width(); } - float bottom() const { return top() + height(); } - - // The logicalLeft position is the left edge of the line box in a horizontal line and the top edge in a vertical line. - float logicalLeft() const { return m_topLeft.x(); } - float logicalRight() const { return logicalLeft() + logicalWidth(); } - void setLogicalLeft(float left) - { - // FIXME(sky): Remove - setX(left); + ADD_BOOLEAN_BITFIELD(dirty, Dirty); + ADD_BOOLEAN_BITFIELD(extracted, Extracted); + ADD_BOOLEAN_BITFIELD(hasVirtualLogicalHeight, HasVirtualLogicalHeight); + // for RootInlineBox + ADD_BOOLEAN_BITFIELD(endsWithBreak, + EndsWithBreak); // Whether the line ends with a
. + // shared between RootInlineBox and InlineTextBox + ADD_BOOLEAN_BITFIELD(hasSelectedChildrenOrCanHaveLeadingExpansion, + HasSelectedChildrenOrCanHaveLeadingExpansion); + ADD_BOOLEAN_BITFIELD(knownToHaveNoOverflow, KnownToHaveNoOverflow); + ADD_BOOLEAN_BITFIELD(hasEllipsisBoxOrHyphen, HasEllipsisBoxOrHyphen); + // for InlineTextBox + ADD_BOOLEAN_BITFIELD(dirOverride, DirOverride); + ADD_BOOLEAN_BITFIELD(isText, IsText); // Whether or not this object + // represents text with a non-zero + // height. Includes non-image list + // markers, text boxes. + ADD_BOOLEAN_BITFIELD(hasAddedEllipsis, HasAddedEllipsis) + + private: + mutable unsigned m_determinedIfNextOnLineExists : 1; + + public: + bool determinedIfNextOnLineExists() const { + return m_determinedIfNextOnLineExists; } - int pixelSnappedLogicalLeft() const { return logicalLeft(); } - int pixelSnappedLogicalRight() const { return ceilf(logicalRight()); } - int pixelSnappedLogicalTop() const { return logicalTop(); } - int pixelSnappedLogicalBottom() const { return ceilf(logicalBottom()); } - - // The logicalTop[ position is the top edge of the line box in a horizontal line and the left edge in a vertical line. - float logicalTop() const { return m_topLeft.y(); } - float logicalBottom() const { return logicalTop() + logicalHeight(); } - void setLogicalTop(float top) - { - setY(top); + void setDeterminedIfNextOnLineExists( + bool determinedIfNextOnLineExists) const { + m_determinedIfNextOnLineExists = determinedIfNextOnLineExists; } - // The logical width is our extent in the line's overall inline direction, i.e., width for horizontal text and height for vertical text. - void setLogicalWidth(float w) { m_logicalWidth = w; } - float logicalWidth() const { return m_logicalWidth; } - - // The logical height is our extent in the block flow direction, i.e., height for horizontal text and width for vertical text. - float logicalHeight() const; - - FloatRect logicalFrameRect() const { return FloatRect(m_topLeft.x(), m_topLeft.y(), m_logicalWidth, logicalHeight()); } - - virtual int baselinePosition(FontBaseline baselineType) const; - virtual LayoutUnit lineHeight() const; - - virtual int caretMinOffset() const; - virtual int caretMaxOffset() const; - - unsigned char bidiLevel() const { return m_bitfields.bidiEmbeddingLevel(); } - void setBidiLevel(unsigned char level) { m_bitfields.setBidiEmbeddingLevel(level); } - TextDirection direction() const { return bidiLevel() % 2 ? RTL : LTR; } - bool isLeftToRightDirection() const { return direction() == LTR; } - int caretLeftmostOffset() const { return isLeftToRightDirection() ? caretMinOffset() : caretMaxOffset(); } - int caretRightmostOffset() const { return isLeftToRightDirection() ? caretMaxOffset() : caretMinOffset(); } - - virtual void clearTruncation() { } - - bool isDirty() const { return m_bitfields.dirty(); } - virtual void markDirty() { m_bitfields.setDirty(true); } - - virtual void dirtyLineBoxes(); + private: + mutable unsigned m_nextOnLineExists : 1; - virtual RenderObject::SelectionState selectionState(); - -#if ENABLE(ASSERT) - void setHasBadParent(); -#endif - - int expansion() const { return m_bitfields.expansion(); } - - bool visibleToHitTestRequest(const HitTestRequest& request) const { return renderer().visibleToHitTestRequest(request); } - - EVerticalAlign verticalAlign() const { return renderer().style(m_bitfields.firstLine())->verticalAlign(); } - - // Use with caution! The type is not checked! - RenderBoxModelObject* boxModelObject() const - { - if (!renderer().isText()) - return toRenderBoxModelObject(&renderer()); - return 0; + public: + bool nextOnLineExists() const { return m_nextOnLineExists; } + void setNextOnLineExists(bool nextOnLineExists) const { + m_nextOnLineExists = nextOnLineExists; } - FloatPoint locationIncludingFlipping(); - - bool knownToHaveNoOverflow() const { return m_bitfields.knownToHaveNoOverflow(); } - void clearKnownToHaveNoOverflow(); - - bool dirOverride() const { return m_bitfields.dirOverride(); } - void setDirOverride(bool dirOverride) { m_bitfields.setDirOverride(dirOverride); } + private: + signed m_expansion : 12; // for justified text -#define ADD_BOOLEAN_BITFIELD(name, Name) \ - private:\ - unsigned m_##name : 1;\ - public:\ - bool name() const { return m_##name; }\ - void set##Name(bool name) { m_##name = name; }\ - - class InlineBoxBitfields { - public: - InlineBoxBitfields(bool firstLine = false, bool constructed = false, bool dirty = false, bool extracted = false) - : m_firstLine(firstLine) - , m_constructed(constructed) - , m_bidiEmbeddingLevel(0) - , m_dirty(dirty) - , m_extracted(extracted) - , m_hasVirtualLogicalHeight(false) - , m_endsWithBreak(false) - , m_hasSelectedChildrenOrCanHaveLeadingExpansion(false) - , m_knownToHaveNoOverflow(true) - , m_hasEllipsisBoxOrHyphen(false) - , m_dirOverride(false) - , m_isText(false) - , m_hasAddedEllipsis(false) - , m_determinedIfNextOnLineExists(false) - , m_nextOnLineExists(false) - , m_expansion(0) - { - } - - // Some of these bits are actually for subclasses and moved here to compact the structures. - // for this class - ADD_BOOLEAN_BITFIELD(firstLine, FirstLine); - ADD_BOOLEAN_BITFIELD(constructed, Constructed); - - private: - unsigned m_bidiEmbeddingLevel : 6; // The maximium bidi level is 62: http://unicode.org/reports/tr9/#Explicit_Levels_and_Directions - - public: - unsigned char bidiEmbeddingLevel() const { return m_bidiEmbeddingLevel; } - void setBidiEmbeddingLevel(unsigned char bidiEmbeddingLevel) { m_bidiEmbeddingLevel = bidiEmbeddingLevel; } - - ADD_BOOLEAN_BITFIELD(dirty, Dirty); - ADD_BOOLEAN_BITFIELD(extracted, Extracted); - ADD_BOOLEAN_BITFIELD(hasVirtualLogicalHeight, HasVirtualLogicalHeight); - // for RootInlineBox - ADD_BOOLEAN_BITFIELD(endsWithBreak, EndsWithBreak); // Whether the line ends with a
. - // shared between RootInlineBox and InlineTextBox - ADD_BOOLEAN_BITFIELD(hasSelectedChildrenOrCanHaveLeadingExpansion, HasSelectedChildrenOrCanHaveLeadingExpansion); - ADD_BOOLEAN_BITFIELD(knownToHaveNoOverflow, KnownToHaveNoOverflow); - ADD_BOOLEAN_BITFIELD(hasEllipsisBoxOrHyphen, HasEllipsisBoxOrHyphen); - // for InlineTextBox - ADD_BOOLEAN_BITFIELD(dirOverride, DirOverride); - ADD_BOOLEAN_BITFIELD(isText, IsText); // Whether or not this object represents text with a non-zero height. Includes non-image list markers, text boxes. - ADD_BOOLEAN_BITFIELD(hasAddedEllipsis, HasAddedEllipsis) - - private: - mutable unsigned m_determinedIfNextOnLineExists : 1; - - public: - bool determinedIfNextOnLineExists() const { return m_determinedIfNextOnLineExists; } - void setDeterminedIfNextOnLineExists(bool determinedIfNextOnLineExists) const { m_determinedIfNextOnLineExists = determinedIfNextOnLineExists; } - - private: - mutable unsigned m_nextOnLineExists : 1; - - public: - bool nextOnLineExists() const { return m_nextOnLineExists; } - void setNextOnLineExists(bool nextOnLineExists) const { m_nextOnLineExists = nextOnLineExists; } - - private: - signed m_expansion : 12; // for justified text - - public: - signed expansion() const { return m_expansion; } - void setExpansion(signed expansion) { m_expansion = expansion; } - }; + public: + signed expansion() const { return m_expansion; } + void setExpansion(signed expansion) { m_expansion = expansion; } + }; #undef ADD_BOOLEAN_BITFIELD -private: - InlineBox* m_next; // The next element on the same line as us. - InlineBox* m_prev; // The previous element on the same line as us. - - InlineFlowBox* m_parent; // The box that contains us. - RenderObject& m_renderer; - -protected: - // For RootInlineBox - bool endsWithBreak() const { return m_bitfields.endsWithBreak(); } - void setEndsWithBreak(bool endsWithBreak) { m_bitfields.setEndsWithBreak(endsWithBreak); } - bool hasEllipsisBox() const { return m_bitfields.hasEllipsisBoxOrHyphen(); } - bool hasSelectedChildren() const { return m_bitfields.hasSelectedChildrenOrCanHaveLeadingExpansion(); } - void setHasSelectedChildren(bool hasSelectedChildren) { m_bitfields.setHasSelectedChildrenOrCanHaveLeadingExpansion(hasSelectedChildren); } - void setHasEllipsisBox(bool hasEllipsisBox) { m_bitfields.setHasEllipsisBoxOrHyphen(hasEllipsisBox); } - - // For InlineTextBox - bool hasHyphen() const { return m_bitfields.hasEllipsisBoxOrHyphen(); } - void setHasHyphen(bool hasHyphen) { m_bitfields.setHasEllipsisBoxOrHyphen(hasHyphen); } - bool canHaveLeadingExpansion() const { return m_bitfields.hasSelectedChildrenOrCanHaveLeadingExpansion(); } - void setCanHaveLeadingExpansion(bool canHaveLeadingExpansion) { m_bitfields.setHasSelectedChildrenOrCanHaveLeadingExpansion(canHaveLeadingExpansion); } - signed expansion() { return m_bitfields.expansion(); } - void setExpansion(signed expansion) { m_bitfields.setExpansion(expansion); } - bool hasAddedEllipsis() const { return m_bitfields.hasAddedEllipsis(); } - void setHasAddedEllipsis(bool hasAddedEllipsis) { m_bitfields.setHasAddedEllipsis(hasAddedEllipsis); } - - // For InlineFlowBox and InlineTextBox - bool extracted() const { return m_bitfields.extracted(); } - - FloatPoint m_topLeft; - float m_logicalWidth; - -private: - InlineBoxBitfields m_bitfields; + private: + InlineBox* m_next; // The next element on the same line as us. + InlineBox* m_prev; // The previous element on the same line as us. + + InlineFlowBox* m_parent; // The box that contains us. + RenderObject& m_renderer; + + protected: + // For RootInlineBox + bool endsWithBreak() const { return m_bitfields.endsWithBreak(); } + void setEndsWithBreak(bool endsWithBreak) { + m_bitfields.setEndsWithBreak(endsWithBreak); + } + bool hasEllipsisBox() const { return m_bitfields.hasEllipsisBoxOrHyphen(); } + bool hasSelectedChildren() const { + return m_bitfields.hasSelectedChildrenOrCanHaveLeadingExpansion(); + } + void setHasSelectedChildren(bool hasSelectedChildren) { + m_bitfields.setHasSelectedChildrenOrCanHaveLeadingExpansion( + hasSelectedChildren); + } + void setHasEllipsisBox(bool hasEllipsisBox) { + m_bitfields.setHasEllipsisBoxOrHyphen(hasEllipsisBox); + } + + // For InlineTextBox + bool hasHyphen() const { return m_bitfields.hasEllipsisBoxOrHyphen(); } + void setHasHyphen(bool hasHyphen) { + m_bitfields.setHasEllipsisBoxOrHyphen(hasHyphen); + } + bool canHaveLeadingExpansion() const { + return m_bitfields.hasSelectedChildrenOrCanHaveLeadingExpansion(); + } + void setCanHaveLeadingExpansion(bool canHaveLeadingExpansion) { + m_bitfields.setHasSelectedChildrenOrCanHaveLeadingExpansion( + canHaveLeadingExpansion); + } + signed expansion() { return m_bitfields.expansion(); } + void setExpansion(signed expansion) { m_bitfields.setExpansion(expansion); } + bool hasAddedEllipsis() const { return m_bitfields.hasAddedEllipsis(); } + void setHasAddedEllipsis(bool hasAddedEllipsis) { + m_bitfields.setHasAddedEllipsis(hasAddedEllipsis); + } + + // For InlineFlowBox and InlineTextBox + bool extracted() const { return m_bitfields.extracted(); } + + FloatPoint m_topLeft; + float m_logicalWidth; + + private: + InlineBoxBitfields m_bitfields; #if ENABLE(ASSERT) - bool m_hasBadParent; + bool m_hasBadParent; #endif }; #if !ENABLE(ASSERT) -inline InlineBox::~InlineBox() -{ -} +inline InlineBox::~InlineBox() {} #endif #if ENABLE(ASSERT) -inline void InlineBox::setHasBadParent() -{ - m_hasBadParent = true; +inline void InlineBox::setHasBadParent() { + m_hasBadParent = true; } #endif -#define DEFINE_INLINE_BOX_TYPE_CASTS(typeName) \ - DEFINE_TYPE_CASTS(typeName, InlineBox, box, box->is##typeName(), box.is##typeName()) +#define DEFINE_INLINE_BOX_TYPE_CASTS(typeName) \ + DEFINE_TYPE_CASTS(typeName, InlineBox, box, box->is##typeName(), \ + box.is##typeName()) -// Allow equality comparisons of InlineBox's by reference or pointer, interchangeably. +// Allow equality comparisons of InlineBox's by reference or pointer, +// interchangeably. DEFINE_COMPARISON_OPERATORS_WITH_REFERENCES(InlineBox) -} // namespace blink +} // namespace blink #ifndef NDEBUG // Outside the WebCore namespace for ease of invocation from gdb. diff --git a/sky/engine/core/rendering/InlineFlowBox.cpp b/sky/engine/core/rendering/InlineFlowBox.cpp index 8de731923d516..38394949549aa 100644 --- a/sky/engine/core/rendering/InlineFlowBox.cpp +++ b/sky/engine/core/rendering/InlineFlowBox.cpp @@ -1,5 +1,6 @@ /* - * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. + * All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -36,1206 +37,1424 @@ namespace blink { struct SameSizeAsInlineFlowBox : public InlineBox { - void* pointers[5]; - uint32_t bitfields : 23; + void* pointers[5]; + uint32_t bitfields : 23; }; -COMPILE_ASSERT(sizeof(InlineFlowBox) == sizeof(SameSizeAsInlineFlowBox), InlineFlowBox_should_stay_small); +COMPILE_ASSERT(sizeof(InlineFlowBox) == sizeof(SameSizeAsInlineFlowBox), + InlineFlowBox_should_stay_small); #if ENABLE(ASSERT) -InlineFlowBox::~InlineFlowBox() -{ - if (!m_hasBadChildList) - for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) - child->setHasBadParent(); +InlineFlowBox::~InlineFlowBox() { + if (!m_hasBadChildList) + for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) + child->setHasBadParent(); } #endif -LayoutUnit InlineFlowBox::getFlowSpacingLogicalWidth() -{ - LayoutUnit totWidth = marginBorderPaddingLogicalLeft() + marginBorderPaddingLogicalRight(); - for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { - if (curr->isInlineFlowBox()) - totWidth += toInlineFlowBox(curr)->getFlowSpacingLogicalWidth(); - } - return totWidth; +LayoutUnit InlineFlowBox::getFlowSpacingLogicalWidth() { + LayoutUnit totWidth = + marginBorderPaddingLogicalLeft() + marginBorderPaddingLogicalRight(); + for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { + if (curr->isInlineFlowBox()) + totWidth += toInlineFlowBox(curr)->getFlowSpacingLogicalWidth(); + } + return totWidth; } -IntRect InlineFlowBox::roundedFrameRect() const -{ - // Begin by snapping the x and y coordinates to the nearest pixel. - int snappedX = lroundf(x()); - int snappedY = lroundf(y()); +IntRect InlineFlowBox::roundedFrameRect() const { + // Begin by snapping the x and y coordinates to the nearest pixel. + int snappedX = lroundf(x()); + int snappedY = lroundf(y()); - int snappedMaxX = lroundf(x() + width()); - int snappedMaxY = lroundf(y() + height()); + int snappedMaxX = lroundf(x() + width()); + int snappedMaxY = lroundf(y() + height()); - return IntRect(snappedX, snappedY, snappedMaxX - snappedX, snappedMaxY - snappedY); + return IntRect(snappedX, snappedY, snappedMaxX - snappedX, + snappedMaxY - snappedY); } -static void setHasTextDescendantsOnAncestors(InlineFlowBox* box) -{ - while (box && !box->hasTextDescendants()) { - box->setHasTextDescendants(); - box = box->parent(); - } +static void setHasTextDescendantsOnAncestors(InlineFlowBox* box) { + while (box && !box->hasTextDescendants()) { + box->setHasTextDescendants(); + box = box->parent(); + } } -static bool hasSelfPaintingLayer(InlineBox* box) -{ - RenderObject& renderer = box->renderer(); - if (renderer.isBox()) - return toRenderBox(renderer).hasSelfPaintingLayer(); - return false; +static bool hasSelfPaintingLayer(InlineBox* box) { + RenderObject& renderer = box->renderer(); + if (renderer.isBox()) + return toRenderBox(renderer).hasSelfPaintingLayer(); + return false; } -void InlineFlowBox::addToLine(InlineBox* child) -{ - ASSERT(!child->parent()); - ASSERT(!child->nextOnLine()); - ASSERT(!child->prevOnLine()); - checkConsistency(); - - child->setParent(this); - if (!m_firstChild) { - m_firstChild = child; - m_lastChild = child; +void InlineFlowBox::addToLine(InlineBox* child) { + ASSERT(!child->parent()); + ASSERT(!child->nextOnLine()); + ASSERT(!child->prevOnLine()); + checkConsistency(); + + child->setParent(this); + if (!m_firstChild) { + m_firstChild = child; + m_lastChild = child; + } else { + m_lastChild->setNextOnLine(child); + child->setPrevOnLine(m_lastChild); + m_lastChild = child; + } + child->setFirstLineStyleBit(isFirstLineStyle()); + if (child->isText()) { + if (child->renderer().parent() == renderer()) + m_hasTextChildren = true; + setHasTextDescendantsOnAncestors(this); + } else if (child->isInlineFlowBox()) { + if (toInlineFlowBox(child)->hasTextDescendants()) + setHasTextDescendantsOnAncestors(this); + } + + if (descendantsHaveSameLineHeightAndBaseline() && + !child->renderer().isOutOfFlowPositioned()) { + RenderStyle* parentStyle = renderer().style(isFirstLineStyle()); + RenderStyle* childStyle = child->renderer().style(isFirstLineStyle()); + bool shouldClearDescendantsHaveSameLineHeightAndBaseline = false; + if (child->renderer().isReplaced()) + shouldClearDescendantsHaveSameLineHeightAndBaseline = true; + else if (child->isText()) { + if (child->renderer().parent() != renderer()) { + if (!parentStyle->font() + .fontMetrics() + .hasIdenticalAscentDescentAndLineGap( + childStyle->font().fontMetrics()) || + parentStyle->lineHeight() != childStyle->lineHeight() || + (parentStyle->verticalAlign() != BASELINE && !isRootInlineBox()) || + childStyle->verticalAlign() != BASELINE) + shouldClearDescendantsHaveSameLineHeightAndBaseline = true; + } + if (childStyle->textEmphasisMark() != TextEmphasisMarkNone) + shouldClearDescendantsHaveSameLineHeightAndBaseline = true; } else { - m_lastChild->setNextOnLine(child); - child->setPrevOnLine(m_lastChild); - m_lastChild = child; - } - child->setFirstLineStyleBit(isFirstLineStyle()); - if (child->isText()) { - if (child->renderer().parent() == renderer()) - m_hasTextChildren = true; - setHasTextDescendantsOnAncestors(this); - } else if (child->isInlineFlowBox()) { - if (toInlineFlowBox(child)->hasTextDescendants()) - setHasTextDescendantsOnAncestors(this); + ASSERT(isInlineFlowBox()); + InlineFlowBox* childFlowBox = toInlineFlowBox(child); + // Check the child's bit, and then also check for differences in font, + // line-height, vertical-align + if (!childFlowBox->descendantsHaveSameLineHeightAndBaseline() || + !parentStyle->font() + .fontMetrics() + .hasIdenticalAscentDescentAndLineGap( + childStyle->font().fontMetrics()) || + parentStyle->lineHeight() != childStyle->lineHeight() || + (parentStyle->verticalAlign() != BASELINE && !isRootInlineBox()) || + childStyle->verticalAlign() != BASELINE || childStyle->hasBorder() || + childStyle->hasPadding()) + shouldClearDescendantsHaveSameLineHeightAndBaseline = true; } - if (descendantsHaveSameLineHeightAndBaseline() && !child->renderer().isOutOfFlowPositioned()) { - RenderStyle* parentStyle = renderer().style(isFirstLineStyle()); - RenderStyle* childStyle = child->renderer().style(isFirstLineStyle()); - bool shouldClearDescendantsHaveSameLineHeightAndBaseline = false; - if (child->renderer().isReplaced()) - shouldClearDescendantsHaveSameLineHeightAndBaseline = true; - else if (child->isText()) { - if (child->renderer().parent() != renderer()) { - if (!parentStyle->font().fontMetrics().hasIdenticalAscentDescentAndLineGap(childStyle->font().fontMetrics()) - || parentStyle->lineHeight() != childStyle->lineHeight() - || (parentStyle->verticalAlign() != BASELINE && !isRootInlineBox()) || childStyle->verticalAlign() != BASELINE) - shouldClearDescendantsHaveSameLineHeightAndBaseline = true; - } - if (childStyle->textEmphasisMark() != TextEmphasisMarkNone) - shouldClearDescendantsHaveSameLineHeightAndBaseline = true; - } else { - ASSERT(isInlineFlowBox()); - InlineFlowBox* childFlowBox = toInlineFlowBox(child); - // Check the child's bit, and then also check for differences in font, line-height, vertical-align - if (!childFlowBox->descendantsHaveSameLineHeightAndBaseline() - || !parentStyle->font().fontMetrics().hasIdenticalAscentDescentAndLineGap(childStyle->font().fontMetrics()) - || parentStyle->lineHeight() != childStyle->lineHeight() - || (parentStyle->verticalAlign() != BASELINE && !isRootInlineBox()) || childStyle->verticalAlign() != BASELINE - || childStyle->hasBorder() || childStyle->hasPadding()) - shouldClearDescendantsHaveSameLineHeightAndBaseline = true; - } + if (shouldClearDescendantsHaveSameLineHeightAndBaseline) + clearDescendantsHaveSameLineHeightAndBaseline(); + } - if (shouldClearDescendantsHaveSameLineHeightAndBaseline) - clearDescendantsHaveSameLineHeightAndBaseline(); + if (!child->renderer().isOutOfFlowPositioned()) { + if (child->isText()) { + RenderStyle* childStyle = child->renderer().style(isFirstLineStyle()); + if (childStyle->letterSpacing() < 0 || childStyle->textShadow() || + childStyle->textEmphasisMark() != TextEmphasisMarkNone || + childStyle->textStrokeWidth()) + child->clearKnownToHaveNoOverflow(); + } else if (child->renderer().isReplaced()) { + RenderBox& box = toRenderBox(child->renderer()); + if (box.hasRenderOverflow() || box.hasSelfPaintingLayer()) + child->clearKnownToHaveNoOverflow(); + } else if (child->renderer().style(isFirstLineStyle())->boxShadow() || + hasSelfPaintingLayer(child) || + child->renderer().style(isFirstLineStyle())->hasOutline()) { + child->clearKnownToHaveNoOverflow(); } - if (!child->renderer().isOutOfFlowPositioned()) { - if (child->isText()) { - RenderStyle* childStyle = child->renderer().style(isFirstLineStyle()); - if (childStyle->letterSpacing() < 0 || childStyle->textShadow() || childStyle->textEmphasisMark() != TextEmphasisMarkNone || childStyle->textStrokeWidth()) - child->clearKnownToHaveNoOverflow(); - } else if (child->renderer().isReplaced()) { - RenderBox& box = toRenderBox(child->renderer()); - if (box.hasRenderOverflow() || box.hasSelfPaintingLayer()) - child->clearKnownToHaveNoOverflow(); - } else if (child->renderer().style(isFirstLineStyle())->boxShadow() || hasSelfPaintingLayer(child) - || child->renderer().style(isFirstLineStyle())->hasOutline()) { - child->clearKnownToHaveNoOverflow(); - } - - if (knownToHaveNoOverflow() && child->isInlineFlowBox() && !toInlineFlowBox(child)->knownToHaveNoOverflow()) - clearKnownToHaveNoOverflow(); - } + if (knownToHaveNoOverflow() && child->isInlineFlowBox() && + !toInlineFlowBox(child)->knownToHaveNoOverflow()) + clearKnownToHaveNoOverflow(); + } - checkConsistency(); + checkConsistency(); } -void InlineFlowBox::removeChild(InlineBox* child, MarkLineBoxes markDirty) -{ - checkConsistency(); +void InlineFlowBox::removeChild(InlineBox* child, MarkLineBoxes markDirty) { + checkConsistency(); - if (markDirty == MarkLineBoxesDirty && !isDirty()) - dirtyLineBoxes(); + if (markDirty == MarkLineBoxesDirty && !isDirty()) + dirtyLineBoxes(); - root().childRemoved(child); + root().childRemoved(child); - if (child == m_firstChild) - m_firstChild = child->nextOnLine(); - if (child == m_lastChild) - m_lastChild = child->prevOnLine(); - if (child->nextOnLine()) - child->nextOnLine()->setPrevOnLine(child->prevOnLine()); - if (child->prevOnLine()) - child->prevOnLine()->setNextOnLine(child->nextOnLine()); + if (child == m_firstChild) + m_firstChild = child->nextOnLine(); + if (child == m_lastChild) + m_lastChild = child->prevOnLine(); + if (child->nextOnLine()) + child->nextOnLine()->setPrevOnLine(child->prevOnLine()); + if (child->prevOnLine()) + child->prevOnLine()->setNextOnLine(child->nextOnLine()); - child->setParent(0); + child->setParent(0); - checkConsistency(); + checkConsistency(); } -void InlineFlowBox::deleteLine() -{ - InlineBox* child = firstChild(); - InlineBox* next = 0; - while (child) { - ASSERT(this == child->parent()); - next = child->nextOnLine(); +void InlineFlowBox::deleteLine() { + InlineBox* child = firstChild(); + InlineBox* next = 0; + while (child) { + ASSERT(this == child->parent()); + next = child->nextOnLine(); #if ENABLE(ASSERT) - child->setParent(0); + child->setParent(0); #endif - child->deleteLine(); - child = next; - } + child->deleteLine(); + child = next; + } #if ENABLE(ASSERT) - m_firstChild = 0; - m_lastChild = 0; + m_firstChild = 0; + m_lastChild = 0; #endif - removeLineBoxFromRenderObject(); - destroy(); + removeLineBoxFromRenderObject(); + destroy(); } -void InlineFlowBox::removeLineBoxFromRenderObject() -{ - rendererLineBoxes()->removeLineBox(this); +void InlineFlowBox::removeLineBoxFromRenderObject() { + rendererLineBoxes()->removeLineBox(this); } -void InlineFlowBox::extractLine() -{ - if (!extracted()) - extractLineBoxFromRenderObject(); - for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) - child->extractLine(); +void InlineFlowBox::extractLine() { + if (!extracted()) + extractLineBoxFromRenderObject(); + for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) + child->extractLine(); } -void InlineFlowBox::extractLineBoxFromRenderObject() -{ - rendererLineBoxes()->extractLineBox(this); +void InlineFlowBox::extractLineBoxFromRenderObject() { + rendererLineBoxes()->extractLineBox(this); } -void InlineFlowBox::attachLine() -{ - if (extracted()) - attachLineBoxToRenderObject(); - for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) - child->attachLine(); +void InlineFlowBox::attachLine() { + if (extracted()) + attachLineBoxToRenderObject(); + for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) + child->attachLine(); } -void InlineFlowBox::attachLineBoxToRenderObject() -{ - rendererLineBoxes()->attachLineBox(this); +void InlineFlowBox::attachLineBoxToRenderObject() { + rendererLineBoxes()->attachLineBox(this); } -void InlineFlowBox::adjustPosition(float dx, float dy) -{ - InlineBox::adjustPosition(dx, dy); - for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) - child->adjustPosition(dx, dy); - if (m_overflow) - m_overflow->move(dx, dy); // FIXME: Rounding error here since overflow was pixel snapped, but nobody other than list markers passes non-integral values here. +void InlineFlowBox::adjustPosition(float dx, float dy) { + InlineBox::adjustPosition(dx, dy); + for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) + child->adjustPosition(dx, dy); + if (m_overflow) + m_overflow->move(dx, dy); // FIXME: Rounding error here since overflow was + // pixel snapped, but nobody other than list + // markers passes non-integral values here. } -RenderLineBoxList* InlineFlowBox::rendererLineBoxes() const -{ - return toRenderInline(renderer()).lineBoxes(); +RenderLineBoxList* InlineFlowBox::rendererLineBoxes() const { + return toRenderInline(renderer()).lineBoxes(); } -static inline bool isLastChildForRenderer(RenderObject* ancestor, RenderObject* child) -{ - if (!child) - return false; +static inline bool isLastChildForRenderer(RenderObject* ancestor, + RenderObject* child) { + if (!child) + return false; - if (child == ancestor) - return true; + if (child == ancestor) + return true; - RenderObject* curr = child; - RenderObject* parent = curr->parent(); - while (parent && (!parent->isRenderBlock() || parent->isInline())) { - if (parent->slowLastChild() != curr) - return false; - if (parent == ancestor) - return true; + RenderObject* curr = child; + RenderObject* parent = curr->parent(); + while (parent && (!parent->isRenderBlock() || parent->isInline())) { + if (parent->slowLastChild() != curr) + return false; + if (parent == ancestor) + return true; - curr = parent; - parent = curr->parent(); - } + curr = parent; + parent = curr->parent(); + } - return true; + return true; } -static bool isAnsectorAndWithinBlock(RenderObject* ancestor, RenderObject* child) -{ - RenderObject* object = child; - while (object && (!object->isRenderBlock() || object->isInline())) { - if (object == ancestor) - return true; - object = object->parent(); - } - return false; +static bool isAnsectorAndWithinBlock(RenderObject* ancestor, + RenderObject* child) { + RenderObject* object = child; + while (object && (!object->isRenderBlock() || object->isInline())) { + if (object == ancestor) + return true; + object = object->parent(); + } + return false; } -void InlineFlowBox::determineSpacingForFlowBoxes(bool lastLine, bool isLogicallyLastRunWrapped, RenderObject* logicallyLastRunRenderer) -{ - // All boxes start off open. They will not apply any margins/border/padding on - // any side. - bool includeLeftEdge = false; - bool includeRightEdge = false; - - // The root inline box never has borders/margins/padding. - if (parent()) { - bool ltr = renderer().style()->isLeftToRightDirection(); - - // Check to see if all initial lines are unconstructed. If so, then - // we know the inline began on this line. - RenderLineBoxList* lineBoxList = rendererLineBoxes(); - if (!lineBoxList->firstLineBox()->isConstructed()) { - if (renderer().style()->boxDecorationBreak() == DCLONE) - includeLeftEdge = includeRightEdge = true; - else if (ltr && lineBoxList->firstLineBox() == this) - includeLeftEdge = true; - else if (!ltr && lineBoxList->lastLineBox() == this) - includeRightEdge = true; - } +void InlineFlowBox::determineSpacingForFlowBoxes( + bool lastLine, + bool isLogicallyLastRunWrapped, + RenderObject* logicallyLastRunRenderer) { + // All boxes start off open. They will not apply any margins/border/padding + // on any side. + bool includeLeftEdge = false; + bool includeRightEdge = false; + + // The root inline box never has borders/margins/padding. + if (parent()) { + bool ltr = renderer().style()->isLeftToRightDirection(); + + // Check to see if all initial lines are unconstructed. If so, then + // we know the inline began on this line. + RenderLineBoxList* lineBoxList = rendererLineBoxes(); + if (!lineBoxList->firstLineBox()->isConstructed()) { + if (renderer().style()->boxDecorationBreak() == DCLONE) + includeLeftEdge = includeRightEdge = true; + else if (ltr && lineBoxList->firstLineBox() == this) + includeLeftEdge = true; + else if (!ltr && lineBoxList->lastLineBox() == this) + includeRightEdge = true; + } - if (!lineBoxList->lastLineBox()->isConstructed()) { - bool isLastObjectOnLine = !isAnsectorAndWithinBlock(&renderer(), logicallyLastRunRenderer) || (isLastChildForRenderer(&renderer(), logicallyLastRunRenderer) && !isLogicallyLastRunWrapped); - - // We include the border under these conditions: - // (1) The next line was not created, or it is constructed. We check the previous line for rtl. - // (2) The logicallyLastRun is not a descendant of this renderer. - // (3) The logicallyLastRun is a descendant of this renderer, but it is the last child of this renderer and it does not wrap to the next line. - // (4) The decoration break is set to clone therefore there will be borders on every sides. - if (renderer().style()->boxDecorationBreak() == DCLONE) - includeLeftEdge = includeRightEdge = true; - else if (ltr) { - if (!nextLineBox() - && (lastLine || isLastObjectOnLine)) - includeRightEdge = true; - } else { - if ((!prevLineBox() || prevLineBox()->isConstructed()) - && (lastLine || isLastObjectOnLine)) - includeLeftEdge = true; - } - } + if (!lineBoxList->lastLineBox()->isConstructed()) { + bool isLastObjectOnLine = + !isAnsectorAndWithinBlock(&renderer(), logicallyLastRunRenderer) || + (isLastChildForRenderer(&renderer(), logicallyLastRunRenderer) && + !isLogicallyLastRunWrapped); + + // We include the border under these conditions: + // (1) The next line was not created, or it is constructed. We check the + // previous line for rtl. (2) The logicallyLastRun is not a descendant of + // this renderer. (3) The logicallyLastRun is a descendant of this + // renderer, but it is the last child of this renderer and it does not + // wrap to the next line. (4) The decoration break is set to clone + // therefore there will be borders on every sides. + if (renderer().style()->boxDecorationBreak() == DCLONE) + includeLeftEdge = includeRightEdge = true; + else if (ltr) { + if (!nextLineBox() && (lastLine || isLastObjectOnLine)) + includeRightEdge = true; + } else { + if ((!prevLineBox() || prevLineBox()->isConstructed()) && + (lastLine || isLastObjectOnLine)) + includeLeftEdge = true; + } } + } - setEdges(includeLeftEdge, includeRightEdge); + setEdges(includeLeftEdge, includeRightEdge); - // Recur into our children. - for (InlineBox* currChild = firstChild(); currChild; currChild = currChild->nextOnLine()) { - if (currChild->isInlineFlowBox()) { - InlineFlowBox* currFlow = toInlineFlowBox(currChild); - currFlow->determineSpacingForFlowBoxes(lastLine, isLogicallyLastRunWrapped, logicallyLastRunRenderer); - } + // Recur into our children. + for (InlineBox* currChild = firstChild(); currChild; + currChild = currChild->nextOnLine()) { + if (currChild->isInlineFlowBox()) { + InlineFlowBox* currFlow = toInlineFlowBox(currChild); + currFlow->determineSpacingForFlowBoxes( + lastLine, isLogicallyLastRunWrapped, logicallyLastRunRenderer); } + } } -float InlineFlowBox::placeBoxesInInlineDirection(float logicalLeft, bool& needsWordSpacing) -{ - // Set our x position. - beginPlacingBoxRangesInInlineDirection(logicalLeft); +float InlineFlowBox::placeBoxesInInlineDirection(float logicalLeft, + bool& needsWordSpacing) { + // Set our x position. + beginPlacingBoxRangesInInlineDirection(logicalLeft); - float startLogicalLeft = logicalLeft; - logicalLeft += borderLogicalLeft() + paddingLogicalLeft(); + float startLogicalLeft = logicalLeft; + logicalLeft += borderLogicalLeft() + paddingLogicalLeft(); - float minLogicalLeft = startLogicalLeft; - float maxLogicalRight = logicalLeft; + float minLogicalLeft = startLogicalLeft; + float maxLogicalRight = logicalLeft; - placeBoxRangeInInlineDirection(firstChild(), 0, logicalLeft, minLogicalLeft, maxLogicalRight, needsWordSpacing); + placeBoxRangeInInlineDirection(firstChild(), 0, logicalLeft, minLogicalLeft, + maxLogicalRight, needsWordSpacing); - logicalLeft += borderLogicalRight() + paddingLogicalRight(); - endPlacingBoxRangesInInlineDirection(startLogicalLeft, logicalLeft, minLogicalLeft, maxLogicalRight); - return logicalLeft; + logicalLeft += borderLogicalRight() + paddingLogicalRight(); + endPlacingBoxRangesInInlineDirection(startLogicalLeft, logicalLeft, + minLogicalLeft, maxLogicalRight); + return logicalLeft; } -float InlineFlowBox::placeBoxRangeInInlineDirection(InlineBox* firstChild, InlineBox* lastChild, - float& logicalLeft, float& minLogicalLeft, float& maxLogicalRight, bool& needsWordSpacing) -{ - for (InlineBox* curr = firstChild; curr && curr != lastChild; curr = curr->nextOnLine()) { - if (curr->renderer().isText()) { - InlineTextBox* text = toInlineTextBox(curr); - RenderText& rt = text->renderer(); - if (rt.textLength()) { - if (needsWordSpacing && isSpaceOrNewline(rt.characterAt(text->start()))) - logicalLeft += rt.style(isFirstLineStyle())->font().fontDescription().wordSpacing(); - needsWordSpacing = !isSpaceOrNewline(rt.characterAt(text->end())); - } - text->setLogicalLeft(logicalLeft); - if (knownToHaveNoOverflow()) - minLogicalLeft = std::min(logicalLeft, minLogicalLeft); - logicalLeft += text->logicalWidth(); - if (knownToHaveNoOverflow()) - maxLogicalRight = std::max(logicalLeft, maxLogicalRight); +float InlineFlowBox::placeBoxRangeInInlineDirection(InlineBox* firstChild, + InlineBox* lastChild, + float& logicalLeft, + float& minLogicalLeft, + float& maxLogicalRight, + bool& needsWordSpacing) { + for (InlineBox* curr = firstChild; curr && curr != lastChild; + curr = curr->nextOnLine()) { + if (curr->renderer().isText()) { + InlineTextBox* text = toInlineTextBox(curr); + RenderText& rt = text->renderer(); + if (rt.textLength()) { + if (needsWordSpacing && isSpaceOrNewline(rt.characterAt(text->start()))) + logicalLeft += rt.style(isFirstLineStyle()) + ->font() + .fontDescription() + .wordSpacing(); + needsWordSpacing = !isSpaceOrNewline(rt.characterAt(text->end())); + } + text->setLogicalLeft(logicalLeft); + if (knownToHaveNoOverflow()) + minLogicalLeft = std::min(logicalLeft, minLogicalLeft); + logicalLeft += text->logicalWidth(); + if (knownToHaveNoOverflow()) + maxLogicalRight = std::max(logicalLeft, maxLogicalRight); + } else { + if (curr->renderer().isOutOfFlowPositioned()) { + if (curr->renderer().parent()->style()->isLeftToRightDirection()) { + curr->setLogicalLeft(logicalLeft); } else { - if (curr->renderer().isOutOfFlowPositioned()) { - if (curr->renderer().parent()->style()->isLeftToRightDirection()) { - curr->setLogicalLeft(logicalLeft); - } else { - // Our offset that we cache needs to be from the edge of the right border box and - // not the left border box. We have to subtract |x| from the width of the block - // (which can be obtained from the root line box). - curr->setLogicalLeft(root().block().logicalWidth() - logicalLeft); - } - continue; // The positioned object has no effect on the width. - } - if (curr->renderer().isRenderInline()) { - InlineFlowBox* flow = toInlineFlowBox(curr); - logicalLeft += flow->marginLogicalLeft(); - if (knownToHaveNoOverflow()) - minLogicalLeft = std::min(logicalLeft, minLogicalLeft); - logicalLeft = flow->placeBoxesInInlineDirection(logicalLeft, needsWordSpacing); - if (knownToHaveNoOverflow()) - maxLogicalRight = std::max(logicalLeft, maxLogicalRight); - logicalLeft += flow->marginLogicalRight(); - } else { - // The box can have a different writing-mode than the overall line, so this is a bit complicated. - // Just get all the physical margin and overflow values by hand based off |isVertical|. - LayoutUnit logicalLeftMargin = curr->boxModelObject()->marginLeft(); - LayoutUnit logicalRightMargin = curr->boxModelObject()->marginRight(); - - logicalLeft += logicalLeftMargin; - curr->setLogicalLeft(logicalLeft); - if (knownToHaveNoOverflow()) - minLogicalLeft = std::min(logicalLeft, minLogicalLeft); - logicalLeft += curr->logicalWidth(); - if (knownToHaveNoOverflow()) - maxLogicalRight = std::max(logicalLeft, maxLogicalRight); - logicalLeft += logicalRightMargin; - // If we encounter any space after this inline block then ensure it is treated as the space between two words. - needsWordSpacing = true; - } + // Our offset that we cache needs to be from the edge of the right + // border box and not the left border box. We have to subtract |x| + // from the width of the block (which can be obtained from the root + // line box). + curr->setLogicalLeft(root().block().logicalWidth() - logicalLeft); } + continue; // The positioned object has no effect on the width. + } + if (curr->renderer().isRenderInline()) { + InlineFlowBox* flow = toInlineFlowBox(curr); + logicalLeft += flow->marginLogicalLeft(); + if (knownToHaveNoOverflow()) + minLogicalLeft = std::min(logicalLeft, minLogicalLeft); + logicalLeft = + flow->placeBoxesInInlineDirection(logicalLeft, needsWordSpacing); + if (knownToHaveNoOverflow()) + maxLogicalRight = std::max(logicalLeft, maxLogicalRight); + logicalLeft += flow->marginLogicalRight(); + } else { + // The box can have a different writing-mode than the overall line, so + // this is a bit complicated. Just get all the physical margin and + // overflow values by hand based off |isVertical|. + LayoutUnit logicalLeftMargin = curr->boxModelObject()->marginLeft(); + LayoutUnit logicalRightMargin = curr->boxModelObject()->marginRight(); + + logicalLeft += logicalLeftMargin; + curr->setLogicalLeft(logicalLeft); + if (knownToHaveNoOverflow()) + minLogicalLeft = std::min(logicalLeft, minLogicalLeft); + logicalLeft += curr->logicalWidth(); + if (knownToHaveNoOverflow()) + maxLogicalRight = std::max(logicalLeft, maxLogicalRight); + logicalLeft += logicalRightMargin; + // If we encounter any space after this inline block then ensure it is + // treated as the space between two words. + needsWordSpacing = true; + } } - return logicalLeft; + } + return logicalLeft; } -void InlineFlowBox::adjustMaxAscentAndDescent(int& maxAscent, int& maxDescent, int maxPositionTop, int maxPositionBottom) -{ - for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { - // The computed lineheight needs to be extended for the - // positioned elements - if (curr->renderer().isOutOfFlowPositioned()) - continue; // Positioned placeholders don't affect calculations. - if (curr->verticalAlign() == TOP || curr->verticalAlign() == BOTTOM) { - int lineHeight = curr->lineHeight(); - if (curr->verticalAlign() == TOP) { - if (maxAscent + maxDescent < lineHeight) - maxDescent = lineHeight - maxAscent; - } - else { - if (maxAscent + maxDescent < lineHeight) - maxAscent = lineHeight - maxDescent; - } - - if (maxAscent + maxDescent >= std::max(maxPositionTop, maxPositionBottom)) - break; - } - - if (curr->isInlineFlowBox()) - toInlineFlowBox(curr)->adjustMaxAscentAndDescent(maxAscent, maxDescent, maxPositionTop, maxPositionBottom); +void InlineFlowBox::adjustMaxAscentAndDescent(int& maxAscent, + int& maxDescent, + int maxPositionTop, + int maxPositionBottom) { + for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { + // The computed lineheight needs to be extended for the + // positioned elements + if (curr->renderer().isOutOfFlowPositioned()) + continue; // Positioned placeholders don't affect calculations. + if (curr->verticalAlign() == TOP || curr->verticalAlign() == BOTTOM) { + int lineHeight = curr->lineHeight(); + if (curr->verticalAlign() == TOP) { + if (maxAscent + maxDescent < lineHeight) + maxDescent = lineHeight - maxAscent; + } else { + if (maxAscent + maxDescent < lineHeight) + maxAscent = lineHeight - maxDescent; + } + + if (maxAscent + maxDescent >= std::max(maxPositionTop, maxPositionBottom)) + break; } + + if (curr->isInlineFlowBox()) + toInlineFlowBox(curr)->adjustMaxAscentAndDescent( + maxAscent, maxDescent, maxPositionTop, maxPositionBottom); + } } -void InlineFlowBox::computeLogicalBoxHeights(RootInlineBox* rootBox, LayoutUnit& maxPositionTop, LayoutUnit& maxPositionBottom, - int& maxAscent, int& maxDescent, bool& setMaxAscent, bool& setMaxDescent, - bool strictMode, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, - FontBaseline baselineType, VerticalPositionCache& verticalPositionCache) -{ - // The primary purpose of this function is to compute the maximal ascent and descent values for - // a line. These values are computed based off the block's line-box-contain property, which indicates - // what parts of descendant boxes have to fit within the line. - // - // The maxAscent value represents the distance of the highest point of any box (typically including line-height) from - // the root box's baseline. The maxDescent value represents the distance of the lowest point of any box - // (also typically including line-height) from the root box baseline. These values can be negative. - // - // A secondary purpose of this function is to store the offset of every box's baseline from the root box's - // baseline. This information is cached in the logicalTop() of every box. We're effectively just using - // the logicalTop() as scratch space. - // - // Because a box can be positioned such that it ends up fully above or fully below the - // root line box, we only consider it to affect the maxAscent and maxDescent values if some - // part of the box (EXCLUDING leading) is above (for ascent) or below (for descent) the root box's baseline. - bool affectsAscent = false; - bool affectsDescent = false; - bool checkChildren = !descendantsHaveSameLineHeightAndBaseline(); - - if (isRootInlineBox()) { - // Examine our root box. - int ascent = 0; - int descent = 0; - rootBox->ascentAndDescentForBox(rootBox, textBoxDataMap, ascent, descent, affectsAscent, affectsDescent); - if (strictMode || hasTextChildren() || (!checkChildren && hasTextDescendants())) { - if (maxAscent < ascent || !setMaxAscent) { - maxAscent = ascent; - setMaxAscent = true; - } - if (maxDescent < descent || !setMaxDescent) { - maxDescent = descent; - setMaxDescent = true; - } - } +void InlineFlowBox::computeLogicalBoxHeights( + RootInlineBox* rootBox, + LayoutUnit& maxPositionTop, + LayoutUnit& maxPositionBottom, + int& maxAscent, + int& maxDescent, + bool& setMaxAscent, + bool& setMaxDescent, + bool strictMode, + GlyphOverflowAndFallbackFontsMap& textBoxDataMap, + FontBaseline baselineType, + VerticalPositionCache& verticalPositionCache) { + // The primary purpose of this function is to compute the maximal ascent and + // descent values for a line. These values are computed based off the block's + // line-box-contain property, which indicates what parts of descendant boxes + // have to fit within the line. + // + // The maxAscent value represents the distance of the highest point of any box + // (typically including line-height) from the root box's baseline. The + // maxDescent value represents the distance of the lowest point of any box + // (also typically including line-height) from the root box baseline. These + // values can be negative. + // + // A secondary purpose of this function is to store the offset of every box's + // baseline from the root box's baseline. This information is cached in the + // logicalTop() of every box. We're effectively just using the logicalTop() as + // scratch space. + // + // Because a box can be positioned such that it ends up fully above or fully + // below the root line box, we only consider it to affect the maxAscent and + // maxDescent values if some part of the box (EXCLUDING leading) is above (for + // ascent) or below (for descent) the root box's baseline. + bool affectsAscent = false; + bool affectsDescent = false; + bool checkChildren = !descendantsHaveSameLineHeightAndBaseline(); + + if (isRootInlineBox()) { + // Examine our root box. + int ascent = 0; + int descent = 0; + rootBox->ascentAndDescentForBox(rootBox, textBoxDataMap, ascent, descent, + affectsAscent, affectsDescent); + if (strictMode || hasTextChildren() || + (!checkChildren && hasTextDescendants())) { + if (maxAscent < ascent || !setMaxAscent) { + maxAscent = ascent; + setMaxAscent = true; + } + if (maxDescent < descent || !setMaxDescent) { + maxDescent = descent; + setMaxDescent = true; + } } + } - if (!checkChildren) - return; - - for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { - if (curr->renderer().isOutOfFlowPositioned()) - continue; // Positioned placeholders don't affect calculations. - - InlineFlowBox* inlineFlowBox = curr->isInlineFlowBox() ? toInlineFlowBox(curr) : 0; - - bool affectsAscent = false; - bool affectsDescent = false; - - // The verticalPositionForBox function returns the distance between the child box's baseline - // and the root box's baseline. The value is negative if the child box's baseline is above the - // root box's baseline, and it is positive if the child box's baseline is below the root box's baseline. - curr->setLogicalTop(rootBox->verticalPositionForBox(curr, verticalPositionCache).toFloat()); - - int ascent = 0; - int descent = 0; - rootBox->ascentAndDescentForBox(curr, textBoxDataMap, ascent, descent, affectsAscent, affectsDescent); - - LayoutUnit boxHeight = ascent + descent; - if (curr->verticalAlign() == TOP) { - if (maxPositionTop < boxHeight) - maxPositionTop = boxHeight; - } else if (curr->verticalAlign() == BOTTOM) { - if (maxPositionBottom < boxHeight) - maxPositionBottom = boxHeight; - } else if (!inlineFlowBox || strictMode || inlineFlowBox->hasTextChildren() || (inlineFlowBox->descendantsHaveSameLineHeightAndBaseline() && inlineFlowBox->hasTextDescendants()) - || inlineFlowBox->boxModelObject()->hasInlineDirectionBordersOrPadding()) { - // Note that these values can be negative. Even though we only affect the maxAscent and maxDescent values - // if our box (excluding line-height) was above (for ascent) or below (for descent) the root baseline, once you factor in line-height - // the final box can end up being fully above or fully below the root box's baseline! This is ok, but what it - // means is that ascent and descent (including leading), can end up being negative. The setMaxAscent and - // setMaxDescent booleans are used to ensure that we're willing to initially set maxAscent/Descent to negative - // values. - ascent -= curr->logicalTop(); - descent += curr->logicalTop(); - if (affectsAscent && (maxAscent < ascent || !setMaxAscent)) { - maxAscent = ascent; - setMaxAscent = true; - } - - if (affectsDescent && (maxDescent < descent || !setMaxDescent)) { - maxDescent = descent; - setMaxDescent = true; - } - } + if (!checkChildren) + return; - if (inlineFlowBox) - inlineFlowBox->computeLogicalBoxHeights(rootBox, maxPositionTop, maxPositionBottom, maxAscent, maxDescent, - setMaxAscent, setMaxDescent, strictMode, textBoxDataMap, - baselineType, verticalPositionCache); - } -} + for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { + if (curr->renderer().isOutOfFlowPositioned()) + continue; // Positioned placeholders don't affect calculations. -void InlineFlowBox::placeBoxesInBlockDirection(LayoutUnit top, LayoutUnit maxHeight, int maxAscent, bool strictMode, LayoutUnit& lineTop, LayoutUnit& lineBottom, LayoutUnit& selectionBottom, bool& setLineTop, - LayoutUnit& lineTopIncludingMargins, LayoutUnit& lineBottomIncludingMargins, bool& hasAnnotationsBefore, bool& hasAnnotationsAfter, FontBaseline baselineType) -{ - bool isRootBox = isRootInlineBox(); - if (isRootBox) { - const FontMetrics& fontMetrics = renderer().style(isFirstLineStyle())->fontMetrics(); - // RootInlineBoxes are always placed on at pixel boundaries in their logical y direction. Not doing - // so results in incorrect rendering of text decorations, most notably underlines. - setLogicalTop(roundToInt(top + maxAscent - fontMetrics.ascent(baselineType))); - } + InlineFlowBox* inlineFlowBox = + curr->isInlineFlowBox() ? toInlineFlowBox(curr) : 0; - LayoutUnit adjustmentForChildrenWithSameLineHeightAndBaseline = 0; - if (descendantsHaveSameLineHeightAndBaseline()) { - adjustmentForChildrenWithSameLineHeightAndBaseline = logicalTop(); - if (parent()) - adjustmentForChildrenWithSameLineHeightAndBaseline += (boxModelObject()->borderBefore() + boxModelObject()->paddingBefore()); + bool affectsAscent = false; + bool affectsDescent = false; + + // The verticalPositionForBox function returns the distance between the + // child box's baseline and the root box's baseline. The value is negative + // if the child box's baseline is above the root box's baseline, and it is + // positive if the child box's baseline is below the root box's baseline. + curr->setLogicalTop( + rootBox->verticalPositionForBox(curr, verticalPositionCache).toFloat()); + + int ascent = 0; + int descent = 0; + rootBox->ascentAndDescentForBox(curr, textBoxDataMap, ascent, descent, + affectsAscent, affectsDescent); + + LayoutUnit boxHeight = ascent + descent; + if (curr->verticalAlign() == TOP) { + if (maxPositionTop < boxHeight) + maxPositionTop = boxHeight; + } else if (curr->verticalAlign() == BOTTOM) { + if (maxPositionBottom < boxHeight) + maxPositionBottom = boxHeight; + } else if (!inlineFlowBox || strictMode || + inlineFlowBox->hasTextChildren() || + (inlineFlowBox->descendantsHaveSameLineHeightAndBaseline() && + inlineFlowBox->hasTextDescendants()) || + inlineFlowBox->boxModelObject() + ->hasInlineDirectionBordersOrPadding()) { + // Note that these values can be negative. Even though we only affect the + // maxAscent and maxDescent values if our box (excluding line-height) was + // above (for ascent) or below (for descent) the root baseline, once you + // factor in line-height the final box can end up being fully above or + // fully below the root box's baseline! This is ok, but what it means is + // that ascent and descent (including leading), can end up being negative. + // The setMaxAscent and setMaxDescent booleans are used to ensure that + // we're willing to initially set maxAscent/Descent to negative values. + ascent -= curr->logicalTop(); + descent += curr->logicalTop(); + if (affectsAscent && (maxAscent < ascent || !setMaxAscent)) { + maxAscent = ascent; + setMaxAscent = true; + } + + if (affectsDescent && (maxDescent < descent || !setMaxDescent)) { + maxDescent = descent; + setMaxDescent = true; + } } - for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { - if (curr->renderer().isOutOfFlowPositioned()) - continue; // Positioned placeholders don't affect calculations. + if (inlineFlowBox) + inlineFlowBox->computeLogicalBoxHeights( + rootBox, maxPositionTop, maxPositionBottom, maxAscent, maxDescent, + setMaxAscent, setMaxDescent, strictMode, textBoxDataMap, baselineType, + verticalPositionCache); + } +} - if (descendantsHaveSameLineHeightAndBaseline()) { - curr->adjustBlockDirectionPosition(adjustmentForChildrenWithSameLineHeightAndBaseline.toFloat()); - continue; - } +void InlineFlowBox::placeBoxesInBlockDirection( + LayoutUnit top, + LayoutUnit maxHeight, + int maxAscent, + bool strictMode, + LayoutUnit& lineTop, + LayoutUnit& lineBottom, + LayoutUnit& selectionBottom, + bool& setLineTop, + LayoutUnit& lineTopIncludingMargins, + LayoutUnit& lineBottomIncludingMargins, + bool& hasAnnotationsBefore, + bool& hasAnnotationsAfter, + FontBaseline baselineType) { + bool isRootBox = isRootInlineBox(); + if (isRootBox) { + const FontMetrics& fontMetrics = + renderer().style(isFirstLineStyle())->fontMetrics(); + // RootInlineBoxes are always placed on at pixel boundaries in their logical + // y direction. Not doing so results in incorrect rendering of text + // decorations, most notably underlines. + setLogicalTop( + roundToInt(top + maxAscent - fontMetrics.ascent(baselineType))); + } + + LayoutUnit adjustmentForChildrenWithSameLineHeightAndBaseline = 0; + if (descendantsHaveSameLineHeightAndBaseline()) { + adjustmentForChildrenWithSameLineHeightAndBaseline = logicalTop(); + if (parent()) + adjustmentForChildrenWithSameLineHeightAndBaseline += + (boxModelObject()->borderBefore() + + boxModelObject()->paddingBefore()); + } - InlineFlowBox* inlineFlowBox = curr->isInlineFlowBox() ? toInlineFlowBox(curr) : 0; - bool childAffectsTopBottomPos = true; - if (curr->verticalAlign() == TOP) - curr->setLogicalTop(top.toFloat()); - else if (curr->verticalAlign() == BOTTOM) - curr->setLogicalTop((top + maxHeight - curr->lineHeight()).toFloat()); - else { - if (!strictMode && inlineFlowBox && !inlineFlowBox->hasTextChildren() && !curr->boxModelObject()->hasInlineDirectionBordersOrPadding() - && !(inlineFlowBox->descendantsHaveSameLineHeightAndBaseline() && inlineFlowBox->hasTextDescendants())) - childAffectsTopBottomPos = false; - LayoutUnit posAdjust = maxAscent - curr->baselinePosition(baselineType); - curr->setLogicalTop(curr->logicalTop() + top + posAdjust); - } + for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { + if (curr->renderer().isOutOfFlowPositioned()) + continue; // Positioned placeholders don't affect calculations. - LayoutUnit newLogicalTop = curr->logicalTop(); - LayoutUnit newLogicalTopIncludingMargins = newLogicalTop; - LayoutUnit boxHeight = curr->logicalHeight(); - LayoutUnit boxHeightIncludingMargins = boxHeight; - LayoutUnit borderPaddingHeight = 0; - if (curr->isText() || curr->isInlineFlowBox()) { - const FontMetrics& fontMetrics = curr->renderer().style(isFirstLineStyle())->fontMetrics(); - newLogicalTop += curr->baselinePosition(baselineType) - fontMetrics.ascent(baselineType); - if (curr->isInlineFlowBox()) { - RenderBoxModelObject& boxObject = toRenderBoxModelObject(curr->renderer()); - newLogicalTop -= boxObject.borderTop() + boxObject.paddingTop(); - borderPaddingHeight = boxObject.borderAndPaddingLogicalHeight(); - } - newLogicalTopIncludingMargins = newLogicalTop; - } else { - RenderBox& box = toRenderBox(curr->renderer()); - newLogicalTopIncludingMargins = newLogicalTop; - LayoutUnit overSideMargin = box.marginTop(); - LayoutUnit underSideMargin = box.marginBottom(); - newLogicalTop += overSideMargin; - boxHeightIncludingMargins += overSideMargin + underSideMargin; - } + if (descendantsHaveSameLineHeightAndBaseline()) { + curr->adjustBlockDirectionPosition( + adjustmentForChildrenWithSameLineHeightAndBaseline.toFloat()); + continue; + } - curr->setLogicalTop(newLogicalTop.toFloat()); - - if (childAffectsTopBottomPos) { - if (curr->isInlineTextBox()) { - TextEmphasisPosition emphasisMarkPosition; - if (toInlineTextBox(curr)->getEmphasisMarkPosition(curr->renderer().style(isFirstLineStyle()), emphasisMarkPosition)) { - bool emphasisMarkIsOver = emphasisMarkPosition == TextEmphasisPositionOver; - if (emphasisMarkIsOver) - hasAnnotationsBefore = true; - else - hasAnnotationsAfter = true; - } - } - - if (!setLineTop) { - setLineTop = true; - lineTop = newLogicalTop; - lineTopIncludingMargins = std::min(lineTop, newLogicalTopIncludingMargins); - } else { - lineTop = std::min(lineTop, newLogicalTop); - lineTopIncludingMargins = std::min(lineTop, std::min(lineTopIncludingMargins, newLogicalTopIncludingMargins)); - } - selectionBottom = std::max(selectionBottom, newLogicalTop + boxHeight - borderPaddingHeight); - lineBottom = std::max(lineBottom, newLogicalTop + boxHeight); - lineBottomIncludingMargins = std::max(lineBottom, std::max(lineBottomIncludingMargins, newLogicalTopIncludingMargins + boxHeightIncludingMargins)); - } + InlineFlowBox* inlineFlowBox = + curr->isInlineFlowBox() ? toInlineFlowBox(curr) : 0; + bool childAffectsTopBottomPos = true; + if (curr->verticalAlign() == TOP) + curr->setLogicalTop(top.toFloat()); + else if (curr->verticalAlign() == BOTTOM) + curr->setLogicalTop((top + maxHeight - curr->lineHeight()).toFloat()); + else { + if (!strictMode && inlineFlowBox && !inlineFlowBox->hasTextChildren() && + !curr->boxModelObject()->hasInlineDirectionBordersOrPadding() && + !(inlineFlowBox->descendantsHaveSameLineHeightAndBaseline() && + inlineFlowBox->hasTextDescendants())) + childAffectsTopBottomPos = false; + LayoutUnit posAdjust = maxAscent - curr->baselinePosition(baselineType); + curr->setLogicalTop(curr->logicalTop() + top + posAdjust); + } - // Adjust boxes to use their real box y/height and not the logical height (as dictated by - // line-height). - if (inlineFlowBox) - inlineFlowBox->placeBoxesInBlockDirection(top, maxHeight, maxAscent, strictMode, lineTop, lineBottom, selectionBottom, setLineTop, - lineTopIncludingMargins, lineBottomIncludingMargins, hasAnnotationsBefore, hasAnnotationsAfter, baselineType); + LayoutUnit newLogicalTop = curr->logicalTop(); + LayoutUnit newLogicalTopIncludingMargins = newLogicalTop; + LayoutUnit boxHeight = curr->logicalHeight(); + LayoutUnit boxHeightIncludingMargins = boxHeight; + LayoutUnit borderPaddingHeight = 0; + if (curr->isText() || curr->isInlineFlowBox()) { + const FontMetrics& fontMetrics = + curr->renderer().style(isFirstLineStyle())->fontMetrics(); + newLogicalTop += curr->baselinePosition(baselineType) - + fontMetrics.ascent(baselineType); + if (curr->isInlineFlowBox()) { + RenderBoxModelObject& boxObject = + toRenderBoxModelObject(curr->renderer()); + newLogicalTop -= boxObject.borderTop() + boxObject.paddingTop(); + borderPaddingHeight = boxObject.borderAndPaddingLogicalHeight(); + } + newLogicalTopIncludingMargins = newLogicalTop; + } else { + RenderBox& box = toRenderBox(curr->renderer()); + newLogicalTopIncludingMargins = newLogicalTop; + LayoutUnit overSideMargin = box.marginTop(); + LayoutUnit underSideMargin = box.marginBottom(); + newLogicalTop += overSideMargin; + boxHeightIncludingMargins += overSideMargin + underSideMargin; } - if (isRootBox) { - if (strictMode || hasTextChildren() || (descendantsHaveSameLineHeightAndBaseline() && hasTextDescendants())) { - if (!setLineTop) { - setLineTop = true; - lineTop = pixelSnappedLogicalTop(); - lineTopIncludingMargins = lineTop; - } else { - lineTop = std::min(lineTop, pixelSnappedLogicalTop()); - lineTopIncludingMargins = std::min(lineTop, lineTopIncludingMargins); - } - selectionBottom = std::max(selectionBottom, pixelSnappedLogicalBottom()); - lineBottom = std::max(lineBottom, pixelSnappedLogicalBottom()); - lineBottomIncludingMargins = std::max(lineBottom, lineBottomIncludingMargins); + curr->setLogicalTop(newLogicalTop.toFloat()); + + if (childAffectsTopBottomPos) { + if (curr->isInlineTextBox()) { + TextEmphasisPosition emphasisMarkPosition; + if (toInlineTextBox(curr)->getEmphasisMarkPosition( + curr->renderer().style(isFirstLineStyle()), + emphasisMarkPosition)) { + bool emphasisMarkIsOver = + emphasisMarkPosition == TextEmphasisPositionOver; + if (emphasisMarkIsOver) + hasAnnotationsBefore = true; + else + hasAnnotationsAfter = true; } + } + + if (!setLineTop) { + setLineTop = true; + lineTop = newLogicalTop; + lineTopIncludingMargins = + std::min(lineTop, newLogicalTopIncludingMargins); + } else { + lineTop = std::min(lineTop, newLogicalTop); + lineTopIncludingMargins = std::min( + lineTop, + std::min(lineTopIncludingMargins, newLogicalTopIncludingMargins)); + } + selectionBottom = std::max( + selectionBottom, newLogicalTop + boxHeight - borderPaddingHeight); + lineBottom = std::max(lineBottom, newLogicalTop + boxHeight); + lineBottomIncludingMargins = std::max( + lineBottom, + std::max(lineBottomIncludingMargins, + newLogicalTopIncludingMargins + boxHeightIncludingMargins)); } -} -void InlineFlowBox::computeMaxLogicalTop(float& maxLogicalTop) const -{ - for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { - if (curr->renderer().isOutOfFlowPositioned()) - continue; // Positioned placeholders don't affect calculations. - - if (descendantsHaveSameLineHeightAndBaseline()) - continue; - - maxLogicalTop = std::max(maxLogicalTop, curr->y()); - float localMaxLogicalTop = 0; - if (curr->isInlineFlowBox()) - toInlineFlowBox(curr)->computeMaxLogicalTop(localMaxLogicalTop); - maxLogicalTop = std::max(maxLogicalTop, localMaxLogicalTop); + // Adjust boxes to use their real box y/height and not the logical height + // (as dictated by line-height). + if (inlineFlowBox) + inlineFlowBox->placeBoxesInBlockDirection( + top, maxHeight, maxAscent, strictMode, lineTop, lineBottom, + selectionBottom, setLineTop, lineTopIncludingMargins, + lineBottomIncludingMargins, hasAnnotationsBefore, hasAnnotationsAfter, + baselineType); + } + + if (isRootBox) { + if (strictMode || hasTextChildren() || + (descendantsHaveSameLineHeightAndBaseline() && hasTextDescendants())) { + if (!setLineTop) { + setLineTop = true; + lineTop = pixelSnappedLogicalTop(); + lineTopIncludingMargins = lineTop; + } else { + lineTop = std::min(lineTop, pixelSnappedLogicalTop()); + lineTopIncludingMargins = std::min(lineTop, lineTopIncludingMargins); + } + selectionBottom = + std::max(selectionBottom, pixelSnappedLogicalBottom()); + lineBottom = + std::max(lineBottom, pixelSnappedLogicalBottom()); + lineBottomIncludingMargins = + std::max(lineBottom, lineBottomIncludingMargins); } + } } -void InlineFlowBox::flipLinesInBlockDirection(LayoutUnit lineTop, LayoutUnit lineBottom) -{ - // Flip the box on the line such that the top is now relative to the lineBottom instead of the lineTop. - setLogicalTop(lineBottom - (logicalTop() - lineTop) - logicalHeight()); +void InlineFlowBox::computeMaxLogicalTop(float& maxLogicalTop) const { + for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { + if (curr->renderer().isOutOfFlowPositioned()) + continue; // Positioned placeholders don't affect calculations. - for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { - if (curr->renderer().isOutOfFlowPositioned()) - continue; // Positioned placeholders aren't affected here. + if (descendantsHaveSameLineHeightAndBaseline()) + continue; - if (curr->isInlineFlowBox()) - toInlineFlowBox(curr)->flipLinesInBlockDirection(lineTop, lineBottom); - else - curr->setLogicalTop(lineBottom - (curr->logicalTop() - lineTop) - curr->logicalHeight()); - } + maxLogicalTop = std::max(maxLogicalTop, curr->y()); + float localMaxLogicalTop = 0; + if (curr->isInlineFlowBox()) + toInlineFlowBox(curr)->computeMaxLogicalTop(localMaxLogicalTop); + maxLogicalTop = std::max(maxLogicalTop, localMaxLogicalTop); + } } -inline void InlineFlowBox::addBoxShadowVisualOverflow(LayoutRect& logicalVisualOverflow) -{ - // box-shadow on root line boxes is applying to the block and not to the lines. - if (!parent()) - return; +void InlineFlowBox::flipLinesInBlockDirection(LayoutUnit lineTop, + LayoutUnit lineBottom) { + // Flip the box on the line such that the top is now relative to the + // lineBottom instead of the lineTop. + setLogicalTop(lineBottom - (logicalTop() - lineTop) - logicalHeight()); - RenderStyle* style = renderer().style(isFirstLineStyle()); - if (!style->boxShadow()) - return; + for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { + if (curr->renderer().isOutOfFlowPositioned()) + continue; // Positioned placeholders aren't affected here. - LayoutUnit boxShadowLogicalTop; - LayoutUnit boxShadowLogicalBottom; - style->getBoxShadowBlockDirectionExtent(boxShadowLogicalTop, boxShadowLogicalBottom); - - // Similar to how glyph overflow works, if our lines are flipped, then it's actually the opposite shadow that applies, since - // the line is "upside down" in terms of block coordinates. - LayoutUnit shadowLogicalTop = boxShadowLogicalTop; - LayoutUnit shadowLogicalBottom = boxShadowLogicalBottom; - - LayoutUnit logicalTopVisualOverflow = std::min(pixelSnappedLogicalTop() + shadowLogicalTop, logicalVisualOverflow.y()); - LayoutUnit logicalBottomVisualOverflow = std::max(pixelSnappedLogicalBottom() + shadowLogicalBottom, logicalVisualOverflow.maxY()); - - LayoutUnit boxShadowLogicalLeft; - LayoutUnit boxShadowLogicalRight; - style->getBoxShadowInlineDirectionExtent(boxShadowLogicalLeft, boxShadowLogicalRight); - - LayoutUnit logicalLeftVisualOverflow = std::min(pixelSnappedLogicalLeft() + boxShadowLogicalLeft, logicalVisualOverflow.x()); - LayoutUnit logicalRightVisualOverflow = std::max(pixelSnappedLogicalRight() + boxShadowLogicalRight, logicalVisualOverflow.maxX()); - - logicalVisualOverflow = LayoutRect(logicalLeftVisualOverflow, logicalTopVisualOverflow, - logicalRightVisualOverflow - logicalLeftVisualOverflow, logicalBottomVisualOverflow - logicalTopVisualOverflow); + if (curr->isInlineFlowBox()) + toInlineFlowBox(curr)->flipLinesInBlockDirection(lineTop, lineBottom); + else + curr->setLogicalTop(lineBottom - (curr->logicalTop() - lineTop) - + curr->logicalHeight()); + } } -inline void InlineFlowBox::addBorderOutsetVisualOverflow(LayoutRect& logicalVisualOverflow) -{ +inline void InlineFlowBox::addBoxShadowVisualOverflow( + LayoutRect& logicalVisualOverflow) { + // box-shadow on root line boxes is applying to the block and not to the + // lines. + if (!parent()) return; -} - -inline void InlineFlowBox::addOutlineVisualOverflow(LayoutRect& logicalVisualOverflow) -{ - // Outline on root line boxes is applied to the block and not to the lines. - if (!parent()) - return; - RenderStyle* style = renderer().style(isFirstLineStyle()); - if (!style->hasOutline()) - return; + RenderStyle* style = renderer().style(isFirstLineStyle()); + if (!style->boxShadow()) + return; - logicalVisualOverflow.inflate(style->outlineSize()); + LayoutUnit boxShadowLogicalTop; + LayoutUnit boxShadowLogicalBottom; + style->getBoxShadowBlockDirectionExtent(boxShadowLogicalTop, + boxShadowLogicalBottom); + + // Similar to how glyph overflow works, if our lines are flipped, then it's + // actually the opposite shadow that applies, since the line is "upside down" + // in terms of block coordinates. + LayoutUnit shadowLogicalTop = boxShadowLogicalTop; + LayoutUnit shadowLogicalBottom = boxShadowLogicalBottom; + + LayoutUnit logicalTopVisualOverflow = std::min( + pixelSnappedLogicalTop() + shadowLogicalTop, logicalVisualOverflow.y()); + LayoutUnit logicalBottomVisualOverflow = + std::max(pixelSnappedLogicalBottom() + shadowLogicalBottom, + logicalVisualOverflow.maxY()); + + LayoutUnit boxShadowLogicalLeft; + LayoutUnit boxShadowLogicalRight; + style->getBoxShadowInlineDirectionExtent(boxShadowLogicalLeft, + boxShadowLogicalRight); + + LayoutUnit logicalLeftVisualOverflow = + std::min(pixelSnappedLogicalLeft() + boxShadowLogicalLeft, + logicalVisualOverflow.x()); + LayoutUnit logicalRightVisualOverflow = + std::max(pixelSnappedLogicalRight() + boxShadowLogicalRight, + logicalVisualOverflow.maxX()); + + logicalVisualOverflow = + LayoutRect(logicalLeftVisualOverflow, logicalTopVisualOverflow, + logicalRightVisualOverflow - logicalLeftVisualOverflow, + logicalBottomVisualOverflow - logicalTopVisualOverflow); } -inline void InlineFlowBox::addTextBoxVisualOverflow(InlineTextBox* textBox, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, LayoutRect& logicalVisualOverflow) -{ - if (textBox->knownToHaveNoOverflow()) - return; - - RenderStyle* style = textBox->renderer().style(isFirstLineStyle()); - - GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.find(textBox); - GlyphOverflow* glyphOverflow = it == textBoxDataMap.end() ? 0 : &it->value.second; - - int topGlyphEdge = glyphOverflow ? glyphOverflow->top : 0; - int bottomGlyphEdge = glyphOverflow ? glyphOverflow->bottom : 0; - int leftGlyphEdge = glyphOverflow ? glyphOverflow->left : 0; - int rightGlyphEdge = glyphOverflow ? glyphOverflow->right : 0; - - int strokeOverflow = static_cast(ceilf(style->textStrokeWidth() / 2.0f)); - int topGlyphOverflow = -strokeOverflow - topGlyphEdge; - int bottomGlyphOverflow = strokeOverflow + bottomGlyphEdge; - int leftGlyphOverflow = -strokeOverflow - leftGlyphEdge; - int rightGlyphOverflow = strokeOverflow + rightGlyphEdge; - - TextEmphasisPosition emphasisMarkPosition; - if (style->textEmphasisMark() != TextEmphasisMarkNone && textBox->getEmphasisMarkPosition(style, emphasisMarkPosition)) { - int emphasisMarkHeight = style->font().emphasisMarkHeight(style->textEmphasisMarkString()); - if (emphasisMarkPosition == TextEmphasisPositionOver) - topGlyphOverflow = std::min(topGlyphOverflow, -emphasisMarkHeight); - else - bottomGlyphOverflow = std::max(bottomGlyphOverflow, emphasisMarkHeight); - } - - // If letter-spacing is negative, we should factor that into right layout overflow. (Even in RTL, letter-spacing is - // applied to the right, so this is not an issue with left overflow. - rightGlyphOverflow -= std::min(0, (int)style->font().fontDescription().letterSpacing()); - - LayoutUnit textShadowLogicalTop; - LayoutUnit textShadowLogicalBottom; - style->getTextShadowBlockDirectionExtent(textShadowLogicalTop, textShadowLogicalBottom); - - LayoutUnit childOverflowLogicalTop = std::min(textShadowLogicalTop + topGlyphOverflow, topGlyphOverflow); - LayoutUnit childOverflowLogicalBottom = std::max(textShadowLogicalBottom + bottomGlyphOverflow, bottomGlyphOverflow); - - LayoutUnit textShadowLogicalLeft; - LayoutUnit textShadowLogicalRight; - style->getTextShadowInlineDirectionExtent(textShadowLogicalLeft, textShadowLogicalRight); - - LayoutUnit childOverflowLogicalLeft = std::min(textShadowLogicalLeft + leftGlyphOverflow, leftGlyphOverflow); - LayoutUnit childOverflowLogicalRight = std::max(textShadowLogicalRight + rightGlyphOverflow, rightGlyphOverflow); +inline void InlineFlowBox::addBorderOutsetVisualOverflow( + LayoutRect& logicalVisualOverflow) { + return; +} - LayoutUnit logicalTopVisualOverflow = std::min(textBox->pixelSnappedLogicalTop() + childOverflowLogicalTop, logicalVisualOverflow.y()); - LayoutUnit logicalBottomVisualOverflow = std::max(textBox->pixelSnappedLogicalBottom() + childOverflowLogicalBottom, logicalVisualOverflow.maxY()); - LayoutUnit logicalLeftVisualOverflow = std::min(textBox->pixelSnappedLogicalLeft() + childOverflowLogicalLeft, logicalVisualOverflow.x()); - LayoutUnit logicalRightVisualOverflow = std::max(textBox->pixelSnappedLogicalRight() + childOverflowLogicalRight, logicalVisualOverflow.maxX()); +inline void InlineFlowBox::addOutlineVisualOverflow( + LayoutRect& logicalVisualOverflow) { + // Outline on root line boxes is applied to the block and not to the lines. + if (!parent()) + return; - logicalVisualOverflow = LayoutRect(logicalLeftVisualOverflow, logicalTopVisualOverflow, - logicalRightVisualOverflow - logicalLeftVisualOverflow, logicalBottomVisualOverflow - logicalTopVisualOverflow); + RenderStyle* style = renderer().style(isFirstLineStyle()); + if (!style->hasOutline()) + return; - textBox->setLogicalOverflowRect(logicalVisualOverflow); + logicalVisualOverflow.inflate(style->outlineSize()); } -inline void InlineFlowBox::addReplacedChildOverflow(const InlineBox* inlineBox, LayoutRect& logicalLayoutOverflow, LayoutRect& logicalVisualOverflow) -{ - RenderBox& box = toRenderBox(inlineBox->renderer()); - - // Visual overflow only propagates if the box doesn't have a self-painting layer. This rectangle does not include - // transforms or relative positioning (since those objects always have self-painting layers), but it does need to be adjusted - // for writing-mode differences. - if (!box.hasSelfPaintingLayer()) { - LayoutRect childLogicalVisualOverflow = box.visualOverflowRect(); - childLogicalVisualOverflow.move(inlineBox->logicalLeft(), inlineBox->logicalTop()); - logicalVisualOverflow.unite(childLogicalVisualOverflow); - } +inline void InlineFlowBox::addTextBoxVisualOverflow( + InlineTextBox* textBox, + GlyphOverflowAndFallbackFontsMap& textBoxDataMap, + LayoutRect& logicalVisualOverflow) { + if (textBox->knownToHaveNoOverflow()) + return; - // Layout overflow internal to the child box only propagates if the child box doesn't have overflow clip set. - // Otherwise the child border box propagates as layout overflow. This rectangle must include transforms and relative positioning - // and be adjusted for writing-mode differences. - LayoutRect childLogicalLayoutOverflow = box.layoutOverflowRectForPropagation(); - childLogicalLayoutOverflow.move(inlineBox->logicalLeft(), inlineBox->logicalTop()); - logicalLayoutOverflow.unite(childLogicalLayoutOverflow); + RenderStyle* style = textBox->renderer().style(isFirstLineStyle()); + + GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.find(textBox); + GlyphOverflow* glyphOverflow = + it == textBoxDataMap.end() ? 0 : &it->value.second; + + int topGlyphEdge = glyphOverflow ? glyphOverflow->top : 0; + int bottomGlyphEdge = glyphOverflow ? glyphOverflow->bottom : 0; + int leftGlyphEdge = glyphOverflow ? glyphOverflow->left : 0; + int rightGlyphEdge = glyphOverflow ? glyphOverflow->right : 0; + + int strokeOverflow = static_cast(ceilf(style->textStrokeWidth() / 2.0f)); + int topGlyphOverflow = -strokeOverflow - topGlyphEdge; + int bottomGlyphOverflow = strokeOverflow + bottomGlyphEdge; + int leftGlyphOverflow = -strokeOverflow - leftGlyphEdge; + int rightGlyphOverflow = strokeOverflow + rightGlyphEdge; + + TextEmphasisPosition emphasisMarkPosition; + if (style->textEmphasisMark() != TextEmphasisMarkNone && + textBox->getEmphasisMarkPosition(style, emphasisMarkPosition)) { + int emphasisMarkHeight = + style->font().emphasisMarkHeight(style->textEmphasisMarkString()); + if (emphasisMarkPosition == TextEmphasisPositionOver) + topGlyphOverflow = std::min(topGlyphOverflow, -emphasisMarkHeight); + else + bottomGlyphOverflow = std::max(bottomGlyphOverflow, emphasisMarkHeight); + } + + // If letter-spacing is negative, we should factor that into right layout + // overflow. (Even in RTL, letter-spacing is applied to the right, so this is + // not an issue with left overflow. + rightGlyphOverflow -= + std::min(0, (int)style->font().fontDescription().letterSpacing()); + + LayoutUnit textShadowLogicalTop; + LayoutUnit textShadowLogicalBottom; + style->getTextShadowBlockDirectionExtent(textShadowLogicalTop, + textShadowLogicalBottom); + + LayoutUnit childOverflowLogicalTop = std::min( + textShadowLogicalTop + topGlyphOverflow, topGlyphOverflow); + LayoutUnit childOverflowLogicalBottom = std::max( + textShadowLogicalBottom + bottomGlyphOverflow, bottomGlyphOverflow); + + LayoutUnit textShadowLogicalLeft; + LayoutUnit textShadowLogicalRight; + style->getTextShadowInlineDirectionExtent(textShadowLogicalLeft, + textShadowLogicalRight); + + LayoutUnit childOverflowLogicalLeft = std::min( + textShadowLogicalLeft + leftGlyphOverflow, leftGlyphOverflow); + LayoutUnit childOverflowLogicalRight = std::max( + textShadowLogicalRight + rightGlyphOverflow, rightGlyphOverflow); + + LayoutUnit logicalTopVisualOverflow = + std::min(textBox->pixelSnappedLogicalTop() + childOverflowLogicalTop, + logicalVisualOverflow.y()); + LayoutUnit logicalBottomVisualOverflow = std::max( + textBox->pixelSnappedLogicalBottom() + childOverflowLogicalBottom, + logicalVisualOverflow.maxY()); + LayoutUnit logicalLeftVisualOverflow = + std::min(textBox->pixelSnappedLogicalLeft() + childOverflowLogicalLeft, + logicalVisualOverflow.x()); + LayoutUnit logicalRightVisualOverflow = + std::max(textBox->pixelSnappedLogicalRight() + childOverflowLogicalRight, + logicalVisualOverflow.maxX()); + + logicalVisualOverflow = + LayoutRect(logicalLeftVisualOverflow, logicalTopVisualOverflow, + logicalRightVisualOverflow - logicalLeftVisualOverflow, + logicalBottomVisualOverflow - logicalTopVisualOverflow); + + textBox->setLogicalOverflowRect(logicalVisualOverflow); } -void InlineFlowBox::computeOverflow(LayoutUnit lineTop, LayoutUnit lineBottom, GlyphOverflowAndFallbackFontsMap& textBoxDataMap) -{ - // If we know we have no overflow, we can just bail. - if (knownToHaveNoOverflow()) { - ASSERT(!m_overflow); - return; - } +inline void InlineFlowBox::addReplacedChildOverflow( + const InlineBox* inlineBox, + LayoutRect& logicalLayoutOverflow, + LayoutRect& logicalVisualOverflow) { + RenderBox& box = toRenderBox(inlineBox->renderer()); + + // Visual overflow only propagates if the box doesn't have a self-painting + // layer. This rectangle does not include transforms or relative positioning + // (since those objects always have self-painting layers), but it does need to + // be adjusted for writing-mode differences. + if (!box.hasSelfPaintingLayer()) { + LayoutRect childLogicalVisualOverflow = box.visualOverflowRect(); + childLogicalVisualOverflow.move(inlineBox->logicalLeft(), + inlineBox->logicalTop()); + logicalVisualOverflow.unite(childLogicalVisualOverflow); + } + + // Layout overflow internal to the child box only propagates if the child box + // doesn't have overflow clip set. Otherwise the child border box propagates + // as layout overflow. This rectangle must include transforms and relative + // positioning and be adjusted for writing-mode differences. + LayoutRect childLogicalLayoutOverflow = + box.layoutOverflowRectForPropagation(); + childLogicalLayoutOverflow.move(inlineBox->logicalLeft(), + inlineBox->logicalTop()); + logicalLayoutOverflow.unite(childLogicalLayoutOverflow); +} - if (m_overflow) - m_overflow.clear(); - - // Visual overflow just includes overflow for stuff we need to issues paint invalidations for ourselves. Self-painting layers are ignored. - // Layout overflow is used to determine scrolling extent, so it still includes child layers and also factors in - // transforms, relative positioning, etc. - LayoutRect logicalLayoutOverflow(enclosingLayoutRect(logicalFrameRectIncludingLineHeight(lineTop, lineBottom))); - LayoutRect logicalVisualOverflow(logicalLayoutOverflow); - - addBoxShadowVisualOverflow(logicalVisualOverflow); - addBorderOutsetVisualOverflow(logicalVisualOverflow); - addOutlineVisualOverflow(logicalVisualOverflow); - - for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { - if (curr->renderer().isOutOfFlowPositioned()) - continue; // Positioned placeholders don't affect calculations. - - if (curr->renderer().isText()) { - InlineTextBox* text = toInlineTextBox(curr); - LayoutRect textBoxOverflow(enclosingLayoutRect(text->logicalFrameRect())); - addTextBoxVisualOverflow(text, textBoxDataMap, textBoxOverflow); - logicalVisualOverflow.unite(textBoxOverflow); - } else if (curr->renderer().isRenderInline()) { - InlineFlowBox* flow = toInlineFlowBox(curr); - flow->computeOverflow(lineTop, lineBottom, textBoxDataMap); - if (!hasSelfPaintingLayer(flow)) - logicalVisualOverflow.unite(flow->logicalVisualOverflowRect(lineTop, lineBottom)); - LayoutRect childLayoutOverflow = flow->logicalLayoutOverflowRect(lineTop, lineBottom); - childLayoutOverflow.move(flow->boxModelObject()->relativePositionLogicalOffset()); - logicalLayoutOverflow.unite(childLayoutOverflow); - } else { - addReplacedChildOverflow(curr, logicalLayoutOverflow, logicalVisualOverflow); - } +void InlineFlowBox::computeOverflow( + LayoutUnit lineTop, + LayoutUnit lineBottom, + GlyphOverflowAndFallbackFontsMap& textBoxDataMap) { + // If we know we have no overflow, we can just bail. + if (knownToHaveNoOverflow()) { + ASSERT(!m_overflow); + return; + } + + if (m_overflow) + m_overflow.clear(); + + // Visual overflow just includes overflow for stuff we need to issues paint + // invalidations for ourselves. Self-painting layers are ignored. Layout + // overflow is used to determine scrolling extent, so it still includes child + // layers and also factors in transforms, relative positioning, etc. + LayoutRect logicalLayoutOverflow(enclosingLayoutRect( + logicalFrameRectIncludingLineHeight(lineTop, lineBottom))); + LayoutRect logicalVisualOverflow(logicalLayoutOverflow); + + addBoxShadowVisualOverflow(logicalVisualOverflow); + addBorderOutsetVisualOverflow(logicalVisualOverflow); + addOutlineVisualOverflow(logicalVisualOverflow); + + for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { + if (curr->renderer().isOutOfFlowPositioned()) + continue; // Positioned placeholders don't affect calculations. + + if (curr->renderer().isText()) { + InlineTextBox* text = toInlineTextBox(curr); + LayoutRect textBoxOverflow(enclosingLayoutRect(text->logicalFrameRect())); + addTextBoxVisualOverflow(text, textBoxDataMap, textBoxOverflow); + logicalVisualOverflow.unite(textBoxOverflow); + } else if (curr->renderer().isRenderInline()) { + InlineFlowBox* flow = toInlineFlowBox(curr); + flow->computeOverflow(lineTop, lineBottom, textBoxDataMap); + if (!hasSelfPaintingLayer(flow)) + logicalVisualOverflow.unite( + flow->logicalVisualOverflowRect(lineTop, lineBottom)); + LayoutRect childLayoutOverflow = + flow->logicalLayoutOverflowRect(lineTop, lineBottom); + childLayoutOverflow.move( + flow->boxModelObject()->relativePositionLogicalOffset()); + logicalLayoutOverflow.unite(childLayoutOverflow); + } else { + addReplacedChildOverflow(curr, logicalLayoutOverflow, + logicalVisualOverflow); } + } - setOverflowFromLogicalRects(logicalLayoutOverflow, logicalVisualOverflow, lineTop, lineBottom); + setOverflowFromLogicalRects(logicalLayoutOverflow, logicalVisualOverflow, + lineTop, lineBottom); } -void InlineFlowBox::setLayoutOverflow(const LayoutRect& rect, const LayoutRect& frameBox) -{ - if (frameBox.contains(rect) || rect.isEmpty()) - return; +void InlineFlowBox::setLayoutOverflow(const LayoutRect& rect, + const LayoutRect& frameBox) { + if (frameBox.contains(rect) || rect.isEmpty()) + return; - if (!m_overflow) - m_overflow = adoptPtr(new RenderOverflow(frameBox, frameBox)); + if (!m_overflow) + m_overflow = adoptPtr(new RenderOverflow(frameBox, frameBox)); - m_overflow->setLayoutOverflow(rect); + m_overflow->setLayoutOverflow(rect); } -void InlineFlowBox::setVisualOverflow(const LayoutRect& rect, const LayoutRect& frameBox) -{ - if (frameBox.contains(rect) || rect.isEmpty()) - return; +void InlineFlowBox::setVisualOverflow(const LayoutRect& rect, + const LayoutRect& frameBox) { + if (frameBox.contains(rect) || rect.isEmpty()) + return; - if (!m_overflow) - m_overflow = adoptPtr(new RenderOverflow(frameBox, frameBox)); + if (!m_overflow) + m_overflow = adoptPtr(new RenderOverflow(frameBox, frameBox)); - m_overflow->setVisualOverflow(rect); + m_overflow->setVisualOverflow(rect); } -void InlineFlowBox::setOverflowFromLogicalRects(const LayoutRect& logicalLayoutOverflow, const LayoutRect& logicalVisualOverflow, LayoutUnit lineTop, LayoutUnit lineBottom) -{ - LayoutRect frameBox = enclosingLayoutRect(frameRectIncludingLineHeight(lineTop, lineBottom)); - setLayoutOverflow(logicalLayoutOverflow, frameBox); - setVisualOverflow(logicalVisualOverflow, frameBox); +void InlineFlowBox::setOverflowFromLogicalRects( + const LayoutRect& logicalLayoutOverflow, + const LayoutRect& logicalVisualOverflow, + LayoutUnit lineTop, + LayoutUnit lineBottom) { + LayoutRect frameBox = + enclosingLayoutRect(frameRectIncludingLineHeight(lineTop, lineBottom)); + setLayoutOverflow(logicalLayoutOverflow, frameBox); + setVisualOverflow(logicalVisualOverflow, frameBox); } -bool InlineFlowBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, LayoutUnit lineTop, LayoutUnit lineBottom) -{ - LayoutRect overflowRect(visualOverflowRect(lineTop, lineBottom)); - overflowRect.moveBy(accumulatedOffset); - if (!locationInContainer.intersects(overflowRect)) - return false; - - // Check children first. - // We need to account for culled inline parents of the hit-tested nodes, so that they may also get included in area-based hit-tests. - RenderObject* culledParent = 0; - for (InlineBox* curr = lastChild(); curr; curr = curr->prevOnLine()) { - if (curr->renderer().isText() || !hasSelfPaintingLayer(curr)) { - RenderObject* newParent = 0; - // Culled parents are only relevant for area-based hit-tests, so ignore it in point-based ones. - if (locationInContainer.isRectBasedTest()) { - newParent = curr->renderer().parent(); - if (newParent == renderer()) - newParent = 0; - } - // Check the culled parent after all its children have been checked, to do this we wait until - // we are about to test an element with a different parent. - if (newParent != culledParent) { - if (!newParent || !newParent->isDescendantOf(culledParent)) { - while (culledParent && culledParent != renderer() && culledParent != newParent) { - if (culledParent->isRenderInline() && toRenderInline(culledParent)->hitTestCulledInline(request, result, locationInContainer, accumulatedOffset)) - return true; - culledParent = culledParent->parent(); - } - } - culledParent = newParent; - } - if (curr->nodeAtPoint(request, result, locationInContainer, accumulatedOffset, lineTop, lineBottom)) { - renderer().updateHitTestResult(result, locationInContainer.point() - toLayoutSize(accumulatedOffset)); - return true; - } +bool InlineFlowBox::nodeAtPoint(const HitTestRequest& request, + HitTestResult& result, + const HitTestLocation& locationInContainer, + const LayoutPoint& accumulatedOffset, + LayoutUnit lineTop, + LayoutUnit lineBottom) { + LayoutRect overflowRect(visualOverflowRect(lineTop, lineBottom)); + overflowRect.moveBy(accumulatedOffset); + if (!locationInContainer.intersects(overflowRect)) + return false; + + // Check children first. + // We need to account for culled inline parents of the hit-tested nodes, so + // that they may also get included in area-based hit-tests. + RenderObject* culledParent = 0; + for (InlineBox* curr = lastChild(); curr; curr = curr->prevOnLine()) { + if (curr->renderer().isText() || !hasSelfPaintingLayer(curr)) { + RenderObject* newParent = 0; + // Culled parents are only relevant for area-based hit-tests, so ignore it + // in point-based ones. + if (locationInContainer.isRectBasedTest()) { + newParent = curr->renderer().parent(); + if (newParent == renderer()) + newParent = 0; + } + // Check the culled parent after all its children have been checked, to do + // this we wait until we are about to test an element with a different + // parent. + if (newParent != culledParent) { + if (!newParent || !newParent->isDescendantOf(culledParent)) { + while (culledParent && culledParent != renderer() && + culledParent != newParent) { + if (culledParent->isRenderInline() && + toRenderInline(culledParent) + ->hitTestCulledInline(request, result, locationInContainer, + accumulatedOffset)) + return true; + culledParent = culledParent->parent(); + } } + culledParent = newParent; + } + if (curr->nodeAtPoint(request, result, locationInContainer, + accumulatedOffset, lineTop, lineBottom)) { + renderer().updateHitTestResult( + result, + locationInContainer.point() - toLayoutSize(accumulatedOffset)); + return true; + } } - // Check any culled ancestor of the final children tested. - while (culledParent && culledParent != renderer()) { - if (culledParent->isRenderInline() && toRenderInline(culledParent)->hitTestCulledInline(request, result, locationInContainer, accumulatedOffset)) - return true; - culledParent = culledParent->parent(); - } - - // Now check ourselves. Pixel snap hit testing. - // Move x/y to our coordinates. - LayoutRect rect(roundedFrameRect()); - rect.moveBy(accumulatedOffset); - - return false; + } + // Check any culled ancestor of the final children tested. + while (culledParent && culledParent != renderer()) { + if (culledParent->isRenderInline() && + toRenderInline(culledParent) + ->hitTestCulledInline(request, result, locationInContainer, + accumulatedOffset)) + return true; + culledParent = culledParent->parent(); + } + + // Now check ourselves. Pixel snap hit testing. + // Move x/y to our coordinates. + LayoutRect rect(roundedFrameRect()); + rect.moveBy(accumulatedOffset); + + return false; } -void InlineFlowBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit lineTop, LayoutUnit lineBottom, Vector& layers) -{ - LayoutRect overflowRect(visualOverflowRect(lineTop, lineBottom)); - overflowRect.moveBy(paintOffset); +void InlineFlowBox::paint(PaintInfo& paintInfo, + const LayoutPoint& paintOffset, + LayoutUnit lineTop, + LayoutUnit lineBottom, + Vector& layers) { + LayoutRect overflowRect(visualOverflowRect(lineTop, lineBottom)); + overflowRect.moveBy(paintOffset); - if (!paintInfo.rect.intersects(pixelSnappedIntRect(overflowRect))) - return; + if (!paintInfo.rect.intersects(pixelSnappedIntRect(overflowRect))) + return; - paintBoxDecorationBackground(paintInfo, paintOffset); + paintBoxDecorationBackground(paintInfo, paintOffset); - for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { - if (curr->renderer().isText() || !hasSelfPaintingLayer(curr)) - curr->paint(paintInfo, paintOffset, lineTop, lineBottom, layers); - } + for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { + if (curr->renderer().isText() || !hasSelfPaintingLayer(curr)) + curr->paint(paintInfo, paintOffset, lineTop, lineBottom, layers); + } } -void InlineFlowBox::paintFillLayers(const PaintInfo& paintInfo, const Color& c, const FillLayer& fillLayer, const LayoutRect& rect) -{ - if (fillLayer.next()) - paintFillLayers(paintInfo, c, *fillLayer.next(), rect); - paintFillLayer(paintInfo, c, fillLayer, rect); +void InlineFlowBox::paintFillLayers(const PaintInfo& paintInfo, + const Color& c, + const FillLayer& fillLayer, + const LayoutRect& rect) { + if (fillLayer.next()) + paintFillLayers(paintInfo, c, *fillLayer.next(), rect); + paintFillLayer(paintInfo, c, fillLayer, rect); } -bool InlineFlowBox::boxShadowCanBeAppliedToBackground(const FillLayer& lastBackgroundLayer) const -{ - // The checks here match how paintFillLayer() decides whether to clip (if it does, the shadow - // would be clipped out, so it has to be drawn separately). - StyleImage* image = lastBackgroundLayer.image(); - bool hasFillImage = image && image->canRender(renderer()); - return (!hasFillImage && !renderer().style()->hasBorderRadius()) || (!prevLineBox() && !nextLineBox()) || !parent(); +bool InlineFlowBox::boxShadowCanBeAppliedToBackground( + const FillLayer& lastBackgroundLayer) const { + // The checks here match how paintFillLayer() decides whether to clip (if it + // does, the shadow would be clipped out, so it has to be drawn separately). + StyleImage* image = lastBackgroundLayer.image(); + bool hasFillImage = image && image->canRender(renderer()); + return (!hasFillImage && !renderer().style()->hasBorderRadius()) || + (!prevLineBox() && !nextLineBox()) || !parent(); } -void InlineFlowBox::paintFillLayer(const PaintInfo& paintInfo, const Color& c, const FillLayer& fillLayer, const LayoutRect& rect) -{ - StyleImage* img = fillLayer.image(); - bool hasFillImage = img && img->canRender(renderer()); - if ((!hasFillImage && !renderer().style()->hasBorderRadius()) || (!prevLineBox() && !nextLineBox()) || !parent()) { - boxModelObject()->paintFillLayerExtended(paintInfo, c, fillLayer, rect, BackgroundBleedNone, this, rect.size()); - } else if (renderer().style()->boxDecorationBreak() == DCLONE) { - GraphicsContextStateSaver stateSaver(*paintInfo.context); - paintInfo.context->clip(LayoutRect(rect.x(), rect.y(), width(), height())); - boxModelObject()->paintFillLayerExtended(paintInfo, c, fillLayer, rect, BackgroundBleedNone, this, rect.size()); +void InlineFlowBox::paintFillLayer(const PaintInfo& paintInfo, + const Color& c, + const FillLayer& fillLayer, + const LayoutRect& rect) { + StyleImage* img = fillLayer.image(); + bool hasFillImage = img && img->canRender(renderer()); + if ((!hasFillImage && !renderer().style()->hasBorderRadius()) || + (!prevLineBox() && !nextLineBox()) || !parent()) { + boxModelObject()->paintFillLayerExtended( + paintInfo, c, fillLayer, rect, BackgroundBleedNone, this, rect.size()); + } else if (renderer().style()->boxDecorationBreak() == DCLONE) { + GraphicsContextStateSaver stateSaver(*paintInfo.context); + paintInfo.context->clip(LayoutRect(rect.x(), rect.y(), width(), height())); + boxModelObject()->paintFillLayerExtended( + paintInfo, c, fillLayer, rect, BackgroundBleedNone, this, rect.size()); + } else { + // We have a fill image that spans multiple lines. + // We need to adjust tx and ty by the width of all previous lines. + // Think of background painting on inlines as though you had one long line, + // a single continuous strip. Even though that strip has been broken up + // across multiple lines, you still paint it as though you had one single + // line. This means each line has to pick up the background where the + // previous line left off. + LayoutUnit logicalOffsetOnLine = 0; + LayoutUnit totalLogicalWidth; + if (renderer().style()->direction() == LTR) { + for (InlineFlowBox* curr = prevLineBox(); curr; + curr = curr->prevLineBox()) + logicalOffsetOnLine += curr->logicalWidth(); + totalLogicalWidth = logicalOffsetOnLine; + for (InlineFlowBox* curr = this; curr; curr = curr->nextLineBox()) + totalLogicalWidth += curr->logicalWidth(); } else { - // We have a fill image that spans multiple lines. - // We need to adjust tx and ty by the width of all previous lines. - // Think of background painting on inlines as though you had one long line, a single continuous - // strip. Even though that strip has been broken up across multiple lines, you still paint it - // as though you had one single line. This means each line has to pick up the background where - // the previous line left off. - LayoutUnit logicalOffsetOnLine = 0; - LayoutUnit totalLogicalWidth; - if (renderer().style()->direction() == LTR) { - for (InlineFlowBox* curr = prevLineBox(); curr; curr = curr->prevLineBox()) - logicalOffsetOnLine += curr->logicalWidth(); - totalLogicalWidth = logicalOffsetOnLine; - for (InlineFlowBox* curr = this; curr; curr = curr->nextLineBox()) - totalLogicalWidth += curr->logicalWidth(); - } else { - for (InlineFlowBox* curr = nextLineBox(); curr; curr = curr->nextLineBox()) - logicalOffsetOnLine += curr->logicalWidth(); - totalLogicalWidth = logicalOffsetOnLine; - for (InlineFlowBox* curr = this; curr; curr = curr->prevLineBox()) - totalLogicalWidth += curr->logicalWidth(); - } - LayoutUnit stripX = rect.x() - logicalOffsetOnLine; - LayoutUnit stripY = rect.y(); - LayoutUnit stripWidth = totalLogicalWidth; - LayoutUnit stripHeight = static_cast(height()); - - GraphicsContextStateSaver stateSaver(*paintInfo.context); - paintInfo.context->clip(LayoutRect(rect.x(), rect.y(), width(), height())); - boxModelObject()->paintFillLayerExtended(paintInfo, c, fillLayer, LayoutRect(stripX, stripY, stripWidth, stripHeight), BackgroundBleedNone, this, rect.size()); + for (InlineFlowBox* curr = nextLineBox(); curr; + curr = curr->nextLineBox()) + logicalOffsetOnLine += curr->logicalWidth(); + totalLogicalWidth = logicalOffsetOnLine; + for (InlineFlowBox* curr = this; curr; curr = curr->prevLineBox()) + totalLogicalWidth += curr->logicalWidth(); } + LayoutUnit stripX = rect.x() - logicalOffsetOnLine; + LayoutUnit stripY = rect.y(); + LayoutUnit stripWidth = totalLogicalWidth; + LayoutUnit stripHeight = static_cast(height()); + + GraphicsContextStateSaver stateSaver(*paintInfo.context); + paintInfo.context->clip(LayoutRect(rect.x(), rect.y(), width(), height())); + boxModelObject()->paintFillLayerExtended( + paintInfo, c, fillLayer, + LayoutRect(stripX, stripY, stripWidth, stripHeight), + BackgroundBleedNone, this, rect.size()); + } } -void InlineFlowBox::paintBoxShadow(const PaintInfo& info, RenderStyle* s, ShadowStyle shadowStyle, const LayoutRect& paintRect) -{ - if ((!prevLineBox() && !nextLineBox()) || !parent()) - boxModelObject()->paintBoxShadow(info, paintRect, s, shadowStyle); - else { - // FIXME: We can do better here in the multi-line case. We want to push a clip so that the shadow doesn't - // protrude incorrectly at the edges, and we want to possibly include shadows cast from the previous/following lines - boxModelObject()->paintBoxShadow(info, paintRect, s, shadowStyle, includeLogicalLeftEdge(), includeLogicalRightEdge()); - } +void InlineFlowBox::paintBoxShadow(const PaintInfo& info, + RenderStyle* s, + ShadowStyle shadowStyle, + const LayoutRect& paintRect) { + if ((!prevLineBox() && !nextLineBox()) || !parent()) + boxModelObject()->paintBoxShadow(info, paintRect, s, shadowStyle); + else { + // FIXME: We can do better here in the multi-line case. We want to push a + // clip so that the shadow doesn't protrude incorrectly at the edges, and we + // want to possibly include shadows cast from the previous/following lines + boxModelObject()->paintBoxShadow(info, paintRect, s, shadowStyle, + includeLogicalLeftEdge(), + includeLogicalRightEdge()); + } } -void InlineFlowBox::paintBoxDecorationBackground(PaintInfo& paintInfo, const LayoutPoint& paintOffset) -{ - // You can use p::first-line to specify a background. If so, the root line boxes for - // a line may actually have to paint a background. - RenderStyle* styleToUse = renderer().style(isFirstLineStyle()); - bool shouldPaintBoxDecorationBackground; - if (parent()) - shouldPaintBoxDecorationBackground = renderer().hasBoxDecorationBackground(); - else - shouldPaintBoxDecorationBackground = isFirstLineStyle() && styleToUse != renderer().style(); - - if (!shouldPaintBoxDecorationBackground) - return; - - // Pixel snap background/border painting. - LayoutRect frameRect = roundedFrameRect(); - - // Move x/y to our coordinates. - LayoutRect localRect(frameRect); - LayoutPoint adjustedPaintOffset = paintOffset + localRect.location(); - - LayoutRect paintRect = LayoutRect(adjustedPaintOffset, frameRect.size()); - - // Shadow comes first and is behind the background and border. - if (!boxModelObject()->boxShadowShouldBeAppliedToBackground(BackgroundBleedNone, this)) - paintBoxShadow(paintInfo, styleToUse, Normal, paintRect); - - Color backgroundColor = styleToUse->resolveColor(styleToUse->backgroundColor()); - paintFillLayers(paintInfo, backgroundColor, styleToUse->backgroundLayers(), paintRect); - paintBoxShadow(paintInfo, styleToUse, Inset, paintRect); +void InlineFlowBox::paintBoxDecorationBackground( + PaintInfo& paintInfo, + const LayoutPoint& paintOffset) { + // You can use p::first-line to specify a background. If so, the root line + // boxes for a line may actually have to paint a background. + RenderStyle* styleToUse = renderer().style(isFirstLineStyle()); + bool shouldPaintBoxDecorationBackground; + if (parent()) + shouldPaintBoxDecorationBackground = + renderer().hasBoxDecorationBackground(); + else + shouldPaintBoxDecorationBackground = + isFirstLineStyle() && styleToUse != renderer().style(); + + if (!shouldPaintBoxDecorationBackground) + return; - // :first-line cannot be used to put borders on a line. Always paint borders with our - // non-first-line style. - if (parent() && renderer().style()->hasBorder()) { - boxModelObject()->paintBorder(paintInfo, paintRect, renderer().style(isFirstLineStyle()), BackgroundBleedNone, includeLogicalLeftEdge(), includeLogicalRightEdge()); - } + // Pixel snap background/border painting. + LayoutRect frameRect = roundedFrameRect(); + + // Move x/y to our coordinates. + LayoutRect localRect(frameRect); + LayoutPoint adjustedPaintOffset = paintOffset + localRect.location(); + + LayoutRect paintRect = LayoutRect(adjustedPaintOffset, frameRect.size()); + + // Shadow comes first and is behind the background and border. + if (!boxModelObject()->boxShadowShouldBeAppliedToBackground( + BackgroundBleedNone, this)) + paintBoxShadow(paintInfo, styleToUse, Normal, paintRect); + + Color backgroundColor = + styleToUse->resolveColor(styleToUse->backgroundColor()); + paintFillLayers(paintInfo, backgroundColor, styleToUse->backgroundLayers(), + paintRect); + paintBoxShadow(paintInfo, styleToUse, Inset, paintRect); + + // :first-line cannot be used to put borders on a line. Always paint borders + // with our non-first-line style. + if (parent() && renderer().style()->hasBorder()) { + boxModelObject()->paintBorder(paintInfo, paintRect, + renderer().style(isFirstLineStyle()), + BackgroundBleedNone, includeLogicalLeftEdge(), + includeLogicalRightEdge()); + } } -InlineBox* InlineFlowBox::firstLeafChild() const -{ - InlineBox* leaf = 0; - for (InlineBox* child = firstChild(); child && !leaf; child = child->nextOnLine()) - leaf = child->isLeaf() ? child : toInlineFlowBox(child)->firstLeafChild(); - return leaf; +InlineBox* InlineFlowBox::firstLeafChild() const { + InlineBox* leaf = 0; + for (InlineBox* child = firstChild(); child && !leaf; + child = child->nextOnLine()) + leaf = child->isLeaf() ? child : toInlineFlowBox(child)->firstLeafChild(); + return leaf; } -InlineBox* InlineFlowBox::lastLeafChild() const -{ - InlineBox* leaf = 0; - for (InlineBox* child = lastChild(); child && !leaf; child = child->prevOnLine()) - leaf = child->isLeaf() ? child : toInlineFlowBox(child)->lastLeafChild(); - return leaf; +InlineBox* InlineFlowBox::lastLeafChild() const { + InlineBox* leaf = 0; + for (InlineBox* child = lastChild(); child && !leaf; + child = child->prevOnLine()) + leaf = child->isLeaf() ? child : toInlineFlowBox(child)->lastLeafChild(); + return leaf; } -RenderObject::SelectionState InlineFlowBox::selectionState() -{ - return RenderObject::SelectionNone; +RenderObject::SelectionState InlineFlowBox::selectionState() { + return RenderObject::SelectionNone; } -void InlineFlowBox::clearTruncation() -{ - for (InlineBox *box = firstChild(); box; box = box->nextOnLine()) - box->clearTruncation(); +void InlineFlowBox::clearTruncation() { + for (InlineBox* box = firstChild(); box; box = box->nextOnLine()) + box->clearTruncation(); } -LayoutUnit InlineFlowBox::computeOverAnnotationAdjustment(LayoutUnit allowedPosition) const -{ - LayoutUnit result = 0; - for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { - if (curr->renderer().isOutOfFlowPositioned()) - continue; // Positioned placeholders don't affect calculations. - - if (curr->isInlineFlowBox()) - result = std::max(result, toInlineFlowBox(curr)->computeOverAnnotationAdjustment(allowedPosition)); - - if (curr->isInlineTextBox()) { - RenderStyle* style = curr->renderer().style(isFirstLineStyle()); - TextEmphasisPosition emphasisMarkPosition; - if (style->textEmphasisMark() != TextEmphasisMarkNone && toInlineTextBox(curr)->getEmphasisMarkPosition(style, emphasisMarkPosition) && emphasisMarkPosition == TextEmphasisPositionOver) { - int topOfEmphasisMark = curr->logicalTop() - style->font().emphasisMarkHeight(style->textEmphasisMarkString()); - result = std::max(result, allowedPosition - topOfEmphasisMark); - } - } +LayoutUnit InlineFlowBox::computeOverAnnotationAdjustment( + LayoutUnit allowedPosition) const { + LayoutUnit result = 0; + for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { + if (curr->renderer().isOutOfFlowPositioned()) + continue; // Positioned placeholders don't affect calculations. + + if (curr->isInlineFlowBox()) + result = std::max(result, + toInlineFlowBox(curr)->computeOverAnnotationAdjustment( + allowedPosition)); + + if (curr->isInlineTextBox()) { + RenderStyle* style = curr->renderer().style(isFirstLineStyle()); + TextEmphasisPosition emphasisMarkPosition; + if (style->textEmphasisMark() != TextEmphasisMarkNone && + toInlineTextBox(curr)->getEmphasisMarkPosition( + style, emphasisMarkPosition) && + emphasisMarkPosition == TextEmphasisPositionOver) { + int topOfEmphasisMark = + curr->logicalTop() - + style->font().emphasisMarkHeight(style->textEmphasisMarkString()); + result = std::max(result, allowedPosition - topOfEmphasisMark); + } } - return result; + } + return result; } -LayoutUnit InlineFlowBox::computeUnderAnnotationAdjustment(LayoutUnit allowedPosition) const -{ - LayoutUnit result = 0; - for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { - if (curr->renderer().isOutOfFlowPositioned()) - continue; // Positioned placeholders don't affect calculations. - - if (curr->isInlineFlowBox()) - result = std::max(result, toInlineFlowBox(curr)->computeUnderAnnotationAdjustment(allowedPosition)); - - if (curr->isInlineTextBox()) { - RenderStyle* style = curr->renderer().style(isFirstLineStyle()); - if (style->textEmphasisMark() != TextEmphasisMarkNone && style->textEmphasisPosition() == TextEmphasisPositionUnder) { - LayoutUnit bottomOfEmphasisMark = curr->logicalBottom() + style->font().emphasisMarkHeight(style->textEmphasisMarkString()); - result = std::max(result, bottomOfEmphasisMark - allowedPosition); - } - } +LayoutUnit InlineFlowBox::computeUnderAnnotationAdjustment( + LayoutUnit allowedPosition) const { + LayoutUnit result = 0; + for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { + if (curr->renderer().isOutOfFlowPositioned()) + continue; // Positioned placeholders don't affect calculations. + + if (curr->isInlineFlowBox()) + result = std::max(result, + toInlineFlowBox(curr)->computeUnderAnnotationAdjustment( + allowedPosition)); + + if (curr->isInlineTextBox()) { + RenderStyle* style = curr->renderer().style(isFirstLineStyle()); + if (style->textEmphasisMark() != TextEmphasisMarkNone && + style->textEmphasisPosition() == TextEmphasisPositionUnder) { + LayoutUnit bottomOfEmphasisMark = + curr->logicalBottom() + + style->font().emphasisMarkHeight(style->textEmphasisMarkString()); + result = std::max(result, bottomOfEmphasisMark - allowedPosition); + } } - return result; + } + return result; } -void InlineFlowBox::collectLeafBoxesInLogicalOrder(Vector& leafBoxesInLogicalOrder, CustomInlineBoxRangeReverse customReverseImplementation, void* userData) const -{ - InlineBox* leaf = firstLeafChild(); - - // FIXME: The reordering code is a copy of parts from BidiResolver::createBidiRunsForLine, operating directly on InlineBoxes, instead of BidiRuns. - // Investigate on how this code could possibly be shared. - unsigned char minLevel = 128; - unsigned char maxLevel = 0; - - // First find highest and lowest levels, and initialize leafBoxesInLogicalOrder with the leaf boxes in visual order. - for (; leaf; leaf = leaf->nextLeafChild()) { - minLevel = std::min(minLevel, leaf->bidiLevel()); - maxLevel = std::max(maxLevel, leaf->bidiLevel()); - leafBoxesInLogicalOrder.append(leaf); - } +void InlineFlowBox::collectLeafBoxesInLogicalOrder( + Vector& leafBoxesInLogicalOrder, + CustomInlineBoxRangeReverse customReverseImplementation, + void* userData) const { + InlineBox* leaf = firstLeafChild(); + + // FIXME: The reordering code is a copy of parts from + // BidiResolver::createBidiRunsForLine, operating directly on InlineBoxes, + // instead of BidiRuns. Investigate on how this code could possibly be shared. + unsigned char minLevel = 128; + unsigned char maxLevel = 0; + + // First find highest and lowest levels, and initialize + // leafBoxesInLogicalOrder with the leaf boxes in visual order. + for (; leaf; leaf = leaf->nextLeafChild()) { + minLevel = std::min(minLevel, leaf->bidiLevel()); + maxLevel = std::max(maxLevel, leaf->bidiLevel()); + leafBoxesInLogicalOrder.append(leaf); + } + + if (renderer().style()->rtlOrdering() == VisualOrder) + return; - if (renderer().style()->rtlOrdering() == VisualOrder) - return; - - // Reverse of reordering of the line (L2 according to Bidi spec): - // L2. From the highest level found in the text to the lowest odd level on each line, - // reverse any contiguous sequence of characters that are at that level or higher. - - // Reversing the reordering of the line is only done up to the lowest odd level. - if (!(minLevel % 2)) - ++minLevel; - - Vector::iterator end = leafBoxesInLogicalOrder.end(); - while (minLevel <= maxLevel) { - Vector::iterator it = leafBoxesInLogicalOrder.begin(); - while (it != end) { - while (it != end) { - if ((*it)->bidiLevel() >= minLevel) - break; - ++it; - } - Vector::iterator first = it; - while (it != end) { - if ((*it)->bidiLevel() < minLevel) - break; - ++it; - } - Vector::iterator last = it; - if (customReverseImplementation) { - ASSERT(userData); - (*customReverseImplementation)(userData, first, last); - } else - std::reverse(first, last); - } - ++minLevel; + // Reverse of reordering of the line (L2 according to Bidi spec): + // L2. From the highest level found in the text to the lowest odd level on + // each line, reverse any contiguous sequence of characters that are at that + // level or higher. + + // Reversing the reordering of the line is only done up to the lowest odd + // level. + if (!(minLevel % 2)) + ++minLevel; + + Vector::iterator end = leafBoxesInLogicalOrder.end(); + while (minLevel <= maxLevel) { + Vector::iterator it = leafBoxesInLogicalOrder.begin(); + while (it != end) { + while (it != end) { + if ((*it)->bidiLevel() >= minLevel) + break; + ++it; + } + Vector::iterator first = it; + while (it != end) { + if ((*it)->bidiLevel() < minLevel) + break; + ++it; + } + Vector::iterator last = it; + if (customReverseImplementation) { + ASSERT(userData); + (*customReverseImplementation)(userData, first, last); + } else + std::reverse(first, last); } + ++minLevel; + } } #ifndef NDEBUG -const char* InlineFlowBox::boxName() const -{ - return "InlineFlowBox"; +const char* InlineFlowBox::boxName() const { + return "InlineFlowBox"; } -void InlineFlowBox::showLineTreeAndMark(const InlineBox* markedBox1, const char* markedLabel1, const InlineBox* markedBox2, const char* markedLabel2, const RenderObject* obj, int depth) const -{ - InlineBox::showLineTreeAndMark(markedBox1, markedLabel1, markedBox2, markedLabel2, obj, depth); - for (const InlineBox* box = firstChild(); box; box = box->nextOnLine()) - box->showLineTreeAndMark(markedBox1, markedLabel1, markedBox2, markedLabel2, obj, depth + 1); +void InlineFlowBox::showLineTreeAndMark(const InlineBox* markedBox1, + const char* markedLabel1, + const InlineBox* markedBox2, + const char* markedLabel2, + const RenderObject* obj, + int depth) const { + InlineBox::showLineTreeAndMark(markedBox1, markedLabel1, markedBox2, + markedLabel2, obj, depth); + for (const InlineBox* box = firstChild(); box; box = box->nextOnLine()) + box->showLineTreeAndMark(markedBox1, markedLabel1, markedBox2, markedLabel2, + obj, depth + 1); } #endif #if ENABLE(ASSERT) -void InlineFlowBox::checkConsistency() const -{ +void InlineFlowBox::checkConsistency() const { #ifdef CHECK_CONSISTENCY - ASSERT(!m_hasBadChildList); - const InlineBox* prev = 0; - for (const InlineBox* child = m_firstChild; child; child = child->nextOnLine()) { - ASSERT(child->parent() == this); - ASSERT(child->prevOnLine() == prev); - prev = child; - } - ASSERT(prev == m_lastChild); + ASSERT(!m_hasBadChildList); + const InlineBox* prev = 0; + for (const InlineBox* child = m_firstChild; child; + child = child->nextOnLine()) { + ASSERT(child->parent() == this); + ASSERT(child->prevOnLine() == prev); + prev = child; + } + ASSERT(prev == m_lastChild); #endif } #endif -} // namespace blink +} // namespace blink diff --git a/sky/engine/core/rendering/InlineFlowBox.h b/sky/engine/core/rendering/InlineFlowBox.h index e5ee507a06b2a..728cb2bf3efcd 100644 --- a/sky/engine/core/rendering/InlineFlowBox.h +++ b/sky/engine/core/rendering/InlineFlowBox.h @@ -37,330 +37,424 @@ class VerticalPositionCache; struct GlyphOverflow; -typedef HashMap, GlyphOverflow> > GlyphOverflowAndFallbackFontsMap; +typedef HashMap, GlyphOverflow>> + GlyphOverflowAndFallbackFontsMap; class InlineFlowBox : public InlineBox { -public: - InlineFlowBox(RenderObject& obj) - : InlineBox(obj) - , m_firstChild(0) - , m_lastChild(0) - , m_prevLineBox(0) - , m_nextLineBox(0) - , m_includeLogicalLeftEdge(false) - , m_includeLogicalRightEdge(false) - , m_descendantsHaveSameLineHeightAndBaseline(true) - , m_baselineType(AlphabeticBaseline) - , m_hasAnnotationsBefore(false) - , m_hasAnnotationsAfter(false) - , m_lineBreakBidiStatusEor(WTF::Unicode::LeftToRight) - , m_lineBreakBidiStatusLastStrong(WTF::Unicode::LeftToRight) - , m_lineBreakBidiStatusLast(WTF::Unicode::LeftToRight) + public: + InlineFlowBox(RenderObject& obj) + : InlineBox(obj), + m_firstChild(0), + m_lastChild(0), + m_prevLineBox(0), + m_nextLineBox(0), + m_includeLogicalLeftEdge(false), + m_includeLogicalRightEdge(false), + m_descendantsHaveSameLineHeightAndBaseline(true), + m_baselineType(AlphabeticBaseline), + m_hasAnnotationsBefore(false), + m_hasAnnotationsAfter(false), + m_lineBreakBidiStatusEor(WTF::Unicode::LeftToRight), + m_lineBreakBidiStatusLastStrong(WTF::Unicode::LeftToRight), + m_lineBreakBidiStatusLast(WTF::Unicode::LeftToRight) #if ENABLE(ASSERT) - , m_hasBadChildList(false) + , + m_hasBadChildList(false) #endif - { - // Internet Explorer and Firefox always create a marker for list items, even when the list-style-type is none. We do not make a marker - // in the list-style-type: none case, since it is wasteful to do so. However, in order to match other browsers we have to pretend like - // an invisible marker exists. The side effect of having an invisible marker is that the quirks mode behavior of shrinking lines with no - // text children must not apply. This change also means that gaps will exist between image bullet list items. Even when the list bullet - // is an image, the line is still considered to be immune from the quirk. - m_hasTextChildren = false; - m_hasTextDescendants = m_hasTextChildren; - } + { + // Internet Explorer and Firefox always create a marker for list items, even + // when the list-style-type is none. We do not make a marker in the + // list-style-type: none case, since it is wasteful to do so. However, in + // order to match other browsers we have to pretend like an invisible marker + // exists. The side effect of having an invisible marker is that the quirks + // mode behavior of shrinking lines with no text children must not apply. + // This change also means that gaps will exist between image bullet list + // items. Even when the list bullet is an image, the line is still + // considered to be immune from the quirk. + m_hasTextChildren = false; + m_hasTextDescendants = m_hasTextChildren; + } #if ENABLE(ASSERT) - virtual ~InlineFlowBox(); + virtual ~InlineFlowBox(); #endif #ifndef NDEBUG - virtual void showLineTreeAndMark(const InlineBox* = 0, const char* = 0, const InlineBox* = 0, const char* = 0, const RenderObject* = 0, int = 0) const override; - virtual const char* boxName() const override; + virtual void showLineTreeAndMark(const InlineBox* = 0, + const char* = 0, + const InlineBox* = 0, + const char* = 0, + const RenderObject* = 0, + int = 0) const override; + virtual const char* boxName() const override; #endif - InlineFlowBox* prevLineBox() const { return m_prevLineBox; } - InlineFlowBox* nextLineBox() const { return m_nextLineBox; } - void setNextLineBox(InlineFlowBox* n) { m_nextLineBox = n; } - void setPreviousLineBox(InlineFlowBox* p) { m_prevLineBox = p; } - - InlineBox* firstChild() const { checkConsistency(); return m_firstChild; } - InlineBox* lastChild() const { checkConsistency(); return m_lastChild; } - - virtual bool isLeaf() const override final { return false; } - - InlineBox* firstLeafChild() const; - InlineBox* lastLeafChild() const; - - typedef void (*CustomInlineBoxRangeReverse)(void* userData, Vector::iterator first, Vector::iterator last); - void collectLeafBoxesInLogicalOrder(Vector&, CustomInlineBoxRangeReverse customReverseImplementation = 0, void* userData = 0) const; - - virtual void setConstructed() override final - { - InlineBox::setConstructed(); - for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) - child->setConstructed(); - } - - void addToLine(InlineBox* child); - virtual void deleteLine() override final; - virtual void extractLine() override final; - virtual void attachLine() override final; - virtual void adjustPosition(float dx, float dy) override; - - virtual void extractLineBoxFromRenderObject(); - virtual void attachLineBoxToRenderObject(); - virtual void removeLineBoxFromRenderObject(); - - virtual void clearTruncation() override; - - IntRect roundedFrameRect() const; - - virtual void paint(PaintInfo&, const LayoutPoint&, LayoutUnit lineTop, LayoutUnit lineBottom, Vector& layers) override; - virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, LayoutUnit lineTop, LayoutUnit lineBottom) override; - - bool boxShadowCanBeAppliedToBackground(const FillLayer&) const; - - virtual RenderLineBoxList* rendererLineBoxes() const; - - // logicalLeft = left in a horizontal line and top in a vertical line. - LayoutUnit marginBorderPaddingLogicalLeft() const { return marginLogicalLeft() + borderLogicalLeft() + paddingLogicalLeft(); } - LayoutUnit marginBorderPaddingLogicalRight() const { return marginLogicalRight() + borderLogicalRight() + paddingLogicalRight(); } - LayoutUnit marginLogicalLeft() const - { - if (!includeLogicalLeftEdge()) - return 0; - return boxModelObject()->marginLeft(); - } - LayoutUnit marginLogicalRight() const - { - if (!includeLogicalRightEdge()) - return 0; - return boxModelObject()->marginRight(); - } - int borderLogicalLeft() const - { - if (!includeLogicalLeftEdge()) - return 0; - return renderer().style(isFirstLineStyle())->borderLeftWidth(); - } - int borderLogicalRight() const - { - if (!includeLogicalRightEdge()) - return 0; - return renderer().style(isFirstLineStyle())->borderRightWidth(); - } - int paddingLogicalLeft() const - { - if (!includeLogicalLeftEdge()) - return 0; - return boxModelObject()->paddingLeft(); - } - int paddingLogicalRight() const - { - if (!includeLogicalRightEdge()) - return 0; - return boxModelObject()->paddingRight(); - } - - bool includeLogicalLeftEdge() const { return m_includeLogicalLeftEdge; } - bool includeLogicalRightEdge() const { return m_includeLogicalRightEdge; } - void setEdges(bool includeLeft, bool includeRight) - { - m_includeLogicalLeftEdge = includeLeft; - m_includeLogicalRightEdge = includeRight; - } - - // Helper functions used during line construction and placement. - void determineSpacingForFlowBoxes(bool lastLine, bool isLogicallyLastRunWrapped, RenderObject* logicallyLastRunRenderer); - LayoutUnit getFlowSpacingLogicalWidth(); - float placeBoxesInInlineDirection(float logicalLeft, bool& needsWordSpacing); - float placeBoxRangeInInlineDirection(InlineBox* firstChild, InlineBox* lastChild, - float& logicalLeft, float& minLogicalLeft, float& maxLogicalRight, bool& needsWordSpacing); - void beginPlacingBoxRangesInInlineDirection(float logicalLeft) { setLogicalLeft(logicalLeft); } - void endPlacingBoxRangesInInlineDirection(float logicalLeft, float logicalRight, float minLogicalLeft, float maxLogicalRight) - { - setLogicalWidth(logicalRight - logicalLeft); - if (knownToHaveNoOverflow() && (minLogicalLeft < logicalLeft || maxLogicalRight > logicalRight)) - clearKnownToHaveNoOverflow(); - } - - void computeLogicalBoxHeights(RootInlineBox*, LayoutUnit& maxPositionTop, LayoutUnit& maxPositionBottom, - int& maxAscent, int& maxDescent, bool& setMaxAscent, bool& setMaxDescent, - bool strictMode, GlyphOverflowAndFallbackFontsMap&, FontBaseline, VerticalPositionCache&); - void adjustMaxAscentAndDescent(int& maxAscent, int& maxDescent, - int maxPositionTop, int maxPositionBottom); - void placeBoxesInBlockDirection(LayoutUnit logicalTop, LayoutUnit maxHeight, int maxAscent, bool strictMode, LayoutUnit& lineTop, LayoutUnit& lineBottom, LayoutUnit& selectionBottom, bool& setLineTop, - LayoutUnit& lineTopIncludingMargins, LayoutUnit& lineBottomIncludingMargins, bool& hasAnnotationsBefore, bool& hasAnnotationsAfter, FontBaseline); - void flipLinesInBlockDirection(LayoutUnit lineTop, LayoutUnit lineBottom); - - LayoutUnit computeOverAnnotationAdjustment(LayoutUnit allowedPosition) const; - LayoutUnit computeUnderAnnotationAdjustment(LayoutUnit allowedPosition) const; - - void computeOverflow(LayoutUnit lineTop, LayoutUnit lineBottom, GlyphOverflowAndFallbackFontsMap&); - - void removeChild(InlineBox* child, MarkLineBoxes); - - virtual RenderObject::SelectionState selectionState() override; - - bool hasTextChildren() const { return m_hasTextChildren; } - bool hasTextDescendants() const { return m_hasTextDescendants; } - void setHasTextChildren() { m_hasTextChildren = true; setHasTextDescendants(); } - void setHasTextDescendants() { m_hasTextDescendants = true; } - - void checkConsistency() const; - void setHasBadChildList(); - - // Line visual and layout overflow are in the coordinate space of the block. This means that they aren't purely physical directions. - // For horizontal-tb and vertical-lr they will match physical directions, but for horizontal-bt and vertical-rl, the top/bottom and left/right - // respectively are flipped when compared to their physical counterparts. For example minX is on the left in vertical-lr, but it is on the right in vertical-rl. - LayoutRect layoutOverflowRect(LayoutUnit lineTop, LayoutUnit lineBottom) const - { - return m_overflow ? m_overflow->layoutOverflowRect() : enclosingLayoutRect(frameRectIncludingLineHeight(lineTop, lineBottom)); - } - LayoutUnit logicalLefxlayoutOverflow() const - { - return m_overflow ? m_overflow->layoutOverflowRect().x() : static_cast(logicalLeft()); - } - LayoutUnit logicalRightLayoutOverflow() const - { - return m_overflow ? m_overflow->layoutOverflowRect().maxX() : static_cast(ceilf(logicalRight())); - } - LayoutUnit logicalTopLayoutOverflow(LayoutUnit lineTop) const - { - if (m_overflow) - return m_overflow->layoutOverflowRect().y(); - return lineTop; - } - LayoutUnit logicalBottomLayoutOverflow(LayoutUnit lineBottom) const - { - if (m_overflow) - return m_overflow->layoutOverflowRect().maxY(); - return lineBottom; - } - LayoutRect logicalLayoutOverflowRect(LayoutUnit lineTop, LayoutUnit lineBottom) const - { - // FIXME(sky): Remove - return layoutOverflowRect(lineTop, lineBottom); - } - - LayoutRect visualOverflowRect(LayoutUnit lineTop, LayoutUnit lineBottom) const - { - return m_overflow ? m_overflow->visualOverflowRect() : enclosingLayoutRect(frameRectIncludingLineHeight(lineTop, lineBottom)); - } - LayoutUnit logicalLeftVisualOverflow() const { return m_overflow ? m_overflow->visualOverflowRect().x() : static_cast(logicalLeft()); } - LayoutUnit logicalRightVisualOverflow() const { return m_overflow ? m_overflow->visualOverflowRect().maxX() : static_cast(ceilf(logicalRight())); } - LayoutUnit logicalTopVisualOverflow(LayoutUnit lineTop) const - { - if (m_overflow) - return m_overflow->visualOverflowRect().y(); - return lineTop; - } - LayoutUnit logicalBottomVisualOverflow(LayoutUnit lineBottom) const - { - if (m_overflow) - return m_overflow->visualOverflowRect().maxY(); - return lineBottom; - } - LayoutRect logicalVisualOverflowRect(LayoutUnit lineTop, LayoutUnit lineBottom) const - { - return visualOverflowRect(lineTop, lineBottom); - } - - void setOverflowFromLogicalRects(const LayoutRect& logicalLayoutOverflow, const LayoutRect& logicalVisualOverflow, LayoutUnit lineTop, LayoutUnit lineBottom); - - FloatRect frameRectIncludingLineHeight(LayoutUnit lineTop, LayoutUnit lineBottom) const - { - return FloatRect(m_topLeft.x(), lineTop.toFloat(), width(), (lineBottom - lineTop).toFloat()); - } - - FloatRect logicalFrameRectIncludingLineHeight(LayoutUnit lineTop, LayoutUnit lineBottom) const - { - return FloatRect(logicalLeft(), lineTop.toFloat(), logicalWidth(), (lineBottom - lineTop).toFloat()); - } - - bool descendantsHaveSameLineHeightAndBaseline() const { return m_descendantsHaveSameLineHeightAndBaseline; } - void clearDescendantsHaveSameLineHeightAndBaseline() - { - m_descendantsHaveSameLineHeightAndBaseline = false; - if (parent() && parent()->descendantsHaveSameLineHeightAndBaseline()) - parent()->clearDescendantsHaveSameLineHeightAndBaseline(); - } - -private: - void paintBoxDecorationBackground(PaintInfo&, const LayoutPoint&); - void paintFillLayers(const PaintInfo&, const Color&, const FillLayer&, const LayoutRect&); - void paintFillLayer(const PaintInfo&, const Color&, const FillLayer&, const LayoutRect&); - void paintBoxShadow(const PaintInfo&, RenderStyle*, ShadowStyle, const LayoutRect&); - - void addBoxShadowVisualOverflow(LayoutRect& logicalVisualOverflow); - void addBorderOutsetVisualOverflow(LayoutRect& logicalVisualOverflow); - void addOutlineVisualOverflow(LayoutRect& logicalVisualOverflow); - void addTextBoxVisualOverflow(InlineTextBox*, GlyphOverflowAndFallbackFontsMap&, LayoutRect& logicalVisualOverflow); - void addReplacedChildOverflow(const InlineBox*, LayoutRect& logicalLayoutOverflow, LayoutRect& logicalVisualOverflow); - - void setLayoutOverflow(const LayoutRect&, const LayoutRect&); - void setVisualOverflow(const LayoutRect&, const LayoutRect&); - -protected: - OwnPtr m_overflow; - - virtual bool isInlineFlowBox() const override final { return true; } - - InlineBox* m_firstChild; - InlineBox* m_lastChild; - - InlineFlowBox* m_prevLineBox; // The previous box that also uses our RenderObject - InlineFlowBox* m_nextLineBox; // The next box that also uses our RenderObject - - // Maximum logicalTop among all children of an InlineFlowBox. Used to - // calculate the offset for TextUnderlinePositionUnder. - void computeMaxLogicalTop(float& maxLogicalTop) const; - -private: - unsigned m_includeLogicalLeftEdge : 1; - unsigned m_includeLogicalRightEdge : 1; - unsigned m_hasTextChildren : 1; - unsigned m_hasTextDescendants : 1; - unsigned m_descendantsHaveSameLineHeightAndBaseline : 1; - -protected: - // The following members are only used by RootInlineBox but moved here to keep the bits packed. - - // Whether or not this line uses alphabetic or ideographic baselines by default. - unsigned m_baselineType : 1; // FontBaseline - - // If the line contains any ruby runs, then this will be true. - unsigned m_hasAnnotationsBefore : 1; - unsigned m_hasAnnotationsAfter : 1; - - unsigned m_lineBreakBidiStatusEor : 5; // WTF::Unicode::Direction - unsigned m_lineBreakBidiStatusLastStrong : 5; // WTF::Unicode::Direction - unsigned m_lineBreakBidiStatusLast : 5; // WTF::Unicode::Direction - - // End of RootInlineBox-specific members. + InlineFlowBox* prevLineBox() const { return m_prevLineBox; } + InlineFlowBox* nextLineBox() const { return m_nextLineBox; } + void setNextLineBox(InlineFlowBox* n) { m_nextLineBox = n; } + void setPreviousLineBox(InlineFlowBox* p) { m_prevLineBox = p; } + + InlineBox* firstChild() const { + checkConsistency(); + return m_firstChild; + } + InlineBox* lastChild() const { + checkConsistency(); + return m_lastChild; + } + + virtual bool isLeaf() const override final { return false; } + + InlineBox* firstLeafChild() const; + InlineBox* lastLeafChild() const; + + typedef void (*CustomInlineBoxRangeReverse)( + void* userData, + Vector::iterator first, + Vector::iterator last); + void collectLeafBoxesInLogicalOrder( + Vector&, + CustomInlineBoxRangeReverse customReverseImplementation = 0, + void* userData = 0) const; + + virtual void setConstructed() override final { + InlineBox::setConstructed(); + for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) + child->setConstructed(); + } + + void addToLine(InlineBox* child); + virtual void deleteLine() override final; + virtual void extractLine() override final; + virtual void attachLine() override final; + virtual void adjustPosition(float dx, float dy) override; + + virtual void extractLineBoxFromRenderObject(); + virtual void attachLineBoxToRenderObject(); + virtual void removeLineBoxFromRenderObject(); + + virtual void clearTruncation() override; + + IntRect roundedFrameRect() const; + + virtual void paint(PaintInfo&, + const LayoutPoint&, + LayoutUnit lineTop, + LayoutUnit lineBottom, + Vector& layers) override; + virtual bool nodeAtPoint(const HitTestRequest&, + HitTestResult&, + const HitTestLocation& locationInContainer, + const LayoutPoint& accumulatedOffset, + LayoutUnit lineTop, + LayoutUnit lineBottom) override; + + bool boxShadowCanBeAppliedToBackground(const FillLayer&) const; + + virtual RenderLineBoxList* rendererLineBoxes() const; + + // logicalLeft = left in a horizontal line and top in a vertical line. + LayoutUnit marginBorderPaddingLogicalLeft() const { + return marginLogicalLeft() + borderLogicalLeft() + paddingLogicalLeft(); + } + LayoutUnit marginBorderPaddingLogicalRight() const { + return marginLogicalRight() + borderLogicalRight() + paddingLogicalRight(); + } + LayoutUnit marginLogicalLeft() const { + if (!includeLogicalLeftEdge()) + return 0; + return boxModelObject()->marginLeft(); + } + LayoutUnit marginLogicalRight() const { + if (!includeLogicalRightEdge()) + return 0; + return boxModelObject()->marginRight(); + } + int borderLogicalLeft() const { + if (!includeLogicalLeftEdge()) + return 0; + return renderer().style(isFirstLineStyle())->borderLeftWidth(); + } + int borderLogicalRight() const { + if (!includeLogicalRightEdge()) + return 0; + return renderer().style(isFirstLineStyle())->borderRightWidth(); + } + int paddingLogicalLeft() const { + if (!includeLogicalLeftEdge()) + return 0; + return boxModelObject()->paddingLeft(); + } + int paddingLogicalRight() const { + if (!includeLogicalRightEdge()) + return 0; + return boxModelObject()->paddingRight(); + } + + bool includeLogicalLeftEdge() const { return m_includeLogicalLeftEdge; } + bool includeLogicalRightEdge() const { return m_includeLogicalRightEdge; } + void setEdges(bool includeLeft, bool includeRight) { + m_includeLogicalLeftEdge = includeLeft; + m_includeLogicalRightEdge = includeRight; + } + + // Helper functions used during line construction and placement. + void determineSpacingForFlowBoxes(bool lastLine, + bool isLogicallyLastRunWrapped, + RenderObject* logicallyLastRunRenderer); + LayoutUnit getFlowSpacingLogicalWidth(); + float placeBoxesInInlineDirection(float logicalLeft, bool& needsWordSpacing); + float placeBoxRangeInInlineDirection(InlineBox* firstChild, + InlineBox* lastChild, + float& logicalLeft, + float& minLogicalLeft, + float& maxLogicalRight, + bool& needsWordSpacing); + void beginPlacingBoxRangesInInlineDirection(float logicalLeft) { + setLogicalLeft(logicalLeft); + } + void endPlacingBoxRangesInInlineDirection(float logicalLeft, + float logicalRight, + float minLogicalLeft, + float maxLogicalRight) { + setLogicalWidth(logicalRight - logicalLeft); + if (knownToHaveNoOverflow() && + (minLogicalLeft < logicalLeft || maxLogicalRight > logicalRight)) + clearKnownToHaveNoOverflow(); + } + + void computeLogicalBoxHeights(RootInlineBox*, + LayoutUnit& maxPositionTop, + LayoutUnit& maxPositionBottom, + int& maxAscent, + int& maxDescent, + bool& setMaxAscent, + bool& setMaxDescent, + bool strictMode, + GlyphOverflowAndFallbackFontsMap&, + FontBaseline, + VerticalPositionCache&); + void adjustMaxAscentAndDescent(int& maxAscent, + int& maxDescent, + int maxPositionTop, + int maxPositionBottom); + void placeBoxesInBlockDirection(LayoutUnit logicalTop, + LayoutUnit maxHeight, + int maxAscent, + bool strictMode, + LayoutUnit& lineTop, + LayoutUnit& lineBottom, + LayoutUnit& selectionBottom, + bool& setLineTop, + LayoutUnit& lineTopIncludingMargins, + LayoutUnit& lineBottomIncludingMargins, + bool& hasAnnotationsBefore, + bool& hasAnnotationsAfter, + FontBaseline); + void flipLinesInBlockDirection(LayoutUnit lineTop, LayoutUnit lineBottom); + + LayoutUnit computeOverAnnotationAdjustment(LayoutUnit allowedPosition) const; + LayoutUnit computeUnderAnnotationAdjustment(LayoutUnit allowedPosition) const; + + void computeOverflow(LayoutUnit lineTop, + LayoutUnit lineBottom, + GlyphOverflowAndFallbackFontsMap&); + + void removeChild(InlineBox* child, MarkLineBoxes); + + virtual RenderObject::SelectionState selectionState() override; + + bool hasTextChildren() const { return m_hasTextChildren; } + bool hasTextDescendants() const { return m_hasTextDescendants; } + void setHasTextChildren() { + m_hasTextChildren = true; + setHasTextDescendants(); + } + void setHasTextDescendants() { m_hasTextDescendants = true; } + + void checkConsistency() const; + void setHasBadChildList(); + + // Line visual and layout overflow are in the coordinate space of the block. + // This means that they aren't purely physical directions. For horizontal-tb + // and vertical-lr they will match physical directions, but for horizontal-bt + // and vertical-rl, the top/bottom and left/right respectively are flipped + // when compared to their physical counterparts. For example minX is on the + // left in vertical-lr, but it is on the right in vertical-rl. + LayoutRect layoutOverflowRect(LayoutUnit lineTop, + LayoutUnit lineBottom) const { + return m_overflow ? m_overflow->layoutOverflowRect() + : enclosingLayoutRect( + frameRectIncludingLineHeight(lineTop, lineBottom)); + } + LayoutUnit logicalLefxlayoutOverflow() const { + return m_overflow ? m_overflow->layoutOverflowRect().x() + : static_cast(logicalLeft()); + } + LayoutUnit logicalRightLayoutOverflow() const { + return m_overflow ? m_overflow->layoutOverflowRect().maxX() + : static_cast(ceilf(logicalRight())); + } + LayoutUnit logicalTopLayoutOverflow(LayoutUnit lineTop) const { + if (m_overflow) + return m_overflow->layoutOverflowRect().y(); + return lineTop; + } + LayoutUnit logicalBottomLayoutOverflow(LayoutUnit lineBottom) const { + if (m_overflow) + return m_overflow->layoutOverflowRect().maxY(); + return lineBottom; + } + LayoutRect logicalLayoutOverflowRect(LayoutUnit lineTop, + LayoutUnit lineBottom) const { + // FIXME(sky): Remove + return layoutOverflowRect(lineTop, lineBottom); + } + + LayoutRect visualOverflowRect(LayoutUnit lineTop, + LayoutUnit lineBottom) const { + return m_overflow ? m_overflow->visualOverflowRect() + : enclosingLayoutRect( + frameRectIncludingLineHeight(lineTop, lineBottom)); + } + LayoutUnit logicalLeftVisualOverflow() const { + return m_overflow ? m_overflow->visualOverflowRect().x() + : static_cast(logicalLeft()); + } + LayoutUnit logicalRightVisualOverflow() const { + return m_overflow ? m_overflow->visualOverflowRect().maxX() + : static_cast(ceilf(logicalRight())); + } + LayoutUnit logicalTopVisualOverflow(LayoutUnit lineTop) const { + if (m_overflow) + return m_overflow->visualOverflowRect().y(); + return lineTop; + } + LayoutUnit logicalBottomVisualOverflow(LayoutUnit lineBottom) const { + if (m_overflow) + return m_overflow->visualOverflowRect().maxY(); + return lineBottom; + } + LayoutRect logicalVisualOverflowRect(LayoutUnit lineTop, + LayoutUnit lineBottom) const { + return visualOverflowRect(lineTop, lineBottom); + } + + void setOverflowFromLogicalRects(const LayoutRect& logicalLayoutOverflow, + const LayoutRect& logicalVisualOverflow, + LayoutUnit lineTop, + LayoutUnit lineBottom); + + FloatRect frameRectIncludingLineHeight(LayoutUnit lineTop, + LayoutUnit lineBottom) const { + return FloatRect(m_topLeft.x(), lineTop.toFloat(), width(), + (lineBottom - lineTop).toFloat()); + } + + FloatRect logicalFrameRectIncludingLineHeight(LayoutUnit lineTop, + LayoutUnit lineBottom) const { + return FloatRect(logicalLeft(), lineTop.toFloat(), logicalWidth(), + (lineBottom - lineTop).toFloat()); + } + + bool descendantsHaveSameLineHeightAndBaseline() const { + return m_descendantsHaveSameLineHeightAndBaseline; + } + void clearDescendantsHaveSameLineHeightAndBaseline() { + m_descendantsHaveSameLineHeightAndBaseline = false; + if (parent() && parent()->descendantsHaveSameLineHeightAndBaseline()) + parent()->clearDescendantsHaveSameLineHeightAndBaseline(); + } + + private: + void paintBoxDecorationBackground(PaintInfo&, const LayoutPoint&); + void paintFillLayers(const PaintInfo&, + const Color&, + const FillLayer&, + const LayoutRect&); + void paintFillLayer(const PaintInfo&, + const Color&, + const FillLayer&, + const LayoutRect&); + void paintBoxShadow(const PaintInfo&, + RenderStyle*, + ShadowStyle, + const LayoutRect&); + + void addBoxShadowVisualOverflow(LayoutRect& logicalVisualOverflow); + void addBorderOutsetVisualOverflow(LayoutRect& logicalVisualOverflow); + void addOutlineVisualOverflow(LayoutRect& logicalVisualOverflow); + void addTextBoxVisualOverflow(InlineTextBox*, + GlyphOverflowAndFallbackFontsMap&, + LayoutRect& logicalVisualOverflow); + void addReplacedChildOverflow(const InlineBox*, + LayoutRect& logicalLayoutOverflow, + LayoutRect& logicalVisualOverflow); + + void setLayoutOverflow(const LayoutRect&, const LayoutRect&); + void setVisualOverflow(const LayoutRect&, const LayoutRect&); + + protected: + OwnPtr m_overflow; + + virtual bool isInlineFlowBox() const override final { return true; } + + InlineBox* m_firstChild; + InlineBox* m_lastChild; + + InlineFlowBox* + m_prevLineBox; // The previous box that also uses our RenderObject + InlineFlowBox* m_nextLineBox; // The next box that also uses our RenderObject + + // Maximum logicalTop among all children of an InlineFlowBox. Used to + // calculate the offset for TextUnderlinePositionUnder. + void computeMaxLogicalTop(float& maxLogicalTop) const; + + private: + unsigned m_includeLogicalLeftEdge : 1; + unsigned m_includeLogicalRightEdge : 1; + unsigned m_hasTextChildren : 1; + unsigned m_hasTextDescendants : 1; + unsigned m_descendantsHaveSameLineHeightAndBaseline : 1; + + protected: + // The following members are only used by RootInlineBox but moved here to keep + // the bits packed. + + // Whether or not this line uses alphabetic or ideographic baselines by + // default. + unsigned m_baselineType : 1; // FontBaseline + + // If the line contains any ruby runs, then this will be true. + unsigned m_hasAnnotationsBefore : 1; + unsigned m_hasAnnotationsAfter : 1; + + unsigned m_lineBreakBidiStatusEor : 5; // WTF::Unicode::Direction + unsigned m_lineBreakBidiStatusLastStrong : 5; // WTF::Unicode::Direction + unsigned m_lineBreakBidiStatusLast : 5; // WTF::Unicode::Direction + + // End of RootInlineBox-specific members. #if ENABLE(ASSERT) -private: - unsigned m_hasBadChildList : 1; + private: + unsigned m_hasBadChildList : 1; #endif }; DEFINE_INLINE_BOX_TYPE_CASTS(InlineFlowBox); #if !ENABLE(ASSERT) -inline void InlineFlowBox::checkConsistency() const -{ -} +inline void InlineFlowBox::checkConsistency() const {} #endif -inline void InlineFlowBox::setHasBadChildList() -{ +inline void InlineFlowBox::setHasBadChildList() { #if ENABLE(ASSERT) - m_hasBadChildList = true; + m_hasBadChildList = true; #endif } -} // namespace blink +} // namespace blink #ifndef NDEBUG // Outside the WebCore namespace for ease of invocation from gdb. diff --git a/sky/engine/core/rendering/InlineIterator.h b/sky/engine/core/rendering/InlineIterator.h index 7422071c7f7c3..0311e8873d466 100644 --- a/sky/engine/core/rendering/InlineIterator.h +++ b/sky/engine/core/rendering/InlineIterator.h @@ -1,6 +1,7 @@ /* * Copyright (C) 2000 Lars Knoll (knoll@kde.org) - * Copyright (C) 2003, 2004, 2006, 2007, 2008, 2009, 2010 Apple Inc. All right reserved. + * Copyright (C) 2003, 2004, 2006, 2007, 2008, 2009, 2010 Apple Inc. + * All right reserved. * Copyright (C) 2010 Google Inc. All rights reserved. * * This library is free software; you can redistribute it and/or @@ -34,696 +35,747 @@ namespace blink { // This class is used to RenderInline subtrees, stepping by character within the // text children. InlineIterator will use bidiNext to find the next RenderText -// optionally notifying a BidiResolver every time it steps into/out of a RenderInline. +// optionally notifying a BidiResolver every time it steps into/out of a +// RenderInline. class InlineIterator { -public: - enum IncrementRule { - FastIncrementInIsolatedRenderer, - FastIncrementInTextNode - }; - - InlineIterator() - : m_root(0) - , m_obj(0) - , m_nextBreakablePosition(-1) - , m_pos(0) - { - } - - InlineIterator(RenderObject* root, RenderObject* o, unsigned p) - : m_root(root) - , m_obj(o) - , m_nextBreakablePosition(-1) - , m_pos(p) - { - } - - void clear() { moveTo(0, 0); } - - void moveToStartOf(RenderObject* object) - { - moveTo(object, 0); - } - - void moveTo(RenderObject* object, unsigned offset, int nextBreak = -1) - { - m_obj = object; - m_pos = offset; - m_nextBreakablePosition = nextBreak; - } - - RenderObject* object() const { return m_obj; } - void setObject(RenderObject* object) { m_obj = object; } - - int nextBreakablePosition() const { return m_nextBreakablePosition; } - void setNextBreakablePosition(int position) { m_nextBreakablePosition = position; } - - unsigned offset() const { return m_pos; } - void setOffset(unsigned position) { m_pos = position; } - RenderObject* root() const { return m_root; } - - void fastIncrementInTextNode(); - void increment(InlineBidiResolver* = 0, IncrementRule = FastIncrementInTextNode); - bool atEnd() const; - - inline bool atTextParagraphSeparator() const - { - return m_obj && m_obj->preservesNewline() && m_obj->isText() && toRenderText(m_obj)->textLength() - && toRenderText(m_obj)->characterAt(m_pos) == '\n'; - } - - inline bool atParagraphSeparator() const - { - return atTextParagraphSeparator(); - } - - UChar characterAt(unsigned) const; - UChar current() const; - UChar previousInSameNode() const; - ALWAYS_INLINE WTF::Unicode::Direction direction() const; - -private: - RenderObject* m_root; - RenderObject* m_obj; - - int m_nextBreakablePosition; - unsigned m_pos; + public: + enum IncrementRule { + FastIncrementInIsolatedRenderer, + FastIncrementInTextNode + }; + + InlineIterator() + : m_root(0), m_obj(0), m_nextBreakablePosition(-1), m_pos(0) {} + + InlineIterator(RenderObject* root, RenderObject* o, unsigned p) + : m_root(root), m_obj(o), m_nextBreakablePosition(-1), m_pos(p) {} + + void clear() { moveTo(0, 0); } + + void moveToStartOf(RenderObject* object) { moveTo(object, 0); } + + void moveTo(RenderObject* object, unsigned offset, int nextBreak = -1) { + m_obj = object; + m_pos = offset; + m_nextBreakablePosition = nextBreak; + } + + RenderObject* object() const { return m_obj; } + void setObject(RenderObject* object) { m_obj = object; } + + int nextBreakablePosition() const { return m_nextBreakablePosition; } + void setNextBreakablePosition(int position) { + m_nextBreakablePosition = position; + } + + unsigned offset() const { return m_pos; } + void setOffset(unsigned position) { m_pos = position; } + RenderObject* root() const { return m_root; } + + void fastIncrementInTextNode(); + void increment(InlineBidiResolver* = 0, + IncrementRule = FastIncrementInTextNode); + bool atEnd() const; + + inline bool atTextParagraphSeparator() const { + return m_obj && m_obj->preservesNewline() && m_obj->isText() && + toRenderText(m_obj)->textLength() && + toRenderText(m_obj)->characterAt(m_pos) == '\n'; + } + + inline bool atParagraphSeparator() const { + return atTextParagraphSeparator(); + } + + UChar characterAt(unsigned) const; + UChar current() const; + UChar previousInSameNode() const; + ALWAYS_INLINE WTF::Unicode::Direction direction() const; + + private: + RenderObject* m_root; + RenderObject* m_obj; + + int m_nextBreakablePosition; + unsigned m_pos; }; -inline bool operator==(const InlineIterator& it1, const InlineIterator& it2) -{ - return it1.offset() == it2.offset() && it1.object() == it2.object(); +inline bool operator==(const InlineIterator& it1, const InlineIterator& it2) { + return it1.offset() == it2.offset() && it1.object() == it2.object(); } -inline bool operator!=(const InlineIterator& it1, const InlineIterator& it2) -{ - return it1.offset() != it2.offset() || it1.object() != it2.object(); +inline bool operator!=(const InlineIterator& it1, const InlineIterator& it2) { + return it1.offset() != it2.offset() || it1.object() != it2.object(); } -static inline WTF::Unicode::Direction embedCharFromDirection(TextDirection dir, EUnicodeBidi unicodeBidi) -{ - using namespace WTF::Unicode; - if (unicodeBidi == Embed) - return dir == RTL ? RightToLeftEmbedding : LeftToRightEmbedding; - return dir == RTL ? RightToLeftOverride : LeftToRightOverride; +static inline WTF::Unicode::Direction embedCharFromDirection( + TextDirection dir, + EUnicodeBidi unicodeBidi) { + using namespace WTF::Unicode; + if (unicodeBidi == Embed) + return dir == RTL ? RightToLeftEmbedding : LeftToRightEmbedding; + return dir == RTL ? RightToLeftOverride : LeftToRightOverride; } template -static inline void notifyObserverEnteredObject(Observer* observer, RenderObject* object) -{ - if (!observer || !object || !object->isRenderInline()) - return; - - RenderStyle* style = object->style(); - EUnicodeBidi unicodeBidi = style->unicodeBidi(); - if (unicodeBidi == UBNormal) { - // http://dev.w3.org/csswg/css3-writing-modes/#unicode-bidi - // "The element does not open an additional level of embedding with respect to the bidirectional algorithm." - // Thus we ignore any possible dir= attribute on the span. - return; - } - if (isIsolated(unicodeBidi)) { - // Make sure that explicit embeddings are committed before we enter the isolated content. - observer->commitExplicitEmbedding(observer->runs()); - observer->enterIsolate(); - // Embedding/Override characters implied by dir= will be handled when - // we process the isolated span, not when laying out the "parent" run. - return; - } - - if (!observer->inIsolate()) - observer->embed(embedCharFromDirection(style->direction(), unicodeBidi), FromStyleOrDOM); +static inline void notifyObserverEnteredObject(Observer* observer, + RenderObject* object) { + if (!observer || !object || !object->isRenderInline()) + return; + + RenderStyle* style = object->style(); + EUnicodeBidi unicodeBidi = style->unicodeBidi(); + if (unicodeBidi == UBNormal) { + // http://dev.w3.org/csswg/css3-writing-modes/#unicode-bidi + // "The element does not open an additional level of embedding with respect + // to the bidirectional algorithm." Thus we ignore any possible dir= + // attribute on the span. + return; + } + if (isIsolated(unicodeBidi)) { + // Make sure that explicit embeddings are committed before we enter the + // isolated content. + observer->commitExplicitEmbedding(observer->runs()); + observer->enterIsolate(); + // Embedding/Override characters implied by dir= will be handled when + // we process the isolated span, not when laying out the "parent" run. + return; + } + + if (!observer->inIsolate()) + observer->embed(embedCharFromDirection(style->direction(), unicodeBidi), + FromStyleOrDOM); } template -static inline void notifyObserverWillExitObject(Observer* observer, RenderObject* object) -{ - if (!observer || !object || !object->isRenderInline()) - return; - - EUnicodeBidi unicodeBidi = object->style()->unicodeBidi(); - if (unicodeBidi == UBNormal) - return; // Nothing to do for unicode-bidi: normal - if (isIsolated(unicodeBidi)) { - observer->exitIsolate(); - return; - } +static inline void notifyObserverWillExitObject(Observer* observer, + RenderObject* object) { + if (!observer || !object || !object->isRenderInline()) + return; + + EUnicodeBidi unicodeBidi = object->style()->unicodeBidi(); + if (unicodeBidi == UBNormal) + return; // Nothing to do for unicode-bidi: normal + if (isIsolated(unicodeBidi)) { + observer->exitIsolate(); + return; + } - // Otherwise we pop any embed/override character we added when we opened this tag. - if (!observer->inIsolate()) - observer->embed(WTF::Unicode::PopDirectionalFormat, FromStyleOrDOM); + // Otherwise we pop any embed/override character we added when we opened this + // tag. + if (!observer->inIsolate()) + observer->embed(WTF::Unicode::PopDirectionalFormat, FromStyleOrDOM); } -static inline bool isIteratorTarget(RenderObject* object) -{ - ASSERT(object); // The iterator will of course return 0, but its not an expected argument to this function. - return object->isText() || object->isOutOfFlowPositioned() || object->isReplaced(); +static inline bool isIteratorTarget(RenderObject* object) { + ASSERT(object); // The iterator will of course return 0, but its not an + // expected argument to this function. + return object->isText() || object->isOutOfFlowPositioned() || + object->isReplaced(); } // This enum is only used for bidiNextShared() enum EmptyInlineBehavior { - SkipEmptyInlines, - IncludeEmptyInlines, + SkipEmptyInlines, + IncludeEmptyInlines, }; -static bool isEmptyInline(RenderObject* object) -{ - if (!object->isRenderInline()) - return false; +static bool isEmptyInline(RenderObject* object) { + if (!object->isRenderInline()) + return false; - for (RenderObject* curr = toRenderInline(object)->firstChild(); curr; curr = curr->nextSibling()) { - if (curr->isFloatingOrOutOfFlowPositioned()) - continue; - if (curr->isText() && toRenderText(curr)->isAllCollapsibleWhitespace()) - continue; + for (RenderObject* curr = toRenderInline(object)->firstChild(); curr; + curr = curr->nextSibling()) { + if (curr->isFloatingOrOutOfFlowPositioned()) + continue; + if (curr->isText() && toRenderText(curr)->isAllCollapsibleWhitespace()) + continue; - if (!isEmptyInline(curr)) - return false; - } - return true; + if (!isEmptyInline(curr)) + return false; + } + return true; } // FIXME: This function is misleadingly named. It has little to do with bidi. // This function will iterate over inlines within a block, optionally notifying -// a bidi resolver as it enters/exits inlines (so it can push/pop embedding levels). +// a bidi resolver as it enters/exits inlines (so it can push/pop embedding +// levels). template -static inline RenderObject* bidiNextShared(RenderObject* root, RenderObject* current, Observer* observer = 0, EmptyInlineBehavior emptyInlineBehavior = SkipEmptyInlines, bool* endOfInlinePtr = 0) -{ - RenderObject* next = 0; - // oldEndOfInline denotes if when we last stopped iterating if we were at the end of an inline. - bool oldEndOfInline = endOfInlinePtr ? *endOfInlinePtr : false; - bool endOfInline = false; - - while (current) { - next = 0; - if (!oldEndOfInline && !isIteratorTarget(current)) { - next = current->slowFirstChild(); - notifyObserverEnteredObject(observer, next); +static inline RenderObject* bidiNextShared( + RenderObject* root, + RenderObject* current, + Observer* observer = 0, + EmptyInlineBehavior emptyInlineBehavior = SkipEmptyInlines, + bool* endOfInlinePtr = 0) { + RenderObject* next = 0; + // oldEndOfInline denotes if when we last stopped iterating if we were at the + // end of an inline. + bool oldEndOfInline = endOfInlinePtr ? *endOfInlinePtr : false; + bool endOfInline = false; + + while (current) { + next = 0; + if (!oldEndOfInline && !isIteratorTarget(current)) { + next = current->slowFirstChild(); + notifyObserverEnteredObject(observer, next); + } + + // We hit this when either current has no children, or when current is not a + // renderer we care about. + if (!next) { + // If it is a renderer we care about, and we're doing our inline-walk, + // return it. + if (emptyInlineBehavior == IncludeEmptyInlines && !oldEndOfInline && + current->isRenderInline()) { + next = current; + endOfInline = true; + break; + } + + while (current && current != root) { + notifyObserverWillExitObject(observer, current); + + next = current->nextSibling(); + if (next) { + notifyObserverEnteredObject(observer, next); + break; } - // We hit this when either current has no children, or when current is not a renderer we care about. - if (!next) { - // If it is a renderer we care about, and we're doing our inline-walk, return it. - if (emptyInlineBehavior == IncludeEmptyInlines && !oldEndOfInline && current->isRenderInline()) { - next = current; - endOfInline = true; - break; - } - - while (current && current != root) { - notifyObserverWillExitObject(observer, current); - - next = current->nextSibling(); - if (next) { - notifyObserverEnteredObject(observer, next); - break; - } - - current = current->parent(); - if (emptyInlineBehavior == IncludeEmptyInlines && current && current != root && current->isRenderInline()) { - next = current; - endOfInline = true; - break; - } - } + current = current->parent(); + if (emptyInlineBehavior == IncludeEmptyInlines && current && + current != root && current->isRenderInline()) { + next = current; + endOfInline = true; + break; } + } + } - if (!next) - break; + if (!next) + break; - if (isIteratorTarget(next) - || ((emptyInlineBehavior == IncludeEmptyInlines || isEmptyInline(next)) // Always return EMPTY inlines. - && next->isRenderInline())) - break; - current = next; - } + if (isIteratorTarget(next) || + ((emptyInlineBehavior == IncludeEmptyInlines || + isEmptyInline(next)) // Always return EMPTY inlines. + && next->isRenderInline())) + break; + current = next; + } - if (endOfInlinePtr) - *endOfInlinePtr = endOfInline; + if (endOfInlinePtr) + *endOfInlinePtr = endOfInline; - return next; + return next; } template -static inline RenderObject* bidiNextSkippingEmptyInlines(RenderObject* root, RenderObject* current, Observer* observer) -{ - // The SkipEmptyInlines callers never care about endOfInlinePtr. - return bidiNextShared(root, current, observer, SkipEmptyInlines); -} - -// This makes callers cleaner as they don't have to specify a type for the observer when not providing one. -static inline RenderObject* bidiNextSkippingEmptyInlines(RenderObject* root, RenderObject* current) -{ - InlineBidiResolver* observer = 0; - return bidiNextSkippingEmptyInlines(root, current, observer); -} - -static inline RenderObject* bidiNextIncludingEmptyInlines(RenderObject* root, RenderObject* current, bool* endOfInlinePtr = 0) -{ - InlineBidiResolver* observer = 0; // Callers who include empty inlines, never use an observer. - return bidiNextShared(root, current, observer, IncludeEmptyInlines, endOfInlinePtr); -} - -static inline RenderObject* bidiFirstSkippingEmptyInlines(RenderParagraph* root, BidiRunList& runs, InlineBidiResolver* resolver = 0) -{ - RenderObject* o = root->firstChild(); - if (!o) - return 0; - - if (o->isRenderInline()) { - notifyObserverEnteredObject(resolver, o); - if (!isEmptyInline(o)) - o = bidiNextSkippingEmptyInlines(root, o, resolver); - else { - // Never skip empty inlines. - if (resolver) - resolver->commitExplicitEmbedding(runs); - return o; - } +static inline RenderObject* bidiNextSkippingEmptyInlines(RenderObject* root, + RenderObject* current, + Observer* observer) { + // The SkipEmptyInlines callers never care about endOfInlinePtr. + return bidiNextShared(root, current, observer, SkipEmptyInlines); +} + +// This makes callers cleaner as they don't have to specify a type for the +// observer when not providing one. +static inline RenderObject* bidiNextSkippingEmptyInlines( + RenderObject* root, + RenderObject* current) { + InlineBidiResolver* observer = 0; + return bidiNextSkippingEmptyInlines(root, current, observer); +} + +static inline RenderObject* bidiNextIncludingEmptyInlines( + RenderObject* root, + RenderObject* current, + bool* endOfInlinePtr = 0) { + InlineBidiResolver* observer = + 0; // Callers who include empty inlines, never use an observer. + return bidiNextShared(root, current, observer, IncludeEmptyInlines, + endOfInlinePtr); +} + +static inline RenderObject* bidiFirstSkippingEmptyInlines( + RenderParagraph* root, + BidiRunList& runs, + InlineBidiResolver* resolver = 0) { + RenderObject* o = root->firstChild(); + if (!o) + return 0; + + if (o->isRenderInline()) { + notifyObserverEnteredObject(resolver, o); + if (!isEmptyInline(o)) + o = bidiNextSkippingEmptyInlines(root, o, resolver); + else { + // Never skip empty inlines. + if (resolver) + resolver->commitExplicitEmbedding(runs); + return o; } + } - // FIXME: Unify this with the bidiNext call above. - if (o && !isIteratorTarget(o)) - o = bidiNextSkippingEmptyInlines(root, o, resolver); + // FIXME: Unify this with the bidiNext call above. + if (o && !isIteratorTarget(o)) + o = bidiNextSkippingEmptyInlines(root, o, resolver); - if (resolver) - resolver->commitExplicitEmbedding(runs); - return o; + if (resolver) + resolver->commitExplicitEmbedding(runs); + return o; } // FIXME: This method needs to be renamed when bidiNext finds a good name. -static inline RenderObject* bidiFirstIncludingEmptyInlines(RenderBlock* root) -{ - RenderObject* o = root->firstChild(); - // If either there are no children to walk, or the first one is correct - // then just return it. - if (!o || o->isRenderInline() || isIteratorTarget(o)) - return o; +static inline RenderObject* bidiFirstIncludingEmptyInlines(RenderBlock* root) { + RenderObject* o = root->firstChild(); + // If either there are no children to walk, or the first one is correct + // then just return it. + if (!o || o->isRenderInline() || isIteratorTarget(o)) + return o; - return bidiNextIncludingEmptyInlines(root, o); + return bidiNextIncludingEmptyInlines(root, o); } -inline void InlineIterator::fastIncrementInTextNode() -{ - ASSERT(m_obj); - ASSERT(m_obj->isText()); - ASSERT(m_pos <= toRenderText(m_obj)->textLength()); - if (m_pos < INT_MAX) - m_pos++; +inline void InlineIterator::fastIncrementInTextNode() { + ASSERT(m_obj); + ASSERT(m_obj->isText()); + ASSERT(m_pos <= toRenderText(m_obj)->textLength()); + if (m_pos < INT_MAX) + m_pos++; } -// FIXME: This is used by RenderParagraph for simplified layout, and has nothing to do with bidi -// it shouldn't use functions called bidiFirst and bidiNext. +// FIXME: This is used by RenderParagraph for simplified layout, and has nothing +// to do with bidi it shouldn't use functions called bidiFirst and bidiNext. class InlineWalker { -public: - InlineWalker(RenderBlock* root) - : m_root(root) - , m_current(0) - , m_atEndOfInline(false) - { - // FIXME: This class should be taught how to do the SkipEmptyInlines codepath as well. - m_current = bidiFirstIncludingEmptyInlines(m_root); - } - - RenderBlock* root() { return m_root; } - RenderObject* current() { return m_current; } - - bool atEndOfInline() { return m_atEndOfInline; } - bool atEnd() const { return !m_current; } - - RenderObject* advance() - { - // FIXME: Support SkipEmptyInlines and observer parameters. - m_current = bidiNextIncludingEmptyInlines(m_root, m_current, &m_atEndOfInline); - return m_current; - } -private: - RenderBlock* m_root; - RenderObject* m_current; - bool m_atEndOfInline; + public: + InlineWalker(RenderBlock* root) + : m_root(root), m_current(0), m_atEndOfInline(false) { + // FIXME: This class should be taught how to do the SkipEmptyInlines + // codepath as well. + m_current = bidiFirstIncludingEmptyInlines(m_root); + } + + RenderBlock* root() { return m_root; } + RenderObject* current() { return m_current; } + + bool atEndOfInline() { return m_atEndOfInline; } + bool atEnd() const { return !m_current; } + + RenderObject* advance() { + // FIXME: Support SkipEmptyInlines and observer parameters. + m_current = + bidiNextIncludingEmptyInlines(m_root, m_current, &m_atEndOfInline); + return m_current; + } + + private: + RenderBlock* m_root; + RenderObject* m_current; + bool m_atEndOfInline; }; -static inline bool endOfLineHasIsolatedObjectAncestor(const InlineIterator& isolatedIterator, const InlineIterator& ancestorItertor) -{ - if (!isolatedIterator.object() || !isIsolated(isolatedIterator.object()->style()->unicodeBidi())) - return false; - - RenderObject* innerIsolatedObject = isolatedIterator.object(); - while (innerIsolatedObject && innerIsolatedObject != isolatedIterator.root()) { - if (innerIsolatedObject == ancestorItertor.object()) - return true; - innerIsolatedObject = innerIsolatedObject->parent(); - } +static inline bool endOfLineHasIsolatedObjectAncestor( + const InlineIterator& isolatedIterator, + const InlineIterator& ancestorItertor) { + if (!isolatedIterator.object() || + !isIsolated(isolatedIterator.object()->style()->unicodeBidi())) return false; -} - -inline void InlineIterator::increment(InlineBidiResolver* resolver, IncrementRule rule) -{ - if (!m_obj) - return; - - if (rule == FastIncrementInIsolatedRenderer - && resolver && resolver->inIsolate() - && !endOfLineHasIsolatedObjectAncestor(resolver->endOfLine(), resolver->position())) { - moveTo(bidiNextSkippingEmptyInlines(m_root, m_obj, resolver), 0); - return; - } - if (m_obj->isText()) { - fastIncrementInTextNode(); - if (m_pos < toRenderText(m_obj)->textLength()) - return; - } - // bidiNext can return 0, so use moveTo instead of moveToStartOf + RenderObject* innerIsolatedObject = isolatedIterator.object(); + while (innerIsolatedObject && + innerIsolatedObject != isolatedIterator.root()) { + if (innerIsolatedObject == ancestorItertor.object()) + return true; + innerIsolatedObject = innerIsolatedObject->parent(); + } + return false; +} + +inline void InlineIterator::increment(InlineBidiResolver* resolver, + IncrementRule rule) { + if (!m_obj) + return; + + if (rule == FastIncrementInIsolatedRenderer && resolver && + resolver->inIsolate() && + !endOfLineHasIsolatedObjectAncestor(resolver->endOfLine(), + resolver->position())) { moveTo(bidiNextSkippingEmptyInlines(m_root, m_obj, resolver), 0); + return; + } + + if (m_obj->isText()) { + fastIncrementInTextNode(); + if (m_pos < toRenderText(m_obj)->textLength()) + return; + } + // bidiNext can return 0, so use moveTo instead of moveToStartOf + moveTo(bidiNextSkippingEmptyInlines(m_root, m_obj, resolver), 0); } -inline bool InlineIterator::atEnd() const -{ - return !m_obj; +inline bool InlineIterator::atEnd() const { + return !m_obj; } -inline UChar InlineIterator::characterAt(unsigned index) const -{ - if (!m_obj || !m_obj->isText()) - return 0; +inline UChar InlineIterator::characterAt(unsigned index) const { + if (!m_obj || !m_obj->isText()) + return 0; - return toRenderText(m_obj)->characterAt(index); + return toRenderText(m_obj)->characterAt(index); } -inline UChar InlineIterator::current() const -{ - return characterAt(m_pos); +inline UChar InlineIterator::current() const { + return characterAt(m_pos); } -inline UChar InlineIterator::previousInSameNode() const -{ - if (!m_pos) - return 0; +inline UChar InlineIterator::previousInSameNode() const { + if (!m_pos) + return 0; - return characterAt(m_pos - 1); + return characterAt(m_pos - 1); } -ALWAYS_INLINE WTF::Unicode::Direction InlineIterator::direction() const -{ - if (UChar c = current()) - return WTF::Unicode::direction(c); - - return WTF::Unicode::OtherNeutral; -} +ALWAYS_INLINE WTF::Unicode::Direction InlineIterator::direction() const { + if (UChar c = current()) + return WTF::Unicode::direction(c); -template<> -inline void InlineBidiResolver::increment() -{ - m_current.increment(this, InlineIterator::FastIncrementInIsolatedRenderer); + return WTF::Unicode::OtherNeutral; } template <> -inline bool InlineBidiResolver::isEndOfLine(const InlineIterator& end) -{ - bool inEndOfLine = m_current == end || m_current.atEnd() || (inIsolate() && m_current.object() == end.object()); - if (inIsolate() && inEndOfLine) { - m_current.moveTo(m_current.object(), end.offset(), m_current.nextBreakablePosition()); - m_last = m_current; - updateStatusLastFromCurrentDirection(WTF::Unicode::OtherNeutral); - } - return inEndOfLine; +inline void InlineBidiResolver::increment() { + m_current.increment(this, InlineIterator::FastIncrementInIsolatedRenderer); } -static inline bool isCollapsibleSpace(UChar character, RenderText* renderer) -{ - if (character == ' ' || character == '\t' || character == softHyphen) - return true; - if (character == '\n') - return !renderer->style()->preserveNewline(); - return false; +template <> +inline bool InlineBidiResolver::isEndOfLine(const InlineIterator& end) { + bool inEndOfLine = m_current == end || m_current.atEnd() || + (inIsolate() && m_current.object() == end.object()); + if (inIsolate() && inEndOfLine) { + m_current.moveTo(m_current.object(), end.offset(), + m_current.nextBreakablePosition()); + m_last = m_current; + updateStatusLastFromCurrentDirection(WTF::Unicode::OtherNeutral); + } + return inEndOfLine; +} + +static inline bool isCollapsibleSpace(UChar character, RenderText* renderer) { + if (character == ' ' || character == '\t' || character == softHyphen) + return true; + if (character == '\n') + return !renderer->style()->preserveNewline(); + return false; } template -static inline int findFirstTrailingSpace(RenderText* lastText, const CharacterType* characters, int start, int stop) -{ - int firstSpace = stop; - while (firstSpace > start) { - UChar current = characters[firstSpace - 1]; - if (!isCollapsibleSpace(current, lastText)) - break; - firstSpace--; - } +static inline int findFirstTrailingSpace(RenderText* lastText, + const CharacterType* characters, + int start, + int stop) { + int firstSpace = stop; + while (firstSpace > start) { + UChar current = characters[firstSpace - 1]; + if (!isCollapsibleSpace(current, lastText)) + break; + firstSpace--; + } - return firstSpace; + return firstSpace; } template <> -inline int InlineBidiResolver::findFirstTrailingSpaceAtRun(BidiRun* run) -{ - ASSERT(run); - RenderObject* lastObject = run->m_object; - if (!lastObject->isText()) - return run->m_stop; - - RenderText* lastText = toRenderText(lastObject); - int firstSpace; - if (lastText->is8Bit()) - firstSpace = findFirstTrailingSpace(lastText, lastText->characters8(), run->start(), run->stop()); - else - firstSpace = findFirstTrailingSpace(lastText, lastText->characters16(), run->start(), run->stop()); - return firstSpace; +inline int InlineBidiResolver::findFirstTrailingSpaceAtRun(BidiRun* run) { + ASSERT(run); + RenderObject* lastObject = run->m_object; + if (!lastObject->isText()) + return run->m_stop; + + RenderText* lastText = toRenderText(lastObject); + int firstSpace; + if (lastText->is8Bit()) + firstSpace = findFirstTrailingSpace(lastText, lastText->characters8(), + run->start(), run->stop()); + else + firstSpace = findFirstTrailingSpace(lastText, lastText->characters16(), + run->start(), run->stop()); + return firstSpace; } template <> -inline BidiRun* InlineBidiResolver::addTrailingRun(BidiRunList& runs, int start, int stop, BidiRun* run, BidiContext* context, TextDirection direction) const -{ - BidiRun* newTrailingRun = new BidiRun(start, stop, run->m_object, context, WTF::Unicode::OtherNeutral); - if (direction == LTR) - runs.addRun(newTrailingRun); - else - runs.prependRun(newTrailingRun); - - return newTrailingRun; +inline BidiRun* InlineBidiResolver::addTrailingRun( + BidiRunList& runs, + int start, + int stop, + BidiRun* run, + BidiContext* context, + TextDirection direction) const { + BidiRun* newTrailingRun = new BidiRun(start, stop, run->m_object, context, + WTF::Unicode::OtherNeutral); + if (direction == LTR) + runs.addRun(newTrailingRun); + else + runs.prependRun(newTrailingRun); + + return newTrailingRun; } template <> -inline bool InlineBidiResolver::needsToApplyL1Rule(BidiRunList& runs) -{ - if (!runs.logicallyLastRun()->m_object->style()->breakOnlyAfterWhiteSpace() - || !runs.logicallyLastRun()->m_object->style()->autoWrap()) - return false; - return true; -} - -static inline bool isIsolatedInline(RenderObject* object) -{ - ASSERT(object); - return object->isRenderInline() && isIsolated(object->style()->unicodeBidi()); -} - -static inline RenderObject* highestContainingIsolateWithinRoot(RenderObject* object, RenderObject* root) -{ - ASSERT(object); - RenderObject* containingIsolateObj = 0; - while (object && object != root) { - if (isIsolatedInline(object)) - containingIsolateObj = object; - - object = object->parent(); - } - return containingIsolateObj; -} - -static inline unsigned numberOfIsolateAncestors(const InlineIterator& iter) -{ - RenderObject* object = iter.object(); - if (!object) - return 0; - unsigned count = 0; - while (object && object != iter.root()) { - if (isIsolatedInline(object)) - count++; - object = object->parent(); - } - return count; -} - -// FIXME: This belongs on InlineBidiResolver, except it's a template specialization -// of BidiResolver which knows nothing about RenderObjects. -static inline BidiRun* addPlaceholderRunForIsolatedInline(InlineBidiResolver& resolver, RenderObject* obj, unsigned pos) -{ - ASSERT(obj); - BidiRun* isolatedRun = new BidiRun(pos, pos, obj, resolver.context(), resolver.dir()); - resolver.runs().addRun(isolatedRun); - // FIXME: isolatedRuns() could be a hash of object->run and then we could cheaply - // ASSERT here that we didn't create multiple objects for the same inline. - resolver.isolatedRuns().append(isolatedRun); - return isolatedRun; -} - -static inline BidiRun* createRun(int start, int end, RenderObject* obj, InlineBidiResolver& resolver) -{ - return new BidiRun(start, end, obj, resolver.context(), resolver.dir()); -} - -enum AppendRunBehavior { - AppendingFakeRun, - AppendingRunsForObject -}; +inline bool InlineBidiResolver::needsToApplyL1Rule(BidiRunList& runs) { + if (!runs.logicallyLastRun()->m_object->style()->breakOnlyAfterWhiteSpace() || + !runs.logicallyLastRun()->m_object->style()->autoWrap()) + return false; + return true; +} + +static inline bool isIsolatedInline(RenderObject* object) { + ASSERT(object); + return object->isRenderInline() && isIsolated(object->style()->unicodeBidi()); +} + +static inline RenderObject* highestContainingIsolateWithinRoot( + RenderObject* object, + RenderObject* root) { + ASSERT(object); + RenderObject* containingIsolateObj = 0; + while (object && object != root) { + if (isIsolatedInline(object)) + containingIsolateObj = object; + + object = object->parent(); + } + return containingIsolateObj; +} + +static inline unsigned numberOfIsolateAncestors(const InlineIterator& iter) { + RenderObject* object = iter.object(); + if (!object) + return 0; + unsigned count = 0; + while (object && object != iter.root()) { + if (isIsolatedInline(object)) + count++; + object = object->parent(); + } + return count; +} + +// FIXME: This belongs on InlineBidiResolver, except it's a template +// specialization of BidiResolver which knows nothing about RenderObjects. +static inline BidiRun* addPlaceholderRunForIsolatedInline( + InlineBidiResolver& resolver, + RenderObject* obj, + unsigned pos) { + ASSERT(obj); + BidiRun* isolatedRun = + new BidiRun(pos, pos, obj, resolver.context(), resolver.dir()); + resolver.runs().addRun(isolatedRun); + // FIXME: isolatedRuns() could be a hash of object->run and then we could + // cheaply ASSERT here that we didn't create multiple objects for the same + // inline. + resolver.isolatedRuns().append(isolatedRun); + return isolatedRun; +} + +static inline BidiRun* createRun(int start, + int end, + RenderObject* obj, + InlineBidiResolver& resolver) { + return new BidiRun(start, end, obj, resolver.context(), resolver.dir()); +} + +enum AppendRunBehavior { AppendingFakeRun, AppendingRunsForObject }; class IsolateTracker { -public: - explicit IsolateTracker(BidiRunList& runs, unsigned nestedIsolateCount) - : m_nestedIsolateCount(nestedIsolateCount) - , m_haveAddedFakeRunForRootIsolate(false) - , m_runs(runs) - { - } - - void setMidpointStateForRootIsolate(const LineMidpointState& midpointState) - { - m_midpointStateForRootIsolate = midpointState; + public: + explicit IsolateTracker(BidiRunList& runs, + unsigned nestedIsolateCount) + : m_nestedIsolateCount(nestedIsolateCount), + m_haveAddedFakeRunForRootIsolate(false), + m_runs(runs) {} + + void setMidpointStateForRootIsolate(const LineMidpointState& midpointState) { + m_midpointStateForRootIsolate = midpointState; + } + + void enterIsolate() { m_nestedIsolateCount++; } + void exitIsolate() { + ASSERT(m_nestedIsolateCount >= 1); + m_nestedIsolateCount--; + if (!inIsolate()) + m_haveAddedFakeRunForRootIsolate = false; + } + bool inIsolate() const { return m_nestedIsolateCount; } + + // We don't care if we encounter bidi directional overrides. + void embed(WTF::Unicode::Direction, BidiEmbeddingSource) {} + void commitExplicitEmbedding(BidiRunList&) {} + BidiRunList& runs() { return m_runs; } + + void addFakeRunIfNecessary(RenderObject* obj, + unsigned pos, + unsigned end, + InlineBidiResolver& resolver) { + // We only need to add a fake run for a given isolated span once during each + // call to createBidiRunsForLine. We'll be called for every span inside the + // isolated span so we just ignore subsequent calls. We also avoid creating + // a fake run until we hit a child that warrants one, e.g. we skip floats. + if (RenderParagraph::shouldSkipCreatingRunsForObject(obj)) + return; + if (!m_haveAddedFakeRunForRootIsolate) { + BidiRun* run = addPlaceholderRunForIsolatedInline(resolver, obj, pos); + resolver.setMidpointStateForIsolatedRun(run, + m_midpointStateForRootIsolate); + m_haveAddedFakeRunForRootIsolate = true; } + // obj and pos together denote a single position in the inline, from which + // the parsing of the isolate will start. We don't need to mark the end of + // the run because this is implicit: it is either endOfLine or the end of + // the isolate, when we call createBidiRunsForLine it will stop at whichever + // comes first. + } + + private: + unsigned m_nestedIsolateCount; + bool m_haveAddedFakeRunForRootIsolate; + LineMidpointState m_midpointStateForRootIsolate; + BidiRunList& m_runs; +}; - void enterIsolate() { m_nestedIsolateCount++; } - void exitIsolate() - { - ASSERT(m_nestedIsolateCount >= 1); - m_nestedIsolateCount--; - if (!inIsolate()) - m_haveAddedFakeRunForRootIsolate = false; - } - bool inIsolate() const { return m_nestedIsolateCount; } - - // We don't care if we encounter bidi directional overrides. - void embed(WTF::Unicode::Direction, BidiEmbeddingSource) { } - void commitExplicitEmbedding(BidiRunList&) { } - BidiRunList& runs() { return m_runs; } - - void addFakeRunIfNecessary(RenderObject* obj, unsigned pos, unsigned end, InlineBidiResolver& resolver) - { - // We only need to add a fake run for a given isolated span once during each call to createBidiRunsForLine. - // We'll be called for every span inside the isolated span so we just ignore subsequent calls. - // We also avoid creating a fake run until we hit a child that warrants one, e.g. we skip floats. - if (RenderParagraph::shouldSkipCreatingRunsForObject(obj)) - return; - if (!m_haveAddedFakeRunForRootIsolate) { - BidiRun* run = addPlaceholderRunForIsolatedInline(resolver, obj, pos); - resolver.setMidpointStateForIsolatedRun(run, m_midpointStateForRootIsolate); - m_haveAddedFakeRunForRootIsolate = true; - } - // obj and pos together denote a single position in the inline, from which the parsing of the isolate will start. - // We don't need to mark the end of the run because this is implicit: it is either endOfLine or the end of the - // isolate, when we call createBidiRunsForLine it will stop at whichever comes first. +static void inline appendRunObjectIfNecessary(RenderObject* obj, + unsigned start, + unsigned end, + InlineBidiResolver& resolver, + AppendRunBehavior behavior, + IsolateTracker& tracker) { + if (behavior == AppendingFakeRun) + tracker.addFakeRunIfNecessary(obj, start, end, resolver); + else + resolver.runs().addRun(createRun(start, end, obj, resolver)); +} + +static void adjustMidpointsAndAppendRunsForObjectIfNeeded( + RenderObject* obj, + unsigned start, + unsigned end, + InlineBidiResolver& resolver, + AppendRunBehavior behavior, + IsolateTracker& tracker) { + if (start > end || RenderParagraph::shouldSkipCreatingRunsForObject(obj)) + return; + + LineMidpointState& lineMidpointState = resolver.midpointState(); + bool haveNextMidpoint = + (lineMidpointState.currentMidpoint() < lineMidpointState.numMidpoints()); + InlineIterator nextMidpoint; + if (haveNextMidpoint) + nextMidpoint = + lineMidpointState.midpoints()[lineMidpointState.currentMidpoint()]; + if (lineMidpointState.betweenMidpoints()) { + if (!(haveNextMidpoint && nextMidpoint.object() == obj)) + return; + // This is a new start point. Stop ignoring objects and + // adjust our start. + lineMidpointState.setBetweenMidpoints(false); + start = nextMidpoint.offset(); + lineMidpointState.incrementCurrentMidpoint(); + if (start < end) + return adjustMidpointsAndAppendRunsForObjectIfNeeded( + obj, start, end, resolver, behavior, tracker); + } else { + if (!haveNextMidpoint || (obj != nextMidpoint.object())) { + appendRunObjectIfNecessary(obj, start, end, resolver, behavior, tracker); + return; } -private: - unsigned m_nestedIsolateCount; - bool m_haveAddedFakeRunForRootIsolate; - LineMidpointState m_midpointStateForRootIsolate; - BidiRunList& m_runs; -}; - -static void inline appendRunObjectIfNecessary(RenderObject* obj, unsigned start, unsigned end, InlineBidiResolver& resolver, AppendRunBehavior behavior, IsolateTracker& tracker) -{ - if (behavior == AppendingFakeRun) - tracker.addFakeRunIfNecessary(obj, start, end, resolver); - else - resolver.runs().addRun(createRun(start, end, obj, resolver)); -} - -static void adjustMidpointsAndAppendRunsForObjectIfNeeded(RenderObject* obj, unsigned start, unsigned end, InlineBidiResolver& resolver, AppendRunBehavior behavior, IsolateTracker& tracker) -{ - if (start > end || RenderParagraph::shouldSkipCreatingRunsForObject(obj)) - return; - - LineMidpointState& lineMidpointState = resolver.midpointState(); - bool haveNextMidpoint = (lineMidpointState.currentMidpoint() < lineMidpointState.numMidpoints()); - InlineIterator nextMidpoint; - if (haveNextMidpoint) - nextMidpoint = lineMidpointState.midpoints()[lineMidpointState.currentMidpoint()]; - if (lineMidpointState.betweenMidpoints()) { - if (!(haveNextMidpoint && nextMidpoint.object() == obj)) - return; - // This is a new start point. Stop ignoring objects and - // adjust our start. - lineMidpointState.setBetweenMidpoints(false); - start = nextMidpoint.offset(); - lineMidpointState.incrementCurrentMidpoint(); - if (start < end) - return adjustMidpointsAndAppendRunsForObjectIfNeeded(obj, start, end, resolver, behavior, tracker); + // An end midpoint has been encountered within our object. We + // need to go ahead and append a run with our endpoint. + if (nextMidpoint.offset() + 1 <= end) { + lineMidpointState.setBetweenMidpoints(true); + lineMidpointState.incrementCurrentMidpoint(); + if (nextMidpoint.offset() != UINT_MAX) { // UINT_MAX means stop at the + // object and don't nclude any + // of it. + if (nextMidpoint.offset() + 1 > start) + appendRunObjectIfNecessary(obj, start, nextMidpoint.offset() + 1, + resolver, behavior, tracker); + return adjustMidpointsAndAppendRunsForObjectIfNeeded( + obj, nextMidpoint.offset() + 1, end, resolver, behavior, tracker); + } } else { - if (!haveNextMidpoint || (obj != nextMidpoint.object())) { - appendRunObjectIfNecessary(obj, start, end, resolver, behavior, tracker); - return; - } - - // An end midpoint has been encountered within our object. We - // need to go ahead and append a run with our endpoint. - if (nextMidpoint.offset() + 1 <= end) { - lineMidpointState.setBetweenMidpoints(true); - lineMidpointState.incrementCurrentMidpoint(); - if (nextMidpoint.offset() != UINT_MAX) { // UINT_MAX means stop at the object and don't nclude any of it. - if (nextMidpoint.offset() + 1 > start) - appendRunObjectIfNecessary(obj, start, nextMidpoint.offset() + 1, resolver, behavior, tracker); - return adjustMidpointsAndAppendRunsForObjectIfNeeded(obj, nextMidpoint.offset() + 1, end, resolver, behavior, tracker); - } - } else { - appendRunObjectIfNecessary(obj, start, end, resolver, behavior, tracker); - } + appendRunObjectIfNecessary(obj, start, end, resolver, behavior, tracker); } + } } -static inline void addFakeRunIfNecessary(RenderObject* obj, unsigned start, unsigned end, InlineBidiResolver& resolver, IsolateTracker& tracker) -{ - tracker.setMidpointStateForRootIsolate(resolver.midpointState()); - adjustMidpointsAndAppendRunsForObjectIfNeeded(obj, start, obj->length(), resolver, AppendingFakeRun, tracker); +static inline void addFakeRunIfNecessary(RenderObject* obj, + unsigned start, + unsigned end, + InlineBidiResolver& resolver, + IsolateTracker& tracker) { + tracker.setMidpointStateForRootIsolate(resolver.midpointState()); + adjustMidpointsAndAppendRunsForObjectIfNeeded( + obj, start, obj->length(), resolver, AppendingFakeRun, tracker); } template <> -inline void InlineBidiResolver::appendRun(BidiRunList& runs) -{ - if (!m_emptyRun && !m_eor.atEnd() && !m_reachedEndOfLine) { - // Keep track of when we enter/leave "unicode-bidi: isolate" inlines. - // Initialize our state depending on if we're starting in the middle of such an inline. - // FIXME: Could this initialize from this->inIsolate() instead of walking up the render tree? - IsolateTracker isolateTracker(runs, numberOfIsolateAncestors(m_sor)); - int start = m_sor.offset(); - RenderObject* obj = m_sor.object(); - while (obj && obj != m_eor.object() && obj != m_endOfRunAtEndOfLine.object()) { - if (isolateTracker.inIsolate()) - addFakeRunIfNecessary(obj, start, obj->length(), *this, isolateTracker); - else - adjustMidpointsAndAppendRunsForObjectIfNeeded(obj, start, obj->length(), *this, AppendingRunsForObject, isolateTracker); - // FIXME: start/obj should be an InlineIterator instead of two separate variables. - start = 0; - obj = bidiNextSkippingEmptyInlines(m_sor.root(), obj, &isolateTracker); - } - bool isEndOfLine = obj == m_endOfLine.object() && !m_endOfLine.offset(); - if (obj && !isEndOfLine) { - unsigned pos = obj == m_eor.object() ? m_eor.offset() : INT_MAX; - if (obj == m_endOfRunAtEndOfLine.object() && m_endOfRunAtEndOfLine.offset() <= pos) { - m_reachedEndOfLine = true; - pos = m_endOfRunAtEndOfLine.offset(); - } - // It's OK to add runs for zero-length RenderObjects, just don't make the run larger than it should be - int end = obj->length() ? pos + 1 : 0; - if (isolateTracker.inIsolate()) - addFakeRunIfNecessary(obj, start, end, *this, isolateTracker); - else - adjustMidpointsAndAppendRunsForObjectIfNeeded(obj, start, end, *this, AppendingRunsForObject, isolateTracker); - } - - if (isEndOfLine) - m_reachedEndOfLine = true; - // If isolateTrack is inIsolate, the next |start of run| can not be the current isolated renderer. - if (isolateTracker.inIsolate()) - m_eor.moveTo(bidiNextSkippingEmptyInlines(m_eor.root(), m_eor.object()), 0); - else - m_eor.increment(); - m_sor = m_eor; +inline void InlineBidiResolver::appendRun(BidiRunList& runs) { + if (!m_emptyRun && !m_eor.atEnd() && !m_reachedEndOfLine) { + // Keep track of when we enter/leave "unicode-bidi: isolate" inlines. + // Initialize our state depending on if we're starting in the middle of such + // an inline. + // FIXME: Could this initialize from this->inIsolate() instead of walking up + // the render tree? + IsolateTracker isolateTracker(runs, numberOfIsolateAncestors(m_sor)); + int start = m_sor.offset(); + RenderObject* obj = m_sor.object(); + while (obj && obj != m_eor.object() && + obj != m_endOfRunAtEndOfLine.object()) { + if (isolateTracker.inIsolate()) + addFakeRunIfNecessary(obj, start, obj->length(), *this, isolateTracker); + else + adjustMidpointsAndAppendRunsForObjectIfNeeded( + obj, start, obj->length(), *this, AppendingRunsForObject, + isolateTracker); + // FIXME: start/obj should be an InlineIterator instead of two separate + // variables. + start = 0; + obj = bidiNextSkippingEmptyInlines(m_sor.root(), obj, &isolateTracker); + } + bool isEndOfLine = obj == m_endOfLine.object() && !m_endOfLine.offset(); + if (obj && !isEndOfLine) { + unsigned pos = obj == m_eor.object() ? m_eor.offset() : INT_MAX; + if (obj == m_endOfRunAtEndOfLine.object() && + m_endOfRunAtEndOfLine.offset() <= pos) { + m_reachedEndOfLine = true; + pos = m_endOfRunAtEndOfLine.offset(); + } + // It's OK to add runs for zero-length RenderObjects, just don't make the + // run larger than it should be + int end = obj->length() ? pos + 1 : 0; + if (isolateTracker.inIsolate()) + addFakeRunIfNecessary(obj, start, end, *this, isolateTracker); + else + adjustMidpointsAndAppendRunsForObjectIfNeeded( + obj, start, end, *this, AppendingRunsForObject, isolateTracker); } - m_direction = WTF::Unicode::OtherNeutral; - m_status.eor = WTF::Unicode::OtherNeutral; -} + if (isEndOfLine) + m_reachedEndOfLine = true; + // If isolateTrack is inIsolate, the next |start of run| can not be the + // current isolated renderer. + if (isolateTracker.inIsolate()) + m_eor.moveTo(bidiNextSkippingEmptyInlines(m_eor.root(), m_eor.object()), + 0); + else + m_eor.increment(); + m_sor = m_eor; + } + m_direction = WTF::Unicode::OtherNeutral; + m_status.eor = WTF::Unicode::OtherNeutral; } +} // namespace blink + #endif // SKY_ENGINE_CORE_RENDERING_INLINEITERATOR_H_ diff --git a/sky/engine/core/rendering/InlineTextBox.cpp b/sky/engine/core/rendering/InlineTextBox.cpp index 0d72f8474c1c7..e2df5206846b9 100644 --- a/sky/engine/core/rendering/InlineTextBox.cpp +++ b/sky/engine/core/rendering/InlineTextBox.cpp @@ -1,7 +1,8 @@ /* * (C) 1999 Lars Knoll (knoll@kde.org) * (C) 2000 Dirk Mueller (mueller@kde.org) - * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All + * rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -43,625 +44,702 @@ namespace blink { struct SameSizeAsInlineTextBox : public InlineBox { - unsigned variables[1]; - unsigned short variables2[2]; - void* pointers[3]; + unsigned variables[1]; + unsigned short variables2[2]; + void* pointers[3]; }; -COMPILE_ASSERT(sizeof(InlineTextBox) == sizeof(SameSizeAsInlineTextBox), InlineTextBox_should_stay_small); +COMPILE_ASSERT(sizeof(InlineTextBox) == sizeof(SameSizeAsInlineTextBox), + InlineTextBox_should_stay_small); typedef WTF::HashMap InlineTextBoxOverflowMap; static InlineTextBoxOverflowMap* gTextBoxesWithOverflow; -void InlineTextBox::destroy() -{ - if (!knownToHaveNoOverflow() && gTextBoxesWithOverflow) - gTextBoxesWithOverflow->remove(this); - InlineBox::destroy(); +void InlineTextBox::destroy() { + if (!knownToHaveNoOverflow() && gTextBoxesWithOverflow) + gTextBoxesWithOverflow->remove(this); + InlineBox::destroy(); } -void InlineTextBox::markDirty() -{ - m_len = 0; - m_start = 0; - InlineBox::markDirty(); +void InlineTextBox::markDirty() { + m_len = 0; + m_start = 0; + InlineBox::markDirty(); } -LayoutRect InlineTextBox::logicalOverflowRect() const -{ - if (knownToHaveNoOverflow() || !gTextBoxesWithOverflow) - return enclosingIntRect(logicalFrameRect()); - return gTextBoxesWithOverflow->get(this); +LayoutRect InlineTextBox::logicalOverflowRect() const { + if (knownToHaveNoOverflow() || !gTextBoxesWithOverflow) + return enclosingIntRect(logicalFrameRect()); + return gTextBoxesWithOverflow->get(this); } -void InlineTextBox::setLogicalOverflowRect(const LayoutRect& rect) -{ - ASSERT(!knownToHaveNoOverflow()); - if (!gTextBoxesWithOverflow) - gTextBoxesWithOverflow = new InlineTextBoxOverflowMap; - gTextBoxesWithOverflow->add(this, rect); +void InlineTextBox::setLogicalOverflowRect(const LayoutRect& rect) { + ASSERT(!knownToHaveNoOverflow()); + if (!gTextBoxesWithOverflow) + gTextBoxesWithOverflow = new InlineTextBoxOverflowMap; + gTextBoxesWithOverflow->add(this, rect); } -int InlineTextBox::baselinePosition(FontBaseline baselineType) const -{ - if (!isText() || !parent()) - return 0; - if (parent()->renderer() == renderer().parent()) - return parent()->baselinePosition(baselineType); - return toRenderBoxModelObject(renderer().parent())->baselinePosition(baselineType, isFirstLineStyle(), HorizontalLine, PositionOnContainingLine); +int InlineTextBox::baselinePosition(FontBaseline baselineType) const { + if (!isText() || !parent()) + return 0; + if (parent()->renderer() == renderer().parent()) + return parent()->baselinePosition(baselineType); + return toRenderBoxModelObject(renderer().parent()) + ->baselinePosition(baselineType, isFirstLineStyle(), HorizontalLine, + PositionOnContainingLine); } -LayoutUnit InlineTextBox::lineHeight() const -{ - if (!isText() || !renderer().parent()) - return 0; - if (parent()->renderer() == renderer().parent()) - return parent()->lineHeight(); - return toRenderBoxModelObject(renderer().parent())->lineHeight(isFirstLineStyle(), HorizontalLine, PositionOnContainingLine); +LayoutUnit InlineTextBox::lineHeight() const { + if (!isText() || !renderer().parent()) + return 0; + if (parent()->renderer() == renderer().parent()) + return parent()->lineHeight(); + return toRenderBoxModelObject(renderer().parent()) + ->lineHeight(isFirstLineStyle(), HorizontalLine, + PositionOnContainingLine); } -LayoutUnit InlineTextBox::selectionTop() -{ - return root().selectionTop(); +LayoutUnit InlineTextBox::selectionTop() { + return root().selectionTop(); } -LayoutUnit InlineTextBox::selectionBottom() -{ - return root().selectionBottom(); +LayoutUnit InlineTextBox::selectionBottom() { + return root().selectionBottom(); } -LayoutUnit InlineTextBox::selectionHeight() -{ - return root().selectionHeight(); +LayoutUnit InlineTextBox::selectionHeight() { + return root().selectionHeight(); } -bool InlineTextBox::isSelected(int startPos, int endPos) const -{ - int sPos = std::max(startPos - m_start, 0); - // The position after a hard line break is considered to be past its end. - // See the corresponding code in InlineTextBox::selectionState. - int ePos = std::min(endPos - m_start, int(m_len) + (isLineBreak() ? 0 : 1)); - return (sPos < ePos); +bool InlineTextBox::isSelected(int startPos, int endPos) const { + int sPos = std::max(startPos - m_start, 0); + // The position after a hard line break is considered to be past its end. + // See the corresponding code in InlineTextBox::selectionState. + int ePos = std::min(endPos - m_start, int(m_len) + (isLineBreak() ? 0 : 1)); + return (sPos < ePos); } -RenderObject::SelectionState InlineTextBox::selectionState() -{ - RenderObject::SelectionState state = renderer().selectionState(); - if (state == RenderObject::SelectionStart || state == RenderObject::SelectionEnd || state == RenderObject::SelectionBoth) { - int startPos, endPos; - renderer().selectionStartEnd(startPos, endPos); - // The position after a hard line break is considered to be past its end. - // See the corresponding code in InlineTextBox::isSelected. - int lastSelectable = start() + len() - (isLineBreak() ? 1 : 0); - - // FIXME: Remove -webkit-line-break: LineBreakAfterWhiteSpace. - int endOfLineAdjustmentForCSSLineBreak = renderer().style()->lineBreak() == LineBreakAfterWhiteSpace ? -1 : 0; - bool start = (state != RenderObject::SelectionEnd && startPos >= m_start && startPos <= m_start + m_len + endOfLineAdjustmentForCSSLineBreak); - bool end = (state != RenderObject::SelectionStart && endPos > m_start && endPos <= lastSelectable); - if (start && end) - state = RenderObject::SelectionBoth; - else if (start) - state = RenderObject::SelectionStart; - else if (end) - state = RenderObject::SelectionEnd; - else if ((state == RenderObject::SelectionEnd || startPos < m_start) && - (state == RenderObject::SelectionStart || endPos > lastSelectable)) - state = RenderObject::SelectionInside; - else if (state == RenderObject::SelectionBoth) - state = RenderObject::SelectionNone; - } - - return state; +RenderObject::SelectionState InlineTextBox::selectionState() { + RenderObject::SelectionState state = renderer().selectionState(); + if (state == RenderObject::SelectionStart || + state == RenderObject::SelectionEnd || + state == RenderObject::SelectionBoth) { + int startPos, endPos; + renderer().selectionStartEnd(startPos, endPos); + // The position after a hard line break is considered to be past its end. + // See the corresponding code in InlineTextBox::isSelected. + int lastSelectable = start() + len() - (isLineBreak() ? 1 : 0); + + // FIXME: Remove -webkit-line-break: LineBreakAfterWhiteSpace. + int endOfLineAdjustmentForCSSLineBreak = + renderer().style()->lineBreak() == LineBreakAfterWhiteSpace ? -1 : 0; + bool start = + (state != RenderObject::SelectionEnd && startPos >= m_start && + startPos <= m_start + m_len + endOfLineAdjustmentForCSSLineBreak); + bool end = (state != RenderObject::SelectionStart && endPos > m_start && + endPos <= lastSelectable); + if (start && end) + state = RenderObject::SelectionBoth; + else if (start) + state = RenderObject::SelectionStart; + else if (end) + state = RenderObject::SelectionEnd; + else if ((state == RenderObject::SelectionEnd || startPos < m_start) && + (state == RenderObject::SelectionStart || endPos > lastSelectable)) + state = RenderObject::SelectionInside; + else if (state == RenderObject::SelectionBoth) + state = RenderObject::SelectionNone; + } + + return state; } -LayoutRect InlineTextBox::localSelectionRect(int startPos, int endPos) -{ - int sPos = std::max(startPos - m_start, 0); - int ePos = std::min(endPos - m_start, (int)m_len); - - if (sPos > ePos) - return LayoutRect(); - - FontCachePurgePreventer fontCachePurgePreventer; - - LayoutUnit selTop = selectionTop(); - LayoutUnit selHeight = selectionHeight(); - RenderStyle* styleToUse = renderer().style(isFirstLineStyle()); - const Font& font = styleToUse->font(); - - StringBuilder charactersWithHyphen; - bool respectHyphen = ePos == m_len && hasHyphen(); - TextRun textRun = constructTextRun(styleToUse, font, respectHyphen ? &charactersWithHyphen : 0); - - FloatPoint startingPoint = FloatPoint(logicalLeft(), selTop.toFloat()); - LayoutRect r; - if (sPos || ePos != static_cast(m_len)) - r = enclosingIntRect(font.selectionRectForText(textRun, startingPoint, selHeight, sPos, ePos)); - else // Avoid computing the font width when the entire line box is selected as an optimization. - r = enclosingIntRect(FloatRect(startingPoint, FloatSize(m_logicalWidth, selHeight.toFloat()))); - - LayoutUnit logicalWidth = r.width(); - if (r.x() > logicalRight()) - logicalWidth = 0; - else if (r.maxX() > logicalRight()) - logicalWidth = logicalRight() - r.x(); - - LayoutPoint topPoint = LayoutPoint(r.x(), selTop); - return LayoutRect(topPoint, LayoutSize(logicalWidth, selHeight)); +LayoutRect InlineTextBox::localSelectionRect(int startPos, int endPos) { + int sPos = std::max(startPos - m_start, 0); + int ePos = std::min(endPos - m_start, (int)m_len); + + if (sPos > ePos) + return LayoutRect(); + + FontCachePurgePreventer fontCachePurgePreventer; + + LayoutUnit selTop = selectionTop(); + LayoutUnit selHeight = selectionHeight(); + RenderStyle* styleToUse = renderer().style(isFirstLineStyle()); + const Font& font = styleToUse->font(); + + StringBuilder charactersWithHyphen; + bool respectHyphen = ePos == m_len && hasHyphen(); + TextRun textRun = constructTextRun(styleToUse, font, + respectHyphen ? &charactersWithHyphen : 0); + + FloatPoint startingPoint = FloatPoint(logicalLeft(), selTop.toFloat()); + LayoutRect r; + if (sPos || ePos != static_cast(m_len)) + r = enclosingIntRect(font.selectionRectForText(textRun, startingPoint, + selHeight, sPos, ePos)); + else // Avoid computing the font width when the entire line box is selected + // as an optimization. + r = enclosingIntRect(FloatRect( + startingPoint, FloatSize(m_logicalWidth, selHeight.toFloat()))); + + LayoutUnit logicalWidth = r.width(); + if (r.x() > logicalRight()) + logicalWidth = 0; + else if (r.maxX() > logicalRight()) + logicalWidth = logicalRight() - r.x(); + + LayoutPoint topPoint = LayoutPoint(r.x(), selTop); + return LayoutRect(topPoint, LayoutSize(logicalWidth, selHeight)); } -void InlineTextBox::deleteLine() -{ - renderer().removeTextBox(this); - destroy(); +void InlineTextBox::deleteLine() { + renderer().removeTextBox(this); + destroy(); } -void InlineTextBox::extractLine() -{ - if (extracted()) - return; +void InlineTextBox::extractLine() { + if (extracted()) + return; - renderer().extractTextBox(this); + renderer().extractTextBox(this); } -void InlineTextBox::attachLine() -{ - if (!extracted()) - return; +void InlineTextBox::attachLine() { + if (!extracted()) + return; - renderer().attachTextBox(this); + renderer().attachTextBox(this); } -bool InlineTextBox::isLineBreak() const -{ - return renderer().style()->preserveNewline() && len() == 1 && (*renderer().text().impl())[start()] == '\n'; +bool InlineTextBox::isLineBreak() const { + return renderer().style()->preserveNewline() && len() == 1 && + (*renderer().text().impl())[start()] == '\n'; } -bool InlineTextBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, LayoutUnit /* lineTop */, LayoutUnit /*lineBottom*/) -{ - if (isLineBreak()) - return false; - - FloatPoint boxOrigin = locationIncludingFlipping(); - boxOrigin.moveBy(accumulatedOffset); - FloatRect rect(boxOrigin, size()); - if (m_truncation != cFullTruncation && visibleToHitTestRequest(request) && locationInContainer.intersects(rect)) { - renderer().updateHitTestResult(result, locationInContainer.point() - toLayoutSize(accumulatedOffset)); - return true; - } +bool InlineTextBox::nodeAtPoint(const HitTestRequest& request, + HitTestResult& result, + const HitTestLocation& locationInContainer, + const LayoutPoint& accumulatedOffset, + LayoutUnit /* lineTop */, + LayoutUnit /*lineBottom*/) { + if (isLineBreak()) return false; -} -bool InlineTextBox::getEmphasisMarkPosition(RenderStyle* style, TextEmphasisPosition& emphasisPosition) const -{ - if (style->textEmphasisMark() == TextEmphasisMarkNone) - return false; - // FIXME(sky): remove this function, it was for ruby. - emphasisPosition = style->textEmphasisPosition(); + FloatPoint boxOrigin = locationIncludingFlipping(); + boxOrigin.moveBy(accumulatedOffset); + FloatRect rect(boxOrigin, size()); + if (m_truncation != cFullTruncation && visibleToHitTestRequest(request) && + locationInContainer.intersects(rect)) { + renderer().updateHitTestResult( + result, locationInContainer.point() - toLayoutSize(accumulatedOffset)); return true; + } + return false; +} + +bool InlineTextBox::getEmphasisMarkPosition( + RenderStyle* style, + TextEmphasisPosition& emphasisPosition) const { + if (style->textEmphasisMark() == TextEmphasisMarkNone) + return false; + // FIXME(sky): remove this function, it was for ruby. + emphasisPosition = style->textEmphasisPosition(); + return true; } namespace { struct TextPaintingStyle { - Color fillColor; - Color strokeColor; - Color emphasisMarkColor; - float strokeWidth; - const ShadowList* shadow; - - bool operator==(const TextPaintingStyle& other) - { - return fillColor == other.fillColor - && strokeColor == other.strokeColor - && emphasisMarkColor == other.emphasisMarkColor - && strokeWidth == other.strokeWidth - && shadow == other.shadow; - } - bool operator!=(const TextPaintingStyle& other) { return !(*this == other); } + Color fillColor; + Color strokeColor; + Color emphasisMarkColor; + float strokeWidth; + const ShadowList* shadow; + + bool operator==(const TextPaintingStyle& other) { + return fillColor == other.fillColor && strokeColor == other.strokeColor && + emphasisMarkColor == other.emphasisMarkColor && + strokeWidth == other.strokeWidth && shadow == other.shadow; + } + bool operator!=(const TextPaintingStyle& other) { return !(*this == other); } }; -TextPaintingStyle textPaintingStyle(RenderText& renderer, RenderStyle* style) -{ - TextPaintingStyle textStyle; - textStyle.fillColor = style->resolveColor(style->textFillColor()); - textStyle.strokeColor = style->resolveColor(style->textStrokeColor()); - textStyle.emphasisMarkColor = style->resolveColor(style->textEmphasisColor()); - textStyle.strokeWidth = style->textStrokeWidth(); - textStyle.shadow = style->textShadow(); - return textStyle; +TextPaintingStyle textPaintingStyle(RenderText& renderer, RenderStyle* style) { + TextPaintingStyle textStyle; + textStyle.fillColor = style->resolveColor(style->textFillColor()); + textStyle.strokeColor = style->resolveColor(style->textStrokeColor()); + textStyle.emphasisMarkColor = style->resolveColor(style->textEmphasisColor()); + textStyle.strokeWidth = style->textStrokeWidth(); + textStyle.shadow = style->textShadow(); + return textStyle; } -TextPaintingStyle selectionPaintingStyle(RenderText& renderer, bool haveSelection, const TextPaintingStyle& textStyle) -{ - TextPaintingStyle selectionStyle = textStyle; +TextPaintingStyle selectionPaintingStyle(RenderText& renderer, + bool haveSelection, + const TextPaintingStyle& textStyle) { + TextPaintingStyle selectionStyle = textStyle; - if (haveSelection) { - selectionStyle.fillColor = renderer.selectionForegroundColor(); - selectionStyle.emphasisMarkColor = renderer.selectionEmphasisMarkColor(); - } + if (haveSelection) { + selectionStyle.fillColor = renderer.selectionForegroundColor(); + selectionStyle.emphasisMarkColor = renderer.selectionEmphasisMarkColor(); + } - return selectionStyle; + return selectionStyle; } -void updateGraphicsContext(GraphicsContext* context, const TextPaintingStyle& textStyle, GraphicsContextStateSaver& stateSaver) -{ - TextDrawingModeFlags mode = context->textDrawingMode(); - if (textStyle.strokeWidth > 0) { - TextDrawingModeFlags newMode = mode | TextModeStroke; - if (mode != newMode) { - if (!stateSaver.saved()) - stateSaver.save(); - context->setTextDrawingMode(newMode); - mode = newMode; - } - } - - if (mode & TextModeFill && textStyle.fillColor != context->fillColor()) - context->setFillColor(textStyle.fillColor); - - if (mode & TextModeStroke) { - if (textStyle.strokeColor != context->strokeColor()) - context->setStrokeColor(textStyle.strokeColor); - if (textStyle.strokeWidth != context->strokeThickness()) - context->setStrokeThickness(textStyle.strokeWidth); - } - - if (textStyle.shadow) { - if (!stateSaver.saved()) - stateSaver.save(); - context->setDrawLooper(textStyle.shadow->createDrawLooper(DrawLooperBuilder::ShadowIgnoresAlpha)); +void updateGraphicsContext(GraphicsContext* context, + const TextPaintingStyle& textStyle, + GraphicsContextStateSaver& stateSaver) { + TextDrawingModeFlags mode = context->textDrawingMode(); + if (textStyle.strokeWidth > 0) { + TextDrawingModeFlags newMode = mode | TextModeStroke; + if (mode != newMode) { + if (!stateSaver.saved()) + stateSaver.save(); + context->setTextDrawingMode(newMode); + mode = newMode; } + } + + if (mode & TextModeFill && textStyle.fillColor != context->fillColor()) + context->setFillColor(textStyle.fillColor); + + if (mode & TextModeStroke) { + if (textStyle.strokeColor != context->strokeColor()) + context->setStrokeColor(textStyle.strokeColor); + if (textStyle.strokeWidth != context->strokeThickness()) + context->setStrokeThickness(textStyle.strokeWidth); + } + + if (textStyle.shadow) { + if (!stateSaver.saved()) + stateSaver.save(); + context->setDrawLooper(textStyle.shadow->createDrawLooper( + DrawLooperBuilder::ShadowIgnoresAlpha)); + } } void paintText(GraphicsContext* context, - const Font& font, const TextRun& textRun, - const AtomicString& emphasisMark, int emphasisMarkOffset, - int startOffset, int endOffset, int truncationPoint, - const FloatPoint& textOrigin, const FloatRect& boxRect, - TextBlobPtr* cachedTextBlob = 0) -{ - TextRunPaintInfo textRunPaintInfo(textRun); - textRunPaintInfo.bounds = boxRect; - if (startOffset <= endOffset) { - textRunPaintInfo.from = startOffset; - textRunPaintInfo.to = endOffset; - // FIXME: We should be able to use cachedTextBlob in more cases. - textRunPaintInfo.cachedTextBlob = cachedTextBlob; - if (emphasisMark.isEmpty()) - context->drawText(font, textRunPaintInfo, textOrigin); - else - context->drawEmphasisMarks(font, textRunPaintInfo, emphasisMark, textOrigin + IntSize(0, emphasisMarkOffset)); - } else { - if (endOffset > 0) { - textRunPaintInfo.from = 0; - textRunPaintInfo.to = endOffset; - if (emphasisMark.isEmpty()) - context->drawText(font, textRunPaintInfo, textOrigin); - else - context->drawEmphasisMarks(font, textRunPaintInfo, emphasisMark, textOrigin + IntSize(0, emphasisMarkOffset)); - } - if (startOffset < truncationPoint) { - textRunPaintInfo.from = startOffset; - textRunPaintInfo.to = truncationPoint; - if (emphasisMark.isEmpty()) - context->drawText(font, textRunPaintInfo, textOrigin); - else - context->drawEmphasisMarks(font, textRunPaintInfo, emphasisMark, textOrigin + IntSize(0, emphasisMarkOffset)); - } + const Font& font, + const TextRun& textRun, + const AtomicString& emphasisMark, + int emphasisMarkOffset, + int startOffset, + int endOffset, + int truncationPoint, + const FloatPoint& textOrigin, + const FloatRect& boxRect, + TextBlobPtr* cachedTextBlob = 0) { + TextRunPaintInfo textRunPaintInfo(textRun); + textRunPaintInfo.bounds = boxRect; + if (startOffset <= endOffset) { + textRunPaintInfo.from = startOffset; + textRunPaintInfo.to = endOffset; + // FIXME: We should be able to use cachedTextBlob in more cases. + textRunPaintInfo.cachedTextBlob = cachedTextBlob; + if (emphasisMark.isEmpty()) + context->drawText(font, textRunPaintInfo, textOrigin); + else + context->drawEmphasisMarks(font, textRunPaintInfo, emphasisMark, + textOrigin + IntSize(0, emphasisMarkOffset)); + } else { + if (endOffset > 0) { + textRunPaintInfo.from = 0; + textRunPaintInfo.to = endOffset; + if (emphasisMark.isEmpty()) + context->drawText(font, textRunPaintInfo, textOrigin); + else + context->drawEmphasisMarks(font, textRunPaintInfo, emphasisMark, + textOrigin + IntSize(0, emphasisMarkOffset)); + } + if (startOffset < truncationPoint) { + textRunPaintInfo.from = startOffset; + textRunPaintInfo.to = truncationPoint; + if (emphasisMark.isEmpty()) + context->drawText(font, textRunPaintInfo, textOrigin); + else + context->drawEmphasisMarks(font, textRunPaintInfo, emphasisMark, + textOrigin + IntSize(0, emphasisMarkOffset)); } + } } inline void paintEmphasisMark(GraphicsContext* context, - const AtomicString& emphasisMark, int emphasisMarkOffset, - int startOffset, int endOffset, int paintRunLength, - const Font& font, const TextRun& textRun, - const FloatPoint& textOrigin, const FloatRect& boxRect) -{ - ASSERT(!emphasisMark.isEmpty()); - paintText(context, font, textRun, emphasisMark, emphasisMarkOffset, startOffset, endOffset, paintRunLength, textOrigin, boxRect); + const AtomicString& emphasisMark, + int emphasisMarkOffset, + int startOffset, + int endOffset, + int paintRunLength, + const Font& font, + const TextRun& textRun, + const FloatPoint& textOrigin, + const FloatRect& boxRect) { + ASSERT(!emphasisMark.isEmpty()); + paintText(context, font, textRun, emphasisMark, emphasisMarkOffset, + startOffset, endOffset, paintRunLength, textOrigin, boxRect); } -void paintTextWithEmphasisMark( - GraphicsContext* context, const Font& font, const TextPaintingStyle& textStyle, const TextRun& textRun, - const AtomicString& emphasisMark, int emphasisMarkOffset, int startOffset, int endOffset, int length, - const FloatPoint& textOrigin, const FloatRect& boxRect, TextBlobPtr* cachedTextBlob = 0) -{ - GraphicsContextStateSaver stateSaver(*context, false); - updateGraphicsContext(context, textStyle, stateSaver); - paintText(context, font, textRun, nullAtom, 0, startOffset, endOffset, length, textOrigin, boxRect, cachedTextBlob); - - if (!emphasisMark.isEmpty()) { - if (textStyle.emphasisMarkColor != textStyle.fillColor) - context->setFillColor(textStyle.emphasisMarkColor); - paintEmphasisMark(context, emphasisMark, emphasisMarkOffset, startOffset, endOffset, length, font, textRun, textOrigin, boxRect); - } +void paintTextWithEmphasisMark(GraphicsContext* context, + const Font& font, + const TextPaintingStyle& textStyle, + const TextRun& textRun, + const AtomicString& emphasisMark, + int emphasisMarkOffset, + int startOffset, + int endOffset, + int length, + const FloatPoint& textOrigin, + const FloatRect& boxRect, + TextBlobPtr* cachedTextBlob = 0) { + GraphicsContextStateSaver stateSaver(*context, false); + updateGraphicsContext(context, textStyle, stateSaver); + paintText(context, font, textRun, nullAtom, 0, startOffset, endOffset, length, + textOrigin, boxRect, cachedTextBlob); + + if (!emphasisMark.isEmpty()) { + if (textStyle.emphasisMarkColor != textStyle.fillColor) + context->setFillColor(textStyle.emphasisMarkColor); + paintEmphasisMark(context, emphasisMark, emphasisMarkOffset, startOffset, + endOffset, length, font, textRun, textOrigin, boxRect); + } } -} // namespace - -void InlineTextBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit /*lineTop*/, LayoutUnit /*lineBottom*/, Vector& layers) -{ - if (isLineBreak() || m_truncation == cFullTruncation || !m_len) - return; - - LayoutRect logicalVisualOverflow = logicalOverflowRect(); - LayoutUnit logicalStart = logicalVisualOverflow.x() + paintOffset.x(); - LayoutUnit logicalExtent = logicalVisualOverflow.width(); - - LayoutUnit paintEnd = paintInfo.rect.maxX(); - LayoutUnit paintStart = paintInfo.rect.x(); - - // When subpixel font scaling is enabled text runs are positioned at - // subpixel boundaries on the x-axis and thus there is no reason to - // snap the x value. We still round the y-axis to ensure consistent - // line heights. - LayoutPoint adjustedPaintOffset = LayoutPoint(paintOffset.x(), paintOffset.y().round()); - - if (logicalStart >= paintEnd || logicalStart + logicalExtent <= paintStart) - return; - - if (m_truncation != cNoTruncation) { - if (renderer().containingBlock()->style()->isLeftToRightDirection() != isLeftToRightDirection()) { - // Make the visible fragment of text hug the edge closest to the rest of the run by moving the origin - // at which we start drawing text. - // e.g. In the case of LTR text truncated in an RTL Context, the correct behavior is: - // |Hello|CBA| -> |...He|CBA| - // In order to draw the fragment "He" aligned to the right edge of it's box, we need to start drawing - // farther to the right. - // NOTE: WebKit's behavior differs from that of IE which appears to just overlay the ellipsis on top of the - // truncated string i.e. |Hello|CBA| -> |...lo|CBA| - LayoutUnit widthOfVisibleText = renderer().width(m_start, m_truncation, textPos(), isLeftToRightDirection() ? LTR : RTL, isFirstLineStyle()); - LayoutUnit widthOfHiddenText = m_logicalWidth - widthOfVisibleText; - // FIXME: The hit testing logic also needs to take this translation into account. - LayoutSize truncationOffset(isLeftToRightDirection() ? widthOfHiddenText : -widthOfHiddenText, 0); - adjustedPaintOffset.move(truncationOffset); - } - } - - GraphicsContext* context = paintInfo.context; - RenderStyle* styleToUse = renderer().style(isFirstLineStyle()); - - FloatPoint boxOrigin = locationIncludingFlipping(); - boxOrigin.move(adjustedPaintOffset.x().toFloat(), adjustedPaintOffset.y().toFloat()); - FloatRect boxRect(boxOrigin, LayoutSize(logicalWidth(), logicalHeight())); - - bool haveSelection = selectionState() != RenderObject::SelectionNone; - - // Determine text colors. - TextPaintingStyle textStyle = textPaintingStyle(renderer(), styleToUse); - TextPaintingStyle selectionStyle = selectionPaintingStyle(renderer(), haveSelection, textStyle); - bool paintSelectedTextSeparately = textStyle != selectionStyle; - - // Set our font. - const Font& font = styleToUse->font(); - - FloatPoint textOrigin = FloatPoint(boxOrigin.x(), boxOrigin.y() + font.fontMetrics().ascent()); - - // 1. Paint backgrounds behind text if needed. Examples of such backgrounds include selection. - if (haveSelection) - paintSelection(context, boxOrigin, styleToUse, font, selectionStyle.fillColor); - - // 2. Now paint the foreground, including text and decorations like underline/overline (in quirks mode only). - int length = m_len; - int maximumLength; - StringView string = renderer().text().createView(); - if (static_cast(length) != string.length() || m_start) - string.narrow(m_start, length); - maximumLength = renderer().textLength() - m_start; - - StringBuilder charactersWithEllipsis; - if (hasAddedEllipsis()) { - const AtomicString& ellipsis = renderer().containingBlock()->style()->ellipsis(); - charactersWithEllipsis.reserveCapacity(string.length() + ellipsis.length()); - charactersWithEllipsis.append(string); - charactersWithEllipsis.append(ellipsis); - string = charactersWithEllipsis.toString().createView(); - maximumLength = string.length(); - } - - StringBuilder charactersWithHyphen; - TextRun textRun = constructTextRun(styleToUse, font, string, maximumLength, hasHyphen() ? &charactersWithHyphen : 0); - if (hasHyphen() || hasAddedEllipsis()) - length = textRun.length(); - - int sPos = 0; - int ePos = 0; - if (paintSelectedTextSeparately) - selectionStartEnd(sPos, ePos); - - bool respectHyphen = ePos == m_len && hasHyphen(); - if (respectHyphen) - ePos = textRun.length(); - - if (m_truncation != cNoTruncation) { - sPos = std::min(sPos, m_truncation); - ePos = std::min(ePos, m_truncation); - length = m_truncation; - } - - int emphasisMarkOffset = 0; - TextEmphasisPosition emphasisMarkPosition; - bool hasTextEmphasis = getEmphasisMarkPosition(styleToUse, emphasisMarkPosition); - const AtomicString& emphasisMark = hasTextEmphasis ? styleToUse->textEmphasisMarkString() : nullAtom; - if (!emphasisMark.isEmpty()) - emphasisMarkOffset = emphasisMarkPosition == TextEmphasisPositionOver ? -font.fontMetrics().ascent() - font.emphasisMarkDescent(emphasisMark) : font.fontMetrics().descent() + font.emphasisMarkAscent(emphasisMark); - - // FIXME: Truncate right-to-left text correctly. - int startOffset = 0; - int endOffset = length; - if (paintSelectedTextSeparately && ePos > sPos) { - startOffset = ePos; - endOffset = sPos; - } - // FIXME: This cache should probably ultimately be held somewhere else. - // A hashmap is convenient to avoid a memory hit when the - // RuntimeEnabledFeature is off. - bool textBlobIsCacheable = startOffset == 0 && endOffset == length; - TextBlobPtr* cachedTextBlob = textBlobIsCacheable ? &m_cachedTextBlob : nullptr; - paintTextWithEmphasisMark(context, font, textStyle, textRun, emphasisMark, emphasisMarkOffset, startOffset, endOffset, length, textOrigin, boxRect, cachedTextBlob); - - if (paintSelectedTextSeparately && sPos < ePos) { - // paint only the text that is selected - bool textBlobIsCacheable = sPos == 0 && ePos == length; - TextBlobPtr* cachedTextBlob = textBlobIsCacheable ? &m_cachedTextBlob : nullptr; - paintTextWithEmphasisMark(context, font, selectionStyle, textRun, emphasisMark, emphasisMarkOffset, sPos, ePos, length, textOrigin, boxRect, cachedTextBlob); +} // namespace + +void InlineTextBox::paint(PaintInfo& paintInfo, + const LayoutPoint& paintOffset, + LayoutUnit /*lineTop*/, + LayoutUnit /*lineBottom*/, + Vector& layers) { + if (isLineBreak() || m_truncation == cFullTruncation || !m_len) + return; + + LayoutRect logicalVisualOverflow = logicalOverflowRect(); + LayoutUnit logicalStart = logicalVisualOverflow.x() + paintOffset.x(); + LayoutUnit logicalExtent = logicalVisualOverflow.width(); + + LayoutUnit paintEnd = paintInfo.rect.maxX(); + LayoutUnit paintStart = paintInfo.rect.x(); + + // When subpixel font scaling is enabled text runs are positioned at + // subpixel boundaries on the x-axis and thus there is no reason to + // snap the x value. We still round the y-axis to ensure consistent + // line heights. + LayoutPoint adjustedPaintOffset = + LayoutPoint(paintOffset.x(), paintOffset.y().round()); + + if (logicalStart >= paintEnd || logicalStart + logicalExtent <= paintStart) + return; + + if (m_truncation != cNoTruncation) { + if (renderer().containingBlock()->style()->isLeftToRightDirection() != + isLeftToRightDirection()) { + // Make the visible fragment of text hug the edge closest to the rest of + // the run by moving the origin at which we start drawing text. e.g. In + // the case of LTR text truncated in an RTL Context, the correct behavior + // is: |Hello|CBA| -> |...He|CBA| In order to draw the fragment "He" + // aligned to the right edge of it's box, we need to start drawing farther + // to the right. NOTE: WebKit's behavior differs from that of IE which + // appears to just overlay the ellipsis on top of the truncated string + // i.e. |Hello|CBA| -> |...lo|CBA| + LayoutUnit widthOfVisibleText = renderer().width( + m_start, m_truncation, textPos(), + isLeftToRightDirection() ? LTR : RTL, isFirstLineStyle()); + LayoutUnit widthOfHiddenText = m_logicalWidth - widthOfVisibleText; + // FIXME: The hit testing logic also needs to take this translation into + // account. + LayoutSize truncationOffset( + isLeftToRightDirection() ? widthOfHiddenText : -widthOfHiddenText, 0); + adjustedPaintOffset.move(truncationOffset); } + } + + GraphicsContext* context = paintInfo.context; + RenderStyle* styleToUse = renderer().style(isFirstLineStyle()); + + FloatPoint boxOrigin = locationIncludingFlipping(); + boxOrigin.move(adjustedPaintOffset.x().toFloat(), + adjustedPaintOffset.y().toFloat()); + FloatRect boxRect(boxOrigin, LayoutSize(logicalWidth(), logicalHeight())); + + bool haveSelection = selectionState() != RenderObject::SelectionNone; + + // Determine text colors. + TextPaintingStyle textStyle = textPaintingStyle(renderer(), styleToUse); + TextPaintingStyle selectionStyle = + selectionPaintingStyle(renderer(), haveSelection, textStyle); + bool paintSelectedTextSeparately = textStyle != selectionStyle; + + // Set our font. + const Font& font = styleToUse->font(); + + FloatPoint textOrigin = + FloatPoint(boxOrigin.x(), boxOrigin.y() + font.fontMetrics().ascent()); + + // 1. Paint backgrounds behind text if needed. Examples of such backgrounds + // include selection. + if (haveSelection) + paintSelection(context, boxOrigin, styleToUse, font, + selectionStyle.fillColor); + + // 2. Now paint the foreground, including text and decorations like + // underline/overline (in quirks mode only). + int length = m_len; + int maximumLength; + StringView string = renderer().text().createView(); + if (static_cast(length) != string.length() || m_start) + string.narrow(m_start, length); + maximumLength = renderer().textLength() - m_start; + + StringBuilder charactersWithEllipsis; + if (hasAddedEllipsis()) { + const AtomicString& ellipsis = + renderer().containingBlock()->style()->ellipsis(); + charactersWithEllipsis.reserveCapacity(string.length() + ellipsis.length()); + charactersWithEllipsis.append(string); + charactersWithEllipsis.append(ellipsis); + string = charactersWithEllipsis.toString().createView(); + maximumLength = string.length(); + } + + StringBuilder charactersWithHyphen; + TextRun textRun = constructTextRun(styleToUse, font, string, maximumLength, + hasHyphen() ? &charactersWithHyphen : 0); + if (hasHyphen() || hasAddedEllipsis()) + length = textRun.length(); + + int sPos = 0; + int ePos = 0; + if (paintSelectedTextSeparately) + selectionStartEnd(sPos, ePos); - // Paint decorations - TextDecoration textDecorations = styleToUse->textDecorationsInEffect(); - if (textDecorations != TextDecorationNone) { - GraphicsContextStateSaver stateSaver(*context, false); - updateGraphicsContext(context, textStyle, stateSaver); - paintDecoration(context, boxOrigin, textDecorations); - } + bool respectHyphen = ePos == m_len && hasHyphen(); + if (respectHyphen) + ePos = textRun.length(); + + if (m_truncation != cNoTruncation) { + sPos = std::min(sPos, m_truncation); + ePos = std::min(ePos, m_truncation); + length = m_truncation; + } + + int emphasisMarkOffset = 0; + TextEmphasisPosition emphasisMarkPosition; + bool hasTextEmphasis = + getEmphasisMarkPosition(styleToUse, emphasisMarkPosition); + const AtomicString& emphasisMark = + hasTextEmphasis ? styleToUse->textEmphasisMarkString() : nullAtom; + if (!emphasisMark.isEmpty()) + emphasisMarkOffset = emphasisMarkPosition == TextEmphasisPositionOver + ? -font.fontMetrics().ascent() - + font.emphasisMarkDescent(emphasisMark) + : font.fontMetrics().descent() + + font.emphasisMarkAscent(emphasisMark); + + // FIXME: Truncate right-to-left text correctly. + int startOffset = 0; + int endOffset = length; + if (paintSelectedTextSeparately && ePos > sPos) { + startOffset = ePos; + endOffset = sPos; + } + // FIXME: This cache should probably ultimately be held somewhere else. + // A hashmap is convenient to avoid a memory hit when the + // RuntimeEnabledFeature is off. + bool textBlobIsCacheable = startOffset == 0 && endOffset == length; + TextBlobPtr* cachedTextBlob = + textBlobIsCacheable ? &m_cachedTextBlob : nullptr; + paintTextWithEmphasisMark(context, font, textStyle, textRun, emphasisMark, + emphasisMarkOffset, startOffset, endOffset, length, + textOrigin, boxRect, cachedTextBlob); + + if (paintSelectedTextSeparately && sPos < ePos) { + // paint only the text that is selected + bool textBlobIsCacheable = sPos == 0 && ePos == length; + TextBlobPtr* cachedTextBlob = + textBlobIsCacheable ? &m_cachedTextBlob : nullptr; + paintTextWithEmphasisMark(context, font, selectionStyle, textRun, + emphasisMark, emphasisMarkOffset, sPos, ePos, + length, textOrigin, boxRect, cachedTextBlob); + } + + // Paint decorations + TextDecoration textDecorations = styleToUse->textDecorationsInEffect(); + if (textDecorations != TextDecorationNone) { + GraphicsContextStateSaver stateSaver(*context, false); + updateGraphicsContext(context, textStyle, stateSaver); + paintDecoration(context, boxOrigin, textDecorations); + } } -void InlineTextBox::selectionStartEnd(int& sPos, int& ePos) -{ - int startPos, endPos; - if (renderer().selectionState() == RenderObject::SelectionInside) { - startPos = 0; - endPos = renderer().textLength(); - } else { - renderer().selectionStartEnd(startPos, endPos); - if (renderer().selectionState() == RenderObject::SelectionStart) - endPos = renderer().textLength(); - else if (renderer().selectionState() == RenderObject::SelectionEnd) - startPos = 0; - } - - sPos = std::max(startPos - m_start, 0); - ePos = std::min(endPos - m_start, (int)m_len); +void InlineTextBox::selectionStartEnd(int& sPos, int& ePos) { + int startPos, endPos; + if (renderer().selectionState() == RenderObject::SelectionInside) { + startPos = 0; + endPos = renderer().textLength(); + } else { + renderer().selectionStartEnd(startPos, endPos); + if (renderer().selectionState() == RenderObject::SelectionStart) + endPos = renderer().textLength(); + else if (renderer().selectionState() == RenderObject::SelectionEnd) + startPos = 0; + } + + sPos = std::max(startPos - m_start, 0); + ePos = std::min(endPos - m_start, (int)m_len); } -void InlineTextBox::paintSelection(GraphicsContext* context, const FloatPoint& boxOrigin, RenderStyle* style, const Font& font, Color textColor) -{ - // See if we have a selection to paint at all. - int sPos, ePos; - selectionStartEnd(sPos, ePos); - if (sPos >= ePos) - return; - - Color c = renderer().selectionBackgroundColor(); - if (!c.alpha()) - return; - - // If the text color ends up being the same as the selection background, invert the selection - // background. - if (textColor == c) - c = Color(0xff - c.red(), 0xff - c.green(), 0xff - c.blue()); - - // If the text is truncated, let the thing being painted in the truncation - // draw its own highlight. - int length = m_truncation != cNoTruncation ? m_truncation : m_len; - StringView string = renderer().text().createView(); - - if (string.length() != static_cast(length) || m_start) - string.narrow(m_start, length); - - StringBuilder charactersWithHyphen; - bool respectHyphen = ePos == length && hasHyphen(); - TextRun textRun = constructTextRun(style, font, string, renderer().textLength() - m_start, respectHyphen ? &charactersWithHyphen : 0); - if (respectHyphen) - ePos = textRun.length(); - - LayoutUnit selectionBottom = root().selectionBottom(); - LayoutUnit selectionTop = root().selectionTopAdjustedForPrecedingBlock(); - - int deltaY = roundToInt(logicalTop() - selectionTop); - int selHeight = std::max(0, roundToInt(selectionBottom - selectionTop)); - - FloatPoint localOrigin(boxOrigin.x(), boxOrigin.y() - deltaY); - FloatRect clipRect(localOrigin, FloatSize(m_logicalWidth, selHeight)); - - GraphicsContextStateSaver stateSaver(*context); - context->clip(clipRect); - context->drawHighlightForText(font, textRun, localOrigin, selHeight, c, sPos, ePos); +void InlineTextBox::paintSelection(GraphicsContext* context, + const FloatPoint& boxOrigin, + RenderStyle* style, + const Font& font, + Color textColor) { + // See if we have a selection to paint at all. + int sPos, ePos; + selectionStartEnd(sPos, ePos); + if (sPos >= ePos) + return; + + Color c = renderer().selectionBackgroundColor(); + if (!c.alpha()) + return; + + // If the text color ends up being the same as the selection background, + // invert the selection background. + if (textColor == c) + c = Color(0xff - c.red(), 0xff - c.green(), 0xff - c.blue()); + + // If the text is truncated, let the thing being painted in the truncation + // draw its own highlight. + int length = m_truncation != cNoTruncation ? m_truncation : m_len; + StringView string = renderer().text().createView(); + + if (string.length() != static_cast(length) || m_start) + string.narrow(m_start, length); + + StringBuilder charactersWithHyphen; + bool respectHyphen = ePos == length && hasHyphen(); + TextRun textRun = + constructTextRun(style, font, string, renderer().textLength() - m_start, + respectHyphen ? &charactersWithHyphen : 0); + if (respectHyphen) + ePos = textRun.length(); + + LayoutUnit selectionBottom = root().selectionBottom(); + LayoutUnit selectionTop = root().selectionTopAdjustedForPrecedingBlock(); + + int deltaY = roundToInt(logicalTop() - selectionTop); + int selHeight = std::max(0, roundToInt(selectionBottom - selectionTop)); + + FloatPoint localOrigin(boxOrigin.x(), boxOrigin.y() - deltaY); + FloatRect clipRect(localOrigin, FloatSize(m_logicalWidth, selHeight)); + + GraphicsContextStateSaver stateSaver(*context); + context->clip(clipRect); + context->drawHighlightForText(font, textRun, localOrigin, selHeight, c, sPos, + ePos); } -unsigned InlineTextBox::underlinePaintStart(const CompositionUnderline& underline) -{ - return std::max(static_cast(m_start), underline.startOffset); +unsigned InlineTextBox::underlinePaintStart( + const CompositionUnderline& underline) { + return std::max(static_cast(m_start), underline.startOffset); } -unsigned InlineTextBox::underlinePaintEnd(const CompositionUnderline& underline) -{ - unsigned paintEnd = std::min(end() + 1, underline.endOffset); // end() points at the last char, not past it. - if (m_truncation != cNoTruncation) - paintEnd = std::min(paintEnd, static_cast(m_start + m_truncation)); - return paintEnd; +unsigned InlineTextBox::underlinePaintEnd( + const CompositionUnderline& underline) { + unsigned paintEnd = std::min( + end() + 1, + underline.endOffset); // end() points at the last char, not past it. + if (m_truncation != cNoTruncation) + paintEnd = + std::min(paintEnd, static_cast(m_start + m_truncation)); + return paintEnd; } -void InlineTextBox::paintSingleCompositionBackgroundRun(GraphicsContext* context, const FloatPoint& boxOrigin, RenderStyle* style, const Font& font, Color backgroundColor, int startPos, int endPos) -{ - int sPos = std::max(startPos - m_start, 0); - int ePos = std::min(endPos - m_start, static_cast(m_len)); - if (sPos >= ePos) - return; - - int deltaY = logicalTop() - selectionTop(); - int selHeight = selectionHeight(); - FloatPoint localOrigin(boxOrigin.x(), boxOrigin.y() - deltaY); - context->drawHighlightForText(font, constructTextRun(style, font), localOrigin, selHeight, backgroundColor, sPos, ePos); +void InlineTextBox::paintSingleCompositionBackgroundRun( + GraphicsContext* context, + const FloatPoint& boxOrigin, + RenderStyle* style, + const Font& font, + Color backgroundColor, + int startPos, + int endPos) { + int sPos = std::max(startPos - m_start, 0); + int ePos = std::min(endPos - m_start, static_cast(m_len)); + if (sPos >= ePos) + return; + + int deltaY = logicalTop() - selectionTop(); + int selHeight = selectionHeight(); + FloatPoint localOrigin(boxOrigin.x(), boxOrigin.y() - deltaY); + context->drawHighlightForText(font, constructTextRun(style, font), + localOrigin, selHeight, backgroundColor, sPos, + ePos); } -static StrokeStyle textDecorationStyleToStrokeStyle(TextDecorationStyle decorationStyle) -{ - StrokeStyle strokeStyle = SolidStroke; - switch (decorationStyle) { +static StrokeStyle textDecorationStyleToStrokeStyle( + TextDecorationStyle decorationStyle) { + StrokeStyle strokeStyle = SolidStroke; + switch (decorationStyle) { case TextDecorationStyleSolid: - strokeStyle = SolidStroke; - break; + strokeStyle = SolidStroke; + break; case TextDecorationStyleDouble: - strokeStyle = DoubleStroke; - break; + strokeStyle = DoubleStroke; + break; case TextDecorationStyleDotted: - strokeStyle = DottedStroke; - break; + strokeStyle = DottedStroke; + break; case TextDecorationStyleDashed: - strokeStyle = DashedStroke; - break; + strokeStyle = DashedStroke; + break; case TextDecorationStyleWavy: - strokeStyle = WavyStroke; - break; - } + strokeStyle = WavyStroke; + break; + } - return strokeStyle; + return strokeStyle; } -static int computeUnderlineOffset(const TextUnderlinePosition underlinePosition, const FontMetrics& fontMetrics, const InlineTextBox* inlineTextBox, const float textDecorationThickness) -{ - // Compute the gap between the font and the underline. Use at least one - // pixel gap, if underline is thick then use a bigger gap. - int gap = 0; - - // Underline position of zero means draw underline on Baseline Position, - // in Blink we need at least 1-pixel gap to adding following check. - // Positive underline Position means underline should be drawn above baselin e - // and negative value means drawing below baseline, negating the value as in Blink - // downward Y-increases. - - if (fontMetrics.underlinePosition()) - gap = -fontMetrics.underlinePosition(); - else - gap = std::max(1, ceilf(textDecorationThickness / 2.f)); - - // FIXME: We support only horizontal text for now. - switch (underlinePosition) { +static int computeUnderlineOffset(const TextUnderlinePosition underlinePosition, + const FontMetrics& fontMetrics, + const InlineTextBox* inlineTextBox, + const float textDecorationThickness) { + // Compute the gap between the font and the underline. Use at least one + // pixel gap, if underline is thick then use a bigger gap. + int gap = 0; + + // Underline position of zero means draw underline on Baseline Position, + // in Blink we need at least 1-pixel gap to adding following check. + // Positive underline Position means underline should be drawn above baselin e + // and negative value means drawing below baseline, negating the value as in + // Blink downward Y-increases. + + if (fontMetrics.underlinePosition()) + gap = -fontMetrics.underlinePosition(); + else + gap = std::max(1, ceilf(textDecorationThickness / 2.f)); + + // FIXME: We support only horizontal text for now. + switch (underlinePosition) { case TextUnderlinePositionAuto: - return fontMetrics.ascent() + gap; // Position underline near the alphabetic baseline. + return fontMetrics.ascent() + + gap; // Position underline near the alphabetic baseline. case TextUnderlinePositionUnder: { - // Position underline relative to the under edge of the lowest element's content box. - const float offset = inlineTextBox->root().maxLogicalTop() - inlineTextBox->logicalTop(); - if (offset > 0) - return inlineTextBox->logicalHeight() + gap + offset; - return inlineTextBox->logicalHeight() + gap; - } + // Position underline relative to the under edge of the lowest element's + // content box. + const float offset = + inlineTextBox->root().maxLogicalTop() - inlineTextBox->logicalTop(); + if (offset > 0) + return inlineTextBox->logicalHeight() + gap + offset; + return inlineTextBox->logicalHeight() + gap; } + } - ASSERT_NOT_REACHED(); - return fontMetrics.ascent() + gap; + ASSERT_NOT_REACHED(); + return fontMetrics.ascent() + gap; } struct CurveAlongX { @@ -713,388 +791,480 @@ struct CurveAlongY { * was doing everything along the X axis; CurveAlongY just flips the * coordinates around. */ -template static void strokeWavyTextDecorationInternal(GraphicsContext* context, FloatPoint p1, FloatPoint p2, float strokeThickness) -{ - ASSERT(Curve::y(p1) == Curve::y(p2)); // verify that this is indeed axis-aligned - - context->adjustLineToPixelBoundaries(p1, p2, strokeThickness, context->strokeStyle()); - - Path path; - path.moveTo(p1); - - float controlPointDistance = 2 * strokeThickness; - float step = controlPointDistance; - - float yAxis = Curve::y(p1); - float x1; - float x2; - - if (Curve::x(p1) < Curve::x(p2)) { - x1 = Curve::x(p1); - x2 = Curve::x(p2); - } else { - x1 = Curve::x(p2); - x2 = Curve::x(p1); - } - - FloatPoint controlPoint1 = Curve::p(0, yAxis + controlPointDistance); - FloatPoint controlPoint2 = Curve::p(0, yAxis - controlPointDistance); - - float x; - for (x = x1; x + 2 * step <= x2;) { - Curve::setX(controlPoint1, x + step); - Curve::setX(controlPoint2, x + step); - x += 2 * step; - path.addBezierCurveTo(controlPoint1, controlPoint2, Curve::p(x, yAxis)); - } - - if (x < x2) { - Curve::setX(controlPoint1, x + step); - Curve::setX(controlPoint2, x + step); - float xScale = 1.0 / (2 * step); - float yScale = 1.0 / (2 * controlPointDistance); - OwnPtr bezier = adoptPtr(new UnitBezier((Curve::x(controlPoint1) - x) * xScale, - (Curve::y(controlPoint1) - yAxis) * yScale, - (Curve::x(controlPoint2) - x) * xScale, - (Curve::y(controlPoint2) - yAxis) * yScale)); - float t = bezier->solveCurveX((x2 - x) / (2.0 * step), std::numeric_limits::epsilon()); - // following math based on http://stackoverflow.com/a/879213 - float u1 = 1.0 - t; - float qxb = x * u1 * u1 + Curve::x(controlPoint1) * 2 * t * u1 + Curve::x(controlPoint2) * t * t; - float qxd = Curve::x(controlPoint1) * u1 * u1 + Curve::x(controlPoint2) * 2 * t * u1 + (x+step) * t * t; - float qyb = yAxis * u1 * u1 + Curve::y(controlPoint1) * 2 * t * u1 + Curve::y(controlPoint2) * t * t; - float qyd = Curve::y(controlPoint1) * u1 * u1 + Curve::y(controlPoint2) * 2 * t * u1 + yAxis * t * t; - float xb = x * u1 + Curve::x(controlPoint1) * t; - float yb = yAxis * u1 + Curve::y(controlPoint1) * t; - float xc = qxb; - float xd = qxb * u1 + qxd * t; - float yc = qyb; - float yd = qyb * u1 + qyd * t; - path.addBezierCurveTo(Curve::p(xb, yb), Curve::p(xc, yc), Curve::p(xd, yd)); - } - - context->setShouldAntialias(true); - context->strokePath(path); +template +static void strokeWavyTextDecorationInternal(GraphicsContext* context, + FloatPoint p1, + FloatPoint p2, + float strokeThickness) { + ASSERT(Curve::y(p1) == + Curve::y(p2)); // verify that this is indeed axis-aligned + + context->adjustLineToPixelBoundaries(p1, p2, strokeThickness, + context->strokeStyle()); + + Path path; + path.moveTo(p1); + + float controlPointDistance = 2 * strokeThickness; + float step = controlPointDistance; + + float yAxis = Curve::y(p1); + float x1; + float x2; + + if (Curve::x(p1) < Curve::x(p2)) { + x1 = Curve::x(p1); + x2 = Curve::x(p2); + } else { + x1 = Curve::x(p2); + x2 = Curve::x(p1); + } + + FloatPoint controlPoint1 = Curve::p(0, yAxis + controlPointDistance); + FloatPoint controlPoint2 = Curve::p(0, yAxis - controlPointDistance); + + float x; + for (x = x1; x + 2 * step <= x2;) { + Curve::setX(controlPoint1, x + step); + Curve::setX(controlPoint2, x + step); + x += 2 * step; + path.addBezierCurveTo(controlPoint1, controlPoint2, Curve::p(x, yAxis)); + } + + if (x < x2) { + Curve::setX(controlPoint1, x + step); + Curve::setX(controlPoint2, x + step); + float xScale = 1.0 / (2 * step); + float yScale = 1.0 / (2 * controlPointDistance); + OwnPtr bezier = + adoptPtr(new UnitBezier((Curve::x(controlPoint1) - x) * xScale, + (Curve::y(controlPoint1) - yAxis) * yScale, + (Curve::x(controlPoint2) - x) * xScale, + (Curve::y(controlPoint2) - yAxis) * yScale)); + float t = bezier->solveCurveX((x2 - x) / (2.0 * step), + std::numeric_limits::epsilon()); + // following math based on http://stackoverflow.com/a/879213 + float u1 = 1.0 - t; + float qxb = x * u1 * u1 + Curve::x(controlPoint1) * 2 * t * u1 + + Curve::x(controlPoint2) * t * t; + float qxd = Curve::x(controlPoint1) * u1 * u1 + + Curve::x(controlPoint2) * 2 * t * u1 + (x + step) * t * t; + float qyb = yAxis * u1 * u1 + Curve::y(controlPoint1) * 2 * t * u1 + + Curve::y(controlPoint2) * t * t; + float qyd = Curve::y(controlPoint1) * u1 * u1 + + Curve::y(controlPoint2) * 2 * t * u1 + yAxis * t * t; + float xb = x * u1 + Curve::x(controlPoint1) * t; + float yb = yAxis * u1 + Curve::y(controlPoint1) * t; + float xc = qxb; + float xd = qxb * u1 + qxd * t; + float yc = qyb; + float yd = qyb * u1 + qyd * t; + path.addBezierCurveTo(Curve::p(xb, yb), Curve::p(xc, yc), Curve::p(xd, yd)); + } + + context->setShouldAntialias(true); + context->strokePath(path); } -static void strokeWavyTextDecoration(GraphicsContext* context, FloatPoint p1, FloatPoint p2, float strokeThickness) -{ - if (p1.y() == p2.y()) // horizontal line - strokeWavyTextDecorationInternal(context, p1, p2, strokeThickness); - else // vertical line - strokeWavyTextDecorationInternal(context, p1, p2, strokeThickness); +static void strokeWavyTextDecoration(GraphicsContext* context, + FloatPoint p1, + FloatPoint p2, + float strokeThickness) { + if (p1.y() == p2.y()) // horizontal line + strokeWavyTextDecorationInternal(context, p1, p2, + strokeThickness); + else // vertical line + strokeWavyTextDecorationInternal(context, p1, p2, + strokeThickness); } -static bool shouldSetDecorationAntialias(TextDecorationStyle decorationStyle) -{ - return decorationStyle == TextDecorationStyleDotted || decorationStyle == TextDecorationStyleDashed; +static bool shouldSetDecorationAntialias(TextDecorationStyle decorationStyle) { + return decorationStyle == TextDecorationStyleDotted || + decorationStyle == TextDecorationStyleDashed; } -static bool shouldSetDecorationAntialias(TextDecorationStyle underline, TextDecorationStyle overline, TextDecorationStyle linethrough) -{ - return shouldSetDecorationAntialias(underline) || shouldSetDecorationAntialias(overline) || shouldSetDecorationAntialias(linethrough); +static bool shouldSetDecorationAntialias(TextDecorationStyle underline, + TextDecorationStyle overline, + TextDecorationStyle linethrough) { + return shouldSetDecorationAntialias(underline) || + shouldSetDecorationAntialias(overline) || + shouldSetDecorationAntialias(linethrough); } -static void paintAppliedDecoration(GraphicsContext* context, FloatPoint start, float width, float doubleOffset, int wavyOffsetFactor, - RenderObject::AppliedTextDecoration decoration, float thickness, bool antialiasDecoration) -{ - context->setStrokeStyle(textDecorationStyleToStrokeStyle(decoration.style)); - context->setStrokeColor(decoration.color); - - switch (decoration.style) { +static void paintAppliedDecoration( + GraphicsContext* context, + FloatPoint start, + float width, + float doubleOffset, + int wavyOffsetFactor, + RenderObject::AppliedTextDecoration decoration, + float thickness, + bool antialiasDecoration) { + context->setStrokeStyle(textDecorationStyleToStrokeStyle(decoration.style)); + context->setStrokeColor(decoration.color); + + switch (decoration.style) { case TextDecorationStyleWavy: - strokeWavyTextDecoration(context, start + FloatPoint(0, doubleOffset * wavyOffsetFactor), start + FloatPoint(width, doubleOffset * wavyOffsetFactor), thickness); - break; + strokeWavyTextDecoration( + context, start + FloatPoint(0, doubleOffset * wavyOffsetFactor), + start + FloatPoint(width, doubleOffset * wavyOffsetFactor), + thickness); + break; case TextDecorationStyleDotted: case TextDecorationStyleDashed: - context->setShouldAntialias(antialiasDecoration); - // Fall through + context->setShouldAntialias(antialiasDecoration); + // Fall through default: - context->drawLineForText(start, width); + context->drawLineForText(start, width); - if (decoration.style == TextDecorationStyleDouble) - context->drawLineForText(start + FloatPoint(0, doubleOffset), width); - } + if (decoration.style == TextDecorationStyleDouble) + context->drawLineForText(start + FloatPoint(0, doubleOffset), width); + } } -void InlineTextBox::paintDecoration(GraphicsContext* context, const FloatPoint& boxOrigin, TextDecoration deco) -{ - GraphicsContextStateSaver stateSaver(*context); - - if (m_truncation == cFullTruncation) - return; - - FloatPoint localOrigin = boxOrigin; - - float width = m_logicalWidth; - if (m_truncation != cNoTruncation) { - width = renderer().width(m_start, m_truncation, textPos(), isLeftToRightDirection() ? LTR : RTL, isFirstLineStyle()); - if (!isLeftToRightDirection()) - localOrigin.move(m_logicalWidth - width, 0); - } - - // Get the text decoration colors. - RenderObject::AppliedTextDecoration underline, overline, linethrough; - renderer().getTextDecorations(deco, underline, overline, linethrough, true); - if (isFirstLineStyle()) - renderer().getTextDecorations(deco, underline, overline, linethrough, true, true); - - // Use a special function for underlines to get the positioning exactly right. - - RenderStyle* styleToUse = renderer().style(isFirstLineStyle()); - int baseline = styleToUse->fontMetrics().ascent(); - - // Set the thick of the line to be 10% (or something else ?)of the computed font size and not less than 1px. - - // Update Underline thickness, in case we have Faulty Font Metrics calculating underline thickness by old method. - float textDecorationThickness = styleToUse->fontMetrics().underlineThickness(); // TODO(ianh): Make this author-controllable - int fontHeightInt = (int)(styleToUse->fontMetrics().floatHeight() + 0.5); - if ((textDecorationThickness == 0.f) || (textDecorationThickness >= (fontHeightInt >> 1))) - textDecorationThickness = std::max(1.f, styleToUse->computedFontSize() / 10.f); - - context->setStrokeThickness(textDecorationThickness); - - bool antialiasDecoration = shouldSetDecorationAntialias(overline.style, underline.style, linethrough.style) - && RenderBoxModelObject::shouldAntialiasLines(context); - - // Offset between lines - always non-zero, so lines never cross each other. - float doubleOffset = textDecorationThickness + 1.f; - - if (deco & TextDecorationUnderline) { - const int underlineOffset = computeUnderlineOffset(styleToUse->textUnderlinePosition(), styleToUse->fontMetrics(), this, textDecorationThickness); - paintAppliedDecoration(context, localOrigin + FloatPoint(0, underlineOffset), width, doubleOffset, 1, underline, textDecorationThickness, antialiasDecoration); - } - if (deco & TextDecorationOverline) { - paintAppliedDecoration(context, localOrigin, width, -doubleOffset, 1, overline, textDecorationThickness, antialiasDecoration); - } - if (deco & TextDecorationLineThrough) { - const float lineThroughOffset = 2 * baseline / 3; - paintAppliedDecoration(context, localOrigin + FloatPoint(0, lineThroughOffset), width, doubleOffset, 0, linethrough, textDecorationThickness, antialiasDecoration); - } +void InlineTextBox::paintDecoration(GraphicsContext* context, + const FloatPoint& boxOrigin, + TextDecoration deco) { + GraphicsContextStateSaver stateSaver(*context); + + if (m_truncation == cFullTruncation) + return; + + FloatPoint localOrigin = boxOrigin; + + float width = m_logicalWidth; + if (m_truncation != cNoTruncation) { + width = renderer().width(m_start, m_truncation, textPos(), + isLeftToRightDirection() ? LTR : RTL, + isFirstLineStyle()); + if (!isLeftToRightDirection()) + localOrigin.move(m_logicalWidth - width, 0); + } + + // Get the text decoration colors. + RenderObject::AppliedTextDecoration underline, overline, linethrough; + renderer().getTextDecorations(deco, underline, overline, linethrough, true); + if (isFirstLineStyle()) + renderer().getTextDecorations(deco, underline, overline, linethrough, true, + true); + + // Use a special function for underlines to get the positioning exactly right. + + RenderStyle* styleToUse = renderer().style(isFirstLineStyle()); + int baseline = styleToUse->fontMetrics().ascent(); + + // Set the thick of the line to be 10% (or something else ?)of the computed + // font size and not less than 1px. + + // Update Underline thickness, in case we have Faulty Font Metrics calculating + // underline thickness by old method. + float textDecorationThickness = + styleToUse->fontMetrics() + .underlineThickness(); // TODO(ianh): Make this author-controllable + int fontHeightInt = (int)(styleToUse->fontMetrics().floatHeight() + 0.5); + if ((textDecorationThickness == 0.f) || + (textDecorationThickness >= (fontHeightInt >> 1))) + textDecorationThickness = + std::max(1.f, styleToUse->computedFontSize() / 10.f); + + context->setStrokeThickness(textDecorationThickness); + + bool antialiasDecoration = + shouldSetDecorationAntialias(overline.style, underline.style, + linethrough.style) && + RenderBoxModelObject::shouldAntialiasLines(context); + + // Offset between lines - always non-zero, so lines never cross each other. + float doubleOffset = textDecorationThickness + 1.f; + + if (deco & TextDecorationUnderline) { + const int underlineOffset = computeUnderlineOffset( + styleToUse->textUnderlinePosition(), styleToUse->fontMetrics(), this, + textDecorationThickness); + paintAppliedDecoration(context, + localOrigin + FloatPoint(0, underlineOffset), width, + doubleOffset, 1, underline, textDecorationThickness, + antialiasDecoration); + } + if (deco & TextDecorationOverline) { + paintAppliedDecoration(context, localOrigin, width, -doubleOffset, 1, + overline, textDecorationThickness, + antialiasDecoration); + } + if (deco & TextDecorationLineThrough) { + const float lineThroughOffset = 2 * baseline / 3; + paintAppliedDecoration(context, + localOrigin + FloatPoint(0, lineThroughOffset), + width, doubleOffset, 0, linethrough, + textDecorationThickness, antialiasDecoration); + } } -void InlineTextBox::paintCompositionBackgrounds(GraphicsContext* pt, const FloatPoint& boxOrigin, RenderStyle* style, const Font& font, bool useCustomUnderlines) -{ - ASSERT_NOT_REACHED(); // TODO(ianh): this is unused right now, but we should probably expose it if it's useful - if (useCustomUnderlines) { - // Paint custom background highlights for compositions. - Vector underlines; // TODO(ianh): if we expose this function, provide a way to let authors set this - CompositionUnderlineRangeFilter filter(underlines, start(), end()); - for (CompositionUnderlineRangeFilter::ConstIterator it = filter.begin(); it != filter.end(); ++it) { - if (it->backgroundColor == Color::transparent) - continue; - paintSingleCompositionBackgroundRun(pt, boxOrigin, style, font, it->backgroundColor, underlinePaintStart(*it), underlinePaintEnd(*it)); - } - - } else { - unsigned start = 0; // TODO(ianh): if we expose this function, provide a way to let authors set this - unsigned end = 0; // TODO(ianh): if we expose this function, provide a way to let authors set this - paintSingleCompositionBackgroundRun(pt, boxOrigin, style, font, RenderTheme::theme().platformDefaultCompositionBackgroundColor(), start, end); +void InlineTextBox::paintCompositionBackgrounds(GraphicsContext* pt, + const FloatPoint& boxOrigin, + RenderStyle* style, + const Font& font, + bool useCustomUnderlines) { + ASSERT_NOT_REACHED(); // TODO(ianh): this is unused right now, but we should + // probably expose it if it's useful + if (useCustomUnderlines) { + // Paint custom background highlights for compositions. + Vector underlines; // TODO(ianh): if we expose this + // function, provide a way to let + // authors set this + CompositionUnderlineRangeFilter filter(underlines, start(), end()); + for (CompositionUnderlineRangeFilter::ConstIterator it = filter.begin(); + it != filter.end(); ++it) { + if (it->backgroundColor == Color::transparent) + continue; + paintSingleCompositionBackgroundRun( + pt, boxOrigin, style, font, it->backgroundColor, + underlinePaintStart(*it), underlinePaintEnd(*it)); } + + } else { + unsigned start = 0; // TODO(ianh): if we expose this function, provide a + // way to let authors set this + unsigned end = 0; // TODO(ianh): if we expose this function, provide a way + // to let authors set this + paintSingleCompositionBackgroundRun( + pt, boxOrigin, style, font, + RenderTheme::theme().platformDefaultCompositionBackgroundColor(), start, + end); + } } -void InlineTextBox::paintCompositionUnderline(GraphicsContext* ctx, const FloatPoint& boxOrigin, const CompositionUnderline& underline) -{ - if (m_truncation == cFullTruncation) - return; - - unsigned paintStart = underlinePaintStart(underline); - unsigned paintEnd = underlinePaintEnd(underline); - - // start of line to draw, relative to paintOffset. - float start = paintStart == static_cast(m_start) ? 0 : - renderer().width(m_start, paintStart - m_start, textPos(), isLeftToRightDirection() ? LTR : RTL, isFirstLineStyle()); - // how much line to draw - float width = (paintStart == static_cast(m_start) && paintEnd == static_cast(end()) + 1) ? m_logicalWidth : - renderer().width(paintStart, paintEnd - paintStart, textPos() + start, isLeftToRightDirection() ? LTR : RTL, isFirstLineStyle()); - - // Thick marked text underlines are 2px thick as long as there is room for the 2px line under the baseline. - // All other marked text underlines are 1px thick. - // If there's not enough space the underline will touch or overlap characters. - int lineThickness = 1; - int baseline = renderer().style(isFirstLineStyle())->fontMetrics().ascent(); - if (underline.thick && logicalHeight() - baseline >= 2) - lineThickness = 2; - - // We need to have some space between underlines of subsequent clauses, because some input methods do not use different underline styles for those. - // We make each line shorter, which has a harmless side effect of shortening the first and last clauses, too. - start += 1; - width -= 2; - - ctx->setStrokeColor(underline.color); - ctx->setStrokeThickness(lineThickness); - ctx->drawLineForText(FloatPoint(boxOrigin.x() + start, boxOrigin.y() + logicalHeight() - lineThickness), width); +void InlineTextBox::paintCompositionUnderline( + GraphicsContext* ctx, + const FloatPoint& boxOrigin, + const CompositionUnderline& underline) { + if (m_truncation == cFullTruncation) + return; + + unsigned paintStart = underlinePaintStart(underline); + unsigned paintEnd = underlinePaintEnd(underline); + + // start of line to draw, relative to paintOffset. + float start = paintStart == static_cast(m_start) + ? 0 + : renderer().width(m_start, paintStart - m_start, textPos(), + isLeftToRightDirection() ? LTR : RTL, + isFirstLineStyle()); + // how much line to draw + float width = (paintStart == static_cast(m_start) && + paintEnd == static_cast(end()) + 1) + ? m_logicalWidth + : renderer().width(paintStart, paintEnd - paintStart, + textPos() + start, + isLeftToRightDirection() ? LTR : RTL, + isFirstLineStyle()); + + // Thick marked text underlines are 2px thick as long as there is room for the + // 2px line under the baseline. All other marked text underlines are 1px + // thick. If there's not enough space the underline will touch or overlap + // characters. + int lineThickness = 1; + int baseline = renderer().style(isFirstLineStyle())->fontMetrics().ascent(); + if (underline.thick && logicalHeight() - baseline >= 2) + lineThickness = 2; + + // We need to have some space between underlines of subsequent clauses, + // because some input methods do not use different underline styles for those. + // We make each line shorter, which has a harmless side effect of shortening + // the first and last clauses, too. + start += 1; + width -= 2; + + ctx->setStrokeColor(underline.color); + ctx->setStrokeThickness(lineThickness); + ctx->drawLineForText( + FloatPoint(boxOrigin.x() + start, + boxOrigin.y() + logicalHeight() - lineThickness), + width); } -int InlineTextBox::caretMinOffset() const -{ - return m_start; +int InlineTextBox::caretMinOffset() const { + return m_start; } -int InlineTextBox::caretMaxOffset() const -{ - return m_start + m_len; +int InlineTextBox::caretMaxOffset() const { + return m_start + m_len; } -float InlineTextBox::textPos() const -{ - // When computing the width of a text run, RenderParagraph::computeInlineDirectionPositionsForLine() doesn't include the actual offset - // from the containing block edge in its measurement. textPos() should be consistent so the text are rendered in the same width. - if (logicalLeft() == 0) - return 0; - return logicalLeft() - root().logicalLeft(); +float InlineTextBox::textPos() const { + // When computing the width of a text run, + // RenderParagraph::computeInlineDirectionPositionsForLine() doesn't include + // the actual offset from the containing block edge in its measurement. + // textPos() should be consistent so the text are rendered in the same width. + if (logicalLeft() == 0) + return 0; + return logicalLeft() - root().logicalLeft(); } -int InlineTextBox::offsetForPosition(float lineOffset, bool includePartialGlyphs) const -{ - if (isLineBreak()) - return 0; +int InlineTextBox::offsetForPosition(float lineOffset, + bool includePartialGlyphs) const { + if (isLineBreak()) + return 0; - if (lineOffset - logicalLeft() > logicalWidth()) - return isLeftToRightDirection() ? len() : 0; - if (lineOffset - logicalLeft() < 0) - return isLeftToRightDirection() ? 0 : len(); + if (lineOffset - logicalLeft() > logicalWidth()) + return isLeftToRightDirection() ? len() : 0; + if (lineOffset - logicalLeft() < 0) + return isLeftToRightDirection() ? 0 : len(); - FontCachePurgePreventer fontCachePurgePreventer; + FontCachePurgePreventer fontCachePurgePreventer; - RenderText& text = renderer(); - RenderStyle* style = text.style(isFirstLineStyle()); - const Font& font = style->font(); - return font.offsetForPosition(constructTextRun(style, font), lineOffset - logicalLeft(), includePartialGlyphs); + RenderText& text = renderer(); + RenderStyle* style = text.style(isFirstLineStyle()); + const Font& font = style->font(); + return font.offsetForPosition(constructTextRun(style, font), + lineOffset - logicalLeft(), + includePartialGlyphs); } -float InlineTextBox::positionForOffset(int offset) const -{ - ASSERT(offset >= m_start); - ASSERT(offset <= m_start + m_len); - - if (isLineBreak()) - return logicalLeft(); - - FontCachePurgePreventer fontCachePurgePreventer; - - RenderText& text = renderer(); - RenderStyle* styleToUse = text.style(isFirstLineStyle()); - ASSERT(styleToUse); - const Font& font = styleToUse->font(); - int from = !isLeftToRightDirection() ? offset - m_start : 0; - int to = !isLeftToRightDirection() ? m_len : offset - m_start; - // FIXME: Do we need to add rightBearing here? - return font.selectionRectForText(constructTextRun(styleToUse, font), IntPoint(logicalLeft(), 0), 0, from, to).maxX(); +float InlineTextBox::positionForOffset(int offset) const { + ASSERT(offset >= m_start); + ASSERT(offset <= m_start + m_len); + + if (isLineBreak()) + return logicalLeft(); + + FontCachePurgePreventer fontCachePurgePreventer; + + RenderText& text = renderer(); + RenderStyle* styleToUse = text.style(isFirstLineStyle()); + ASSERT(styleToUse); + const Font& font = styleToUse->font(); + int from = !isLeftToRightDirection() ? offset - m_start : 0; + int to = !isLeftToRightDirection() ? m_len : offset - m_start; + // FIXME: Do we need to add rightBearing here? + return font + .selectionRectForText(constructTextRun(styleToUse, font), + IntPoint(logicalLeft(), 0), 0, from, to) + .maxX(); } -bool InlineTextBox::containsCaretOffset(int offset) const -{ - // Offsets before the box are never "in". - if (offset < m_start) - return false; +bool InlineTextBox::containsCaretOffset(int offset) const { + // Offsets before the box are never "in". + if (offset < m_start) + return false; - int pastEnd = m_start + m_len; + int pastEnd = m_start + m_len; - // Offsets inside the box (not at either edge) are always "in". - if (offset < pastEnd) - return true; + // Offsets inside the box (not at either edge) are always "in". + if (offset < pastEnd) + return true; - // Offsets outside the box are always "out". - if (offset > pastEnd) - return false; + // Offsets outside the box are always "out". + if (offset > pastEnd) + return false; - // Offsets at the end are "out" for line breaks (they are on the next line). - if (isLineBreak()) - return false; + // Offsets at the end are "out" for line breaks (they are on the next line). + if (isLineBreak()) + return false; - // Offsets at the end are "in" for normal boxes (but the caller has to check affinity). - return true; + // Offsets at the end are "in" for normal boxes (but the caller has to check + // affinity). + return true; } -void InlineTextBox::characterWidths(Vector& widths) const -{ - FontCachePurgePreventer fontCachePurgePreventer; +void InlineTextBox::characterWidths(Vector& widths) const { + FontCachePurgePreventer fontCachePurgePreventer; - RenderStyle* styleToUse = renderer().style(isFirstLineStyle()); - const Font& font = styleToUse->font(); + RenderStyle* styleToUse = renderer().style(isFirstLineStyle()); + const Font& font = styleToUse->font(); - TextRun textRun = constructTextRun(styleToUse, font); + TextRun textRun = constructTextRun(styleToUse, font); - GlyphBuffer glyphBuffer; - WidthIterator it(&font, textRun); - float lastWidth = 0; - widths.resize(m_len); - for (unsigned i = 0; i < m_len; i++) { - it.advance(i + 1, &glyphBuffer); - widths[i] = it.m_runWidthSoFar - lastWidth; - lastWidth = it.m_runWidthSoFar; - } + GlyphBuffer glyphBuffer; + WidthIterator it(&font, textRun); + float lastWidth = 0; + widths.resize(m_len); + for (unsigned i = 0; i < m_len; i++) { + it.advance(i + 1, &glyphBuffer); + widths[i] = it.m_runWidthSoFar - lastWidth; + lastWidth = it.m_runWidthSoFar; + } } -TextRun InlineTextBox::constructTextRun(RenderStyle* style, const Font& font, StringBuilder* charactersWithHyphen) const -{ - ASSERT(style); - ASSERT(renderer().text()); +TextRun InlineTextBox::constructTextRun( + RenderStyle* style, + const Font& font, + StringBuilder* charactersWithHyphen) const { + ASSERT(style); + ASSERT(renderer().text()); - StringView string = renderer().text().createView(); - unsigned startPos = start(); - unsigned length = len(); + StringView string = renderer().text().createView(); + unsigned startPos = start(); + unsigned length = len(); - if (string.length() != length || startPos) - string.narrow(startPos, length); + if (string.length() != length || startPos) + string.narrow(startPos, length); - return constructTextRun(style, font, string, renderer().textLength() - startPos, charactersWithHyphen); + return constructTextRun(style, font, string, + renderer().textLength() - startPos, + charactersWithHyphen); } -TextRun InlineTextBox::constructTextRun(RenderStyle* style, const Font& font, StringView string, int maximumLength, StringBuilder* charactersWithHyphen) const -{ - ASSERT(style); - - if (charactersWithHyphen) { - const AtomicString& hyphenString = style->hyphenString(); - charactersWithHyphen->reserveCapacity(string.length() + hyphenString.length()); - charactersWithHyphen->append(string); - charactersWithHyphen->append(hyphenString); - string = charactersWithHyphen->toString().createView(); - maximumLength = string.length(); - } - - ASSERT(maximumLength >= static_cast(string.length())); - - TextRun run(string, textPos(), expansion(), expansionBehavior(), direction(), dirOverride() || style->rtlOrdering() == VisualOrder, !renderer().canUseSimpleFontCodePath()); - run.setTabSize(!style->collapseWhiteSpace(), style->tabSize()); - run.setCharacterScanForCodePath(!renderer().canUseSimpleFontCodePath()); - // Propagate the maximum length of the characters buffer to the TextRun, even when we're only processing a substring. - run.setCharactersLength(maximumLength); - ASSERT(run.charactersLength() >= run.length()); - return run; +TextRun InlineTextBox::constructTextRun( + RenderStyle* style, + const Font& font, + StringView string, + int maximumLength, + StringBuilder* charactersWithHyphen) const { + ASSERT(style); + + if (charactersWithHyphen) { + const AtomicString& hyphenString = style->hyphenString(); + charactersWithHyphen->reserveCapacity(string.length() + + hyphenString.length()); + charactersWithHyphen->append(string); + charactersWithHyphen->append(hyphenString); + string = charactersWithHyphen->toString().createView(); + maximumLength = string.length(); + } + + ASSERT(maximumLength >= static_cast(string.length())); + + TextRun run(string, textPos(), expansion(), expansionBehavior(), direction(), + dirOverride() || style->rtlOrdering() == VisualOrder, + !renderer().canUseSimpleFontCodePath()); + run.setTabSize(!style->collapseWhiteSpace(), style->tabSize()); + run.setCharacterScanForCodePath(!renderer().canUseSimpleFontCodePath()); + // Propagate the maximum length of the characters buffer to the TextRun, even + // when we're only processing a substring. + run.setCharactersLength(maximumLength); + ASSERT(run.charactersLength() >= run.length()); + return run; } -TextRun InlineTextBox::constructTextRunForInspector(RenderStyle* style, const Font& font) const -{ - return InlineTextBox::constructTextRun(style, font); +TextRun InlineTextBox::constructTextRunForInspector(RenderStyle* style, + const Font& font) const { + return InlineTextBox::constructTextRun(style, font); } #ifndef NDEBUG -const char* InlineTextBox::boxName() const -{ - return "InlineTextBox"; +const char* InlineTextBox::boxName() const { + return "InlineTextBox"; } -void InlineTextBox::showBox(int printedCharacters) const -{ - const RenderText& obj = renderer(); - String value = obj.text(); - value = value.substring(start(), len()); - value.replaceWithLiteral('\\', "\\\\"); - value.replaceWithLiteral('\n', "\\n"); - printedCharacters += fprintf(stderr, "%s\t%p", boxName(), this); - for (; printedCharacters < showTreeCharacterOffset; printedCharacters++) - fputc(' ', stderr); - printedCharacters = fprintf(stderr, "\t%s %p", obj.renderName(), &obj); - const int rendererCharacterOffset = 24; - for (; printedCharacters < rendererCharacterOffset; printedCharacters++) - fputc(' ', stderr); - fprintf(stderr, "(%d,%d) \"%s\"\n", start(), start() + len(), value.utf8().data()); +void InlineTextBox::showBox(int printedCharacters) const { + const RenderText& obj = renderer(); + String value = obj.text(); + value = value.substring(start(), len()); + value.replaceWithLiteral('\\', "\\\\"); + value.replaceWithLiteral('\n', "\\n"); + printedCharacters += fprintf(stderr, "%s\t%p", boxName(), this); + for (; printedCharacters < showTreeCharacterOffset; printedCharacters++) + fputc(' ', stderr); + printedCharacters = fprintf(stderr, "\t%s %p", obj.renderName(), &obj); + const int rendererCharacterOffset = 24; + for (; printedCharacters < rendererCharacterOffset; printedCharacters++) + fputc(' ', stderr); + fprintf(stderr, "(%d,%d) \"%s\"\n", start(), start() + len(), + value.utf8().data()); } #endif -} // namespace blink +} // namespace blink diff --git a/sky/engine/core/rendering/InlineTextBox.h b/sky/engine/core/rendering/InlineTextBox.h index 5fa996ed114c8..89d0f19f567f5 100644 --- a/sky/engine/core/rendering/InlineTextBox.h +++ b/sky/engine/core/rendering/InlineTextBox.h @@ -1,7 +1,8 @@ /* * (C) 1999 Lars Knoll (knoll@kde.org) * (C) 2000 Dirk Mueller (mueller@kde.org) - * Copyright (C) 2004, 2005, 2006, 2009, 2010, 2011 Apple Inc. All rights reserved. + * Copyright (C) 2004, 2005, 2006, 2009, 2010, 2011 Apple Inc. + * All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -40,166 +41,224 @@ const unsigned short cNoTruncation = USHRT_MAX; const unsigned short cFullTruncation = USHRT_MAX - 1; class InlineTextBox : public InlineBox { -public: - InlineTextBox(RenderObject& obj) - : InlineBox(obj) - , m_prevTextBox(0) - , m_nextTextBox(0) - , m_start(0) - , m_len(0) - , m_truncation(cNoTruncation) - { - } - - RenderText& renderer() const { return toRenderText(InlineBox::renderer()); } - - virtual void destroy() override final; - - InlineTextBox* prevTextBox() const { return m_prevTextBox; } - InlineTextBox* nextTextBox() const { return m_nextTextBox; } - void setNextTextBox(InlineTextBox* n) { m_nextTextBox = n; } - void setPreviousTextBox(InlineTextBox* p) { m_prevTextBox = p; } - - // FIXME: These accessors should ASSERT(!isDirty()). See https://bugs.webkit.org/show_bug.cgi?id=97264 - unsigned start() const { return m_start; } - unsigned end() const { return m_len ? m_start + m_len - 1 : m_start; } - unsigned len() const { return m_len; } - - void setStart(unsigned start) { m_start = start; } - void setLen(unsigned len) { m_len = len; } - - void offsetRun(int d) { ASSERT(!isDirty()); m_start += d; } - - unsigned short truncation() { return m_truncation; } - - virtual void markDirty() override final; - - using InlineBox::hasHyphen; - using InlineBox::setHasHyphen; - using InlineBox::setHasAddedEllipsis; - using InlineBox::canHaveLeadingExpansion; - using InlineBox::setCanHaveLeadingExpansion; - - static inline bool compareByStart(const InlineTextBox* first, const InlineTextBox* second) { return first->start() < second->start(); } - - virtual int baselinePosition(FontBaseline) const override final; - virtual LayoutUnit lineHeight() const override final; - - bool getEmphasisMarkPosition(RenderStyle*, TextEmphasisPosition&) const; - - LayoutRect logicalOverflowRect() const; - void setLogicalOverflowRect(const LayoutRect&); - LayoutUnit logicalTopVisualOverflow() const { return logicalOverflowRect().y(); } - LayoutUnit logicalBottomVisualOverflow() const { return logicalOverflowRect().maxY(); } + public: + InlineTextBox(RenderObject& obj) + : InlineBox(obj), + m_prevTextBox(0), + m_nextTextBox(0), + m_start(0), + m_len(0), + m_truncation(cNoTruncation) {} + + RenderText& renderer() const { return toRenderText(InlineBox::renderer()); } + + virtual void destroy() override final; + + InlineTextBox* prevTextBox() const { return m_prevTextBox; } + InlineTextBox* nextTextBox() const { return m_nextTextBox; } + void setNextTextBox(InlineTextBox* n) { m_nextTextBox = n; } + void setPreviousTextBox(InlineTextBox* p) { m_prevTextBox = p; } + + // FIXME: These accessors should ASSERT(!isDirty()). See + // https://bugs.webkit.org/show_bug.cgi?id=97264 + unsigned start() const { return m_start; } + unsigned end() const { return m_len ? m_start + m_len - 1 : m_start; } + unsigned len() const { return m_len; } + + void setStart(unsigned start) { m_start = start; } + void setLen(unsigned len) { m_len = len; } + + void offsetRun(int d) { + ASSERT(!isDirty()); + m_start += d; + } + + unsigned short truncation() { return m_truncation; } + + virtual void markDirty() override final; + + using InlineBox::canHaveLeadingExpansion; + using InlineBox::hasHyphen; + using InlineBox::setCanHaveLeadingExpansion; + using InlineBox::setHasAddedEllipsis; + using InlineBox::setHasHyphen; + + static inline bool compareByStart(const InlineTextBox* first, + const InlineTextBox* second) { + return first->start() < second->start(); + } + + virtual int baselinePosition(FontBaseline) const override final; + virtual LayoutUnit lineHeight() const override final; + + bool getEmphasisMarkPosition(RenderStyle*, TextEmphasisPosition&) const; + + LayoutRect logicalOverflowRect() const; + void setLogicalOverflowRect(const LayoutRect&); + LayoutUnit logicalTopVisualOverflow() const { + return logicalOverflowRect().y(); + } + LayoutUnit logicalBottomVisualOverflow() const { + return logicalOverflowRect().maxY(); + } #ifndef NDEBUG - virtual void showBox(int = 0) const override; - virtual const char* boxName() const override; + virtual void showBox(int = 0) const override; + virtual const char* boxName() const override; #endif - enum RotationDirection { Counterclockwise, Clockwise }; - static AffineTransform rotation(const FloatRect& boxRect, RotationDirection); -private: - LayoutUnit selectionTop(); - LayoutUnit selectionBottom(); - LayoutUnit selectionHeight(); - - // charactersWithHyphen, if provided, must not be destroyed before the TextRun. - TextRun constructTextRun(RenderStyle*, const Font&, StringBuilder* charactersWithHyphen = 0) const; - TextRun constructTextRun(RenderStyle*, const Font&, StringView, int maximumLength, StringBuilder* charactersWithHyphen = 0) const; - -public: - TextRun constructTextRunForInspector(RenderStyle*, const Font&) const; - virtual FloatRect calculateBoundaries() const override { return FloatRect(x(), y(), width(), height()); } - - virtual LayoutRect localSelectionRect(int startPos, int endPos); - bool isSelected(int startPos, int endPos) const; - void selectionStartEnd(int& sPos, int& ePos); - -protected: - virtual void paint(PaintInfo&, const LayoutPoint&, LayoutUnit lineTop, LayoutUnit lineBottom, Vector& layers) override; - virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, LayoutUnit lineTop, LayoutUnit lineBottom) override; - -private: - virtual void deleteLine() override final; - virtual void extractLine() override final; - virtual void attachLine() override final; - -public: - virtual RenderObject::SelectionState selectionState() override final; - -private: - virtual void clearTruncation() override final { m_truncation = cNoTruncation; } - -public: - virtual bool isLineBreak() const override final; - - void setExpansion(int newExpansion) - { - m_logicalWidth -= expansion(); - InlineBox::setExpansion(newExpansion); - m_logicalWidth += newExpansion; - } - -private: - virtual bool isInlineTextBox() const override final { return true; } - -public: - virtual int caretMinOffset() const override final; - virtual int caretMaxOffset() const override final; - -private: - float textPos() const; // returns the x position relative to the left start of the text line. - -public: - virtual int offsetForPosition(float x, bool includePartialGlyphs = true) const; - virtual float positionForOffset(int offset) const; - - bool containsCaretOffset(int offset) const; // false for offset after line break - - // Fills a vector with the pixel width of each character. - void characterWidths(Vector&) const; - -private: - InlineTextBox* m_prevTextBox; // The previous box that also uses our RenderObject - InlineTextBox* m_nextTextBox; // The next box that also uses our RenderObject - TextBlobPtr m_cachedTextBlob; - - int m_start; - unsigned short m_len; - - unsigned short m_truncation; // Where to truncate when text overflow is applied. We use special constants to - // denote no truncation (the whole run paints) and full truncation (nothing paints at all). - - unsigned underlinePaintStart(const CompositionUnderline&); - unsigned underlinePaintEnd(const CompositionUnderline&); - -protected: - void paintSingleCompositionBackgroundRun(GraphicsContext*, const FloatPoint& boxOrigin, RenderStyle*, const Font&, Color backgroundColor, int startPos, int endPos); - void paintCompositionBackgrounds(GraphicsContext*, const FloatPoint& boxOrigin, RenderStyle*, const Font&, bool useCustomUnderlines); - void paintCompositionUnderline(GraphicsContext*, const FloatPoint& boxOrigin, const CompositionUnderline&); - -private: - void paintDecoration(GraphicsContext*, const FloatPoint& boxOrigin, TextDecoration); - void paintSelection(GraphicsContext*, const FloatPoint& boxOrigin, RenderStyle*, const Font&, Color textColor); - - TextRun::ExpansionBehavior expansionBehavior() const - { - return (canHaveLeadingExpansion() ? TextRun::AllowLeadingExpansion : TextRun::ForbidLeadingExpansion) - | (expansion() && nextLeafChild() ? TextRun::AllowTrailingExpansion : TextRun::ForbidTrailingExpansion); - } + enum RotationDirection { Counterclockwise, Clockwise }; + static AffineTransform rotation(const FloatRect& boxRect, RotationDirection); + + private: + LayoutUnit selectionTop(); + LayoutUnit selectionBottom(); + LayoutUnit selectionHeight(); + + // charactersWithHyphen, if provided, must not be destroyed before the + // TextRun. + TextRun constructTextRun(RenderStyle*, + const Font&, + StringBuilder* charactersWithHyphen = 0) const; + TextRun constructTextRun(RenderStyle*, + const Font&, + StringView, + int maximumLength, + StringBuilder* charactersWithHyphen = 0) const; + + public: + TextRun constructTextRunForInspector(RenderStyle*, const Font&) const; + virtual FloatRect calculateBoundaries() const override { + return FloatRect(x(), y(), width(), height()); + } + + virtual LayoutRect localSelectionRect(int startPos, int endPos); + bool isSelected(int startPos, int endPos) const; + void selectionStartEnd(int& sPos, int& ePos); + + protected: + virtual void paint(PaintInfo&, + const LayoutPoint&, + LayoutUnit lineTop, + LayoutUnit lineBottom, + Vector& layers) override; + virtual bool nodeAtPoint(const HitTestRequest&, + HitTestResult&, + const HitTestLocation& locationInContainer, + const LayoutPoint& accumulatedOffset, + LayoutUnit lineTop, + LayoutUnit lineBottom) override; + + private: + virtual void deleteLine() override final; + virtual void extractLine() override final; + virtual void attachLine() override final; + + public: + virtual RenderObject::SelectionState selectionState() override final; + + private: + virtual void clearTruncation() override final { + m_truncation = cNoTruncation; + } + + public: + virtual bool isLineBreak() const override final; + + void setExpansion(int newExpansion) { + m_logicalWidth -= expansion(); + InlineBox::setExpansion(newExpansion); + m_logicalWidth += newExpansion; + } + + private: + virtual bool isInlineTextBox() const override final { return true; } + + public: + virtual int caretMinOffset() const override final; + virtual int caretMaxOffset() const override final; + + private: + float textPos() const; // returns the x position relative to the left start + // of the text line. + + public: + virtual int offsetForPosition(float x, + bool includePartialGlyphs = true) const; + virtual float positionForOffset(int offset) const; + + bool containsCaretOffset( + int offset) const; // false for offset after line break + + // Fills a vector with the pixel width of each character. + void characterWidths(Vector&) const; + + private: + InlineTextBox* + m_prevTextBox; // The previous box that also uses our RenderObject + InlineTextBox* m_nextTextBox; // The next box that also uses our RenderObject + TextBlobPtr m_cachedTextBlob; + + int m_start; + unsigned short m_len; + + unsigned short m_truncation; // Where to truncate when text overflow is + // applied. We use special constants to denote + // no truncation (the whole run paints) and full + // truncation (nothing paints at all). + + unsigned underlinePaintStart(const CompositionUnderline&); + unsigned underlinePaintEnd(const CompositionUnderline&); + + protected: + void paintSingleCompositionBackgroundRun(GraphicsContext*, + const FloatPoint& boxOrigin, + RenderStyle*, + const Font&, + Color backgroundColor, + int startPos, + int endPos); + void paintCompositionBackgrounds(GraphicsContext*, + const FloatPoint& boxOrigin, + RenderStyle*, + const Font&, + bool useCustomUnderlines); + void paintCompositionUnderline(GraphicsContext*, + const FloatPoint& boxOrigin, + const CompositionUnderline&); + + private: + void paintDecoration(GraphicsContext*, + const FloatPoint& boxOrigin, + TextDecoration); + void paintSelection(GraphicsContext*, + const FloatPoint& boxOrigin, + RenderStyle*, + const Font&, + Color textColor); + + TextRun::ExpansionBehavior expansionBehavior() const { + return (canHaveLeadingExpansion() ? TextRun::AllowLeadingExpansion + : TextRun::ForbidLeadingExpansion) | + (expansion() && nextLeafChild() ? TextRun::AllowTrailingExpansion + : TextRun::ForbidTrailingExpansion); + } }; DEFINE_INLINE_BOX_TYPE_CASTS(InlineTextBox); void alignSelectionRectToDevicePixels(FloatRect&); -inline AffineTransform InlineTextBox::rotation(const FloatRect& boxRect, RotationDirection rotationDirection) -{ - return rotationDirection == Clockwise ? AffineTransform(0, 1, -1, 0, boxRect.x() + boxRect.maxY(), boxRect.maxY() - boxRect.x()) - : AffineTransform(0, -1, 1, 0, boxRect.x() - boxRect.maxY(), boxRect.x() + boxRect.maxY()); +inline AffineTransform InlineTextBox::rotation( + const FloatRect& boxRect, + RotationDirection rotationDirection) { + return rotationDirection == Clockwise + ? AffineTransform(0, 1, -1, 0, boxRect.x() + boxRect.maxY(), + boxRect.maxY() - boxRect.x()) + : AffineTransform(0, -1, 1, 0, boxRect.x() - boxRect.maxY(), + boxRect.x() + boxRect.maxY()); } -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_INLINETEXTBOX_H_ diff --git a/sky/engine/core/rendering/LayerPaintingInfo.h b/sky/engine/core/rendering/LayerPaintingInfo.h index f20ae864943d7..898b3ba3c63a3 100644 --- a/sky/engine/core/rendering/LayerPaintingInfo.h +++ b/sky/engine/core/rendering/LayerPaintingInfo.h @@ -25,7 +25,7 @@ * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Alternatively, the contents of this file may be used under the terms * of either the Mozilla Public License Version 1.1, found at @@ -53,17 +53,17 @@ namespace blink { class RenderLayer; struct LayerPaintingInfo { - LayerPaintingInfo(RenderLayer* inRootLayer, const LayoutRect& inDirtyRect, - const LayoutSize& inSubPixelAccumulation) - : rootLayer(inRootLayer) - , paintDirtyRect(inDirtyRect) - , subPixelAccumulation(inSubPixelAccumulation) - { } - RenderLayer* rootLayer; - LayoutRect paintDirtyRect; // relative to rootLayer; - LayoutSize subPixelAccumulation; + LayerPaintingInfo(RenderLayer* inRootLayer, + const LayoutRect& inDirtyRect, + const LayoutSize& inSubPixelAccumulation) + : rootLayer(inRootLayer), + paintDirtyRect(inDirtyRect), + subPixelAccumulation(inSubPixelAccumulation) {} + RenderLayer* rootLayer; + LayoutRect paintDirtyRect; // relative to rootLayer; + LayoutSize subPixelAccumulation; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_LAYERPAINTINGINFO_H_ diff --git a/sky/engine/core/rendering/OrderIterator.cpp b/sky/engine/core/rendering/OrderIterator.cpp index 08073fae54613..8b49c151279b6 100644 --- a/sky/engine/core/rendering/OrderIterator.cpp +++ b/sky/engine/core/rendering/OrderIterator.cpp @@ -35,57 +35,49 @@ namespace blink { OrderIterator::OrderIterator(const RenderBox* containerBox) - : m_containerBox(containerBox) - , m_currentChild(0) - , m_isReset(false) -{ -} + : m_containerBox(containerBox), m_currentChild(0), m_isReset(false) {} -RenderBox* OrderIterator::first() -{ - reset(); - return next(); +RenderBox* OrderIterator::first() { + reset(); + return next(); } -RenderBox* OrderIterator::next() -{ - do { - if (!m_currentChild) { - if (m_orderValuesIterator == m_orderValues.end()) - return 0; +RenderBox* OrderIterator::next() { + do { + if (!m_currentChild) { + if (m_orderValuesIterator == m_orderValues.end()) + return 0; - if (!m_isReset) { - ++m_orderValuesIterator; - if (m_orderValuesIterator == m_orderValues.end()) - return 0; - } else { - m_isReset = false; - } + if (!m_isReset) { + ++m_orderValuesIterator; + if (m_orderValuesIterator == m_orderValues.end()) + return 0; + } else { + m_isReset = false; + } - m_currentChild = m_containerBox->firstChildBox(); - } else { - m_currentChild = m_currentChild->nextSiblingBox(); - } - } while (!m_currentChild || m_currentChild->style()->order() != *m_orderValuesIterator); + m_currentChild = m_containerBox->firstChildBox(); + } else { + m_currentChild = m_currentChild->nextSiblingBox(); + } + } while (!m_currentChild || + m_currentChild->style()->order() != *m_orderValuesIterator); - return m_currentChild; + return m_currentChild; } -void OrderIterator::reset() -{ - m_currentChild = 0; - m_orderValuesIterator = m_orderValues.begin(); - m_isReset = true; +void OrderIterator::reset() { + m_currentChild = 0; + m_orderValuesIterator = m_orderValues.begin(); + m_isReset = true; } -OrderIteratorPopulator::~OrderIteratorPopulator() -{ - m_iterator.reset(); +OrderIteratorPopulator::~OrderIteratorPopulator() { + m_iterator.reset(); } -void OrderIteratorPopulator::collectChild(const RenderBox* child) -{ - m_iterator.m_orderValues.insert(child->style()->order()); +void OrderIteratorPopulator::collectChild(const RenderBox* child) { + m_iterator.m_orderValues.insert(child->style()->order()); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/core/rendering/OrderIterator.h b/sky/engine/core/rendering/OrderIterator.h index bdbfdf8b6a088..354dfeefd1aca 100644 --- a/sky/engine/core/rendering/OrderIterator.h +++ b/sky/engine/core/rendering/OrderIterator.h @@ -39,44 +39,44 @@ namespace blink { class RenderBox; class OrderIterator { - WTF_MAKE_NONCOPYABLE(OrderIterator); -public: - friend class OrderIteratorPopulator; + WTF_MAKE_NONCOPYABLE(OrderIterator); - OrderIterator(const RenderBox*); + public: + friend class OrderIteratorPopulator; - RenderBox* currentChild() const { return m_currentChild; } - RenderBox* first(); - RenderBox* next(); - void reset(); + OrderIterator(const RenderBox*); -private: - const RenderBox* m_containerBox; + RenderBox* currentChild() const { return m_currentChild; } + RenderBox* first(); + RenderBox* next(); + void reset(); - RenderBox* m_currentChild; + private: + const RenderBox* m_containerBox; - typedef std::set OrderValues; - OrderValues m_orderValues; - OrderValues::const_iterator m_orderValuesIterator; - bool m_isReset; + RenderBox* m_currentChild; + + typedef std::set OrderValues; + OrderValues m_orderValues; + OrderValues::const_iterator m_orderValuesIterator; + bool m_isReset; }; class OrderIteratorPopulator { -public: - explicit OrderIteratorPopulator(OrderIterator& iterator) - : m_iterator(iterator) - { - m_iterator.m_orderValues.clear(); - } + public: + explicit OrderIteratorPopulator(OrderIterator& iterator) + : m_iterator(iterator) { + m_iterator.m_orderValues.clear(); + } - ~OrderIteratorPopulator(); + ~OrderIteratorPopulator(); - void collectChild(const RenderBox*); + void collectChild(const RenderBox*); -private: - OrderIterator& m_iterator; + private: + OrderIterator& m_iterator; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_ORDERITERATOR_H_ diff --git a/sky/engine/core/rendering/PaintInfo.h b/sky/engine/core/rendering/PaintInfo.h index 5ed9740cd5219..9b2e5508005ea 100644 --- a/sky/engine/core/rendering/PaintInfo.h +++ b/sky/engine/core/rendering/PaintInfo.h @@ -3,7 +3,8 @@ * (C) 2000 Antti Koivisto (koivisto@kde.org) * (C) 2000 Dirk Mueller (mueller@kde.org) * (C) 2004 Allan Sandfeld Jensen (kde@carewolf.com) - * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. + * All rights reserved. * Copyright (C) 2009 Google Inc. All rights reserved. * * This library is free software; you can redistribute it and/or @@ -45,41 +46,42 @@ class RenderObject; * (tx|ty) is the calculated position of the parent */ struct PaintInfo { - PaintInfo(GraphicsContext* newContext, const IntRect& newRect, - const RenderBox* newPaintContainer) - : context(newContext) - , rect(newRect) - , m_paintContainer(newPaintContainer) - { - } + PaintInfo(GraphicsContext* newContext, + const IntRect& newRect, + const RenderBox* newPaintContainer) + : context(newContext), + rect(newRect), + m_paintContainer(newPaintContainer) {} - void applyTransform(const AffineTransform& localToAncestorTransform, bool identityStatusUnknown = true) - { - if (identityStatusUnknown && localToAncestorTransform.isIdentity()) - return; + void applyTransform(const AffineTransform& localToAncestorTransform, + bool identityStatusUnknown = true) { + if (identityStatusUnknown && localToAncestorTransform.isIdentity()) + return; - context->concatCTM(localToAncestorTransform); + context->concatCTM(localToAncestorTransform); - if (rect == infiniteRect()) - return; + if (rect == infiniteRect()) + return; - if (localToAncestorTransform.isInvertible()) - rect = localToAncestorTransform.inverse().mapRect(rect); - else - rect.setSize(IntSize(0, 0)); - } + if (localToAncestorTransform.isInvertible()) + rect = localToAncestorTransform.inverse().mapRect(rect); + else + rect.setSize(IntSize(0, 0)); + } - static IntRect infiniteRect() { return IntRect(LayoutRect::infiniteRect()); } - const RenderBox* paintContainer() const { return m_paintContainer; } + static IntRect infiniteRect() { return IntRect(LayoutRect::infiniteRect()); } + const RenderBox* paintContainer() const { return m_paintContainer; } - // FIXME: Introduce setters/getters at some point. Requires a lot of changes throughout rendering/. - GraphicsContext* context; - IntRect rect; + // FIXME: Introduce setters/getters at some point. Requires a lot of changes + // throughout rendering/. + GraphicsContext* context; + IntRect rect; -private: - const RenderBox* m_paintContainer; // the layer object that originates the current painting + private: + const RenderBox* m_paintContainer; // the layer object that originates the + // current painting }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_PAINTINFO_H_ diff --git a/sky/engine/core/rendering/PointerEventsHitRules.cpp b/sky/engine/core/rendering/PointerEventsHitRules.cpp index e64b3e754399c..695441829149c 100644 --- a/sky/engine/core/rendering/PointerEventsHitRules.cpp +++ b/sky/engine/core/rendering/PointerEventsHitRules.cpp @@ -21,97 +21,96 @@ namespace blink { -PointerEventsHitRules::PointerEventsHitRules(EHitTesting hitTesting, const HitTestRequest& request, EPointerEvents pointerEvents) - : requireVisible(false) - , requireFill(false) - , requireStroke(false) - , canHitStroke(false) - , canHitFill(false) - , canHitBoundingBox(false) -{ - if (request.svgClipContent()) - pointerEvents = PE_FILL; +PointerEventsHitRules::PointerEventsHitRules(EHitTesting hitTesting, + const HitTestRequest& request, + EPointerEvents pointerEvents) + : requireVisible(false), + requireFill(false), + requireStroke(false), + canHitStroke(false), + canHitFill(false), + canHitBoundingBox(false) { + if (request.svgClipContent()) + pointerEvents = PE_FILL; - if (hitTesting == SVG_GEOMETRY_HITTESTING) { - switch (pointerEvents) - { - case PE_BOUNDINGBOX: - canHitBoundingBox = true; - break; - case PE_VISIBLE_PAINTED: - case PE_AUTO: // "auto" is like "visiblePainted" when in SVG content - requireFill = true; - requireStroke = true; - case PE_VISIBLE: - requireVisible = true; - canHitFill = true; - canHitStroke = true; - break; - case PE_VISIBLE_FILL: - requireVisible = true; - canHitFill = true; - break; - case PE_VISIBLE_STROKE: - requireVisible = true; - canHitStroke = true; - break; - case PE_PAINTED: - requireFill = true; - requireStroke = true; - case PE_ALL: - canHitFill = true; - canHitStroke = true; - break; - case PE_FILL: - canHitFill = true; - break; - case PE_STROKE: - canHitStroke = true; - break; - case PE_NONE: - // nothing to do here, defaults are all false. - break; - } - } else { - switch (pointerEvents) - { - case PE_BOUNDINGBOX: - canHitBoundingBox = true; - break; - case PE_VISIBLE_PAINTED: - case PE_AUTO: // "auto" is like "visiblePainted" when in SVG content - requireVisible = true; - requireFill = true; - requireStroke = true; - canHitFill = true; - canHitStroke = true; - break; - case PE_VISIBLE_FILL: - case PE_VISIBLE_STROKE: - case PE_VISIBLE: - requireVisible = true; - canHitFill = true; - canHitStroke = true; - break; - case PE_PAINTED: - requireFill = true; - requireStroke = true; - canHitFill = true; - canHitStroke = true; - break; - case PE_FILL: - case PE_STROKE: - case PE_ALL: - canHitFill = true; - canHitStroke = true; - break; - case PE_NONE: - // nothing to do here, defaults are all false. - break; - } + if (hitTesting == SVG_GEOMETRY_HITTESTING) { + switch (pointerEvents) { + case PE_BOUNDINGBOX: + canHitBoundingBox = true; + break; + case PE_VISIBLE_PAINTED: + case PE_AUTO: // "auto" is like "visiblePainted" when in SVG content + requireFill = true; + requireStroke = true; + case PE_VISIBLE: + requireVisible = true; + canHitFill = true; + canHitStroke = true; + break; + case PE_VISIBLE_FILL: + requireVisible = true; + canHitFill = true; + break; + case PE_VISIBLE_STROKE: + requireVisible = true; + canHitStroke = true; + break; + case PE_PAINTED: + requireFill = true; + requireStroke = true; + case PE_ALL: + canHitFill = true; + canHitStroke = true; + break; + case PE_FILL: + canHitFill = true; + break; + case PE_STROKE: + canHitStroke = true; + break; + case PE_NONE: + // nothing to do here, defaults are all false. + break; } + } else { + switch (pointerEvents) { + case PE_BOUNDINGBOX: + canHitBoundingBox = true; + break; + case PE_VISIBLE_PAINTED: + case PE_AUTO: // "auto" is like "visiblePainted" when in SVG content + requireVisible = true; + requireFill = true; + requireStroke = true; + canHitFill = true; + canHitStroke = true; + break; + case PE_VISIBLE_FILL: + case PE_VISIBLE_STROKE: + case PE_VISIBLE: + requireVisible = true; + canHitFill = true; + canHitStroke = true; + break; + case PE_PAINTED: + requireFill = true; + requireStroke = true; + canHitFill = true; + canHitStroke = true; + break; + case PE_FILL: + case PE_STROKE: + case PE_ALL: + canHitFill = true; + canHitStroke = true; + break; + case PE_NONE: + // nothing to do here, defaults are all false. + break; + } + } } -} +} // namespace blink // vim:ts=4:noet diff --git a/sky/engine/core/rendering/PointerEventsHitRules.h b/sky/engine/core/rendering/PointerEventsHitRules.h index 075f304250379..65347917495d9 100644 --- a/sky/engine/core/rendering/PointerEventsHitRules.h +++ b/sky/engine/core/rendering/PointerEventsHitRules.h @@ -26,24 +26,24 @@ namespace blink { class PointerEventsHitRules { -public: - enum EHitTesting { - SVG_IMAGE_HITTESTING, - SVG_GEOMETRY_HITTESTING, - SVG_TEXT_HITTESTING - }; - - PointerEventsHitRules(EHitTesting, const HitTestRequest&, EPointerEvents); - - bool requireVisible; - bool requireFill; - bool requireStroke; - bool canHitStroke; - bool canHitFill; - bool canHitBoundingBox; + public: + enum EHitTesting { + SVG_IMAGE_HITTESTING, + SVG_GEOMETRY_HITTESTING, + SVG_TEXT_HITTESTING + }; + + PointerEventsHitRules(EHitTesting, const HitTestRequest&, EPointerEvents); + + bool requireVisible; + bool requireFill; + bool requireStroke; + bool canHitStroke; + bool canHitFill; + bool canHitBoundingBox; }; -} +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_POINTEREVENTSHITRULES_H_ diff --git a/sky/engine/core/rendering/RenderBlock.cpp b/sky/engine/core/rendering/RenderBlock.cpp index 865d8db210af3..0d9bc30869495 100644 --- a/sky/engine/core/rendering/RenderBlock.cpp +++ b/sky/engine/core/rendering/RenderBlock.cpp @@ -2,7 +2,8 @@ * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2007 David Smith (catfish.man@gmail.com) - * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. + * All rights reserved. * Copyright (C) Research In Motion Limited 2010. All rights reserved. * * This library is free software; you can redistribute it and/or @@ -47,13 +48,14 @@ using namespace Unicode; namespace blink { struct SameSizeAsRenderBlock : public RenderBox { - RenderObjectChildList children; - RenderLineBoxList lineBoxes; - int pageLogicalOffset; - uint32_t bitfields; + RenderObjectChildList children; + RenderLineBoxList lineBoxes; + int pageLogicalOffset; + uint32_t bitfields; }; -COMPILE_ASSERT(sizeof(RenderBlock) == sizeof(SameSizeAsRenderBlock), RenderBlock_should_stay_small); +COMPILE_ASSERT(sizeof(RenderBlock) == sizeof(SameSizeAsRenderBlock), + RenderBlock_should_stay_small); static TrackedDescendantsMap* gPositionedDescendantsMap = 0; static TrackedDescendantsMap* gPercentHeightDescendantsMap = 0; @@ -62,1444 +64,1661 @@ static TrackedContainerMap* gPositionedContainerMap = 0; static TrackedContainerMap* gPercentHeightContainerMap = 0; RenderBlock::RenderBlock() - : m_hasMarginBeforeQuirk(false) - , m_hasMarginAfterQuirk(false) - , m_beingDestroyed(false) - , m_hasBorderOrPaddingLogicalWidthChanged(false) -{ -} - -static void removeBlockFromDescendantAndContainerMaps(RenderBlock* block, TrackedDescendantsMap*& descendantMap, TrackedContainerMap*& containerMap) -{ - if (OwnPtr descendantSet = descendantMap->take(block)) { - TrackedRendererListHashSet::iterator end = descendantSet->end(); - for (TrackedRendererListHashSet::iterator descendant = descendantSet->begin(); descendant != end; ++descendant) { - TrackedContainerMap::iterator it = containerMap->find(*descendant); - ASSERT(it != containerMap->end()); - if (it == containerMap->end()) - continue; - HashSet* containerSet = it->value.get(); - ASSERT(containerSet->contains(block)); - containerSet->remove(block); - if (containerSet->isEmpty()) - containerMap->remove(it); - } + : m_hasMarginBeforeQuirk(false), + m_hasMarginAfterQuirk(false), + m_beingDestroyed(false), + m_hasBorderOrPaddingLogicalWidthChanged(false) {} + +static void removeBlockFromDescendantAndContainerMaps( + RenderBlock* block, + TrackedDescendantsMap*& descendantMap, + TrackedContainerMap*& containerMap) { + if (OwnPtr descendantSet = + descendantMap->take(block)) { + TrackedRendererListHashSet::iterator end = descendantSet->end(); + for (TrackedRendererListHashSet::iterator descendant = + descendantSet->begin(); + descendant != end; ++descendant) { + TrackedContainerMap::iterator it = containerMap->find(*descendant); + ASSERT(it != containerMap->end()); + if (it == containerMap->end()) + continue; + HashSet* containerSet = it->value.get(); + ASSERT(containerSet->contains(block)); + containerSet->remove(block); + if (containerSet->isEmpty()) + containerMap->remove(it); } + } } -void RenderBlock::removeFromGlobalMaps() -{ - if (gPercentHeightDescendantsMap) - removeBlockFromDescendantAndContainerMaps(this, gPercentHeightDescendantsMap, gPercentHeightContainerMap); - if (gPositionedDescendantsMap) - removeBlockFromDescendantAndContainerMaps(this, gPositionedDescendantsMap, gPositionedContainerMap); +void RenderBlock::removeFromGlobalMaps() { + if (gPercentHeightDescendantsMap) + removeBlockFromDescendantAndContainerMaps( + this, gPercentHeightDescendantsMap, gPercentHeightContainerMap); + if (gPositionedDescendantsMap) + removeBlockFromDescendantAndContainerMaps(this, gPositionedDescendantsMap, + gPositionedContainerMap); } -RenderBlock::~RenderBlock() -{ +RenderBlock::~RenderBlock() { #if !ENABLE(OILPAN) - removeFromGlobalMaps(); + removeFromGlobalMaps(); #endif } -void RenderBlock::destroy() -{ - RenderBox::destroy(); +void RenderBlock::destroy() { + RenderBox::destroy(); #if ENABLE(OILPAN) - removeFromGlobalMaps(); + removeFromGlobalMaps(); #endif } -void RenderBlock::willBeDestroyed() -{ - // Mark as being destroyed to avoid trouble with merges in removeChild(). - m_beingDestroyed = true; - - // Make sure to destroy anonymous children first while they are still connected to the rest of the tree, so that they will - // properly dirty line boxes that they are removed from. Effects that do :before/:after only on hover could crash otherwise. - children()->destroyLeftoverChildren(); - - if (!documentBeingDestroyed()) { - if (!firstLineBox() && parent()) - parent()->dirtyLinesFromChangedChild(this); +void RenderBlock::willBeDestroyed() { + // Mark as being destroyed to avoid trouble with merges in removeChild(). + m_beingDestroyed = true; + + // Make sure to destroy anonymous children first while they are still + // connected to the rest of the tree, so that they will properly dirty line + // boxes that they are removed from. Effects that do :before/:after only on + // hover could crash otherwise. + children()->destroyLeftoverChildren(); + + if (!documentBeingDestroyed()) { + if (!firstLineBox() && parent()) + parent()->dirtyLinesFromChangedChild(this); + } + + m_lineBoxes.deleteLineBoxes(); + + RenderBox::willBeDestroyed(); +} + +void RenderBlock::styleWillChange(StyleDifference diff, + const RenderStyle& newStyle) { + RenderStyle* oldStyle = style(); + + setReplaced(newStyle.isDisplayInlineType()); + + if (oldStyle && parent()) { + bool oldStyleIsContainer = oldStyle->position() != StaticPosition || + oldStyle->hasTransformRelatedProperty(); + bool newStyleIsContainer = newStyle.position() != StaticPosition || + newStyle.hasTransformRelatedProperty(); + + if (oldStyleIsContainer && !newStyleIsContainer) { + // Clear our positioned objects list. Our absolutely positioned + // descendants will be inserted into our containing block's positioned + // objects list during layout. + removePositionedObjects(0, NewContainingBlock); + } else if (!oldStyleIsContainer && newStyleIsContainer) { + // Remove our absolutely positioned descendants from their current + // containing block. They will be inserted into our positioned objects + // list during layout. + RenderObject* cb = parent(); + while (cb && + (cb->style()->position() == StaticPosition || + (cb->isInline() && !cb->isReplaced())) && + !cb->isRenderView()) { + cb = cb->parent(); + } + + if (cb->isRenderBlock()) + toRenderBlock(cb)->removePositionedObjects(this, NewContainingBlock); } + } - m_lineBoxes.deleteLineBoxes(); - - RenderBox::willBeDestroyed(); + RenderBox::styleWillChange(diff, newStyle); } -void RenderBlock::styleWillChange(StyleDifference diff, const RenderStyle& newStyle) -{ - RenderStyle* oldStyle = style(); - - setReplaced(newStyle.isDisplayInlineType()); - - if (oldStyle && parent()) { - bool oldStyleIsContainer = oldStyle->position() != StaticPosition || oldStyle->hasTransformRelatedProperty(); - bool newStyleIsContainer = newStyle.position() != StaticPosition || newStyle.hasTransformRelatedProperty(); - - if (oldStyleIsContainer && !newStyleIsContainer) { - // Clear our positioned objects list. Our absolutely positioned descendants will be - // inserted into our containing block's positioned objects list during layout. - removePositionedObjects(0, NewContainingBlock); - } else if (!oldStyleIsContainer && newStyleIsContainer) { - // Remove our absolutely positioned descendants from their current containing block. - // They will be inserted into our positioned objects list during layout. - RenderObject* cb = parent(); - while (cb && (cb->style()->position() == StaticPosition || (cb->isInline() && !cb->isReplaced())) && !cb->isRenderView()) { - cb = cb->parent(); - } - - if (cb->isRenderBlock()) - toRenderBlock(cb)->removePositionedObjects(this, NewContainingBlock); - } - } - - RenderBox::styleWillChange(diff, newStyle); -} - -static bool borderOrPaddingLogicalWidthChanged(const RenderStyle* oldStyle, const RenderStyle* newStyle) -{ - return oldStyle->borderLeftWidth() != newStyle->borderLeftWidth() - || oldStyle->borderRightWidth() != newStyle->borderRightWidth() - || oldStyle->paddingLeft() != newStyle->paddingLeft() - || oldStyle->paddingRight() != newStyle->paddingRight(); +static bool borderOrPaddingLogicalWidthChanged(const RenderStyle* oldStyle, + const RenderStyle* newStyle) { + return oldStyle->borderLeftWidth() != newStyle->borderLeftWidth() || + oldStyle->borderRightWidth() != newStyle->borderRightWidth() || + oldStyle->paddingLeft() != newStyle->paddingLeft() || + oldStyle->paddingRight() != newStyle->paddingRight(); } -void RenderBlock::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) -{ - RenderBox::styleDidChange(diff, oldStyle); +void RenderBlock::styleDidChange(StyleDifference diff, + const RenderStyle* oldStyle) { + RenderBox::styleDidChange(diff, oldStyle); - // It's possible for our border/padding to change, but for the overall logical width of the block to - // end up being the same. We keep track of this change so in layoutBlock, we can know to set relayoutChildren=true. - m_hasBorderOrPaddingLogicalWidthChanged = oldStyle && diff.needsFullLayout() && needsLayout() && borderOrPaddingLogicalWidthChanged(oldStyle, style()); + // It's possible for our border/padding to change, but for the overall logical + // width of the block to end up being the same. We keep track of this change + // so in layoutBlock, we can know to set relayoutChildren=true. + m_hasBorderOrPaddingLogicalWidthChanged = + oldStyle && diff.needsFullLayout() && needsLayout() && + borderOrPaddingLogicalWidthChanged(oldStyle, style()); } -void RenderBlock::addChild(RenderObject* newChild, RenderObject* beforeChild) -{ - ASSERT(isRenderParagraph() || !newChild->isInline()); - RenderBox::addChild(newChild, beforeChild); +void RenderBlock::addChild(RenderObject* newChild, RenderObject* beforeChild) { + ASSERT(isRenderParagraph() || !newChild->isInline()); + RenderBox::addChild(newChild, beforeChild); } -void RenderBlock::deleteLineBoxTree() -{ - ASSERT(!m_lineBoxes.firstLineBox()); +void RenderBlock::deleteLineBoxTree() { + ASSERT(!m_lineBoxes.firstLineBox()); } -void RenderBlock::removeChild(RenderObject* oldChild) -{ - RenderBox::removeChild(oldChild); - - // No need to waste time deleting the line box tree if we're getting destroyed. - if (documentBeingDestroyed()) - return; - - // If this was our last child be sure to clear out our line boxes. - if (!firstChild() && isRenderParagraph()) - deleteLineBoxTree(); -} - -bool RenderBlock::widthAvailableToChildrenHasChanged() -{ - bool widthAvailableToChildrenHasChanged = m_hasBorderOrPaddingLogicalWidthChanged; - m_hasBorderOrPaddingLogicalWidthChanged = false; - - // If we use border-box sizing, have percentage padding, and our parent has changed width then the width available to our children has changed even - // though our own width has remained the same. - widthAvailableToChildrenHasChanged |= style()->boxSizing() == BORDER_BOX && needsPreferredWidthsRecalculation(); - - return widthAvailableToChildrenHasChanged; -} - -bool RenderBlock::updateLogicalWidthAndColumnWidth() -{ - LayoutUnit oldWidth = logicalWidth(); - updateLogicalWidth(); - return oldWidth != logicalWidth() || widthAvailableToChildrenHasChanged(); -} - -void RenderBlock::addOverflowFromChildren() -{ - for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { - if (!child->isFloatingOrOutOfFlowPositioned()) - addOverflowFromChild(child); - } -} - -void RenderBlock::computeOverflow(LayoutUnit oldClientAfterEdge, bool) -{ - m_overflow.clear(); - - // Add overflow from children. - addOverflowFromChildren(); +void RenderBlock::removeChild(RenderObject* oldChild) { + RenderBox::removeChild(oldChild); - // Add in the overflow from positioned objects. - addOverflowFromPositionedObjects(); + // No need to waste time deleting the line box tree if we're getting + // destroyed. + if (documentBeingDestroyed()) + return; - if (hasOverflowClip()) { - // When we have overflow clip, propagate the original spillout since it will include collapsed bottom margins - // and bottom padding. Set the axis we don't care about to be 1, since we want this overflow to always - // be considered reachable. - LayoutRect clientRect(paddingBoxRect()); - LayoutRect rectToApply; - rectToApply = LayoutRect(clientRect.x(), clientRect.y(), 1, std::max(0, oldClientAfterEdge - clientRect.y())); - addLayoutOverflow(rectToApply); - if (hasRenderOverflow()) - m_overflow->setLayoutClientAfterEdge(oldClientAfterEdge); - } - - addVisualEffectOverflow(); + // If this was our last child be sure to clear out our line boxes. + if (!firstChild() && isRenderParagraph()) + deleteLineBoxTree(); } -void RenderBlock::addOverflowFromPositionedObjects() -{ - TrackedRendererListHashSet* positionedDescendants = positionedObjects(); - if (!positionedDescendants) - return; +bool RenderBlock::widthAvailableToChildrenHasChanged() { + bool widthAvailableToChildrenHasChanged = + m_hasBorderOrPaddingLogicalWidthChanged; + m_hasBorderOrPaddingLogicalWidthChanged = false; - RenderBox* positionedObject; - TrackedRendererListHashSet::iterator end = positionedDescendants->end(); - for (TrackedRendererListHashSet::iterator it = positionedDescendants->begin(); it != end; ++it) { - positionedObject = *it; - addOverflowFromChild(positionedObject, LayoutSize(positionedObject->x(), positionedObject->y())); - } -} - -void RenderBlock::updateBlockChildDirtyBitsBeforeLayout(bool relayoutChildren, RenderBox* child) -{ - // FIXME: Technically percentage height objects only need a relayout if their percentage isn't going to be turned into - // an auto value. Add a method to determine this, so that we can avoid the relayout. - if (relayoutChildren || (child->hasRelativeLogicalHeight() && !isRenderView())) - child->setChildNeedsLayout(MarkOnlyThis); + // If we use border-box sizing, have percentage padding, and our parent has + // changed width then the width available to our children has changed even + // though our own width has remained the same. + widthAvailableToChildrenHasChanged |= + style()->boxSizing() == BORDER_BOX && needsPreferredWidthsRecalculation(); - // If relayoutChildren is set and the child has percentage padding or an embedded content box, we also need to invalidate the childs pref widths. - if (relayoutChildren && child->needsPreferredWidthsRecalculation()) - child->setPreferredLogicalWidthsDirty(MarkOnlyThis); + return widthAvailableToChildrenHasChanged; } -void RenderBlock::simplifiedNormalFlowLayout() -{ - for (RenderBox* box = firstChildBox(); box; box = box->nextSiblingBox()) { - if (!box->isOutOfFlowPositioned()) - box->layoutIfNeeded(); - } +bool RenderBlock::updateLogicalWidthAndColumnWidth() { + LayoutUnit oldWidth = logicalWidth(); + updateLogicalWidth(); + return oldWidth != logicalWidth() || widthAvailableToChildrenHasChanged(); } -bool RenderBlock::simplifiedLayout() -{ - // Check if we need to do a full layout. - if (normalChildNeedsLayout() || selfNeedsLayout()) - return false; - - // Check that we actually need to do a simplified layout. - if (!posChildNeedsLayout() && !(needsSimplifiedNormalFlowLayout() || needsPositionedMovementLayout())) - return false; - - if (needsPositionedMovementLayout() && !tryLayoutDoingPositionedMovementOnly()) - return false; - - // Lay out positioned descendants or objects that just need to recompute overflow. - if (needsSimplifiedNormalFlowLayout()) - simplifiedNormalFlowLayout(); - - if (posChildNeedsLayout() || needsPositionedMovementLayout()) - layoutPositionedObjects(false, needsPositionedMovementLayout() ? ForcedLayoutAfterContainingBlockMoved : DefaultLayout); - - // Recompute our overflow information. - // FIXME: We could do better here by computing a temporary overflow object from layoutPositionedObjects and only - // updating our overflow if we either used to have overflow or if the new temporary object has overflow. - // For now just always recompute overflow. This is no worse performance-wise than the old code that called rightmostPosition and - // lowestPosition on every relayout so it's not a regression. - // computeOverflow expects the bottom edge before we clamp our height. Since this information isn't available during - // simplifiedLayout, we cache the value in m_overflow. - LayoutUnit oldClientAfterEdge = hasRenderOverflow() ? m_overflow->layoutClientAfterEdge() : clientLogicalBottom(); - computeOverflow(oldClientAfterEdge, true); - - updateLayerTransformAfterLayout(); - - clearNeedsLayout(); - return true; -} - -LayoutUnit RenderBlock::marginIntrinsicLogicalWidthForChild(RenderBox* child) const -{ - // A margin has three types: fixed, percentage, and auto (variable). - // Auto and percentage margins become 0 when computing min/max width. - // Fixed margins can be added in as is. - Length marginLeft = child->style()->marginStartUsing(style()); - Length marginRight = child->style()->marginEndUsing(style()); - LayoutUnit margin = 0; - if (marginLeft.isFixed()) - margin += marginLeft.value(); - if (marginRight.isFixed()) - margin += marginRight.value(); - return margin; +void RenderBlock::addOverflowFromChildren() { + for (RenderBox* child = firstChildBox(); child; + child = child->nextSiblingBox()) { + if (!child->isFloatingOrOutOfFlowPositioned()) + addOverflowFromChild(child); + } } -void RenderBlock::layoutPositionedObjects(bool relayoutChildren, PositionedLayoutBehavior info) -{ - TrackedRendererListHashSet* positionedDescendants = positionedObjects(); - if (!positionedDescendants) - return; +void RenderBlock::computeOverflow(LayoutUnit oldClientAfterEdge, bool) { + m_overflow.clear(); - RenderBox* r; - TrackedRendererListHashSet::iterator end = positionedDescendants->end(); - for (TrackedRendererListHashSet::iterator it = positionedDescendants->begin(); it != end; ++it) { - r = *it; - - // If relayoutChildren is set and the child has percentage padding or an embedded content box, we also need to invalidate the childs pref widths. - if (relayoutChildren && r->needsPreferredWidthsRecalculation()) - r->setPreferredLogicalWidthsDirty(MarkOnlyThis); + // Add overflow from children. + addOverflowFromChildren(); - if (info == ForcedLayoutAfterContainingBlockMoved) - r->setNeedsPositionedMovementLayout(); + // Add in the overflow from positioned objects. + addOverflowFromPositionedObjects(); - r->layoutIfNeeded(); - } -} + if (hasOverflowClip()) { + // When we have overflow clip, propagate the original spillout since it will + // include collapsed bottom margins and bottom padding. Set the axis we + // don't care about to be 1, since we want this overflow to always be + // considered reachable. + LayoutRect clientRect(paddingBoxRect()); + LayoutRect rectToApply; + rectToApply = LayoutRect( + clientRect.x(), clientRect.y(), 1, + std::max(0, oldClientAfterEdge - clientRect.y())); + addLayoutOverflow(rectToApply); + if (hasRenderOverflow()) + m_overflow->setLayoutClientAfterEdge(oldClientAfterEdge); + } -void RenderBlock::markPositionedObjectsForLayout() -{ - if (TrackedRendererListHashSet* positionedDescendants = positionedObjects()) { - TrackedRendererListHashSet::iterator end = positionedDescendants->end(); - for (TrackedRendererListHashSet::iterator it = positionedDescendants->begin(); it != end; ++it) - (*it)->setChildNeedsLayout(); - } + addVisualEffectOverflow(); } -void RenderBlock::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, Vector& layers) -{ - LayoutPoint adjustedPaintOffset = paintOffset + location(); - - LayoutRect overflowBox = visualOverflowRect(); - overflowBox.moveBy(adjustedPaintOffset); - if (!overflowBox.intersects(paintInfo.rect)) - return; +void RenderBlock::addOverflowFromPositionedObjects() { + TrackedRendererListHashSet* positionedDescendants = positionedObjects(); + if (!positionedDescendants) + return; - // There are some cases where not all clipped visual overflow is accounted for. - // FIXME: reduce the number of such cases. - ContentsClipBehavior contentsClipBehavior = ForceContentsClip; - if (hasOverflowClip() && !shouldPaintSelectionGaps()) - contentsClipBehavior = SkipContentsClipIfPossible; - - bool pushedClip = pushContentsClip(paintInfo, adjustedPaintOffset, contentsClipBehavior); - paintObject(paintInfo, adjustedPaintOffset, layers); - if (pushedClip) - popContentsClip(paintInfo, adjustedPaintOffset); -} - -void RenderBlock::paintChildren(PaintInfo& paintInfo, const LayoutPoint& paintOffset, Vector& layers) -{ - for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { - if (child->hasSelfPaintingLayer()) - layers.append(child); - else - child->paint(paintInfo, paintOffset, layers); - } + RenderBox* positionedObject; + TrackedRendererListHashSet::iterator end = positionedDescendants->end(); + for (TrackedRendererListHashSet::iterator it = positionedDescendants->begin(); + it != end; ++it) { + positionedObject = *it; + addOverflowFromChild(positionedObject, LayoutSize(positionedObject->x(), + positionedObject->y())); + } } -void RenderBlock::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffset, Vector& layers) -{ - if (hasBoxDecorationBackground()) - paintBoxDecorationBackground(paintInfo, paintOffset); +void RenderBlock::updateBlockChildDirtyBitsBeforeLayout(bool relayoutChildren, + RenderBox* child) { + // FIXME: Technically percentage height objects only need a relayout if their + // percentage isn't going to be turned into an auto value. Add a method to + // determine this, so that we can avoid the relayout. + if (relayoutChildren || + (child->hasRelativeLogicalHeight() && !isRenderView())) + child->setChildNeedsLayout(MarkOnlyThis); - paintChildren(paintInfo, paintOffset, layers); - paintSelection(paintInfo, paintOffset); // Fill in gaps in selection on lines and between blocks. + // If relayoutChildren is set and the child has percentage padding or an + // embedded content box, we also need to invalidate the childs pref widths. + if (relayoutChildren && child->needsPreferredWidthsRecalculation()) + child->setPreferredLogicalWidthsDirty(MarkOnlyThis); } -bool RenderBlock::shouldPaintSelectionGaps() const -{ - return selectionState() != SelectionNone && isSelectionRoot(); -} - -bool RenderBlock::isSelectionRoot() const -{ +void RenderBlock::simplifiedNormalFlowLayout() { + for (RenderBox* box = firstChildBox(); box; box = box->nextSiblingBox()) { + if (!box->isOutOfFlowPositioned()) + box->layoutIfNeeded(); + } +} + +bool RenderBlock::simplifiedLayout() { + // Check if we need to do a full layout. + if (normalChildNeedsLayout() || selfNeedsLayout()) return false; -} -void RenderBlock::paintSelection(PaintInfo& paintInfo, const LayoutPoint& paintOffset) -{ - if (shouldPaintSelectionGaps()) { - LayoutUnit lastTop = 0; - LayoutUnit lastLeft = logicalLeftSelectionOffset(this, lastTop); - LayoutUnit lastRight = logicalRightSelectionOffset(this, lastTop); - GraphicsContextStateSaver stateSaver(*paintInfo.context); - - // TODO(ojan): In sky, we don't use the return value, but we - // need this in order to actually paint selection gaps. - // We should rename it appropriately. - selectionGaps(this, paintOffset, LayoutSize(), lastTop, lastLeft, lastRight, &paintInfo); - } -} - -static void clipOutPositionedObjects(const PaintInfo* paintInfo, const LayoutPoint& offset, TrackedRendererListHashSet* positionedObjects) -{ - if (!positionedObjects) - return; - - TrackedRendererListHashSet::const_iterator end = positionedObjects->end(); - for (TrackedRendererListHashSet::const_iterator it = positionedObjects->begin(); it != end; ++it) { - RenderBox* r = *it; - paintInfo->context->clipOut(IntRect(offset.x() + r->x(), offset.y() + r->y(), r->width(), r->height())); - } -} - -LayoutUnit RenderBlock::blockDirectionOffset(const LayoutSize& offsetFromBlock) const -{ - // FIXME(sky): Remove - return offsetFromBlock.height(); -} - -LayoutUnit RenderBlock::inlineDirectionOffset(const LayoutSize& offsetFromBlock) const -{ - // FIXME(sky): Remove - return offsetFromBlock.width(); -} - -LayoutRect RenderBlock::logicalRectToPhysicalRect(const LayoutPoint& rootBlockPhysicalPosition, const LayoutRect& logicalRect) -{ - LayoutRect result = logicalRect; - result.moveBy(rootBlockPhysicalPosition); - return result; -} - -GapRects RenderBlock::selectionGaps(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, - LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const PaintInfo* paintInfo) -{ - // IMPORTANT: Callers of this method that intend for painting to happen need to do a save/restore. - // Clip out floating and positioned objects when painting selection gaps. - if (paintInfo) { - // Note that we don't clip out overflow for positioned objects. We just stick to the border box. - LayoutRect blockRect(offsetFromRootBlock.width(), offsetFromRootBlock.height(), width(), height()); - blockRect.moveBy(rootBlockPhysicalPosition); - clipOutPositionedObjects(paintInfo, blockRect.location(), positionedObjects()); - } + // Check that we actually need to do a simplified layout. + if (!posChildNeedsLayout() && + !(needsSimplifiedNormalFlowLayout() || needsPositionedMovementLayout())) + return false; - // FIXME: overflow: auto/scroll regions need more math here, since painting in the border box is different from painting in the padding box (one is scrolled, the other is - // fixed). - GapRects result; - if (!isRenderParagraph()) // FIXME: Make multi-column selection gap filling work someday. - return result; - - if (hasTransform()) { - // FIXME: We should learn how to gap fill multiple columns and transforms eventually. - lastLogicalTop = rootBlock->blockDirectionOffset(offsetFromRootBlock) + logicalHeight(); - lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, logicalHeight()); - lastLogicalRight = logicalRightSelectionOffset(rootBlock, logicalHeight()); - return result; - } + if (needsPositionedMovementLayout() && + !tryLayoutDoingPositionedMovementOnly()) + return false; - if (isRenderParagraph()) - result = toRenderParagraph(this)->inlineSelectionGaps(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, paintInfo); + // Lay out positioned descendants or objects that just need to recompute + // overflow. + if (needsSimplifiedNormalFlowLayout()) + simplifiedNormalFlowLayout(); + + if (posChildNeedsLayout() || needsPositionedMovementLayout()) + layoutPositionedObjects(false, needsPositionedMovementLayout() + ? ForcedLayoutAfterContainingBlockMoved + : DefaultLayout); + + // Recompute our overflow information. + // FIXME: We could do better here by computing a temporary overflow object + // from layoutPositionedObjects and only updating our overflow if we either + // used to have overflow or if the new temporary object has overflow. For now + // just always recompute overflow. This is no worse performance-wise than the + // old code that called rightmostPosition and lowestPosition on every relayout + // so it's not a regression. computeOverflow expects the bottom edge before we + // clamp our height. Since this information isn't available during + // simplifiedLayout, we cache the value in m_overflow. + LayoutUnit oldClientAfterEdge = hasRenderOverflow() + ? m_overflow->layoutClientAfterEdge() + : clientLogicalBottom(); + computeOverflow(oldClientAfterEdge, true); + + updateLayerTransformAfterLayout(); + + clearNeedsLayout(); + return true; +} + +LayoutUnit RenderBlock::marginIntrinsicLogicalWidthForChild( + RenderBox* child) const { + // A margin has three types: fixed, percentage, and auto (variable). + // Auto and percentage margins become 0 when computing min/max width. + // Fixed margins can be added in as is. + Length marginLeft = child->style()->marginStartUsing(style()); + Length marginRight = child->style()->marginEndUsing(style()); + LayoutUnit margin = 0; + if (marginLeft.isFixed()) + margin += marginLeft.value(); + if (marginRight.isFixed()) + margin += marginRight.value(); + return margin; +} + +void RenderBlock::layoutPositionedObjects(bool relayoutChildren, + PositionedLayoutBehavior info) { + TrackedRendererListHashSet* positionedDescendants = positionedObjects(); + if (!positionedDescendants) + return; + + RenderBox* r; + TrackedRendererListHashSet::iterator end = positionedDescendants->end(); + for (TrackedRendererListHashSet::iterator it = positionedDescendants->begin(); + it != end; ++it) { + r = *it; + + // If relayoutChildren is set and the child has percentage padding or an + // embedded content box, we also need to invalidate the childs pref widths. + if (relayoutChildren && r->needsPreferredWidthsRecalculation()) + r->setPreferredLogicalWidthsDirty(MarkOnlyThis); + + if (info == ForcedLayoutAfterContainingBlockMoved) + r->setNeedsPositionedMovementLayout(); + + r->layoutIfNeeded(); + } +} + +void RenderBlock::markPositionedObjectsForLayout() { + if (TrackedRendererListHashSet* positionedDescendants = positionedObjects()) { + TrackedRendererListHashSet::iterator end = positionedDescendants->end(); + for (TrackedRendererListHashSet::iterator it = + positionedDescendants->begin(); + it != end; ++it) + (*it)->setChildNeedsLayout(); + } +} + +void RenderBlock::paint(PaintInfo& paintInfo, + const LayoutPoint& paintOffset, + Vector& layers) { + LayoutPoint adjustedPaintOffset = paintOffset + location(); + + LayoutRect overflowBox = visualOverflowRect(); + overflowBox.moveBy(adjustedPaintOffset); + if (!overflowBox.intersects(paintInfo.rect)) + return; + + // There are some cases where not all clipped visual overflow is accounted + // for. + // FIXME: reduce the number of such cases. + ContentsClipBehavior contentsClipBehavior = ForceContentsClip; + if (hasOverflowClip() && !shouldPaintSelectionGaps()) + contentsClipBehavior = SkipContentsClipIfPossible; + + bool pushedClip = + pushContentsClip(paintInfo, adjustedPaintOffset, contentsClipBehavior); + paintObject(paintInfo, adjustedPaintOffset, layers); + if (pushedClip) + popContentsClip(paintInfo, adjustedPaintOffset); +} + +void RenderBlock::paintChildren(PaintInfo& paintInfo, + const LayoutPoint& paintOffset, + Vector& layers) { + for (RenderBox* child = firstChildBox(); child; + child = child->nextSiblingBox()) { + if (child->hasSelfPaintingLayer()) + layers.append(child); else - result = blockSelectionGaps(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, paintInfo); - - // Go ahead and fill the vertical gap all the way to the bottom of our block if the selection extends past our block. - if (rootBlock == this && (selectionState() != SelectionBoth && selectionState() != SelectionEnd)) - result.uniteCenter(blockSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, - logicalHeight(), paintInfo)); + child->paint(paintInfo, paintOffset, layers); + } +} + +void RenderBlock::paintObject(PaintInfo& paintInfo, + const LayoutPoint& paintOffset, + Vector& layers) { + if (hasBoxDecorationBackground()) + paintBoxDecorationBackground(paintInfo, paintOffset); + + paintChildren(paintInfo, paintOffset, layers); + paintSelection( + paintInfo, + paintOffset); // Fill in gaps in selection on lines and between blocks. +} + +bool RenderBlock::shouldPaintSelectionGaps() const { + return selectionState() != SelectionNone && isSelectionRoot(); +} + +bool RenderBlock::isSelectionRoot() const { + return false; +} + +void RenderBlock::paintSelection(PaintInfo& paintInfo, + const LayoutPoint& paintOffset) { + if (shouldPaintSelectionGaps()) { + LayoutUnit lastTop = 0; + LayoutUnit lastLeft = logicalLeftSelectionOffset(this, lastTop); + LayoutUnit lastRight = logicalRightSelectionOffset(this, lastTop); + GraphicsContextStateSaver stateSaver(*paintInfo.context); + + // TODO(ojan): In sky, we don't use the return value, but we + // need this in order to actually paint selection gaps. + // We should rename it appropriately. + selectionGaps(this, paintOffset, LayoutSize(), lastTop, lastLeft, lastRight, + &paintInfo); + } +} + +static void clipOutPositionedObjects( + const PaintInfo* paintInfo, + const LayoutPoint& offset, + TrackedRendererListHashSet* positionedObjects) { + if (!positionedObjects) + return; + + TrackedRendererListHashSet::const_iterator end = positionedObjects->end(); + for (TrackedRendererListHashSet::const_iterator it = + positionedObjects->begin(); + it != end; ++it) { + RenderBox* r = *it; + paintInfo->context->clipOut(IntRect( + offset.x() + r->x(), offset.y() + r->y(), r->width(), r->height())); + } +} + +LayoutUnit RenderBlock::blockDirectionOffset( + const LayoutSize& offsetFromBlock) const { + // FIXME(sky): Remove + return offsetFromBlock.height(); +} + +LayoutUnit RenderBlock::inlineDirectionOffset( + const LayoutSize& offsetFromBlock) const { + // FIXME(sky): Remove + return offsetFromBlock.width(); +} + +LayoutRect RenderBlock::logicalRectToPhysicalRect( + const LayoutPoint& rootBlockPhysicalPosition, + const LayoutRect& logicalRect) { + LayoutRect result = logicalRect; + result.moveBy(rootBlockPhysicalPosition); + return result; +} + +GapRects RenderBlock::selectionGaps( + RenderBlock* rootBlock, + const LayoutPoint& rootBlockPhysicalPosition, + const LayoutSize& offsetFromRootBlock, + LayoutUnit& lastLogicalTop, + LayoutUnit& lastLogicalLeft, + LayoutUnit& lastLogicalRight, + const PaintInfo* paintInfo) { + // IMPORTANT: Callers of this method that intend for painting to happen need + // to do a save/restore. Clip out floating and positioned objects when + // painting selection gaps. + if (paintInfo) { + // Note that we don't clip out overflow for positioned objects. We just + // stick to the border box. + LayoutRect blockRect(offsetFromRootBlock.width(), + offsetFromRootBlock.height(), width(), height()); + blockRect.moveBy(rootBlockPhysicalPosition); + clipOutPositionedObjects(paintInfo, blockRect.location(), + positionedObjects()); + } + + // FIXME: overflow: auto/scroll regions need more math here, since painting in + // the border box is different from painting in the padding box (one is + // scrolled, the other is fixed). + GapRects result; + if (!isRenderParagraph()) // FIXME: Make multi-column selection gap filling + // work someday. return result; -} -GapRects RenderBlock::blockSelectionGaps(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, - LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const PaintInfo* paintInfo) -{ - GapRects result; - - // Go ahead and jump right to the first block child that contains some selected objects. - RenderBox* curr; - for (curr = firstChildBox(); curr && curr->selectionState() == SelectionNone; curr = curr->nextSiblingBox()) { } - - for (bool sawSelectionEnd = false; curr && !sawSelectionEnd; curr = curr->nextSiblingBox()) { - SelectionState childState = curr->selectionState(); - if (childState == SelectionBoth || childState == SelectionEnd) - sawSelectionEnd = true; - - if (curr->isFloatingOrOutOfFlowPositioned()) - continue; // We must be a normal flow object in order to even be considered. - - bool paintsOwnSelection = curr->shouldPaintSelectionGaps(); - bool fillBlockGaps = paintsOwnSelection || (curr->canBeSelectionLeaf() && childState != SelectionNone); - if (fillBlockGaps) { - // We need to fill the vertical gap above this object. - if (childState == SelectionEnd || childState == SelectionInside) - // Fill the gap above the object. - result.uniteCenter(blockSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, - curr->logicalTop(), paintInfo)); - - // Only fill side gaps for objects that paint their own selection if we know for sure the selection is going to extend all the way *past* - // our object. We know this if the selection did not end inside our object. - if (paintsOwnSelection && (childState == SelectionStart || sawSelectionEnd)) - childState = SelectionNone; - - // Fill side gaps on this object based off its state. - bool leftGap, rightGap; - getSelectionGapInfo(childState, leftGap, rightGap); - - if (leftGap) - result.uniteLeft(logicalLeftSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, this, curr->logicalLeft(), curr->logicalTop(), curr->logicalHeight(), paintInfo)); - if (rightGap) - result.uniteRight(logicalRightSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, this, curr->logicalRight(), curr->logicalTop(), curr->logicalHeight(), paintInfo)); - - // Update lastLogicalTop to be just underneath the object. lastLogicalLeft and lastLogicalRight extend as far as - // they can without bumping into floating or positioned objects. Ideally they will go right up - // to the border of the root selection block. - lastLogicalTop = rootBlock->blockDirectionOffset(offsetFromRootBlock) + curr->logicalBottom(); - lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, curr->logicalBottom()); - lastLogicalRight = logicalRightSelectionOffset(rootBlock, curr->logicalBottom()); - } else if (childState != SelectionNone) - // We must be a block that has some selected object inside it. Go ahead and recur. - result.unite(toRenderBlock(curr)->selectionGaps(rootBlock, rootBlockPhysicalPosition, LayoutSize(offsetFromRootBlock.width() + curr->x(), offsetFromRootBlock.height() + curr->y()), - lastLogicalTop, lastLogicalLeft, lastLogicalRight, paintInfo)); - } + if (hasTransform()) { + // FIXME: We should learn how to gap fill multiple columns and transforms + // eventually. + lastLogicalTop = + rootBlock->blockDirectionOffset(offsetFromRootBlock) + logicalHeight(); + lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, logicalHeight()); + lastLogicalRight = logicalRightSelectionOffset(rootBlock, logicalHeight()); return result; -} - -IntRect alignSelectionRectToDevicePixels(LayoutRect& rect) -{ - LayoutUnit roundedX = rect.x().round(); - return IntRect(roundedX, rect.y().round(), - (rect.maxX() - roundedX).round(), - snapSizeToPixel(rect.height(), rect.y())); -} - -LayoutRect RenderBlock::blockSelectionGap(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, - LayoutUnit lastLogicalTop, LayoutUnit lastLogicalLeft, LayoutUnit lastLogicalRight, LayoutUnit logicalBottom, const PaintInfo* paintInfo) -{ - LayoutUnit logicalTop = lastLogicalTop; - LayoutUnit logicalHeight = rootBlock->blockDirectionOffset(offsetFromRootBlock) + logicalBottom - logicalTop; - if (logicalHeight <= 0) - return LayoutRect(); - - // Get the selection offsets for the bottom of the gap - LayoutUnit logicalLeft = std::max(lastLogicalLeft, logicalLeftSelectionOffset(rootBlock, logicalBottom)); - LayoutUnit logicalRight = std::min(lastLogicalRight, logicalRightSelectionOffset(rootBlock, logicalBottom)); - LayoutUnit logicalWidth = logicalRight - logicalLeft; - if (logicalWidth <= 0) - return LayoutRect(); - - LayoutRect gapRect = rootBlock->logicalRectToPhysicalRect(rootBlockPhysicalPosition, LayoutRect(logicalLeft, logicalTop, logicalWidth, logicalHeight)); - if (paintInfo) - paintInfo->context->fillRect(alignSelectionRectToDevicePixels(gapRect), selectionBackgroundColor()); - return gapRect; -} - -LayoutRect RenderBlock::logicalLeftSelectionGap(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, - RenderObject* selObj, LayoutUnit logicalLeft, LayoutUnit logicalTop, LayoutUnit logicalHeight, const PaintInfo* paintInfo) -{ - LayoutUnit rootBlockLogicalTop = rootBlock->blockDirectionOffset(offsetFromRootBlock) + logicalTop; - LayoutUnit rootBlockLogicalLeft = std::max(logicalLeftSelectionOffset(rootBlock, logicalTop), logicalLeftSelectionOffset(rootBlock, logicalTop + logicalHeight)); - LayoutUnit rootBlockLogicalRight = std::min(rootBlock->inlineDirectionOffset(offsetFromRootBlock) + logicalLeft, std::min(logicalRightSelectionOffset(rootBlock, logicalTop), logicalRightSelectionOffset(rootBlock, logicalTop + logicalHeight))); - LayoutUnit rootBlockLogicalWidth = rootBlockLogicalRight - rootBlockLogicalLeft; - if (rootBlockLogicalWidth <= 0) - return LayoutRect(); - - LayoutRect gapRect = rootBlock->logicalRectToPhysicalRect(rootBlockPhysicalPosition, LayoutRect(rootBlockLogicalLeft, rootBlockLogicalTop, rootBlockLogicalWidth, logicalHeight)); - if (paintInfo) - paintInfo->context->fillRect(alignSelectionRectToDevicePixels(gapRect), selObj->selectionBackgroundColor()); - return gapRect; -} - -LayoutRect RenderBlock::logicalRightSelectionGap(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, - RenderObject* selObj, LayoutUnit logicalRight, LayoutUnit logicalTop, LayoutUnit logicalHeight, const PaintInfo* paintInfo) -{ - LayoutUnit rootBlockLogicalTop = rootBlock->blockDirectionOffset(offsetFromRootBlock) + logicalTop; - LayoutUnit rootBlockLogicalLeft = std::max(rootBlock->inlineDirectionOffset(offsetFromRootBlock) + logicalRight, max(logicalLeftSelectionOffset(rootBlock, logicalTop), logicalLeftSelectionOffset(rootBlock, logicalTop + logicalHeight))); - LayoutUnit rootBlockLogicalRight = std::min(logicalRightSelectionOffset(rootBlock, logicalTop), logicalRightSelectionOffset(rootBlock, logicalTop + logicalHeight)); - LayoutUnit rootBlockLogicalWidth = rootBlockLogicalRight - rootBlockLogicalLeft; - if (rootBlockLogicalWidth <= 0) - return LayoutRect(); - - LayoutRect gapRect = rootBlock->logicalRectToPhysicalRect(rootBlockPhysicalPosition, LayoutRect(rootBlockLogicalLeft, rootBlockLogicalTop, rootBlockLogicalWidth, logicalHeight)); - if (paintInfo) - paintInfo->context->fillRect(alignSelectionRectToDevicePixels(gapRect), selObj->selectionBackgroundColor()); - return gapRect; -} - -void RenderBlock::getSelectionGapInfo(SelectionState state, bool& leftGap, bool& rightGap) -{ - bool ltr = style()->isLeftToRightDirection(); - leftGap = (state == RenderObject::SelectionInside) || - (state == RenderObject::SelectionEnd && ltr) || - (state == RenderObject::SelectionStart && !ltr); - rightGap = (state == RenderObject::SelectionInside) || - (state == RenderObject::SelectionStart && ltr) || - (state == RenderObject::SelectionEnd && !ltr); -} - -LayoutUnit RenderBlock::logicalLeftSelectionOffset(RenderBlock* rootBlock, LayoutUnit position) -{ - // The border can potentially be further extended by our containingBlock(). - if (rootBlock != this) - return containingBlock()->logicalLeftSelectionOffset(rootBlock, position + logicalTop()); - return logicalLeftOffsetForContent(); -} - -LayoutUnit RenderBlock::logicalRightSelectionOffset(RenderBlock* rootBlock, LayoutUnit position) -{ - // The border can potentially be further extended by our containingBlock(). - if (rootBlock != this) - return containingBlock()->logicalRightSelectionOffset(rootBlock, position + logicalTop()); - return logicalRightOffsetForContent(); -} - -RenderBlock* RenderBlock::blockBeforeWithinSelectionRoot(LayoutSize& offset) const -{ - if (isSelectionRoot()) - return 0; - - const RenderObject* object = this; - RenderObject* sibling; - do { - sibling = object->previousSibling(); - while (sibling && (!sibling->isRenderBlock() || toRenderBlock(sibling)->isSelectionRoot())) - sibling = sibling->previousSibling(); - - offset -= LayoutSize(toRenderBlock(object)->logicalLeft(), toRenderBlock(object)->logicalTop()); - object = object->parent(); - } while (!sibling && object && object->isRenderBlock() && !toRenderBlock(object)->isSelectionRoot()); - - if (!sibling) - return 0; - - RenderBlock* beforeBlock = toRenderBlock(sibling); - - offset += LayoutSize(beforeBlock->logicalLeft(), beforeBlock->logicalTop()); - - RenderObject* child = beforeBlock->lastChild(); - while (child && child->isRenderBlock()) { - beforeBlock = toRenderBlock(child); - offset += LayoutSize(beforeBlock->logicalLeft(), beforeBlock->logicalTop()); - child = beforeBlock->lastChild(); - } - return beforeBlock; -} - -void RenderBlock::setSelectionState(SelectionState state) -{ - RenderBox::setSelectionState(state); - - if (inlineBoxWrapper() && canUpdateSelectionOnRootLineBoxes()) - inlineBoxWrapper()->root().setHasSelectedChildren(state != SelectionNone); -} - -void RenderBlock::insertIntoTrackedRendererMaps(RenderBox* descendant, TrackedDescendantsMap*& descendantsMap, TrackedContainerMap*& containerMap) -{ - if (!descendantsMap) { - descendantsMap = new TrackedDescendantsMap; - containerMap = new TrackedContainerMap; - } - - TrackedRendererListHashSet* descendantSet = descendantsMap->get(this); - if (!descendantSet) { - descendantSet = new TrackedRendererListHashSet; - descendantsMap->set(this, adoptPtr(descendantSet)); - } - bool added = descendantSet->add(descendant).isNewEntry; - if (!added) { - ASSERT(containerMap->get(descendant)); - ASSERT(containerMap->get(descendant)->contains(this)); - return; - } - - HashSet* containerSet = containerMap->get(descendant); - if (!containerSet) { - containerSet = new HashSet; - containerMap->set(descendant, adoptPtr(containerSet)); - } - ASSERT(!containerSet->contains(this)); - containerSet->add(this); -} - -void RenderBlock::removeFromTrackedRendererMaps(RenderBox* descendant, TrackedDescendantsMap*& descendantsMap, TrackedContainerMap*& containerMap) -{ - if (!descendantsMap) - return; - - OwnPtr > containerSet = containerMap->take(descendant); - if (!containerSet) - return; - - HashSet::iterator end = containerSet->end(); - for (HashSet::iterator it = containerSet->begin(); it != end; ++it) { - RenderBlock* container = *it; - - // FIXME: Disabling this assert temporarily until we fix the layout - // bugs associated with positioned objects not properly cleared from - // their ancestor chain before being moved. See webkit bug 93766. - // ASSERT(descendant->isDescendantOf(container)); - - TrackedDescendantsMap::iterator descendantsMapIterator = descendantsMap->find(container); - ASSERT(descendantsMapIterator != descendantsMap->end()); - if (descendantsMapIterator == descendantsMap->end()) - continue; - TrackedRendererListHashSet* descendantSet = descendantsMapIterator->value.get(); - ASSERT(descendantSet->contains(descendant)); - descendantSet->remove(descendant); - if (descendantSet->isEmpty()) - descendantsMap->remove(descendantsMapIterator); - } -} - -TrackedRendererListHashSet* RenderBlock::positionedObjects() const -{ - if (gPositionedDescendantsMap) - return gPositionedDescendantsMap->get(this); + } + + if (isRenderParagraph()) + result = toRenderParagraph(this)->inlineSelectionGaps( + rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, + lastLogicalTop, lastLogicalLeft, lastLogicalRight, paintInfo); + else + result = blockSelectionGaps(rootBlock, rootBlockPhysicalPosition, + offsetFromRootBlock, lastLogicalTop, + lastLogicalLeft, lastLogicalRight, paintInfo); + + // Go ahead and fill the vertical gap all the way to the bottom of our block + // if the selection extends past our block. + if (rootBlock == this && + (selectionState() != SelectionBoth && selectionState() != SelectionEnd)) + result.uniteCenter(blockSelectionGap(rootBlock, rootBlockPhysicalPosition, + offsetFromRootBlock, lastLogicalTop, + lastLogicalLeft, lastLogicalRight, + logicalHeight(), paintInfo)); + return result; +} + +GapRects RenderBlock::blockSelectionGaps( + RenderBlock* rootBlock, + const LayoutPoint& rootBlockPhysicalPosition, + const LayoutSize& offsetFromRootBlock, + LayoutUnit& lastLogicalTop, + LayoutUnit& lastLogicalLeft, + LayoutUnit& lastLogicalRight, + const PaintInfo* paintInfo) { + GapRects result; + + // Go ahead and jump right to the first block child that contains some + // selected objects. + RenderBox* curr; + for (curr = firstChildBox(); curr && curr->selectionState() == SelectionNone; + curr = curr->nextSiblingBox()) { + } + + for (bool sawSelectionEnd = false; curr && !sawSelectionEnd; + curr = curr->nextSiblingBox()) { + SelectionState childState = curr->selectionState(); + if (childState == SelectionBoth || childState == SelectionEnd) + sawSelectionEnd = true; + + if (curr->isFloatingOrOutOfFlowPositioned()) + continue; // We must be a normal flow object in order to even be + // considered. + + bool paintsOwnSelection = curr->shouldPaintSelectionGaps(); + bool fillBlockGaps = paintsOwnSelection || (curr->canBeSelectionLeaf() && + childState != SelectionNone); + if (fillBlockGaps) { + // We need to fill the vertical gap above this object. + if (childState == SelectionEnd || childState == SelectionInside) + // Fill the gap above the object. + result.uniteCenter(blockSelectionGap( + rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, + lastLogicalTop, lastLogicalLeft, lastLogicalRight, + curr->logicalTop(), paintInfo)); + + // Only fill side gaps for objects that paint their own selection if we + // know for sure the selection is going to extend all the way *past* our + // object. We know this if the selection did not end inside our object. + if (paintsOwnSelection && + (childState == SelectionStart || sawSelectionEnd)) + childState = SelectionNone; + + // Fill side gaps on this object based off its state. + bool leftGap, rightGap; + getSelectionGapInfo(childState, leftGap, rightGap); + + if (leftGap) + result.uniteLeft(logicalLeftSelectionGap( + rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, this, + curr->logicalLeft(), curr->logicalTop(), curr->logicalHeight(), + paintInfo)); + if (rightGap) + result.uniteRight(logicalRightSelectionGap( + rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, this, + curr->logicalRight(), curr->logicalTop(), curr->logicalHeight(), + paintInfo)); + + // Update lastLogicalTop to be just underneath the object. + // lastLogicalLeft and lastLogicalRight extend as far as they can without + // bumping into floating or positioned objects. Ideally they will go + // right up to the border of the root selection block. + lastLogicalTop = rootBlock->blockDirectionOffset(offsetFromRootBlock) + + curr->logicalBottom(); + lastLogicalLeft = + logicalLeftSelectionOffset(rootBlock, curr->logicalBottom()); + lastLogicalRight = + logicalRightSelectionOffset(rootBlock, curr->logicalBottom()); + } else if (childState != SelectionNone) + // We must be a block that has some selected object inside it. Go ahead + // and recur. + result.unite(toRenderBlock(curr)->selectionGaps( + rootBlock, rootBlockPhysicalPosition, + LayoutSize(offsetFromRootBlock.width() + curr->x(), + offsetFromRootBlock.height() + curr->y()), + lastLogicalTop, lastLogicalLeft, lastLogicalRight, paintInfo)); + } + return result; +} + +IntRect alignSelectionRectToDevicePixels(LayoutRect& rect) { + LayoutUnit roundedX = rect.x().round(); + return IntRect(roundedX, rect.y().round(), (rect.maxX() - roundedX).round(), + snapSizeToPixel(rect.height(), rect.y())); +} + +LayoutRect RenderBlock::blockSelectionGap( + RenderBlock* rootBlock, + const LayoutPoint& rootBlockPhysicalPosition, + const LayoutSize& offsetFromRootBlock, + LayoutUnit lastLogicalTop, + LayoutUnit lastLogicalLeft, + LayoutUnit lastLogicalRight, + LayoutUnit logicalBottom, + const PaintInfo* paintInfo) { + LayoutUnit logicalTop = lastLogicalTop; + LayoutUnit logicalHeight = + rootBlock->blockDirectionOffset(offsetFromRootBlock) + logicalBottom - + logicalTop; + if (logicalHeight <= 0) + return LayoutRect(); + + // Get the selection offsets for the bottom of the gap + LayoutUnit logicalLeft = std::max( + lastLogicalLeft, logicalLeftSelectionOffset(rootBlock, logicalBottom)); + LayoutUnit logicalRight = std::min( + lastLogicalRight, logicalRightSelectionOffset(rootBlock, logicalBottom)); + LayoutUnit logicalWidth = logicalRight - logicalLeft; + if (logicalWidth <= 0) + return LayoutRect(); + + LayoutRect gapRect = rootBlock->logicalRectToPhysicalRect( + rootBlockPhysicalPosition, + LayoutRect(logicalLeft, logicalTop, logicalWidth, logicalHeight)); + if (paintInfo) + paintInfo->context->fillRect(alignSelectionRectToDevicePixels(gapRect), + selectionBackgroundColor()); + return gapRect; +} + +LayoutRect RenderBlock::logicalLeftSelectionGap( + RenderBlock* rootBlock, + const LayoutPoint& rootBlockPhysicalPosition, + const LayoutSize& offsetFromRootBlock, + RenderObject* selObj, + LayoutUnit logicalLeft, + LayoutUnit logicalTop, + LayoutUnit logicalHeight, + const PaintInfo* paintInfo) { + LayoutUnit rootBlockLogicalTop = + rootBlock->blockDirectionOffset(offsetFromRootBlock) + logicalTop; + LayoutUnit rootBlockLogicalLeft = std::max( + logicalLeftSelectionOffset(rootBlock, logicalTop), + logicalLeftSelectionOffset(rootBlock, logicalTop + logicalHeight)); + LayoutUnit rootBlockLogicalRight = std::min( + rootBlock->inlineDirectionOffset(offsetFromRootBlock) + logicalLeft, + std::min( + logicalRightSelectionOffset(rootBlock, logicalTop), + logicalRightSelectionOffset(rootBlock, logicalTop + logicalHeight))); + LayoutUnit rootBlockLogicalWidth = + rootBlockLogicalRight - rootBlockLogicalLeft; + if (rootBlockLogicalWidth <= 0) + return LayoutRect(); + + LayoutRect gapRect = rootBlock->logicalRectToPhysicalRect( + rootBlockPhysicalPosition, + LayoutRect(rootBlockLogicalLeft, rootBlockLogicalTop, + rootBlockLogicalWidth, logicalHeight)); + if (paintInfo) + paintInfo->context->fillRect(alignSelectionRectToDevicePixels(gapRect), + selObj->selectionBackgroundColor()); + return gapRect; +} + +LayoutRect RenderBlock::logicalRightSelectionGap( + RenderBlock* rootBlock, + const LayoutPoint& rootBlockPhysicalPosition, + const LayoutSize& offsetFromRootBlock, + RenderObject* selObj, + LayoutUnit logicalRight, + LayoutUnit logicalTop, + LayoutUnit logicalHeight, + const PaintInfo* paintInfo) { + LayoutUnit rootBlockLogicalTop = + rootBlock->blockDirectionOffset(offsetFromRootBlock) + logicalTop; + LayoutUnit rootBlockLogicalLeft = std::max( + rootBlock->inlineDirectionOffset(offsetFromRootBlock) + logicalRight, + max(logicalLeftSelectionOffset(rootBlock, logicalTop), + logicalLeftSelectionOffset(rootBlock, logicalTop + logicalHeight))); + LayoutUnit rootBlockLogicalRight = std::min( + logicalRightSelectionOffset(rootBlock, logicalTop), + logicalRightSelectionOffset(rootBlock, logicalTop + logicalHeight)); + LayoutUnit rootBlockLogicalWidth = + rootBlockLogicalRight - rootBlockLogicalLeft; + if (rootBlockLogicalWidth <= 0) + return LayoutRect(); + + LayoutRect gapRect = rootBlock->logicalRectToPhysicalRect( + rootBlockPhysicalPosition, + LayoutRect(rootBlockLogicalLeft, rootBlockLogicalTop, + rootBlockLogicalWidth, logicalHeight)); + if (paintInfo) + paintInfo->context->fillRect(alignSelectionRectToDevicePixels(gapRect), + selObj->selectionBackgroundColor()); + return gapRect; +} + +void RenderBlock::getSelectionGapInfo(SelectionState state, + bool& leftGap, + bool& rightGap) { + bool ltr = style()->isLeftToRightDirection(); + leftGap = (state == RenderObject::SelectionInside) || + (state == RenderObject::SelectionEnd && ltr) || + (state == RenderObject::SelectionStart && !ltr); + rightGap = (state == RenderObject::SelectionInside) || + (state == RenderObject::SelectionStart && ltr) || + (state == RenderObject::SelectionEnd && !ltr); +} + +LayoutUnit RenderBlock::logicalLeftSelectionOffset(RenderBlock* rootBlock, + LayoutUnit position) { + // The border can potentially be further extended by our containingBlock(). + if (rootBlock != this) + return containingBlock()->logicalLeftSelectionOffset( + rootBlock, position + logicalTop()); + return logicalLeftOffsetForContent(); +} + +LayoutUnit RenderBlock::logicalRightSelectionOffset(RenderBlock* rootBlock, + LayoutUnit position) { + // The border can potentially be further extended by our containingBlock(). + if (rootBlock != this) + return containingBlock()->logicalRightSelectionOffset( + rootBlock, position + logicalTop()); + return logicalRightOffsetForContent(); +} + +RenderBlock* RenderBlock::blockBeforeWithinSelectionRoot( + LayoutSize& offset) const { + if (isSelectionRoot()) return 0; -} - -void RenderBlock::insertPositionedObject(RenderBox* o) -{ - insertIntoTrackedRendererMaps(o, gPositionedDescendantsMap, gPositionedContainerMap); -} - -void RenderBlock::removePositionedObject(RenderBox* o) -{ - removeFromTrackedRendererMaps(o, gPositionedDescendantsMap, gPositionedContainerMap); -} - -void RenderBlock::removePositionedObjects(RenderBlock* o, ContainingBlockState containingBlockState) -{ - TrackedRendererListHashSet* positionedDescendants = positionedObjects(); - if (!positionedDescendants) - return; - - RenderBox* r; - TrackedRendererListHashSet::iterator end = positionedDescendants->end(); - - Vector deadObjects; + const RenderObject* object = this; + RenderObject* sibling; + do { + sibling = object->previousSibling(); + while (sibling && (!sibling->isRenderBlock() || + toRenderBlock(sibling)->isSelectionRoot())) + sibling = sibling->previousSibling(); + + offset -= LayoutSize(toRenderBlock(object)->logicalLeft(), + toRenderBlock(object)->logicalTop()); + object = object->parent(); + } while (!sibling && object && object->isRenderBlock() && + !toRenderBlock(object)->isSelectionRoot()); + + if (!sibling) + return 0; - for (TrackedRendererListHashSet::iterator it = positionedDescendants->begin(); it != end; ++it) { - r = *it; - if (!o || r->isDescendantOf(o)) { - if (containingBlockState == NewContainingBlock) - r->setChildNeedsLayout(MarkOnlyThis); + RenderBlock* beforeBlock = toRenderBlock(sibling); - // It is parent blocks job to add positioned child to positioned objects list of its containing block - // Parent layout needs to be invalidated to ensure this happens. - RenderObject* p = r->parent(); - while (p && !p->isRenderBlock()) - p = p->parent(); - if (p) - p->setChildNeedsLayout(); + offset += LayoutSize(beforeBlock->logicalLeft(), beforeBlock->logicalTop()); - deadObjects.append(r); - } + RenderObject* child = beforeBlock->lastChild(); + while (child && child->isRenderBlock()) { + beforeBlock = toRenderBlock(child); + offset += LayoutSize(beforeBlock->logicalLeft(), beforeBlock->logicalTop()); + child = beforeBlock->lastChild(); + } + return beforeBlock; +} + +void RenderBlock::setSelectionState(SelectionState state) { + RenderBox::setSelectionState(state); + + if (inlineBoxWrapper() && canUpdateSelectionOnRootLineBoxes()) + inlineBoxWrapper()->root().setHasSelectedChildren(state != SelectionNone); +} + +void RenderBlock::insertIntoTrackedRendererMaps( + RenderBox* descendant, + TrackedDescendantsMap*& descendantsMap, + TrackedContainerMap*& containerMap) { + if (!descendantsMap) { + descendantsMap = new TrackedDescendantsMap; + containerMap = new TrackedContainerMap; + } + + TrackedRendererListHashSet* descendantSet = descendantsMap->get(this); + if (!descendantSet) { + descendantSet = new TrackedRendererListHashSet; + descendantsMap->set(this, adoptPtr(descendantSet)); + } + bool added = descendantSet->add(descendant).isNewEntry; + if (!added) { + ASSERT(containerMap->get(descendant)); + ASSERT(containerMap->get(descendant)->contains(this)); + return; + } + + HashSet* containerSet = containerMap->get(descendant); + if (!containerSet) { + containerSet = new HashSet; + containerMap->set(descendant, adoptPtr(containerSet)); + } + ASSERT(!containerSet->contains(this)); + containerSet->add(this); +} + +void RenderBlock::removeFromTrackedRendererMaps( + RenderBox* descendant, + TrackedDescendantsMap*& descendantsMap, + TrackedContainerMap*& containerMap) { + if (!descendantsMap) + return; + + OwnPtr> containerSet = containerMap->take(descendant); + if (!containerSet) + return; + + HashSet::iterator end = containerSet->end(); + for (HashSet::iterator it = containerSet->begin(); it != end; + ++it) { + RenderBlock* container = *it; + + // FIXME: Disabling this assert temporarily until we fix the layout + // bugs associated with positioned objects not properly cleared from + // their ancestor chain before being moved. See webkit bug 93766. + // ASSERT(descendant->isDescendantOf(container)); + + TrackedDescendantsMap::iterator descendantsMapIterator = + descendantsMap->find(container); + ASSERT(descendantsMapIterator != descendantsMap->end()); + if (descendantsMapIterator == descendantsMap->end()) + continue; + TrackedRendererListHashSet* descendantSet = + descendantsMapIterator->value.get(); + ASSERT(descendantSet->contains(descendant)); + descendantSet->remove(descendant); + if (descendantSet->isEmpty()) + descendantsMap->remove(descendantsMapIterator); + } +} + +TrackedRendererListHashSet* RenderBlock::positionedObjects() const { + if (gPositionedDescendantsMap) + return gPositionedDescendantsMap->get(this); + return 0; +} + +void RenderBlock::insertPositionedObject(RenderBox* o) { + insertIntoTrackedRendererMaps(o, gPositionedDescendantsMap, + gPositionedContainerMap); +} + +void RenderBlock::removePositionedObject(RenderBox* o) { + removeFromTrackedRendererMaps(o, gPositionedDescendantsMap, + gPositionedContainerMap); +} + +void RenderBlock::removePositionedObjects( + RenderBlock* o, + ContainingBlockState containingBlockState) { + TrackedRendererListHashSet* positionedDescendants = positionedObjects(); + if (!positionedDescendants) + return; + + RenderBox* r; + + TrackedRendererListHashSet::iterator end = positionedDescendants->end(); + + Vector deadObjects; + + for (TrackedRendererListHashSet::iterator it = positionedDescendants->begin(); + it != end; ++it) { + r = *it; + if (!o || r->isDescendantOf(o)) { + if (containingBlockState == NewContainingBlock) + r->setChildNeedsLayout(MarkOnlyThis); + + // It is parent blocks job to add positioned child to positioned objects + // list of its containing block Parent layout needs to be invalidated to + // ensure this happens. + RenderObject* p = r->parent(); + while (p && !p->isRenderBlock()) + p = p->parent(); + if (p) + p->setChildNeedsLayout(); + + deadObjects.append(r); } + } - for (unsigned i = 0; i < deadObjects.size(); i++) - removePositionedObject(deadObjects.at(i)); + for (unsigned i = 0; i < deadObjects.size(); i++) + removePositionedObject(deadObjects.at(i)); } -void RenderBlock::addPercentHeightDescendant(RenderBox* descendant) -{ - insertIntoTrackedRendererMaps(descendant, gPercentHeightDescendantsMap, gPercentHeightContainerMap); +void RenderBlock::addPercentHeightDescendant(RenderBox* descendant) { + insertIntoTrackedRendererMaps(descendant, gPercentHeightDescendantsMap, + gPercentHeightContainerMap); } -void RenderBlock::removePercentHeightDescendant(RenderBox* descendant) -{ - removeFromTrackedRendererMaps(descendant, gPercentHeightDescendantsMap, gPercentHeightContainerMap); +void RenderBlock::removePercentHeightDescendant(RenderBox* descendant) { + removeFromTrackedRendererMaps(descendant, gPercentHeightDescendantsMap, + gPercentHeightContainerMap); } -TrackedRendererListHashSet* RenderBlock::percentHeightDescendants() const -{ - return gPercentHeightDescendantsMap ? gPercentHeightDescendantsMap->get(this) : 0; +TrackedRendererListHashSet* RenderBlock::percentHeightDescendants() const { + return gPercentHeightDescendantsMap ? gPercentHeightDescendantsMap->get(this) + : 0; } -bool RenderBlock::hasPercentHeightContainerMap() -{ - return gPercentHeightContainerMap; +bool RenderBlock::hasPercentHeightContainerMap() { + return gPercentHeightContainerMap; } -bool RenderBlock::hasPercentHeightDescendant(RenderBox* descendant) -{ - // We don't null check gPercentHeightContainerMap since the caller - // already ensures this and we need to call this function on every - // descendant in clearPercentHeightDescendantsFrom(). - ASSERT(gPercentHeightContainerMap); - return gPercentHeightContainerMap->contains(descendant); +bool RenderBlock::hasPercentHeightDescendant(RenderBox* descendant) { + // We don't null check gPercentHeightContainerMap since the caller + // already ensures this and we need to call this function on every + // descendant in clearPercentHeightDescendantsFrom(). + ASSERT(gPercentHeightContainerMap); + return gPercentHeightContainerMap->contains(descendant); } -void RenderBlock::dirtyForLayoutFromPercentageHeightDescendants(SubtreeLayoutScope& layoutScope) -{ - if (!gPercentHeightDescendantsMap) - return; +void RenderBlock::dirtyForLayoutFromPercentageHeightDescendants( + SubtreeLayoutScope& layoutScope) { + if (!gPercentHeightDescendantsMap) + return; - TrackedRendererListHashSet* descendants = gPercentHeightDescendantsMap->get(this); - if (!descendants) - return; + TrackedRendererListHashSet* descendants = + gPercentHeightDescendantsMap->get(this); + if (!descendants) + return; - TrackedRendererListHashSet::iterator end = descendants->end(); - for (TrackedRendererListHashSet::iterator it = descendants->begin(); it != end; ++it) { - RenderBox* box = *it; - while (box != this) { - if (box->normalChildNeedsLayout()) - break; - layoutScope.setChildNeedsLayout(box); - box = box->containingBlock(); - ASSERT(box); - if (!box) - break; - } + TrackedRendererListHashSet::iterator end = descendants->end(); + for (TrackedRendererListHashSet::iterator it = descendants->begin(); + it != end; ++it) { + RenderBox* box = *it; + while (box != this) { + if (box->normalChildNeedsLayout()) + break; + layoutScope.setChildNeedsLayout(box); + box = box->containingBlock(); + ASSERT(box); + if (!box) + break; } + } } -void RenderBlock::removePercentHeightDescendantIfNeeded(RenderBox* descendant) -{ - // We query the map directly, rather than looking at style's - // logicalHeight()/logicalMinHeight()/logicalMaxHeight() since those - // can change with writing mode/directional changes. - if (!hasPercentHeightContainerMap()) - return; +void RenderBlock::removePercentHeightDescendantIfNeeded(RenderBox* descendant) { + // We query the map directly, rather than looking at style's + // logicalHeight()/logicalMinHeight()/logicalMaxHeight() since those + // can change with writing mode/directional changes. + if (!hasPercentHeightContainerMap()) + return; - if (!hasPercentHeightDescendant(descendant)) - return; + if (!hasPercentHeightDescendant(descendant)) + return; - removePercentHeightDescendant(descendant); + removePercentHeightDescendant(descendant); } -void RenderBlock::clearPercentHeightDescendantsFrom(RenderBox* parent) -{ - ASSERT(gPercentHeightContainerMap); - for (RenderObject* curr = parent->slowFirstChild(); curr; curr = curr->nextInPreOrder(parent)) { - if (!curr->isBox()) - continue; +void RenderBlock::clearPercentHeightDescendantsFrom(RenderBox* parent) { + ASSERT(gPercentHeightContainerMap); + for (RenderObject* curr = parent->slowFirstChild(); curr; + curr = curr->nextInPreOrder(parent)) { + if (!curr->isBox()) + continue; - RenderBox* box = toRenderBox(curr); - if (!hasPercentHeightDescendant(box)) - continue; + RenderBox* box = toRenderBox(curr); + if (!hasPercentHeightDescendant(box)) + continue; - removePercentHeightDescendant(box); - } + removePercentHeightDescendant(box); + } } -LayoutUnit RenderBlock::textIndentOffset() const -{ - LayoutUnit cw = 0; - if (style()->textIndent().isPercent()) - cw = containingBlock()->availableLogicalWidth(); - return minimumValueForLength(style()->textIndent(), cw); +LayoutUnit RenderBlock::textIndentOffset() const { + LayoutUnit cw = 0; + if (style()->textIndent().isPercent()) + cw = containingBlock()->availableLogicalWidth(); + return minimumValueForLength(style()->textIndent(), cw); } -bool RenderBlock::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset) -{ - LayoutPoint adjustedLocation(accumulatedOffset + location()); - LayoutSize localOffset = toLayoutSize(adjustedLocation); - - if (!isRenderView()) { - // Check if we need to do anything at all. - // If we have clipping, then we can't have any spillout. - LayoutRect overflowBox = hasOverflowClip() ? borderBoxRect() : visualOverflowRect(); - overflowBox.moveBy(adjustedLocation); - if (!locationInContainer.intersects(overflowBox)) - return false; - } - - if (style()->clipPath()) { - switch (style()->clipPath()->type()) { - case ClipPathOperation::SHAPE: - break; - case ClipPathOperation::REFERENCE: - break; - } - } +bool RenderBlock::nodeAtPoint(const HitTestRequest& request, + HitTestResult& result, + const HitTestLocation& locationInContainer, + const LayoutPoint& accumulatedOffset) { + LayoutPoint adjustedLocation(accumulatedOffset + location()); + LayoutSize localOffset = toLayoutSize(adjustedLocation); + if (!isRenderView()) { + // Check if we need to do anything at all. // If we have clipping, then we can't have any spillout. - bool useOverflowClip = hasOverflowClip() && !hasSelfPaintingLayer(); - bool checkChildren = !useOverflowClip; - if (!checkChildren) { - LayoutRect clipRect = overflowClipRect(adjustedLocation); - if (style()->hasBorderRadius()) - checkChildren = locationInContainer.intersects(style()->getRoundedBorderFor(clipRect)); - else - checkChildren = locationInContainer.intersects(clipRect); - } - if (checkChildren) { - if (hitTestContents(request, result, locationInContainer, toLayoutPoint(localOffset))) { - updateHitTestResult(result, locationInContainer.point() - localOffset); - return true; - } - } - - // Check if the point is outside radii. - if (style()->hasBorderRadius()) { - LayoutRect borderRect = borderBoxRect(); - borderRect.moveBy(adjustedLocation); - RoundedRect border = style()->getRoundedBorderFor(borderRect); - if (!locationInContainer.intersects(border)) - return false; + LayoutRect overflowBox = + hasOverflowClip() ? borderBoxRect() : visualOverflowRect(); + overflowBox.moveBy(adjustedLocation); + if (!locationInContainer.intersects(overflowBox)) + return false; + } + + if (style()->clipPath()) { + switch (style()->clipPath()->type()) { + case ClipPathOperation::SHAPE: + break; + case ClipPathOperation::REFERENCE: + break; } - - // Now hit test our background - LayoutRect boundsRect(adjustedLocation, size()); - if (visibleToHitTestRequest(request) && locationInContainer.intersects(boundsRect)) { - updateHitTestResult(result, locationInContainer.point() - localOffset); - return true; + } + + // If we have clipping, then we can't have any spillout. + bool useOverflowClip = hasOverflowClip() && !hasSelfPaintingLayer(); + bool checkChildren = !useOverflowClip; + if (!checkChildren) { + LayoutRect clipRect = overflowClipRect(adjustedLocation); + if (style()->hasBorderRadius()) + checkChildren = locationInContainer.intersects( + style()->getRoundedBorderFor(clipRect)); + else + checkChildren = locationInContainer.intersects(clipRect); + } + if (checkChildren) { + if (hitTestContents(request, result, locationInContainer, + toLayoutPoint(localOffset))) { + updateHitTestResult(result, locationInContainer.point() - localOffset); + return true; } + } + + // Check if the point is outside radii. + if (style()->hasBorderRadius()) { + LayoutRect borderRect = borderBoxRect(); + borderRect.moveBy(adjustedLocation); + RoundedRect border = style()->getRoundedBorderFor(borderRect); + if (!locationInContainer.intersects(border)) + return false; + } + + // Now hit test our background + LayoutRect boundsRect(adjustedLocation, size()); + if (visibleToHitTestRequest(request) && + locationInContainer.intersects(boundsRect)) { + updateHitTestResult(result, locationInContainer.point() - localOffset); + return true; + } - return false; + return false; } -bool RenderBlock::hitTestContents(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset) -{ - for (RenderBox* child = lastChildBox(); child; child = child->previousSiblingBox()) { - if (!child->hasSelfPaintingLayer() && child->nodeAtPoint(request, result, locationInContainer, accumulatedOffset)) - return true; - } +bool RenderBlock::hitTestContents(const HitTestRequest& request, + HitTestResult& result, + const HitTestLocation& locationInContainer, + const LayoutPoint& accumulatedOffset) { + for (RenderBox* child = lastChildBox(); child; + child = child->previousSiblingBox()) { + if (!child->hasSelfPaintingLayer() && + child->nodeAtPoint(request, result, locationInContainer, + accumulatedOffset)) + return true; + } - return false; + return false; } -static PositionWithAffinity positionForPointInChild(RenderBox* child, const LayoutPoint& pointInParentCoordinates) -{ - LayoutPoint childLocation = child->location(); +static PositionWithAffinity positionForPointInChild( + RenderBox* child, + const LayoutPoint& pointInParentCoordinates) { + LayoutPoint childLocation = child->location(); - // FIXME: This is wrong if the child's writing-mode is different from the parent's. - LayoutPoint pointInChildCoordinates(toLayoutPoint(pointInParentCoordinates - childLocation)); - return child->positionForPoint(pointInChildCoordinates); + // FIXME: This is wrong if the child's writing-mode is different from the + // parent's. + LayoutPoint pointInChildCoordinates( + toLayoutPoint(pointInParentCoordinates - childLocation)); + return child->positionForPoint(pointInChildCoordinates); } -PositionWithAffinity RenderBlock::positionForPointWithInlineChildren(const LayoutPoint& pointInLogicalContents) -{ - ASSERT(isRenderParagraph()); +PositionWithAffinity RenderBlock::positionForPointWithInlineChildren( + const LayoutPoint& pointInLogicalContents) { + ASSERT(isRenderParagraph()); - if (!firstRootBox()) - return createPositionWithAffinity(0, DOWNSTREAM); - - // look for the closest line box in the root box which is at the passed-in y coordinate - InlineBox* closestBox = 0; - RootInlineBox* firstRootBoxWithChildren = 0; - RootInlineBox* lastRootBoxWithChildren = 0; - for (RootInlineBox* root = firstRootBox(); root; root = root->nextRootBox()) { - if (!root->firstLeafChild()) - continue; - if (!firstRootBoxWithChildren) - firstRootBoxWithChildren = root; - - lastRootBoxWithChildren = root; - - // check if this root line box is located at this y coordinate - if (pointInLogicalContents.y() < root->selectionBottom()) { - closestBox = root->closestLeafChildForLogicalLeftPosition(pointInLogicalContents.x()); - if (closestBox) - break; - } - } - - if (!closestBox && lastRootBoxWithChildren) { - // y coordinate is below last root line box, pretend we hit it - closestBox = lastRootBoxWithChildren->closestLeafChildForLogicalLeftPosition(pointInLogicalContents.x()); - } - - if (closestBox) { - // pass the box a top position that is inside it - LayoutPoint point(pointInLogicalContents.x(), closestBox->root().blockDirectionPointInLine()); - if (closestBox->renderer().isReplaced()) - return positionForPointInChild(&toRenderBox(closestBox->renderer()), point); - return closestBox->renderer().positionForPoint(point); - } - - // Can't reach this. We have a root line box, but it has no kids. - // FIXME: This should ASSERT_NOT_REACHED(), but clicking on placeholder text - // seems to hit this code path. + if (!firstRootBox()) return createPositionWithAffinity(0, DOWNSTREAM); -} - -static inline bool isChildHitTestCandidate(RenderBox* box) -{ - return box->height() && !box->isFloatingOrOutOfFlowPositioned(); -} - -PositionWithAffinity RenderBlock::positionForPoint(const LayoutPoint& point) -{ - if (isReplaced()) { - // FIXME: This seems wrong when the object's writing-mode doesn't match the line's writing-mode. - LayoutUnit pointLogicalLeft = point.x(); - LayoutUnit pointLogicalTop = point.y(); - - if (pointLogicalLeft < 0) - return createPositionWithAffinity(caretMinOffset(), DOWNSTREAM); - if (pointLogicalLeft >= logicalWidth()) - return createPositionWithAffinity(caretMaxOffset(), DOWNSTREAM); - if (pointLogicalTop < 0) - return createPositionWithAffinity(caretMinOffset(), DOWNSTREAM); - if (pointLogicalTop >= logicalHeight()) - return createPositionWithAffinity(caretMaxOffset(), DOWNSTREAM); - } - - LayoutPoint pointInContents = point; - LayoutPoint pointInLogicalContents(pointInContents); - - if (isRenderParagraph()) - return positionForPointWithInlineChildren(pointInLogicalContents); - - RenderBox* lastCandidateBox = lastChildBox(); - while (lastCandidateBox && !isChildHitTestCandidate(lastCandidateBox)) - lastCandidateBox = lastCandidateBox->previousSiblingBox(); - - if (lastCandidateBox) { - if (pointInLogicalContents.y() > logicalTopForChild(lastCandidateBox) - || (pointInLogicalContents.y() == logicalTopForChild(lastCandidateBox))) - return positionForPointInChild(lastCandidateBox, pointInContents); - - for (RenderBox* childBox = firstChildBox(); childBox; childBox = childBox->nextSiblingBox()) { - if (!isChildHitTestCandidate(childBox)) - continue; - LayoutUnit childLogicalBottom = logicalTopForChild(childBox) + logicalHeightForChild(childBox); - // We hit child if our click is above the bottom of its padding box (like IE6/7 and FF3). - if (isChildHitTestCandidate(childBox) && (pointInLogicalContents.y() < childLogicalBottom)) - return positionForPointInChild(childBox, pointInContents); - } - } - - // We only get here if there are no hit test candidate children below the click. - return RenderBox::positionForPoint(point); -} - -LayoutUnit RenderBlock::availableLogicalWidth() const -{ - return RenderBox::availableLogicalWidth(); -} - -void RenderBlock::computePreferredLogicalWidths() -{ - ASSERT(preferredLogicalWidthsDirty()); - - m_minPreferredLogicalWidth = 0; - m_maxPreferredLogicalWidth = 0; - - // FIXME: The isFixed() calls here should probably be checking for isSpecified since you - // should be able to use percentage, calc or viewport relative values for width. - RenderStyle* styleToUse = style(); - if (styleToUse->logicalWidth().isFixed() && styleToUse->logicalWidth().value() >= 0) - m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalWidth().value()); - else - computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth); - if (styleToUse->logicalMinWidth().isFixed() && styleToUse->logicalMinWidth().value() > 0) { - m_maxPreferredLogicalWidth = std::max(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMinWidth().value())); - m_minPreferredLogicalWidth = std::max(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMinWidth().value())); + // look for the closest line box in the root box which is at the passed-in y + // coordinate + InlineBox* closestBox = 0; + RootInlineBox* firstRootBoxWithChildren = 0; + RootInlineBox* lastRootBoxWithChildren = 0; + for (RootInlineBox* root = firstRootBox(); root; root = root->nextRootBox()) { + if (!root->firstLeafChild()) + continue; + if (!firstRootBoxWithChildren) + firstRootBoxWithChildren = root; + + lastRootBoxWithChildren = root; + + // check if this root line box is located at this y coordinate + if (pointInLogicalContents.y() < root->selectionBottom()) { + closestBox = root->closestLeafChildForLogicalLeftPosition( + pointInLogicalContents.x()); + if (closestBox) + break; } - - if (styleToUse->logicalMaxWidth().isFixed()) { - m_maxPreferredLogicalWidth = std::min(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMaxWidth().value())); - m_minPreferredLogicalWidth = std::min(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMaxWidth().value())); + } + + if (!closestBox && lastRootBoxWithChildren) { + // y coordinate is below last root line box, pretend we hit it + closestBox = + lastRootBoxWithChildren->closestLeafChildForLogicalLeftPosition( + pointInLogicalContents.x()); + } + + if (closestBox) { + // pass the box a top position that is inside it + LayoutPoint point(pointInLogicalContents.x(), + closestBox->root().blockDirectionPointInLine()); + if (closestBox->renderer().isReplaced()) + return positionForPointInChild(&toRenderBox(closestBox->renderer()), + point); + return closestBox->renderer().positionForPoint(point); + } + + // Can't reach this. We have a root line box, but it has no kids. + // FIXME: This should ASSERT_NOT_REACHED(), but clicking on placeholder text + // seems to hit this code path. + return createPositionWithAffinity(0, DOWNSTREAM); +} + +static inline bool isChildHitTestCandidate(RenderBox* box) { + return box->height() && !box->isFloatingOrOutOfFlowPositioned(); +} + +PositionWithAffinity RenderBlock::positionForPoint(const LayoutPoint& point) { + if (isReplaced()) { + // FIXME: This seems wrong when the object's writing-mode doesn't match the + // line's writing-mode. + LayoutUnit pointLogicalLeft = point.x(); + LayoutUnit pointLogicalTop = point.y(); + + if (pointLogicalLeft < 0) + return createPositionWithAffinity(caretMinOffset(), DOWNSTREAM); + if (pointLogicalLeft >= logicalWidth()) + return createPositionWithAffinity(caretMaxOffset(), DOWNSTREAM); + if (pointLogicalTop < 0) + return createPositionWithAffinity(caretMinOffset(), DOWNSTREAM); + if (pointLogicalTop >= logicalHeight()) + return createPositionWithAffinity(caretMaxOffset(), DOWNSTREAM); + } + + LayoutPoint pointInContents = point; + LayoutPoint pointInLogicalContents(pointInContents); + + if (isRenderParagraph()) + return positionForPointWithInlineChildren(pointInLogicalContents); + + RenderBox* lastCandidateBox = lastChildBox(); + while (lastCandidateBox && !isChildHitTestCandidate(lastCandidateBox)) + lastCandidateBox = lastCandidateBox->previousSiblingBox(); + + if (lastCandidateBox) { + if (pointInLogicalContents.y() > logicalTopForChild(lastCandidateBox) || + (pointInLogicalContents.y() == logicalTopForChild(lastCandidateBox))) + return positionForPointInChild(lastCandidateBox, pointInContents); + + for (RenderBox* childBox = firstChildBox(); childBox; + childBox = childBox->nextSiblingBox()) { + if (!isChildHitTestCandidate(childBox)) + continue; + LayoutUnit childLogicalBottom = + logicalTopForChild(childBox) + logicalHeightForChild(childBox); + // We hit child if our click is above the bottom of its padding box (like + // IE6/7 and FF3). + if (isChildHitTestCandidate(childBox) && + (pointInLogicalContents.y() < childLogicalBottom)) + return positionForPointInChild(childBox, pointInContents); } - - LayoutUnit borderAndPadding = borderAndPaddingLogicalWidth(); - m_minPreferredLogicalWidth += borderAndPadding; - m_maxPreferredLogicalWidth += borderAndPadding; - - clearPreferredLogicalWidthsDirty(); -} - -void RenderBlock::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const -{ - RenderStyle* styleToUse = style(); - bool nowrap = styleToUse->whiteSpace() == NOWRAP; - - RenderObject* child = firstChild(); - while (child) { - // Positioned children don't affect the min/max width - if (child->isOutOfFlowPositioned()) { - child = child->nextSibling(); - continue; - } - - RefPtr childStyle = child->style(); - - // A margin basically has three types: fixed, percentage, and auto (variable). - // Auto and percentage margins simply become 0 when computing min/max width. - // Fixed margins can be added in as is. - Length startMarginLength = childStyle->marginStartUsing(styleToUse); - Length endMarginLength = childStyle->marginEndUsing(styleToUse); - LayoutUnit margin = 0; - LayoutUnit marginStart = 0; - LayoutUnit marginEnd = 0; - if (startMarginLength.isFixed()) - marginStart += startMarginLength.value(); - if (endMarginLength.isFixed()) - marginEnd += endMarginLength.value(); - margin = marginStart + marginEnd; - - LayoutUnit childMinPreferredLogicalWidth = child->minPreferredLogicalWidth(); - LayoutUnit childMaxPreferredLogicalWidth = child->maxPreferredLogicalWidth(); - - LayoutUnit w = childMinPreferredLogicalWidth + margin; - minLogicalWidth = std::max(w, minLogicalWidth); - - // IE ignores tables for calculation of nowrap. Makes some sense. - if (nowrap) - maxLogicalWidth = std::max(w, maxLogicalWidth); - - w = childMaxPreferredLogicalWidth + margin; - - maxLogicalWidth = std::max(w, maxLogicalWidth); - - child = child->nextSibling(); - } - - // Always make sure these values are non-negative. - minLogicalWidth = std::max(0, minLogicalWidth); - maxLogicalWidth = std::max(minLogicalWidth, maxLogicalWidth); -} - -bool RenderBlock::hasLineIfEmpty() const -{ - return false; -} - -LayoutUnit RenderBlock::lineHeight(bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const -{ - // Inline blocks are replaced elements. Otherwise, just pass off to - // the base class. If we're being queried as though we're the root line - // box, then the fact that we're an inline-block is irrelevant, and we behave - // just like a block. - if (isReplaced() && linePositionMode == PositionOnContainingLine) - return RenderBox::lineHeight(firstLine, direction, linePositionMode); - return style()->computedLineHeight(); -} - -int RenderBlock::beforeMarginInLineDirection(LineDirectionMode direction) const -{ - return direction == HorizontalLine ? marginTop() : marginRight(); -} - -int RenderBlock::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const -{ - // Inline blocks are replaced elements. Otherwise, just pass off to - // the base class. If we're being queried as though we're the root line - // box, then the fact that we're an inline-block is irrelevant, and we behave - // just like a block. - if (isInline() && linePositionMode == PositionOnContainingLine) { - // CSS2.1 states that the baseline of an inline block is the baseline of the last line box in - // the normal flow. We make an exception for marquees, since their baselines are meaningless - // (the content inside them moves). This matches WinIE as well, which just bottom-aligns them. - // We also give up on finding a baseline if we have a vertical scrollbar, or if we are scrolled - // vertically (e.g., an overflow:hidden block that has had scrollTop moved). - int baselinePos = inlineBlockBaseline(direction); - if (baselinePos != -1) - return beforeMarginInLineDirection(direction) + baselinePos; - - return RenderBox::baselinePosition(baselineType, firstLine, direction, linePositionMode); + } + + // We only get here if there are no hit test candidate children below the + // click. + return RenderBox::positionForPoint(point); +} + +LayoutUnit RenderBlock::availableLogicalWidth() const { + return RenderBox::availableLogicalWidth(); +} + +void RenderBlock::computePreferredLogicalWidths() { + ASSERT(preferredLogicalWidthsDirty()); + + m_minPreferredLogicalWidth = 0; + m_maxPreferredLogicalWidth = 0; + + // FIXME: The isFixed() calls here should probably be checking for isSpecified + // since you should be able to use percentage, calc or viewport relative + // values for width. + RenderStyle* styleToUse = style(); + if (styleToUse->logicalWidth().isFixed() && + styleToUse->logicalWidth().value() >= 0) + m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = + adjustContentBoxLogicalWidthForBoxSizing( + styleToUse->logicalWidth().value()); + else + computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, + m_maxPreferredLogicalWidth); + + if (styleToUse->logicalMinWidth().isFixed() && + styleToUse->logicalMinWidth().value() > 0) { + m_maxPreferredLogicalWidth = std::max( + m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing( + styleToUse->logicalMinWidth().value())); + m_minPreferredLogicalWidth = std::max( + m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing( + styleToUse->logicalMinWidth().value())); + } + + if (styleToUse->logicalMaxWidth().isFixed()) { + m_maxPreferredLogicalWidth = std::min( + m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing( + styleToUse->logicalMaxWidth().value())); + m_minPreferredLogicalWidth = std::min( + m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing( + styleToUse->logicalMaxWidth().value())); + } + + LayoutUnit borderAndPadding = borderAndPaddingLogicalWidth(); + m_minPreferredLogicalWidth += borderAndPadding; + m_maxPreferredLogicalWidth += borderAndPadding; + + clearPreferredLogicalWidthsDirty(); +} + +void RenderBlock::computeIntrinsicLogicalWidths( + LayoutUnit& minLogicalWidth, + LayoutUnit& maxLogicalWidth) const { + RenderStyle* styleToUse = style(); + bool nowrap = styleToUse->whiteSpace() == NOWRAP; + + RenderObject* child = firstChild(); + while (child) { + // Positioned children don't affect the min/max width + if (child->isOutOfFlowPositioned()) { + child = child->nextSibling(); + continue; } - // If we're not replaced, we'll only get called with PositionOfInteriorLineBoxes. - // Note that inline-block counts as replaced here. - ASSERT(linePositionMode == PositionOfInteriorLineBoxes); - - const FontMetrics& fontMetrics = style(firstLine)->fontMetrics(); - return fontMetrics.ascent(baselineType) + (lineHeight(firstLine, direction, linePositionMode) - fontMetrics.height()) / 2; -} + RefPtr childStyle = child->style(); -LayoutUnit RenderBlock::minLineHeightForReplacedRenderer(bool isFirstLine, LayoutUnit replacedHeight) const -{ - if (!(style(isFirstLine)->lineBoxContain() & LineBoxContainBlock)) - return 0; + // A margin basically has three types: fixed, percentage, and auto + // (variable). Auto and percentage margins simply become 0 when computing + // min/max width. Fixed margins can be added in as is. + Length startMarginLength = childStyle->marginStartUsing(styleToUse); + Length endMarginLength = childStyle->marginEndUsing(styleToUse); + LayoutUnit margin = 0; + LayoutUnit marginStart = 0; + LayoutUnit marginEnd = 0; + if (startMarginLength.isFixed()) + marginStart += startMarginLength.value(); + if (endMarginLength.isFixed()) + marginEnd += endMarginLength.value(); + margin = marginStart + marginEnd; + + LayoutUnit childMinPreferredLogicalWidth = + child->minPreferredLogicalWidth(); + LayoutUnit childMaxPreferredLogicalWidth = + child->maxPreferredLogicalWidth(); + + LayoutUnit w = childMinPreferredLogicalWidth + margin; + minLogicalWidth = std::max(w, minLogicalWidth); + + // IE ignores tables for calculation of nowrap. Makes some sense. + if (nowrap) + maxLogicalWidth = std::max(w, maxLogicalWidth); + + w = childMaxPreferredLogicalWidth + margin; + + maxLogicalWidth = std::max(w, maxLogicalWidth); + + child = child->nextSibling(); + } + + // Always make sure these values are non-negative. + minLogicalWidth = std::max(0, minLogicalWidth); + maxLogicalWidth = std::max(minLogicalWidth, maxLogicalWidth); +} + +bool RenderBlock::hasLineIfEmpty() const { + return false; +} + +LayoutUnit RenderBlock::lineHeight(bool firstLine, + LineDirectionMode direction, + LinePositionMode linePositionMode) const { + // Inline blocks are replaced elements. Otherwise, just pass off to + // the base class. If we're being queried as though we're the root line + // box, then the fact that we're an inline-block is irrelevant, and we behave + // just like a block. + if (isReplaced() && linePositionMode == PositionOnContainingLine) + return RenderBox::lineHeight(firstLine, direction, linePositionMode); + return style()->computedLineHeight(); +} + +int RenderBlock::beforeMarginInLineDirection( + LineDirectionMode direction) const { + return direction == HorizontalLine ? marginTop() : marginRight(); +} + +int RenderBlock::baselinePosition(FontBaseline baselineType, + bool firstLine, + LineDirectionMode direction, + LinePositionMode linePositionMode) const { + // Inline blocks are replaced elements. Otherwise, just pass off to + // the base class. If we're being queried as though we're the root line + // box, then the fact that we're an inline-block is irrelevant, and we behave + // just like a block. + if (isInline() && linePositionMode == PositionOnContainingLine) { + // CSS2.1 states that the baseline of an inline block is the baseline of the + // last line box in the normal flow. We make an exception for marquees, + // since their baselines are meaningless (the content inside them moves). + // This matches WinIE as well, which just bottom-aligns them. We also give + // up on finding a baseline if we have a vertical scrollbar, or if we are + // scrolled vertically (e.g., an overflow:hidden block that has had + // scrollTop moved). + int baselinePos = inlineBlockBaseline(direction); + if (baselinePos != -1) + return beforeMarginInLineDirection(direction) + baselinePos; + + return RenderBox::baselinePosition(baselineType, firstLine, direction, + linePositionMode); + } + + // If we're not replaced, we'll only get called with + // PositionOfInteriorLineBoxes. Note that inline-block counts as replaced + // here. + ASSERT(linePositionMode == PositionOfInteriorLineBoxes); + + const FontMetrics& fontMetrics = style(firstLine)->fontMetrics(); + return fontMetrics.ascent(baselineType) + + (lineHeight(firstLine, direction, linePositionMode) - + fontMetrics.height()) / + 2; +} + +LayoutUnit RenderBlock::minLineHeightForReplacedRenderer( + bool isFirstLine, + LayoutUnit replacedHeight) const { + if (!(style(isFirstLine)->lineBoxContain() & LineBoxContainBlock)) + return 0; - return std::max(replacedHeight, lineHeight(isFirstLine, HorizontalLine, PositionOfInteriorLineBoxes)); + return std::max( + replacedHeight, + lineHeight(isFirstLine, HorizontalLine, PositionOfInteriorLineBoxes)); } -int RenderBlock::firstLineBoxBaseline(FontBaselineOrAuto baselineType) const -{ - for (RenderBox* curr = firstChildBox(); curr; curr = curr->nextSiblingBox()) { - if (!curr->isFloatingOrOutOfFlowPositioned()) { - int result = curr->firstLineBoxBaseline(baselineType); - if (result != -1) - return curr->logicalTop() + result; // Translate to our coordinate space. - } +int RenderBlock::firstLineBoxBaseline(FontBaselineOrAuto baselineType) const { + for (RenderBox* curr = firstChildBox(); curr; curr = curr->nextSiblingBox()) { + if (!curr->isFloatingOrOutOfFlowPositioned()) { + int result = curr->firstLineBoxBaseline(baselineType); + if (result != -1) + return curr->logicalTop() + + result; // Translate to our coordinate space. } + } - return -1; + return -1; } -int RenderBlock::inlineBlockBaseline(LineDirectionMode direction) const -{ - if (!style()->isOverflowVisible()) { - // We are not calling RenderBox::baselinePosition here because the caller should add the margin-top/margin-right, not us. - return direction == HorizontalLine ? height() + m_marginBox.bottom() : width() + m_marginBox.left(); - } +int RenderBlock::inlineBlockBaseline(LineDirectionMode direction) const { + if (!style()->isOverflowVisible()) { + // We are not calling RenderBox::baselinePosition here because the caller + // should add the margin-top/margin-right, not us. + return direction == HorizontalLine ? height() + m_marginBox.bottom() + : width() + m_marginBox.left(); + } - return lastLineBoxBaseline(direction); + return lastLineBoxBaseline(direction); } -int RenderBlock::lastLineBoxBaseline(LineDirectionMode lineDirection) const -{ - bool haveNormalFlowChild = false; - for (RenderBox* curr = lastChildBox(); curr; curr = curr->previousSiblingBox()) { - if (!curr->isFloatingOrOutOfFlowPositioned()) { - haveNormalFlowChild = true; - int result = curr->inlineBlockBaseline(lineDirection); - if (result != -1) - return curr->logicalTop() + result; // Translate to our coordinate space. - } - } - if (!haveNormalFlowChild && hasLineIfEmpty()) { - const FontMetrics& fontMetrics = firstLineStyle()->fontMetrics(); - return fontMetrics.ascent() - + (lineHeight(true, lineDirection, PositionOfInteriorLineBoxes) - fontMetrics.height()) / 2 - + (lineDirection == HorizontalLine ? borderTop() + paddingTop() : borderRight() + paddingRight()); - } - - return -1; -} - -RenderBlock* RenderBlock::firstLineBlock() const -{ - RenderBlock* firstLineBlock = const_cast(this); - bool hasPseudo = false; - while (true) { - // FIXME(sky): Remove all this. - hasPseudo = false; - if (hasPseudo) - break; - RenderObject* parentBlock = firstLineBlock->parent(); - if (firstLineBlock->isReplaced() - || !parentBlock - || !parentBlock->isRenderParagraph()) - break; - ASSERT_WITH_SECURITY_IMPLICATION(parentBlock->isRenderBlock()); - if (toRenderParagraph(parentBlock)->firstChild() != firstLineBlock) - break; - firstLineBlock = toRenderBlock(parentBlock); +int RenderBlock::lastLineBoxBaseline(LineDirectionMode lineDirection) const { + bool haveNormalFlowChild = false; + for (RenderBox* curr = lastChildBox(); curr; + curr = curr->previousSiblingBox()) { + if (!curr->isFloatingOrOutOfFlowPositioned()) { + haveNormalFlowChild = true; + int result = curr->inlineBlockBaseline(lineDirection); + if (result != -1) + return curr->logicalTop() + + result; // Translate to our coordinate space. } + } + if (!haveNormalFlowChild && hasLineIfEmpty()) { + const FontMetrics& fontMetrics = firstLineStyle()->fontMetrics(); + return fontMetrics.ascent() + + (lineHeight(true, lineDirection, PositionOfInteriorLineBoxes) - + fontMetrics.height()) / + 2 + + (lineDirection == HorizontalLine ? borderTop() + paddingTop() + : borderRight() + paddingRight()); + } + + return -1; +} + +RenderBlock* RenderBlock::firstLineBlock() const { + RenderBlock* firstLineBlock = const_cast(this); + bool hasPseudo = false; + while (true) { + // FIXME(sky): Remove all this. + hasPseudo = false; + if (hasPseudo) + break; + RenderObject* parentBlock = firstLineBlock->parent(); + if (firstLineBlock->isReplaced() || !parentBlock || + !parentBlock->isRenderParagraph()) + break; + ASSERT_WITH_SECURITY_IMPLICATION(parentBlock->isRenderBlock()); + if (toRenderParagraph(parentBlock)->firstChild() != firstLineBlock) + break; + firstLineBlock = toRenderBlock(parentBlock); + } + + if (!hasPseudo) + return 0; - if (!hasPseudo) - return 0; - - return firstLineBlock; + return firstLineBlock; } -// Helper methods for obtaining the last line, computing line counts and heights for line counts -// (crawling into blocks). -static bool shouldCheckLines(RenderObject* obj) -{ - return !obj->isFloatingOrOutOfFlowPositioned() - && obj->isRenderBlock() && obj->style()->height().isAuto(); +// Helper methods for obtaining the last line, computing line counts and heights +// for line counts (crawling into blocks). +static bool shouldCheckLines(RenderObject* obj) { + return !obj->isFloatingOrOutOfFlowPositioned() && obj->isRenderBlock() && + obj->style()->height().isAuto(); } -RootInlineBox* RenderBlock::lineAtIndex(int i) const -{ - ASSERT(i >= 0); +RootInlineBox* RenderBlock::lineAtIndex(int i) const { + ASSERT(i >= 0); - for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { - if (!shouldCheckLines(child)) - continue; - if (RootInlineBox* box = toRenderBlock(child)->lineAtIndex(i)) - return box; - } + for (RenderObject* child = firstChild(); child; + child = child->nextSibling()) { + if (!shouldCheckLines(child)) + continue; + if (RootInlineBox* box = toRenderBlock(child)->lineAtIndex(i)) + return box; + } - return 0; + return 0; } -int RenderBlock::lineCount(const RootInlineBox* stopRootInlineBox, bool* found) const -{ - int count = 0; - for (RenderObject* obj = firstChild(); obj; obj = obj->nextSibling()) { - if (shouldCheckLines(obj)) { - bool recursiveFound = false; - count += toRenderBlock(obj)->lineCount(stopRootInlineBox, &recursiveFound); - if (recursiveFound) { - if (found) - *found = true; - break; - } - } +int RenderBlock::lineCount(const RootInlineBox* stopRootInlineBox, + bool* found) const { + int count = 0; + for (RenderObject* obj = firstChild(); obj; obj = obj->nextSibling()) { + if (shouldCheckLines(obj)) { + bool recursiveFound = false; + count += + toRenderBlock(obj)->lineCount(stopRootInlineBox, &recursiveFound); + if (recursiveFound) { + if (found) + *found = true; + break; + } } + } - return count; + return count; } -void RenderBlock::clearTruncation() -{ - for (RenderObject* obj = firstChild(); obj; obj = obj->nextSibling()) { - if (shouldCheckLines(obj)) - toRenderBlock(obj)->clearTruncation(); - } +void RenderBlock::clearTruncation() { + for (RenderObject* obj = firstChild(); obj; obj = obj->nextSibling()) { + if (shouldCheckLines(obj)) + toRenderBlock(obj)->clearTruncation(); + } } -void RenderBlock::absoluteQuads(Vector& quads) const -{ - quads.append(RenderBox::localToAbsoluteQuad(FloatRect(0, 0, width().toFloat(), height().toFloat()), 0 /* mode */)); +void RenderBlock::absoluteQuads(Vector& quads) const { + quads.append(RenderBox::localToAbsoluteQuad( + FloatRect(0, 0, width().toFloat(), height().toFloat()), 0 /* mode */)); } -void RenderBlock::updateHitTestResult(HitTestResult& result, const LayoutPoint& point) -{ -} +void RenderBlock::updateHitTestResult(HitTestResult& result, + const LayoutPoint& point) {} -LayoutRect RenderBlock::localCaretRect(InlineBox* inlineBox, int caretOffset, LayoutUnit* extraWidthToEndOfLine) -{ - // Do the normal calculation in most cases. - if (firstChild()) - return RenderBox::localCaretRect(inlineBox, caretOffset, extraWidthToEndOfLine); +LayoutRect RenderBlock::localCaretRect(InlineBox* inlineBox, + int caretOffset, + LayoutUnit* extraWidthToEndOfLine) { + // Do the normal calculation in most cases. + if (firstChild()) + return RenderBox::localCaretRect(inlineBox, caretOffset, + extraWidthToEndOfLine); - LayoutRect caretRect = localCaretRectForEmptyElement(width(), textIndentOffset()); + LayoutRect caretRect = + localCaretRectForEmptyElement(width(), textIndentOffset()); - if (extraWidthToEndOfLine) - *extraWidthToEndOfLine = width() - caretRect.maxX(); + if (extraWidthToEndOfLine) + *extraWidthToEndOfLine = width() - caretRect.maxX(); - return caretRect; + return caretRect; } -void RenderBlock::addFocusRingRects(Vector& rects, const LayoutPoint& additionalOffset, const RenderBox* paintContainer) const -{ - if (width() && height()) - rects.append(pixelSnappedIntRect(additionalOffset, size())); +void RenderBlock::addFocusRingRects(Vector& rects, + const LayoutPoint& additionalOffset, + const RenderBox* paintContainer) const { + if (width() && height()) + rects.append(pixelSnappedIntRect(additionalOffset, size())); - if (!hasOverflowClip()) { - for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) { - LayoutUnit top = std::max(curr->lineTop(), curr->top()); - LayoutUnit bottom = std::min(curr->lineBottom(), curr->top() + curr->height()); - LayoutRect rect(additionalOffset.x() + curr->x(), additionalOffset.y() + top, curr->width(), bottom - top); - if (!rect.isEmpty()) - rects.append(pixelSnappedIntRect(rect)); - } - - addChildFocusRingRects(rects, additionalOffset, paintContainer); + if (!hasOverflowClip()) { + for (RootInlineBox* curr = firstRootBox(); curr; + curr = curr->nextRootBox()) { + LayoutUnit top = std::max(curr->lineTop(), curr->top()); + LayoutUnit bottom = std::min(curr->lineBottom(), + curr->top() + curr->height()); + LayoutRect rect(additionalOffset.x() + curr->x(), + additionalOffset.y() + top, curr->width(), bottom - top); + if (!rect.isEmpty()) + rects.append(pixelSnappedIntRect(rect)); } + + addChildFocusRingRects(rects, additionalOffset, paintContainer); + } } -LayoutUnit RenderBlock::marginBeforeForChild(const RenderBox* child) const -{ - // FIXME(sky): Remove - return child->marginBefore(); +LayoutUnit RenderBlock::marginBeforeForChild(const RenderBox* child) const { + // FIXME(sky): Remove + return child->marginBefore(); } -LayoutUnit RenderBlock::marginAfterForChild(const RenderBox* child) const -{ - // FIXME(sky): Remove - return child->marginAfter(); +LayoutUnit RenderBlock::marginAfterForChild(const RenderBox* child) const { + // FIXME(sky): Remove + return child->marginAfter(); } -bool RenderBlock::hasMarginBeforeQuirk(const RenderBox* child) const -{ - return child->isRenderBlock() ? toRenderBlock(child)->hasMarginBeforeQuirk() : child->style()->hasMarginBeforeQuirk(); +bool RenderBlock::hasMarginBeforeQuirk(const RenderBox* child) const { + return child->isRenderBlock() ? toRenderBlock(child)->hasMarginBeforeQuirk() + : child->style()->hasMarginBeforeQuirk(); } -bool RenderBlock::hasMarginAfterQuirk(const RenderBox* child) const -{ - return child->isRenderBlock() ? toRenderBlock(child)->hasMarginAfterQuirk() : child->style()->hasMarginAfterQuirk(); +bool RenderBlock::hasMarginAfterQuirk(const RenderBox* child) const { + return child->isRenderBlock() ? toRenderBlock(child)->hasMarginAfterQuirk() + : child->style()->hasMarginAfterQuirk(); } -const char* RenderBlock::renderName() const -{ - if (isInlineBlock()) - return "RenderBlock (inline-block)"; - if (isOutOfFlowPositioned()) - return "RenderBlock (positioned)"; - return "RenderBlock"; +const char* RenderBlock::renderName() const { + if (isInlineBlock()) + return "RenderBlock (inline-block)"; + if (isOutOfFlowPositioned()) + return "RenderBlock (positioned)"; + return "RenderBlock"; } -static bool recalcNormalFlowChildOverflowIfNeeded(RenderObject* renderer) -{ - if (renderer->isOutOfFlowPositioned() || !renderer->needsOverflowRecalcAfterStyleChange()) - return false; +static bool recalcNormalFlowChildOverflowIfNeeded(RenderObject* renderer) { + if (renderer->isOutOfFlowPositioned() || + !renderer->needsOverflowRecalcAfterStyleChange()) + return false; - ASSERT(renderer->isRenderBlock()); - return toRenderBlock(renderer)->recalcOverflowAfterStyleChange(); + ASSERT(renderer->isRenderBlock()); + return toRenderBlock(renderer)->recalcOverflowAfterStyleChange(); } -bool RenderBlock::recalcChildOverflowAfterStyleChange() -{ - ASSERT(childNeedsOverflowRecalcAfterStyleChange()); - setChildNeedsOverflowRecalcAfterStyleChange(false); +bool RenderBlock::recalcChildOverflowAfterStyleChange() { + ASSERT(childNeedsOverflowRecalcAfterStyleChange()); + setChildNeedsOverflowRecalcAfterStyleChange(false); - bool childrenOverflowChanged = false; + bool childrenOverflowChanged = false; - if (isRenderParagraph()) { - ListHashSet lineBoxes; - for (InlineWalker walker(this); !walker.atEnd(); walker.advance()) { - RenderObject* renderer = walker.current(); - if (recalcNormalFlowChildOverflowIfNeeded(renderer)) { - childrenOverflowChanged = true; - if (InlineBox* inlineBoxWrapper = toRenderBlock(renderer)->inlineBoxWrapper()) - lineBoxes.add(&inlineBoxWrapper->root()); - } - } + if (isRenderParagraph()) { + ListHashSet lineBoxes; + for (InlineWalker walker(this); !walker.atEnd(); walker.advance()) { + RenderObject* renderer = walker.current(); + if (recalcNormalFlowChildOverflowIfNeeded(renderer)) { + childrenOverflowChanged = true; + if (InlineBox* inlineBoxWrapper = + toRenderBlock(renderer)->inlineBoxWrapper()) + lineBoxes.add(&inlineBoxWrapper->root()); + } + } - // FIXME: Glyph overflow will get lost in this case, but not really a big deal. - GlyphOverflowAndFallbackFontsMap textBoxDataMap; - for (ListHashSet::const_iterator it = lineBoxes.begin(); it != lineBoxes.end(); ++it) { - RootInlineBox* box = *it; - box->computeOverflow(box->lineTop(), box->lineBottom(), textBoxDataMap); - } - } else { - for (RenderBox* box = firstChildBox(); box; box = box->nextSiblingBox()) { - if (recalcNormalFlowChildOverflowIfNeeded(box)) - childrenOverflowChanged = true; - } + // FIXME: Glyph overflow will get lost in this case, but not really a big + // deal. + GlyphOverflowAndFallbackFontsMap textBoxDataMap; + for (ListHashSet::const_iterator it = lineBoxes.begin(); + it != lineBoxes.end(); ++it) { + RootInlineBox* box = *it; + box->computeOverflow(box->lineTop(), box->lineBottom(), textBoxDataMap); + } + } else { + for (RenderBox* box = firstChildBox(); box; box = box->nextSiblingBox()) { + if (recalcNormalFlowChildOverflowIfNeeded(box)) + childrenOverflowChanged = true; } + } - TrackedRendererListHashSet* positionedDescendants = positionedObjects(); - if (!positionedDescendants) - return childrenOverflowChanged; + TrackedRendererListHashSet* positionedDescendants = positionedObjects(); + if (!positionedDescendants) + return childrenOverflowChanged; - TrackedRendererListHashSet::iterator end = positionedDescendants->end(); - for (TrackedRendererListHashSet::iterator it = positionedDescendants->begin(); it != end; ++it) { - RenderBox* box = *it; + TrackedRendererListHashSet::iterator end = positionedDescendants->end(); + for (TrackedRendererListHashSet::iterator it = positionedDescendants->begin(); + it != end; ++it) { + RenderBox* box = *it; - if (!box->needsOverflowRecalcAfterStyleChange()) - continue; - RenderBlock* block = toRenderBlock(box); - if (!block->recalcOverflowAfterStyleChange()) - continue; + if (!box->needsOverflowRecalcAfterStyleChange()) + continue; + RenderBlock* block = toRenderBlock(box); + if (!block->recalcOverflowAfterStyleChange()) + continue; - childrenOverflowChanged = true; - } - return childrenOverflowChanged; + childrenOverflowChanged = true; + } + return childrenOverflowChanged; } -bool RenderBlock::recalcOverflowAfterStyleChange() -{ - ASSERT(needsOverflowRecalcAfterStyleChange()); +bool RenderBlock::recalcOverflowAfterStyleChange() { + ASSERT(needsOverflowRecalcAfterStyleChange()); - bool childrenOverflowChanged = false; - if (childNeedsOverflowRecalcAfterStyleChange()) - childrenOverflowChanged = recalcChildOverflowAfterStyleChange(); + bool childrenOverflowChanged = false; + if (childNeedsOverflowRecalcAfterStyleChange()) + childrenOverflowChanged = recalcChildOverflowAfterStyleChange(); - if (!selfNeedsOverflowRecalcAfterStyleChange() && !childrenOverflowChanged) - return false; + if (!selfNeedsOverflowRecalcAfterStyleChange() && !childrenOverflowChanged) + return false; - setSelfNeedsOverflowRecalcAfterStyleChange(false); - // If the current block needs layout, overflow will be recalculated during - // layout time anyway. We can safely exit here. - if (needsLayout()) - return false; + setSelfNeedsOverflowRecalcAfterStyleChange(false); + // If the current block needs layout, overflow will be recalculated during + // layout time anyway. We can safely exit here. + if (needsLayout()) + return false; - LayoutUnit oldClientAfterEdge = hasRenderOverflow() ? m_overflow->layoutClientAfterEdge() : clientLogicalBottom(); - computeOverflow(oldClientAfterEdge, true); + LayoutUnit oldClientAfterEdge = hasRenderOverflow() + ? m_overflow->layoutClientAfterEdge() + : clientLogicalBottom(); + computeOverflow(oldClientAfterEdge, true); - return !hasOverflowClip(); + return !hasOverflowClip(); } #if ENABLE(ASSERT) -void RenderBlock::checkPositionedObjectsNeedLayout() -{ - if (!gPositionedDescendantsMap) - return; - - if (TrackedRendererListHashSet* positionedDescendantSet = positionedObjects()) { - TrackedRendererListHashSet::const_iterator end = positionedDescendantSet->end(); - for (TrackedRendererListHashSet::const_iterator it = positionedDescendantSet->begin(); it != end; ++it) { - RenderBox* currBox = *it; - ASSERT(!currBox->needsLayout()); - } +void RenderBlock::checkPositionedObjectsNeedLayout() { + if (!gPositionedDescendantsMap) + return; + + if (TrackedRendererListHashSet* positionedDescendantSet = + positionedObjects()) { + TrackedRendererListHashSet::const_iterator end = + positionedDescendantSet->end(); + for (TrackedRendererListHashSet::const_iterator it = + positionedDescendantSet->begin(); + it != end; ++it) { + RenderBox* currBox = *it; + ASSERT(!currBox->needsLayout()); } + } } #endif #ifndef NDEBUG -void RenderBlock::showLineTreeAndMark(const InlineBox* markedBox1, const char* markedLabel1, const InlineBox* markedBox2, const char* markedLabel2, const RenderObject* obj) const -{ - showRenderObject(); - for (const RootInlineBox* root = firstRootBox(); root; root = root->nextRootBox()) - root->showLineTreeAndMark(markedBox1, markedLabel1, markedBox2, markedLabel2, obj, 1); +void RenderBlock::showLineTreeAndMark(const InlineBox* markedBox1, + const char* markedLabel1, + const InlineBox* markedBox2, + const char* markedLabel2, + const RenderObject* obj) const { + showRenderObject(); + for (const RootInlineBox* root = firstRootBox(); root; + root = root->nextRootBox()) + root->showLineTreeAndMark(markedBox1, markedLabel1, markedBox2, + markedLabel2, obj, 1); } #endif -} // namespace blink +} // namespace blink diff --git a/sky/engine/core/rendering/RenderBlock.h b/sky/engine/core/rendering/RenderBlock.h index 075c940aaa895..a1f6ca67fffa0 100644 --- a/sky/engine/core/rendering/RenderBlock.h +++ b/sky/engine/core/rendering/RenderBlock.h @@ -2,7 +2,8 @@ * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2007 David Smith (catfish.man@gmail.com) - * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All + * rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -42,269 +43,413 @@ class RenderInline; class RenderText; class WordMeasurement; -template class BidiRunList; +template +class BidiRunList; typedef WTF::ListHashSet TrackedRendererListHashSet; -typedef WTF::HashMap > TrackedDescendantsMap; -typedef WTF::HashMap > > TrackedContainerMap; +typedef WTF::HashMap> + TrackedDescendantsMap; +typedef WTF::HashMap>> + TrackedContainerMap; typedef Vector WordMeasurements; enum ContainingBlockState { NewContainingBlock, SameContainingBlock }; class RenderBlock : public RenderBox { -public: - virtual void destroy() override; - friend class LineLayoutState; - -protected: - explicit RenderBlock(); - virtual ~RenderBlock(); - -public: - RenderObject* firstChild() const { ASSERT(children() == virtualChildren()); return children()->firstChild(); } - RenderObject* lastChild() const { ASSERT(children() == virtualChildren()); return children()->lastChild(); } - - // If you have a RenderBlock, use firstChild or lastChild instead. - void slowFirstChild() const = delete; - void slowLastChild() const = delete; - - const RenderObjectChildList* children() const { return &m_children; } - RenderObjectChildList* children() { return &m_children; } - - bool beingDestroyed() const { return m_beingDestroyed; } - - // These two functions are overridden for inline-block. - virtual LayoutUnit lineHeight(bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const override final; - virtual int baselinePosition(FontBaseline, bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const override; - - LayoutUnit minLineHeightForReplacedRenderer(bool isFirstLine, LayoutUnit replacedHeight) const; - -protected: - RenderLineBoxList* lineBoxes() { return &m_lineBoxes; } - - InlineFlowBox* firstLineBox() const { return m_lineBoxes.firstLineBox(); } - InlineFlowBox* lastLineBox() const { return m_lineBoxes.lastLineBox(); } - - RootInlineBox* firstRootBox() const { return static_cast(firstLineBox()); } - RootInlineBox* lastRootBox() const { return static_cast(lastLineBox()); } - -public: - // FIXME-BLOCKFLOW: Remove virtualizaion when all callers have moved to RenderParagraph - virtual void deleteLineBoxTree(); - - virtual void addChild(RenderObject* newChild, RenderObject* beforeChild = 0) override; - virtual void removeChild(RenderObject*) override; - - void insertPositionedObject(RenderBox*); - static void removePositionedObject(RenderBox*); - void removePositionedObjects(RenderBlock*, ContainingBlockState = SameContainingBlock); - - TrackedRendererListHashSet* positionedObjects() const; - bool hasPositionedObjects() const - { - TrackedRendererListHashSet* objects = positionedObjects(); - return objects && !objects->isEmpty(); - } - - void addPercentHeightDescendant(RenderBox*); - static void removePercentHeightDescendant(RenderBox*); - static bool hasPercentHeightContainerMap(); - static bool hasPercentHeightDescendant(RenderBox*); - static void clearPercentHeightDescendantsFrom(RenderBox*); - static void removePercentHeightDescendantIfNeeded(RenderBox*); - - TrackedRendererListHashSet* percentHeightDescendants() const; - bool hasPercentHeightDescendants() const - { - TrackedRendererListHashSet* descendants = percentHeightDescendants(); - return descendants && !descendants->isEmpty(); - } - - void setHasMarginBeforeQuirk(bool b) { m_hasMarginBeforeQuirk = b; } - void setHasMarginAfterQuirk(bool b) { m_hasMarginAfterQuirk = b; } - - bool hasMarginBeforeQuirk() const { return m_hasMarginBeforeQuirk; } - bool hasMarginAfterQuirk() const { return m_hasMarginAfterQuirk; } - - bool hasMarginBeforeQuirk(const RenderBox* child) const; - bool hasMarginAfterQuirk(const RenderBox* child) const; - - void markPositionedObjectsForLayout(); - - LayoutUnit textIndentOffset() const; - - virtual PositionWithAffinity positionForPoint(const LayoutPoint&) override; - - // Block flows subclass availableWidth to handle multi column layout (shrinking the width available to children when laying out.) - virtual LayoutUnit availableLogicalWidth() const override final; - - LayoutUnit blockDirectionOffset(const LayoutSize& offsetFromBlock) const; - LayoutUnit inlineDirectionOffset(const LayoutSize& offsetFromBlock) const; - - virtual bool shouldPaintSelectionGaps() const override final; - LayoutRect logicalLeftSelectionGap(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, - RenderObject* selObj, LayoutUnit logicalLeft, LayoutUnit logicalTop, LayoutUnit logicalHeight, const PaintInfo*); - LayoutRect logicalRightSelectionGap(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, - RenderObject* selObj, LayoutUnit logicalRight, LayoutUnit logicalTop, LayoutUnit logicalHeight, const PaintInfo*); - void getSelectionGapInfo(SelectionState, bool& leftGap, bool& rightGap); - RenderBlock* blockBeforeWithinSelectionRoot(LayoutSize& offset) const; - - virtual void setSelectionState(SelectionState) override; - - LayoutRect logicalRectToPhysicalRect(const LayoutPoint& physicalPosition, const LayoutRect& logicalRect); - - // Helper methods for computing line counts and heights for line counts. - virtual RootInlineBox* lineAtIndex(int) const; - virtual int lineCount(const RootInlineBox* = 0, bool* = 0) const; - void clearTruncation(); - - // Accessors for logical width/height and margins in the containing block's block-flow direction. - LayoutUnit logicalWidthForChild(const RenderBox* child) const { return child->width(); } - LayoutUnit logicalHeightForChild(const RenderBox* child) const { return child->height(); } - LayoutSize logicalSizeForChild(const RenderBox* child) const { return child->size(); } - LayoutUnit logicalTopForChild(const RenderBox* child) const { return child->y(); } - LayoutUnit marginBeforeForChild(const RenderBoxModelObject* child) const { return child->marginBefore(style()); } - LayoutUnit marginAfterForChild(const RenderBoxModelObject* child) const { return child->marginAfter(style()); } - LayoutUnit marginStartForChild(const RenderBoxModelObject* child) const { return child->marginStart(style()); } - LayoutUnit marginEndForChild(const RenderBoxModelObject* child) const { return child->marginEnd(style()); } - void setMarginStartForChild(RenderBox* child, LayoutUnit value) const { child->setMarginStart(value, style()); } - void setMarginEndForChild(RenderBox* child, LayoutUnit value) const { child->setMarginEnd(value, style()); } - void setMarginBeforeForChild(RenderBox* child, LayoutUnit value) const { child->setMarginBefore(value, style()); } - void setMarginAfterForChild(RenderBox* child, LayoutUnit value) const { child->setMarginAfter(value, style()); } - LayoutUnit marginBeforeForChild(const RenderBox* child) const; - LayoutUnit marginAfterForChild(const RenderBox* child) const; - - virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset) override; - - LayoutUnit availableLogicalWidthForContent() const { return max(0, logicalRightOffsetForContent() - logicalLeftOffsetForContent()); } - LayoutUnit logicalLeftOffsetForContent() const { return borderLeft() + paddingLeft(); } - LayoutUnit logicalRightOffsetForContent() const { return logicalLeftOffsetForContent() + availableLogicalWidth(); } - LayoutUnit startOffsetForContent() const { return style()->isLeftToRightDirection() ? logicalLeftOffsetForContent() : logicalWidth() - logicalRightOffsetForContent(); } - LayoutUnit endOffsetForContent() const { return !style()->isLeftToRightDirection() ? logicalLeftOffsetForContent() : logicalWidth() - logicalRightOffsetForContent(); } + public: + virtual void destroy() override; + friend class LineLayoutState; + + protected: + explicit RenderBlock(); + virtual ~RenderBlock(); + + public: + RenderObject* firstChild() const { + ASSERT(children() == virtualChildren()); + return children()->firstChild(); + } + RenderObject* lastChild() const { + ASSERT(children() == virtualChildren()); + return children()->lastChild(); + } + + // If you have a RenderBlock, use firstChild or lastChild instead. + void slowFirstChild() const = delete; + void slowLastChild() const = delete; + + const RenderObjectChildList* children() const { return &m_children; } + RenderObjectChildList* children() { return &m_children; } + + bool beingDestroyed() const { return m_beingDestroyed; } + + // These two functions are overridden for inline-block. + virtual LayoutUnit lineHeight( + bool firstLine, + LineDirectionMode, + LinePositionMode = PositionOnContainingLine) const override final; + virtual int baselinePosition( + FontBaseline, + bool firstLine, + LineDirectionMode, + LinePositionMode = PositionOnContainingLine) const override; + + LayoutUnit minLineHeightForReplacedRenderer(bool isFirstLine, + LayoutUnit replacedHeight) const; + + protected: + RenderLineBoxList* lineBoxes() { return &m_lineBoxes; } + + InlineFlowBox* firstLineBox() const { return m_lineBoxes.firstLineBox(); } + InlineFlowBox* lastLineBox() const { return m_lineBoxes.lastLineBox(); } + + RootInlineBox* firstRootBox() const { + return static_cast(firstLineBox()); + } + RootInlineBox* lastRootBox() const { + return static_cast(lastLineBox()); + } + + public: + // FIXME-BLOCKFLOW: Remove virtualizaion when all callers have moved to + // RenderParagraph + virtual void deleteLineBoxTree(); + + virtual void addChild(RenderObject* newChild, + RenderObject* beforeChild = 0) override; + virtual void removeChild(RenderObject*) override; + + void insertPositionedObject(RenderBox*); + static void removePositionedObject(RenderBox*); + void removePositionedObjects(RenderBlock*, + ContainingBlockState = SameContainingBlock); + + TrackedRendererListHashSet* positionedObjects() const; + bool hasPositionedObjects() const { + TrackedRendererListHashSet* objects = positionedObjects(); + return objects && !objects->isEmpty(); + } + + void addPercentHeightDescendant(RenderBox*); + static void removePercentHeightDescendant(RenderBox*); + static bool hasPercentHeightContainerMap(); + static bool hasPercentHeightDescendant(RenderBox*); + static void clearPercentHeightDescendantsFrom(RenderBox*); + static void removePercentHeightDescendantIfNeeded(RenderBox*); + + TrackedRendererListHashSet* percentHeightDescendants() const; + bool hasPercentHeightDescendants() const { + TrackedRendererListHashSet* descendants = percentHeightDescendants(); + return descendants && !descendants->isEmpty(); + } + + void setHasMarginBeforeQuirk(bool b) { m_hasMarginBeforeQuirk = b; } + void setHasMarginAfterQuirk(bool b) { m_hasMarginAfterQuirk = b; } + + bool hasMarginBeforeQuirk() const { return m_hasMarginBeforeQuirk; } + bool hasMarginAfterQuirk() const { return m_hasMarginAfterQuirk; } + + bool hasMarginBeforeQuirk(const RenderBox* child) const; + bool hasMarginAfterQuirk(const RenderBox* child) const; + + void markPositionedObjectsForLayout(); + + LayoutUnit textIndentOffset() const; + + virtual PositionWithAffinity positionForPoint(const LayoutPoint&) override; + + // Block flows subclass availableWidth to handle multi column layout + // (shrinking the width available to children when laying out.) + virtual LayoutUnit availableLogicalWidth() const override final; + + LayoutUnit blockDirectionOffset(const LayoutSize& offsetFromBlock) const; + LayoutUnit inlineDirectionOffset(const LayoutSize& offsetFromBlock) const; + + virtual bool shouldPaintSelectionGaps() const override final; + LayoutRect logicalLeftSelectionGap( + RenderBlock* rootBlock, + const LayoutPoint& rootBlockPhysicalPosition, + const LayoutSize& offsetFromRootBlock, + RenderObject* selObj, + LayoutUnit logicalLeft, + LayoutUnit logicalTop, + LayoutUnit logicalHeight, + const PaintInfo*); + LayoutRect logicalRightSelectionGap( + RenderBlock* rootBlock, + const LayoutPoint& rootBlockPhysicalPosition, + const LayoutSize& offsetFromRootBlock, + RenderObject* selObj, + LayoutUnit logicalRight, + LayoutUnit logicalTop, + LayoutUnit logicalHeight, + const PaintInfo*); + void getSelectionGapInfo(SelectionState, bool& leftGap, bool& rightGap); + RenderBlock* blockBeforeWithinSelectionRoot(LayoutSize& offset) const; + + virtual void setSelectionState(SelectionState) override; + + LayoutRect logicalRectToPhysicalRect(const LayoutPoint& physicalPosition, + const LayoutRect& logicalRect); + + // Helper methods for computing line counts and heights for line counts. + virtual RootInlineBox* lineAtIndex(int) const; + virtual int lineCount(const RootInlineBox* = 0, bool* = 0) const; + void clearTruncation(); + + // Accessors for logical width/height and margins in the containing block's + // block-flow direction. + LayoutUnit logicalWidthForChild(const RenderBox* child) const { + return child->width(); + } + LayoutUnit logicalHeightForChild(const RenderBox* child) const { + return child->height(); + } + LayoutSize logicalSizeForChild(const RenderBox* child) const { + return child->size(); + } + LayoutUnit logicalTopForChild(const RenderBox* child) const { + return child->y(); + } + LayoutUnit marginBeforeForChild(const RenderBoxModelObject* child) const { + return child->marginBefore(style()); + } + LayoutUnit marginAfterForChild(const RenderBoxModelObject* child) const { + return child->marginAfter(style()); + } + LayoutUnit marginStartForChild(const RenderBoxModelObject* child) const { + return child->marginStart(style()); + } + LayoutUnit marginEndForChild(const RenderBoxModelObject* child) const { + return child->marginEnd(style()); + } + void setMarginStartForChild(RenderBox* child, LayoutUnit value) const { + child->setMarginStart(value, style()); + } + void setMarginEndForChild(RenderBox* child, LayoutUnit value) const { + child->setMarginEnd(value, style()); + } + void setMarginBeforeForChild(RenderBox* child, LayoutUnit value) const { + child->setMarginBefore(value, style()); + } + void setMarginAfterForChild(RenderBox* child, LayoutUnit value) const { + child->setMarginAfter(value, style()); + } + LayoutUnit marginBeforeForChild(const RenderBox* child) const; + LayoutUnit marginAfterForChild(const RenderBox* child) const; + + virtual bool nodeAtPoint(const HitTestRequest&, + HitTestResult&, + const HitTestLocation& locationInContainer, + const LayoutPoint& accumulatedOffset) override; + + LayoutUnit availableLogicalWidthForContent() const { + return max( + 0, logicalRightOffsetForContent() - logicalLeftOffsetForContent()); + } + LayoutUnit logicalLeftOffsetForContent() const { + return borderLeft() + paddingLeft(); + } + LayoutUnit logicalRightOffsetForContent() const { + return logicalLeftOffsetForContent() + availableLogicalWidth(); + } + LayoutUnit startOffsetForContent() const { + return style()->isLeftToRightDirection() + ? logicalLeftOffsetForContent() + : logicalWidth() - logicalRightOffsetForContent(); + } + LayoutUnit endOffsetForContent() const { + return !style()->isLeftToRightDirection() + ? logicalLeftOffsetForContent() + : logicalWidth() - logicalRightOffsetForContent(); + } #if ENABLE(ASSERT) - void checkPositionedObjectsNeedLayout(); + void checkPositionedObjectsNeedLayout(); #endif #ifndef NDEBUG - void showLineTreeAndMark(const InlineBox* = 0, const char* = 0, const InlineBox* = 0, const char* = 0, const RenderObject* = 0) const; + void showLineTreeAndMark(const InlineBox* = 0, + const char* = 0, + const InlineBox* = 0, + const char* = 0, + const RenderObject* = 0) const; #endif - bool recalcChildOverflowAfterStyleChange(); - bool recalcOverflowAfterStyleChange(); - -protected: - virtual void willBeDestroyed() override; - - void dirtyForLayoutFromPercentageHeightDescendants(SubtreeLayoutScope&); - - enum PositionedLayoutBehavior { - DefaultLayout, - ForcedLayoutAfterContainingBlockMoved - }; - - void layoutPositionedObjects(bool relayoutChildren, PositionedLayoutBehavior = DefaultLayout); - - LayoutUnit marginIntrinsicLogicalWidthForChild(RenderBox* child) const; - - int beforeMarginInLineDirection(LineDirectionMode) const; - - virtual void paint(PaintInfo&, const LayoutPoint&, Vector& layers) override; - void paintObject(PaintInfo&, const LayoutPoint&, Vector& layers); - virtual void paintChildren(PaintInfo&, const LayoutPoint&, Vector& layers); - - virtual void adjustInlineDirectionLineBounds(unsigned /* expansionOpportunityCount */, float& /* logicalLeft */, float& /* logicalWidth */) const { } - - virtual void computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const override; - virtual void computePreferredLogicalWidths() override; - - virtual int firstLineBoxBaseline(FontBaselineOrAuto baselineType) const override; - virtual int inlineBlockBaseline(LineDirectionMode) const override; - virtual int lastLineBoxBaseline(LineDirectionMode) const; - - virtual void updateHitTestResult(HitTestResult&, const LayoutPoint&) override; - - virtual void styleWillChange(StyleDifference, const RenderStyle& newStyle) override; - virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle) override; - - virtual bool hasLineIfEmpty() const; - - bool simplifiedLayout(); - virtual void simplifiedNormalFlowLayout(); - -public: - virtual void computeOverflow(LayoutUnit oldClientAfterEdge, bool = false); -protected: - virtual void addOverflowFromChildren(); - void addOverflowFromPositionedObjects(); - - virtual void addFocusRingRects(Vector&, const LayoutPoint& additionalOffset, const RenderBox* paintContainer = 0) const override; - - void updateBlockChildDirtyBitsBeforeLayout(bool relayoutChildren, RenderBox*); - - virtual bool isInlineBlock() const override final { return isInline() && isReplaced(); } - - virtual bool hitTestContents(const HitTestRequest&, HitTestResult&, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset); - -private: - virtual RenderObjectChildList* virtualChildren() override final { return children(); } - virtual const RenderObjectChildList* virtualChildren() const override final { return children(); } - - virtual const char* renderName() const override; - - virtual bool isRenderBlock() const override final { return true; } - - virtual void dirtyLinesFromChangedChild(RenderObject* child) override final { m_lineBoxes.dirtyLinesFromChangedChild(this, child); } - - void insertIntoTrackedRendererMaps(RenderBox* descendant, TrackedDescendantsMap*&, TrackedContainerMap*&); - static void removeFromTrackedRendererMaps(RenderBox* descendant, TrackedDescendantsMap*&, TrackedContainerMap*&); - - void paintSelection(PaintInfo&, const LayoutPoint&); - - // Obtains the nearest enclosing block (including this block) that contributes a first-line style to our inline - // children. - virtual RenderBlock* firstLineBlock() const override; - - bool isSelectionRoot() const; - GapRects selectionGaps(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, - LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const PaintInfo* = 0); - GapRects blockSelectionGaps(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, - LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const PaintInfo*); - LayoutRect blockSelectionGap(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, - LayoutUnit lastLogicalTop, LayoutUnit lastLogicalLeft, LayoutUnit lastLogicalRight, LayoutUnit logicalBottom, const PaintInfo*); - virtual LayoutUnit logicalLeftSelectionOffset(RenderBlock* rootBlock, LayoutUnit position); - virtual LayoutUnit logicalRightSelectionOffset(RenderBlock* rootBlock, LayoutUnit position); - - virtual void absoluteQuads(Vector&) const override; - - virtual LayoutRect localCaretRect(InlineBox*, int caretOffset, LayoutUnit* extraWidthToEndOfLine = 0) override final; - - PositionWithAffinity positionForPointWithInlineChildren(const LayoutPoint&); - - void removeFromGlobalMaps(); - bool widthAvailableToChildrenHasChanged(); + bool recalcChildOverflowAfterStyleChange(); + bool recalcOverflowAfterStyleChange(); -protected: - bool updateLogicalWidthAndColumnWidth(); + protected: + virtual void willBeDestroyed() override; - RenderObjectChildList m_children; - RenderLineBoxList m_lineBoxes; // All of the root line boxes created for this block flow. For example,
Hello
world.
will have two total lines for the
. + void dirtyForLayoutFromPercentageHeightDescendants(SubtreeLayoutScope&); - LayoutUnit m_pageLogicalOffset; + enum PositionedLayoutBehavior { + DefaultLayout, + ForcedLayoutAfterContainingBlockMoved + }; - unsigned m_hasMarginBeforeQuirk : 1; // Note these quirk values can't be put in RenderBlockRareData since they are set too frequently. - unsigned m_hasMarginAfterQuirk : 1; - unsigned m_beingDestroyed : 1; - unsigned m_hasBorderOrPaddingLogicalWidthChanged : 1; + void layoutPositionedObjects(bool relayoutChildren, + PositionedLayoutBehavior = DefaultLayout); - // FIXME-BLOCKFLOW: Remove this when the line layout stuff has all moved out of RenderBlock - friend class LineBreaker; + LayoutUnit marginIntrinsicLogicalWidthForChild(RenderBox* child) const; - // FIXME: This is temporary as we move code that accesses block flow - // member variables out of RenderBlock and into RenderParagraph. - friend class RenderParagraph; + int beforeMarginInLineDirection(LineDirectionMode) const; + + virtual void paint(PaintInfo&, + const LayoutPoint&, + Vector& layers) override; + void paintObject(PaintInfo&, const LayoutPoint&, Vector& layers); + virtual void paintChildren(PaintInfo&, + const LayoutPoint&, + Vector& layers); + + virtual void adjustInlineDirectionLineBounds( + unsigned /* expansionOpportunityCount */, + float& /* logicalLeft */, + float& /* logicalWidth */) const {} + + virtual void computeIntrinsicLogicalWidths( + LayoutUnit& minLogicalWidth, + LayoutUnit& maxLogicalWidth) const override; + virtual void computePreferredLogicalWidths() override; + + virtual int firstLineBoxBaseline( + FontBaselineOrAuto baselineType) const override; + virtual int inlineBlockBaseline(LineDirectionMode) const override; + virtual int lastLineBoxBaseline(LineDirectionMode) const; + + virtual void updateHitTestResult(HitTestResult&, const LayoutPoint&) override; + + virtual void styleWillChange(StyleDifference, + const RenderStyle& newStyle) override; + virtual void styleDidChange(StyleDifference, + const RenderStyle* oldStyle) override; + + virtual bool hasLineIfEmpty() const; + + bool simplifiedLayout(); + virtual void simplifiedNormalFlowLayout(); + + public: + virtual void computeOverflow(LayoutUnit oldClientAfterEdge, bool = false); + + protected: + virtual void addOverflowFromChildren(); + void addOverflowFromPositionedObjects(); + + virtual void addFocusRingRects( + Vector&, + const LayoutPoint& additionalOffset, + const RenderBox* paintContainer = 0) const override; + + void updateBlockChildDirtyBitsBeforeLayout(bool relayoutChildren, RenderBox*); + + virtual bool isInlineBlock() const override final { + return isInline() && isReplaced(); + } + + virtual bool hitTestContents(const HitTestRequest&, + HitTestResult&, + const HitTestLocation& locationInContainer, + const LayoutPoint& accumulatedOffset); + + private: + virtual RenderObjectChildList* virtualChildren() override final { + return children(); + } + virtual const RenderObjectChildList* virtualChildren() const override final { + return children(); + } + + virtual const char* renderName() const override; + + virtual bool isRenderBlock() const override final { return true; } + + virtual void dirtyLinesFromChangedChild(RenderObject* child) override final { + m_lineBoxes.dirtyLinesFromChangedChild(this, child); + } + + void insertIntoTrackedRendererMaps(RenderBox* descendant, + TrackedDescendantsMap*&, + TrackedContainerMap*&); + static void removeFromTrackedRendererMaps(RenderBox* descendant, + TrackedDescendantsMap*&, + TrackedContainerMap*&); + + void paintSelection(PaintInfo&, const LayoutPoint&); + + // Obtains the nearest enclosing block (including this block) that contributes + // a first-line style to our inline children. + virtual RenderBlock* firstLineBlock() const override; + + bool isSelectionRoot() const; + GapRects selectionGaps(RenderBlock* rootBlock, + const LayoutPoint& rootBlockPhysicalPosition, + const LayoutSize& offsetFromRootBlock, + LayoutUnit& lastLogicalTop, + LayoutUnit& lastLogicalLeft, + LayoutUnit& lastLogicalRight, + const PaintInfo* = 0); + GapRects blockSelectionGaps(RenderBlock* rootBlock, + const LayoutPoint& rootBlockPhysicalPosition, + const LayoutSize& offsetFromRootBlock, + LayoutUnit& lastLogicalTop, + LayoutUnit& lastLogicalLeft, + LayoutUnit& lastLogicalRight, + const PaintInfo*); + LayoutRect blockSelectionGap(RenderBlock* rootBlock, + const LayoutPoint& rootBlockPhysicalPosition, + const LayoutSize& offsetFromRootBlock, + LayoutUnit lastLogicalTop, + LayoutUnit lastLogicalLeft, + LayoutUnit lastLogicalRight, + LayoutUnit logicalBottom, + const PaintInfo*); + virtual LayoutUnit logicalLeftSelectionOffset(RenderBlock* rootBlock, + LayoutUnit position); + virtual LayoutUnit logicalRightSelectionOffset(RenderBlock* rootBlock, + LayoutUnit position); + + virtual void absoluteQuads(Vector&) const override; + + virtual LayoutRect localCaretRect( + InlineBox*, + int caretOffset, + LayoutUnit* extraWidthToEndOfLine = 0) override final; + + PositionWithAffinity positionForPointWithInlineChildren(const LayoutPoint&); + + void removeFromGlobalMaps(); + bool widthAvailableToChildrenHasChanged(); + + protected: + bool updateLogicalWidthAndColumnWidth(); + + RenderObjectChildList m_children; + RenderLineBoxList m_lineBoxes; // All of the root line boxes created for this + // block flow. For example, + //
Hello
world.
will have two + // total lines for the
. + + LayoutUnit m_pageLogicalOffset; + + unsigned m_hasMarginBeforeQuirk : 1; // Note these quirk values can't be put + // in RenderBlockRareData since they are + // set too frequently. + unsigned m_hasMarginAfterQuirk : 1; + unsigned m_beingDestroyed : 1; + unsigned m_hasBorderOrPaddingLogicalWidthChanged : 1; + + // FIXME-BLOCKFLOW: Remove this when the line layout stuff has all moved out + // of RenderBlock + friend class LineBreaker; + + // FIXME: This is temporary as we move code that accesses block flow + // member variables out of RenderBlock and into RenderParagraph. + friend class RenderParagraph; }; DEFINE_RENDER_OBJECT_TYPE_CASTS(RenderBlock, isRenderBlock()); -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_RENDERBLOCK_H_ diff --git a/sky/engine/core/rendering/RenderBox.cpp b/sky/engine/core/rendering/RenderBox.cpp index 5454a3f05f805..cf9feb1cc29fd 100644 --- a/sky/engine/core/rendering/RenderBox.cpp +++ b/sky/engine/core/rendering/RenderBox.cpp @@ -3,7 +3,8 @@ * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com) * (C) 2005, 2006 Samuel Weinig (sam.weinig@gmail.com) - * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. + * All rights reserved. * Copyright (C) 2013 Adobe Systems Incorporated. All rights reserved. * * This library is free software; you can redistribute it and/or @@ -43,2830 +44,3296 @@ namespace blink { RenderBox::RenderBox() - : m_intrinsicContentLogicalHeight(-1) - , m_minPreferredLogicalWidth(-1) - , m_maxPreferredLogicalWidth(-1) -{ - setIsBox(); + : m_intrinsicContentLogicalHeight(-1), + m_minPreferredLogicalWidth(-1), + m_maxPreferredLogicalWidth(-1) { + setIsBox(); } -void RenderBox::willBeDestroyed() -{ - clearOverrideSize(); - RenderBlock::removePercentHeightDescendantIfNeeded(this); - RenderBoxModelObject::willBeDestroyed(); - destroyLayer(); +void RenderBox::willBeDestroyed() { + clearOverrideSize(); + RenderBlock::removePercentHeightDescendantIfNeeded(this); + RenderBoxModelObject::willBeDestroyed(); + destroyLayer(); } -void RenderBox::destroyLayer() -{ - setHasLayer(false); - m_layer = nullptr; +void RenderBox::destroyLayer() { + setHasLayer(false); + m_layer = nullptr; } -void RenderBox::createLayer(LayerType type) -{ - ASSERT(!m_layer); - m_layer = adoptPtr(new RenderLayer(this, type)); - setHasLayer(true); - m_layer->insertOnlyThisLayer(); +void RenderBox::createLayer(LayerType type) { + ASSERT(!m_layer); + m_layer = adoptPtr(new RenderLayer(this, type)); + setHasLayer(true); + m_layer->insertOnlyThisLayer(); } -bool RenderBox::hasSelfPaintingLayer() const -{ - return m_layer && m_layer->isSelfPaintingLayer(); +bool RenderBox::hasSelfPaintingLayer() const { + return m_layer && m_layer->isSelfPaintingLayer(); } -void RenderBox::removeFloatingOrPositionedChildFromBlockLists() -{ - ASSERT(isFloatingOrOutOfFlowPositioned()); +void RenderBox::removeFloatingOrPositionedChildFromBlockLists() { + ASSERT(isFloatingOrOutOfFlowPositioned()); - if (documentBeingDestroyed()) - return; + if (documentBeingDestroyed()) + return; - if (isOutOfFlowPositioned()) - RenderBlock::removePositionedObject(this); + if (isOutOfFlowPositioned()) + RenderBlock::removePositionedObject(this); } -void RenderBox::styleWillChange(StyleDifference diff, const RenderStyle& newStyle) -{ - RenderStyle* oldStyle = style(); - if (oldStyle && parent()) { - // When a layout hint happens and an object's position style changes, we have to do a layout - // to dirty the render tree using the old position value now. - if (diff.needsFullLayout() && oldStyle->position() != newStyle.position()) { - markContainingBlocksForLayout(); - if (newStyle.hasOutOfFlowPosition()) - parent()->setChildNeedsLayout(); - } - - if (oldStyle->hasAutoClip() != newStyle.hasAutoClip() - || oldStyle->clip() != newStyle.clip()) - layer()->clipper().clearClipRectsIncludingDescendants(); +void RenderBox::styleWillChange(StyleDifference diff, + const RenderStyle& newStyle) { + RenderStyle* oldStyle = style(); + if (oldStyle && parent()) { + // When a layout hint happens and an object's position style changes, we + // have to do a layout to dirty the render tree using the old position value + // now. + if (diff.needsFullLayout() && oldStyle->position() != newStyle.position()) { + markContainingBlocksForLayout(); + if (newStyle.hasOutOfFlowPosition()) + parent()->setChildNeedsLayout(); } - RenderBoxModelObject::styleWillChange(diff, newStyle); + if (oldStyle->hasAutoClip() != newStyle.hasAutoClip() || + oldStyle->clip() != newStyle.clip()) + layer()->clipper().clearClipRectsIncludingDescendants(); + } + + RenderBoxModelObject::styleWillChange(diff, newStyle); } -void RenderBox::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) -{ - bool hadTransform = hasTransform(); +void RenderBox::styleDidChange(StyleDifference diff, + const RenderStyle* oldStyle) { + bool hadTransform = hasTransform(); - RenderObject::styleDidChange(diff, oldStyle); - updateFromStyle(); + RenderObject::styleDidChange(diff, oldStyle); + updateFromStyle(); - LayerType type = layerTypeRequired(); - if (type != NoLayer) { - if (!layer()) { - createLayer(type); - if (parent() && !needsLayout()) { - // FIXME: We should call a specialized version of this function. - layer()->updateLayerPositionsAfterLayout(); - } - } - } else if (layer() && layer()->parent()) { - setHasTransform(false); // Either a transform wasn't specified or the object doesn't support transforms, so just null out the bit. - layer()->removeOnlyThisLayer(); // calls destroyLayer() which clears m_layer - if (hadTransform) - setNeedsLayoutAndPrefWidthsRecalc(); + LayerType type = layerTypeRequired(); + if (type != NoLayer) { + if (!layer()) { + createLayer(type); + if (parent() && !needsLayout()) { + // FIXME: We should call a specialized version of this function. + layer()->updateLayerPositionsAfterLayout(); + } } + } else if (layer() && layer()->parent()) { + setHasTransform(false); // Either a transform wasn't specified or the + // object doesn't support transforms, so just null + // out the bit. + layer() + ->removeOnlyThisLayer(); // calls destroyLayer() which clears m_layer + if (hadTransform) + setNeedsLayoutAndPrefWidthsRecalc(); + } - if (layer()) { - // FIXME: Ideally we shouldn't need this setter but we can't easily infer an overflow-only layer - // from the style. - layer()->setLayerType(type); - layer()->styleChanged(diff, oldStyle); - } + if (layer()) { + // FIXME: Ideally we shouldn't need this setter but we can't easily infer an + // overflow-only layer from the style. + layer()->setLayerType(type); + layer()->styleChanged(diff, oldStyle); + } - updateTransform(oldStyle); + updateTransform(oldStyle); - if (needsLayout() && oldStyle) - RenderBlock::removePercentHeightDescendantIfNeeded(this); + if (needsLayout() && oldStyle) + RenderBlock::removePercentHeightDescendantIfNeeded(this); } -void RenderBox::updateTransformationMatrix() -{ - if (m_transform) { - m_transform->makeIdentity(); - style()->applyTransform(*m_transform, pixelSnappedBorderBoxRect().size(), RenderStyle::IncludeTransformOrigin); - // FIXME(sky): We shouldn't need to do this once Skia has 4x4 matrix support. - // Until then, 3d transforms don't work right. - m_transform->makeAffine(); - } +void RenderBox::updateTransformationMatrix() { + if (m_transform) { + m_transform->makeIdentity(); + style()->applyTransform(*m_transform, pixelSnappedBorderBoxRect().size(), + RenderStyle::IncludeTransformOrigin); + // FIXME(sky): We shouldn't need to do this once Skia has 4x4 matrix + // support. Until then, 3d transforms don't work right. + m_transform->makeAffine(); + } } -void RenderBox::updateTransform(const RenderStyle* oldStyle) -{ - if (oldStyle && style()->transformDataEquivalent(*oldStyle)) - return; +void RenderBox::updateTransform(const RenderStyle* oldStyle) { + if (oldStyle && style()->transformDataEquivalent(*oldStyle)) + return; - // hasTransform() on the renderer is also true when there is transform-style: preserve-3d or perspective set, - // so check style too. - bool localHasTransform = hasTransform() && style()->hasTransform(); - bool had3DTransform = has3DTransform(); + // hasTransform() on the renderer is also true when there is transform-style: + // preserve-3d or perspective set, so check style too. + bool localHasTransform = hasTransform() && style()->hasTransform(); + bool had3DTransform = has3DTransform(); - bool hadTransform = m_transform; - if (localHasTransform != hadTransform) { - if (localHasTransform) - m_transform = adoptPtr(new TransformationMatrix); - else - m_transform.clear(); + bool hadTransform = m_transform; + if (localHasTransform != hadTransform) { + if (localHasTransform) + m_transform = adoptPtr(new TransformationMatrix); + else + m_transform.clear(); - // Layers with transforms act as clip rects roots, so clear the cached clip rects here. - if (layer()) - layer()->clipper().clearClipRectsIncludingDescendants(); - } + // Layers with transforms act as clip rects roots, so clear the cached clip + // rects here. + if (layer()) + layer()->clipper().clearClipRectsIncludingDescendants(); + } - updateTransformationMatrix(); + updateTransformationMatrix(); - if (layer() && had3DTransform != has3DTransform()) - layer()->dirty3DTransformedDescendantStatus(); + if (layer() && had3DTransform != has3DTransform()) + layer()->dirty3DTransformedDescendantStatus(); } // TODO(ojan): Inline this into styleDidChange, -void RenderBox::updateFromStyle() -{ - RenderStyle* styleToUse = style(); - - setHasBoxDecorationBackground(hasBackground() || styleToUse->hasBorder() || styleToUse->boxShadow()); - setInline(styleToUse->isDisplayInlineType()); - setPositionState(styleToUse->position()); - - if (isRenderView()) { - // TODO(ojan): Merge this into the same call above. - setHasBoxDecorationBackground(true); - } else if (isRenderBlock()) { - // TODO(esprehn): Why do we not want to set this on the RenderView? - setHasOverflowClip(!styleToUse->isOverflowVisible()); - } +void RenderBox::updateFromStyle() { + RenderStyle* styleToUse = style(); - setHasTransform(styleToUse->hasTransformRelatedProperty()); -} + setHasBoxDecorationBackground(hasBackground() || styleToUse->hasBorder() || + styleToUse->boxShadow()); + setInline(styleToUse->isDisplayInlineType()); + setPositionState(styleToUse->position()); -void RenderBox::layout() -{ - ASSERT(needsLayout()); + if (isRenderView()) { + // TODO(ojan): Merge this into the same call above. + setHasBoxDecorationBackground(true); + } else if (isRenderBlock()) { + // TODO(esprehn): Why do we not want to set this on the RenderView? + setHasOverflowClip(!styleToUse->isOverflowVisible()); + } - RenderObject* child = slowFirstChild(); - if (!child) { - clearNeedsLayout(); - return; - } + setHasTransform(styleToUse->hasTransformRelatedProperty()); +} - while (child) { - child->layoutIfNeeded(); - ASSERT(!child->needsLayout()); - child = child->nextSibling(); - } +void RenderBox::layout() { + ASSERT(needsLayout()); + + RenderObject* child = slowFirstChild(); + if (!child) { clearNeedsLayout(); + return; + } + + while (child) { + child->layoutIfNeeded(); + ASSERT(!child->needsLayout()); + child = child->nextSibling(); + } + clearNeedsLayout(); } -// More IE extensions. clientWidth and clientHeight represent the interior of an object -// excluding border and scrollbar. -LayoutUnit RenderBox::clientWidth() const -{ - return width() - borderLeft() - borderRight(); +// More IE extensions. clientWidth and clientHeight represent the interior of +// an object excluding border and scrollbar. +LayoutUnit RenderBox::clientWidth() const { + return width() - borderLeft() - borderRight(); } -LayoutUnit RenderBox::clientHeight() const -{ - return height() - borderTop() - borderBottom(); +LayoutUnit RenderBox::clientHeight() const { + return height() - borderTop() - borderBottom(); } -int RenderBox::pixelSnappedClientWidth() const -{ - return snapSizeToPixel(clientWidth(), x() + clientLeft()); +int RenderBox::pixelSnappedClientWidth() const { + return snapSizeToPixel(clientWidth(), x() + clientLeft()); } -int RenderBox::pixelSnappedClientHeight() const -{ - return snapSizeToPixel(clientHeight(), y() + clientTop()); +int RenderBox::pixelSnappedClientHeight() const { + return snapSizeToPixel(clientHeight(), y() + clientTop()); } -int RenderBox::pixelSnappedOffsetWidth() const -{ - return snapSizeToPixel(offsetWidth(), x() + clientLeft()); +int RenderBox::pixelSnappedOffsetWidth() const { + return snapSizeToPixel(offsetWidth(), x() + clientLeft()); } -int RenderBox::pixelSnappedOffsetHeight() const -{ - return snapSizeToPixel(offsetHeight(), y() + clientTop()); +int RenderBox::pixelSnappedOffsetHeight() const { + return snapSizeToPixel(offsetHeight(), y() + clientTop()); } -void RenderBox::absoluteQuads(Vector& quads) const -{ - quads.append(localToAbsoluteQuad(FloatRect(0, 0, width().toFloat(), height().toFloat()), 0 /* mode */)); +void RenderBox::absoluteQuads(Vector& quads) const { + quads.append(localToAbsoluteQuad( + FloatRect(0, 0, width().toFloat(), height().toFloat()), 0 /* mode */)); } -void RenderBox::updateLayerTransformAfterLayout() -{ - // Transform-origin depends on box size, so we need to update the transform after layout. - updateTransformationMatrix(); +void RenderBox::updateLayerTransformAfterLayout() { + // Transform-origin depends on box size, so we need to update the transform + // after layout. + updateTransformationMatrix(); } -LayoutUnit RenderBox::constrainLogicalWidthByMinMax(LayoutUnit logicalWidth, LayoutUnit availableWidth, RenderBlock* cb) const -{ - RenderStyle* styleToUse = style(); - if (!styleToUse->logicalMaxWidth().isMaxSizeNone()) - logicalWidth = std::min(logicalWidth, computeLogicalWidthUsing(MaxSize, styleToUse->logicalMaxWidth(), availableWidth, cb)); - return std::max(logicalWidth, computeLogicalWidthUsing(MinSize, styleToUse->logicalMinWidth(), availableWidth, cb)); +LayoutUnit RenderBox::constrainLogicalWidthByMinMax(LayoutUnit logicalWidth, + LayoutUnit availableWidth, + RenderBlock* cb) const { + RenderStyle* styleToUse = style(); + if (!styleToUse->logicalMaxWidth().isMaxSizeNone()) + logicalWidth = std::min( + logicalWidth, + computeLogicalWidthUsing(MaxSize, styleToUse->logicalMaxWidth(), + availableWidth, cb)); + return std::max(logicalWidth, computeLogicalWidthUsing( + MinSize, styleToUse->logicalMinWidth(), + availableWidth, cb)); } -LayoutUnit RenderBox::constrainLogicalHeightByMinMax(LayoutUnit logicalHeight, LayoutUnit intrinsicContentHeight) const -{ - RenderStyle* styleToUse = style(); - if (!styleToUse->logicalMaxHeight().isMaxSizeNone()) { - LayoutUnit maxH = computeLogicalHeightUsing(styleToUse->logicalMaxHeight(), intrinsicContentHeight); - if (maxH != -1) - logicalHeight = std::min(logicalHeight, maxH); - } - return std::max(logicalHeight, computeLogicalHeightUsing(styleToUse->logicalMinHeight(), intrinsicContentHeight)); +LayoutUnit RenderBox::constrainLogicalHeightByMinMax( + LayoutUnit logicalHeight, + LayoutUnit intrinsicContentHeight) const { + RenderStyle* styleToUse = style(); + if (!styleToUse->logicalMaxHeight().isMaxSizeNone()) { + LayoutUnit maxH = computeLogicalHeightUsing(styleToUse->logicalMaxHeight(), + intrinsicContentHeight); + if (maxH != -1) + logicalHeight = std::min(logicalHeight, maxH); + } + return std::max(logicalHeight, + computeLogicalHeightUsing(styleToUse->logicalMinHeight(), + intrinsicContentHeight)); } -LayoutUnit RenderBox::constrainContentBoxLogicalHeightByMinMax(LayoutUnit logicalHeight, LayoutUnit intrinsicContentHeight) const -{ - RenderStyle* styleToUse = style(); - if (!styleToUse->logicalMaxHeight().isMaxSizeNone()) { - LayoutUnit maxH = computeContentLogicalHeight(styleToUse->logicalMaxHeight(), intrinsicContentHeight); - if (maxH != -1) - logicalHeight = std::min(logicalHeight, maxH); - } - return std::max(logicalHeight, computeContentLogicalHeight(styleToUse->logicalMinHeight(), intrinsicContentHeight)); +LayoutUnit RenderBox::constrainContentBoxLogicalHeightByMinMax( + LayoutUnit logicalHeight, + LayoutUnit intrinsicContentHeight) const { + RenderStyle* styleToUse = style(); + if (!styleToUse->logicalMaxHeight().isMaxSizeNone()) { + LayoutUnit maxH = computeContentLogicalHeight( + styleToUse->logicalMaxHeight(), intrinsicContentHeight); + if (maxH != -1) + logicalHeight = std::min(logicalHeight, maxH); + } + return std::max(logicalHeight, + computeContentLogicalHeight(styleToUse->logicalMinHeight(), + intrinsicContentHeight)); } -IntRect RenderBox::absoluteContentBox() const -{ - // This is wrong with transforms and flipped writing modes. - IntRect rect = pixelSnappedIntRect(contentBoxRect()); - FloatPoint absPos = localToAbsolute(); - rect.move(absPos.x(), absPos.y()); - return rect; +IntRect RenderBox::absoluteContentBox() const { + // This is wrong with transforms and flipped writing modes. + IntRect rect = pixelSnappedIntRect(contentBoxRect()); + FloatPoint absPos = localToAbsolute(); + rect.move(absPos.x(), absPos.y()); + return rect; } -FloatQuad RenderBox::absoluteContentQuad() const -{ - LayoutRect rect = contentBoxRect(); - return localToAbsoluteQuad(FloatRect(rect)); +FloatQuad RenderBox::absoluteContentQuad() const { + LayoutRect rect = contentBoxRect(); + return localToAbsoluteQuad(FloatRect(rect)); } -FloatPoint RenderBox::perspectiveOrigin() const -{ - if (!hasTransform()) - return FloatPoint(); +FloatPoint RenderBox::perspectiveOrigin() const { + if (!hasTransform()) + return FloatPoint(); - const LayoutRect borderBox = borderBoxRect(); - return FloatPoint( - floatValueForLength(style()->perspectiveOriginX(), borderBox.width().toFloat()), - floatValueForLength(style()->perspectiveOriginY(), borderBox.height().toFloat())); + const LayoutRect borderBox = borderBoxRect(); + return FloatPoint(floatValueForLength(style()->perspectiveOriginX(), + borderBox.width().toFloat()), + floatValueForLength(style()->perspectiveOriginY(), + borderBox.height().toFloat())); } -void RenderBox::addFocusRingRects(Vector& rects, const LayoutPoint& additionalOffset, const RenderBox*) const -{ - if (!size().isEmpty()) - rects.append(pixelSnappedIntRect(additionalOffset, size())); +void RenderBox::addFocusRingRects(Vector& rects, + const LayoutPoint& additionalOffset, + const RenderBox*) const { + if (!size().isEmpty()) + rects.append(pixelSnappedIntRect(additionalOffset, size())); } -bool RenderBox::needsPreferredWidthsRecalculation() const -{ - return style()->paddingStart().isPercent() || style()->paddingEnd().isPercent(); +bool RenderBox::needsPreferredWidthsRecalculation() const { + return style()->paddingStart().isPercent() || + style()->paddingEnd().isPercent(); } -void RenderBox::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const -{ - minLogicalWidth = minPreferredLogicalWidth() - borderAndPaddingLogicalWidth(); - maxLogicalWidth = maxPreferredLogicalWidth() - borderAndPaddingLogicalWidth(); -} +void RenderBox::computeIntrinsicLogicalWidths( + LayoutUnit& minLogicalWidth, + LayoutUnit& maxLogicalWidth) const { + minLogicalWidth = minPreferredLogicalWidth() - borderAndPaddingLogicalWidth(); + maxLogicalWidth = maxPreferredLogicalWidth() - borderAndPaddingLogicalWidth(); +} -LayoutUnit RenderBox::minPreferredLogicalWidth() const -{ - if (preferredLogicalWidthsDirty()) { +LayoutUnit RenderBox::minPreferredLogicalWidth() const { + if (preferredLogicalWidthsDirty()) { #if ENABLE(ASSERT) - SetLayoutNeededForbiddenScope layoutForbiddenScope(const_cast(*this)); + SetLayoutNeededForbiddenScope layoutForbiddenScope( + const_cast(*this)); #endif - const_cast(this)->computePreferredLogicalWidths(); - } + const_cast(this)->computePreferredLogicalWidths(); + } - return m_minPreferredLogicalWidth; + return m_minPreferredLogicalWidth; } -LayoutUnit RenderBox::maxPreferredLogicalWidth() const -{ - if (preferredLogicalWidthsDirty()) { +LayoutUnit RenderBox::maxPreferredLogicalWidth() const { + if (preferredLogicalWidthsDirty()) { #if ENABLE(ASSERT) - SetLayoutNeededForbiddenScope layoutForbiddenScope(const_cast(*this)); + SetLayoutNeededForbiddenScope layoutForbiddenScope( + const_cast(*this)); #endif - const_cast(this)->computePreferredLogicalWidths(); - } + const_cast(this)->computePreferredLogicalWidths(); + } - return m_maxPreferredLogicalWidth; + return m_maxPreferredLogicalWidth; } -void RenderBox::setMinPreferredLogicalWidth(LayoutUnit width) -{ - m_minPreferredLogicalWidth = width; +void RenderBox::setMinPreferredLogicalWidth(LayoutUnit width) { + m_minPreferredLogicalWidth = width; } -void RenderBox::setMaxPreferredLogicalWidth(LayoutUnit width) -{ - m_maxPreferredLogicalWidth = width; +void RenderBox::setMaxPreferredLogicalWidth(LayoutUnit width) { + m_maxPreferredLogicalWidth = width; } -bool RenderBox::hasOverrideHeight() const -{ - return m_rareData && m_rareData->m_overrideLogicalContentHeight != -1; +bool RenderBox::hasOverrideHeight() const { + return m_rareData && m_rareData->m_overrideLogicalContentHeight != -1; } -bool RenderBox::hasOverrideWidth() const -{ - return m_rareData && m_rareData->m_overrideLogicalContentWidth != -1; +bool RenderBox::hasOverrideWidth() const { + return m_rareData && m_rareData->m_overrideLogicalContentWidth != -1; } -void RenderBox::setOverrideLogicalContentHeight(LayoutUnit height) -{ - ASSERT(height >= 0); - ensureRareData().m_overrideLogicalContentHeight = height; +void RenderBox::setOverrideLogicalContentHeight(LayoutUnit height) { + ASSERT(height >= 0); + ensureRareData().m_overrideLogicalContentHeight = height; } -void RenderBox::setOverrideLogicalContentWidth(LayoutUnit width) -{ - ASSERT(width >= 0); - ensureRareData().m_overrideLogicalContentWidth = width; +void RenderBox::setOverrideLogicalContentWidth(LayoutUnit width) { + ASSERT(width >= 0); + ensureRareData().m_overrideLogicalContentWidth = width; } -void RenderBox::clearOverrideLogicalContentHeight() -{ - if (m_rareData) - m_rareData->m_overrideLogicalContentHeight = -1; +void RenderBox::clearOverrideLogicalContentHeight() { + if (m_rareData) + m_rareData->m_overrideLogicalContentHeight = -1; } -void RenderBox::clearOverrideLogicalContentWidth() -{ - if (m_rareData) - m_rareData->m_overrideLogicalContentWidth = -1; +void RenderBox::clearOverrideLogicalContentWidth() { + if (m_rareData) + m_rareData->m_overrideLogicalContentWidth = -1; } -void RenderBox::clearOverrideSize() -{ - clearOverrideLogicalContentHeight(); - clearOverrideLogicalContentWidth(); +void RenderBox::clearOverrideSize() { + clearOverrideLogicalContentHeight(); + clearOverrideLogicalContentWidth(); } -LayoutUnit RenderBox::overrideLogicalContentWidth() const -{ - ASSERT(hasOverrideWidth()); - return m_rareData->m_overrideLogicalContentWidth; +LayoutUnit RenderBox::overrideLogicalContentWidth() const { + ASSERT(hasOverrideWidth()); + return m_rareData->m_overrideLogicalContentWidth; } -LayoutUnit RenderBox::overrideLogicalContentHeight() const -{ - ASSERT(hasOverrideHeight()); - return m_rareData->m_overrideLogicalContentHeight; +LayoutUnit RenderBox::overrideLogicalContentHeight() const { + ASSERT(hasOverrideHeight()); + return m_rareData->m_overrideLogicalContentHeight; } -LayoutUnit RenderBox::adjustBorderBoxLogicalWidthForBoxSizing(LayoutUnit width) const -{ - LayoutUnit bordersPlusPadding = borderAndPaddingLogicalWidth(); - if (style()->boxSizing() == CONTENT_BOX) - return width + bordersPlusPadding; - return std::max(width, bordersPlusPadding); +LayoutUnit RenderBox::adjustBorderBoxLogicalWidthForBoxSizing( + LayoutUnit width) const { + LayoutUnit bordersPlusPadding = borderAndPaddingLogicalWidth(); + if (style()->boxSizing() == CONTENT_BOX) + return width + bordersPlusPadding; + return std::max(width, bordersPlusPadding); } -LayoutUnit RenderBox::adjustBorderBoxLogicalHeightForBoxSizing(LayoutUnit height) const -{ - LayoutUnit bordersPlusPadding = borderAndPaddingLogicalHeight(); - if (style()->boxSizing() == CONTENT_BOX) - return height + bordersPlusPadding; - return std::max(height, bordersPlusPadding); +LayoutUnit RenderBox::adjustBorderBoxLogicalHeightForBoxSizing( + LayoutUnit height) const { + LayoutUnit bordersPlusPadding = borderAndPaddingLogicalHeight(); + if (style()->boxSizing() == CONTENT_BOX) + return height + bordersPlusPadding; + return std::max(height, bordersPlusPadding); } -LayoutUnit RenderBox::adjustContentBoxLogicalWidthForBoxSizing(LayoutUnit width) const -{ - if (style()->boxSizing() == BORDER_BOX) - width -= borderAndPaddingLogicalWidth(); - return std::max(0, width); +LayoutUnit RenderBox::adjustContentBoxLogicalWidthForBoxSizing( + LayoutUnit width) const { + if (style()->boxSizing() == BORDER_BOX) + width -= borderAndPaddingLogicalWidth(); + return std::max(0, width); } -LayoutUnit RenderBox::adjustContentBoxLogicalHeightForBoxSizing(LayoutUnit height) const -{ - if (style()->boxSizing() == BORDER_BOX) - height -= borderAndPaddingLogicalHeight(); - return std::max(0, height); +LayoutUnit RenderBox::adjustContentBoxLogicalHeightForBoxSizing( + LayoutUnit height) const { + if (style()->boxSizing() == BORDER_BOX) + height -= borderAndPaddingLogicalHeight(); + return std::max(0, height); } -bool RenderBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset) -{ - LayoutPoint adjustedLocation = accumulatedOffset + location(); +bool RenderBox::nodeAtPoint(const HitTestRequest& request, + HitTestResult& result, + const HitTestLocation& locationInContainer, + const LayoutPoint& accumulatedOffset) { + LayoutPoint adjustedLocation = accumulatedOffset + location(); - // Check kids first. - for (RenderObject* child = slowLastChild(); child; child = child->previousSibling()) { - if ((!child->hasLayer() || !toRenderBox(child)->layer()->isSelfPaintingLayer()) && child->nodeAtPoint(request, result, locationInContainer, adjustedLocation)) { - updateHitTestResult(result, locationInContainer.point() - toLayoutSize(adjustedLocation)); - return true; - } + // Check kids first. + for (RenderObject* child = slowLastChild(); child; + child = child->previousSibling()) { + if ((!child->hasLayer() || + !toRenderBox(child)->layer()->isSelfPaintingLayer()) && + child->nodeAtPoint(request, result, locationInContainer, + adjustedLocation)) { + updateHitTestResult( + result, locationInContainer.point() - toLayoutSize(adjustedLocation)); + return true; } + } - // Check our bounds next. - LayoutRect boundsRect = borderBoxRect(); - boundsRect.moveBy(adjustedLocation); - if (visibleToHitTestRequest(request) && locationInContainer.intersects(boundsRect)) { - updateHitTestResult(result, locationInContainer.point() - toLayoutSize(adjustedLocation)); - return true; - } + // Check our bounds next. + LayoutRect boundsRect = borderBoxRect(); + boundsRect.moveBy(adjustedLocation); + if (visibleToHitTestRequest(request) && + locationInContainer.intersects(boundsRect)) { + updateHitTestResult( + result, locationInContainer.point() - toLayoutSize(adjustedLocation)); + return true; + } - return false; + return false; } PassRefPtr RenderBox::createLocalTransformState( - RenderLayer* rootLayer, RenderLayer* containerLayer, - const LayoutRect& hitTestRect, const HitTestLocation& hitTestLocation, - const HitTestingTransformState* containerTransformState) const -{ - RefPtr transformState; - LayoutPoint offset; - if (containerTransformState) { - // If we're already computing transform state, then it's relative to the container (which we know is non-null). - transformState = HitTestingTransformState::create(*containerTransformState); - layer()->convertToLayerCoords(containerLayer, offset); - } else { - // If this is the first time we need to make transform state, then base it off of hitTestLocation, - // which is relative to rootLayer. - transformState = HitTestingTransformState::create(hitTestLocation.transformedPoint(), hitTestLocation.transformedRect(), FloatQuad(hitTestRect)); - layer()->convertToLayerCoords(rootLayer, offset); - } + RenderLayer* rootLayer, + RenderLayer* containerLayer, + const LayoutRect& hitTestRect, + const HitTestLocation& hitTestLocation, + const HitTestingTransformState* containerTransformState) const { + RefPtr transformState; + LayoutPoint offset; + if (containerTransformState) { + // If we're already computing transform state, then it's relative to the + // container (which we know is non-null). + transformState = HitTestingTransformState::create(*containerTransformState); + layer()->convertToLayerCoords(containerLayer, offset); + } else { + // If this is the first time we need to make transform state, then base it + // off of hitTestLocation, which is relative to rootLayer. + transformState = HitTestingTransformState::create( + hitTestLocation.transformedPoint(), hitTestLocation.transformedRect(), + FloatQuad(hitTestRect)); + layer()->convertToLayerCoords(rootLayer, offset); + } + + RenderObject* containerRenderer = + containerLayer ? containerLayer->renderer() : 0; + if (shouldUseTransformFromContainer(containerRenderer)) { + TransformationMatrix containerTransform; + getTransformFromContainer(containerRenderer, toLayoutSize(offset), + containerTransform); + transformState->applyTransform( + containerTransform, HitTestingTransformState::AccumulateTransform); + } else { + transformState->translate(offset.x(), offset.y(), + HitTestingTransformState::AccumulateTransform); + } + + return transformState; +} - RenderObject* containerRenderer = containerLayer ? containerLayer->renderer() : 0; - if (shouldUseTransformFromContainer(containerRenderer)) { - TransformationMatrix containerTransform; - getTransformFromContainer(containerRenderer, toLayoutSize(offset), containerTransform); - transformState->applyTransform(containerTransform, HitTestingTransformState::AccumulateTransform); - } else { - transformState->translate(offset.x(), offset.y(), HitTestingTransformState::AccumulateTransform); - } +// Compute the z-offset of the point in the transformState. +// This is effectively projecting a ray normal to the plane of ancestor, finding +// where that ray intersects target, and computing the z delta between those two +// points. +static double computeZOffset(const HitTestingTransformState& transformState) { + // We got an affine transform, so no z-offset + if (transformState.m_accumulatedTransform.isAffine()) + return 0; + + // Flatten the point into the target plane + FloatPoint targetPoint = transformState.mappedPoint(); - return transformState; + // Now map the point back through the transform, which computes Z. + FloatPoint3D backmappedPoint = + transformState.m_accumulatedTransform.mapPoint(FloatPoint3D(targetPoint)); + return backmappedPoint.z(); } -// Compute the z-offset of the point in the transformState. -// This is effectively projecting a ray normal to the plane of ancestor, finding where that -// ray intersects target, and computing the z delta between those two points. -static double computeZOffset(const HitTestingTransformState& transformState) -{ - // We got an affine transform, so no z-offset - if (transformState.m_accumulatedTransform.isAffine()) - return 0; - - // Flatten the point into the target plane - FloatPoint targetPoint = transformState.mappedPoint(); - - // Now map the point back through the transform, which computes Z. - FloatPoint3D backmappedPoint = transformState.m_accumulatedTransform.mapPoint(FloatPoint3D(targetPoint)); - return backmappedPoint.z(); -} - -static bool isHitCandidate(bool canDepthSort, double* zOffset, const HitTestingTransformState* transformState) -{ - // The hit layer is depth-sorting with other layers, so just say that it was hit. - if (canDepthSort) - return true; +static bool isHitCandidate(bool canDepthSort, + double* zOffset, + const HitTestingTransformState* transformState) { + // The hit layer is depth-sorting with other layers, so just say that it was + // hit. + if (canDepthSort) + return true; - // We need to look at z-depth to decide if this layer was hit. - if (zOffset) { - ASSERT(transformState); - // This is actually computing our z, but that's OK because the hitLayer is coplanar with us. - double childZOffset = computeZOffset(*transformState); - if (childZOffset > *zOffset) { - *zOffset = childZOffset; - return true; - } - return false; + // We need to look at z-depth to decide if this layer was hit. + if (zOffset) { + ASSERT(transformState); + // This is actually computing our z, but that's OK because the hitLayer is + // coplanar with us. + double childZOffset = computeZOffset(*transformState); + if (childZOffset > *zOffset) { + *zOffset = childZOffset; + return true; } + return false; + } - return true; + return true; } -static inline bool forwardCompareZIndex(RenderBox* first, RenderBox* second) -{ - return first->style()->zIndex() < second->style()->zIndex(); +static inline bool forwardCompareZIndex(RenderBox* first, RenderBox* second) { + return first->style()->zIndex() < second->style()->zIndex(); } // hitTestLocation and hitTestRect are relative to rootLayer. // A 'flattening' layer is one preserves3D() == false. -// transformState.m_accumulatedTransform holds the transform from the containing flattening layer. -// transformState.m_lastPlanarPoint is the hitTestLocation in the plane of the containing flattening layer. -// transformState.m_lastPlanarQuad is the hitTestRect as a quad in the plane of the containing flattening layer. +// transformState.m_accumulatedTransform holds the transform from the containing +// flattening layer. transformState.m_lastPlanarPoint is the hitTestLocation in +// the plane of the containing flattening layer. transformState.m_lastPlanarQuad +// is the hitTestRect as a quad in the plane of the containing flattening layer. // -// If zOffset is non-null (which indicates that the caller wants z offset information), -// *zOffset on return is the z offset of the hit point relative to the containing flattening layer. -bool RenderBox::hitTestLayer(RenderLayer* rootLayer, RenderLayer* containerLayer, const HitTestRequest& request, HitTestResult& result, - const LayoutRect& hitTestRect, const HitTestLocation& hitTestLocation, - const HitTestingTransformState* transformState, double* zOffset) -{ - ASSERT(layer()->isSelfPaintingLayer()); - - // The natural thing would be to keep HitTestingTransformState on the stack, but it's big, so we heap-allocate. - RefPtr localTransformState; - - LayoutRect localHitTestRect = hitTestRect; - HitTestLocation localHitTestLocation = hitTestLocation; - - // We need transform state for the first time, or to offset the container state, or to accumulate the new transform. - if (transform() || transformState || layer()->has3DTransformedDescendant() || style()->preserves3D()) - localTransformState = createLocalTransformState(rootLayer, containerLayer, localHitTestRect, localHitTestLocation, transformState); - - // Apply a transform if we have one. - if (transform()) { - // The RenderView cannot have transforms. - ASSERT(parent()); - // Make sure the parent's clip rects have been calculated. - ClipRect clipRect = layer()->clipper().backgroundClipRect(ClipRectsContext(rootLayer, RootRelativeClipRects)); - // Go ahead and test the enclosing clip now. - if (!clipRect.intersects(localHitTestLocation)) - return 0; - - // If the transform can't be inverted, then don't hit test this layer at all. - if (!localTransformState->m_accumulatedTransform.isInvertible()) - return 0; - - // Compute the point and the hit test rect in the coords of this layer by using the values - // from the transformState, which store the point and quad in the coords of the last flattened - // layer, and the accumulated transform which lets up map through preserve-3d layers. - // - // We can't just map hitTestLocation and hitTestRect because they may have been flattened (losing z) - // by our container. - FloatPoint localPoint = localTransformState->mappedPoint(); - FloatQuad localPointQuad = localTransformState->mappedQuad(); - localHitTestRect = localTransformState->boundsOfMappedArea(); - if (localHitTestLocation.isRectBasedTest()) - localHitTestLocation = HitTestLocation(localPoint, localPointQuad); - else - localHitTestLocation = HitTestLocation(localPoint); - - // Now do a hit test with the root layer shifted to be us. - rootLayer = layer(); - } - - // Ensure our lists and 3d status are up-to-date. - layer()->stackingNode()->updateLayerListsIfNeeded(); - layer()->update3DTransformedDescendantStatus(); - - RefPtr unflattenedTransformState = localTransformState; - if (localTransformState && !style()->preserves3D()) { - // Keep a copy of the pre-flattening state, for computing z-offsets for the container - unflattenedTransformState = HitTestingTransformState::create(*localTransformState); - // This layer is flattening, so flatten the state passed to descendants. - localTransformState->flatten(); - } - - // The following are used for keeping track of the z-depth of the hit point of 3d-transformed - // descendants. - double localZOffset = -std::numeric_limits::infinity(); - double* zOffsetForDescendantsPtr = 0; - double* zOffsetForContentsPtr = 0; - - bool depthSortDescendants = false; - if (style()->preserves3D()) { - depthSortDescendants = true; - // Our layers can depth-test with our container, so share the z depth pointer with the container, if it passed one down. - zOffsetForDescendantsPtr = zOffset ? zOffset : &localZOffset; - zOffsetForContentsPtr = zOffset ? zOffset : &localZOffset; - } else if (zOffset) { - zOffsetForDescendantsPtr = 0; - // Container needs us to give back a z offset for the hit layer. - zOffsetForContentsPtr = zOffset; - } - - Vector layers; - collectSelfPaintingLayers(layers); - // Hit testing needs to walk in the backwards direction from paint. - // Forward compare and then reverse instead of just reverse comparing - // so that elements with the same z-index are walked in reverse tree order. - std::stable_sort(layers.begin(), layers.end(), forwardCompareZIndex); - layers.reverse(); - - bool hitLayer = false; - for (auto& currentLayer : layers) { - HitTestResult tempResult(result.hitTestLocation()); - bool localHitLayer = currentLayer->hitTestLayer(rootLayer, layer(), request, tempResult, - localHitTestRect, localHitTestLocation, localTransformState.get(), zOffsetForDescendantsPtr); - - // If it a rect-based test, we can safely append the temporary result since it might had hit - // nodes but not necesserily had hitLayer set. - if (result.isRectBasedTest()) - result.append(tempResult); - - if (localHitLayer && isHitCandidate(depthSortDescendants, zOffset, unflattenedTransformState.get())) { - hitLayer = localHitLayer; - if (!result.isRectBasedTest()) - result = tempResult; - if (!depthSortDescendants) - return true; - } +// If zOffset is non-null (which indicates that the caller wants z offset +// information), +// *zOffset on return is the z offset of the hit point relative to the +// containing flattening layer. +bool RenderBox::hitTestLayer(RenderLayer* rootLayer, + RenderLayer* containerLayer, + const HitTestRequest& request, + HitTestResult& result, + const LayoutRect& hitTestRect, + const HitTestLocation& hitTestLocation, + const HitTestingTransformState* transformState, + double* zOffset) { + ASSERT(layer()->isSelfPaintingLayer()); + + // The natural thing would be to keep HitTestingTransformState on the stack, + // but it's big, so we heap-allocate. + RefPtr localTransformState; + + LayoutRect localHitTestRect = hitTestRect; + HitTestLocation localHitTestLocation = hitTestLocation; + + // We need transform state for the first time, or to offset the container + // state, or to accumulate the new transform. + if (transform() || transformState || layer()->has3DTransformedDescendant() || + style()->preserves3D()) + localTransformState = + createLocalTransformState(rootLayer, containerLayer, localHitTestRect, + localHitTestLocation, transformState); + + // Apply a transform if we have one. + if (transform()) { + // The RenderView cannot have transforms. + ASSERT(parent()); + // Make sure the parent's clip rects have been calculated. + ClipRect clipRect = layer()->clipper().backgroundClipRect( + ClipRectsContext(rootLayer, RootRelativeClipRects)); + // Go ahead and test the enclosing clip now. + if (!clipRect.intersects(localHitTestLocation)) + return 0; + + // If the transform can't be inverted, then don't hit test this layer at + // all. + if (!localTransformState->m_accumulatedTransform.isInvertible()) + return 0; + + // Compute the point and the hit test rect in the coords of this layer by + // using the values from the transformState, which store the point and quad + // in the coords of the last flattened layer, and the accumulated transform + // which lets up map through preserve-3d layers. + // + // We can't just map hitTestLocation and hitTestRect because they may have + // been flattened (losing z) by our container. + FloatPoint localPoint = localTransformState->mappedPoint(); + FloatQuad localPointQuad = localTransformState->mappedQuad(); + localHitTestRect = localTransformState->boundsOfMappedArea(); + if (localHitTestLocation.isRectBasedTest()) + localHitTestLocation = HitTestLocation(localPoint, localPointQuad); + else + localHitTestLocation = HitTestLocation(localPoint); + + // Now do a hit test with the root layer shifted to be us. + rootLayer = layer(); + } + + // Ensure our lists and 3d status are up-to-date. + layer()->stackingNode()->updateLayerListsIfNeeded(); + layer()->update3DTransformedDescendantStatus(); + + RefPtr unflattenedTransformState = + localTransformState; + if (localTransformState && !style()->preserves3D()) { + // Keep a copy of the pre-flattening state, for computing z-offsets for the + // container + unflattenedTransformState = + HitTestingTransformState::create(*localTransformState); + // This layer is flattening, so flatten the state passed to descendants. + localTransformState->flatten(); + } + + // The following are used for keeping track of the z-depth of the hit point of + // 3d-transformed descendants. + double localZOffset = -std::numeric_limits::infinity(); + double* zOffsetForDescendantsPtr = 0; + double* zOffsetForContentsPtr = 0; + + bool depthSortDescendants = false; + if (style()->preserves3D()) { + depthSortDescendants = true; + // Our layers can depth-test with our container, so share the z depth + // pointer with the container, if it passed one down. + zOffsetForDescendantsPtr = zOffset ? zOffset : &localZOffset; + zOffsetForContentsPtr = zOffset ? zOffset : &localZOffset; + } else if (zOffset) { + zOffsetForDescendantsPtr = 0; + // Container needs us to give back a z offset for the hit layer. + zOffsetForContentsPtr = zOffset; + } + + Vector layers; + collectSelfPaintingLayers(layers); + // Hit testing needs to walk in the backwards direction from paint. + // Forward compare and then reverse instead of just reverse comparing + // so that elements with the same z-index are walked in reverse tree order. + std::stable_sort(layers.begin(), layers.end(), forwardCompareZIndex); + layers.reverse(); + + bool hitLayer = false; + for (auto& currentLayer : layers) { + HitTestResult tempResult(result.hitTestLocation()); + bool localHitLayer = currentLayer->hitTestLayer( + rootLayer, layer(), request, tempResult, localHitTestRect, + localHitTestLocation, localTransformState.get(), + zOffsetForDescendantsPtr); + + // If it a rect-based test, we can safely append the temporary result since + // it might had hit nodes but not necesserily had hitLayer set. + if (result.isRectBasedTest()) + result.append(tempResult); + + if (localHitLayer && isHitCandidate(depthSortDescendants, zOffset, + unflattenedTransformState.get())) { + hitLayer = localHitLayer; + if (!result.isRectBasedTest()) + result = tempResult; + if (!depthSortDescendants) + return true; } - - LayoutRect layerBounds; - ClipRect contentRect; - ClipRectsContext clipRectsContext(rootLayer, RootRelativeClipRects); - layer()->clipper().calculateRects(clipRectsContext, localHitTestRect, layerBounds, contentRect); - - // Next we want to see if the mouse pos is inside the child RenderObjects of the layer. - if (contentRect.intersects(localHitTestLocation)) { - // Hit test with a temporary HitTestResult, because we only want to commit to 'result' if we know we're frontmost. - HitTestResult tempResult(result.hitTestLocation()); - if (hitTestNonLayerDescendants(request, tempResult, layerBounds, localHitTestLocation) - && isHitCandidate(false, zOffsetForContentsPtr, unflattenedTransformState.get())) { - if (result.isRectBasedTest()) - result.append(tempResult); - else - result = tempResult; - if (!depthSortDescendants) - return true; - // Foreground can depth-sort with descendant layers, so keep this as a candidate. - hitLayer = true; - } else if (result.isRectBasedTest()) { - result.append(tempResult); - } + } + + LayoutRect layerBounds; + ClipRect contentRect; + ClipRectsContext clipRectsContext(rootLayer, RootRelativeClipRects); + layer()->clipper().calculateRects(clipRectsContext, localHitTestRect, + layerBounds, contentRect); + + // Next we want to see if the mouse pos is inside the child RenderObjects of + // the layer. + if (contentRect.intersects(localHitTestLocation)) { + // Hit test with a temporary HitTestResult, because we only want to commit + // to 'result' if we know we're frontmost. + HitTestResult tempResult(result.hitTestLocation()); + if (hitTestNonLayerDescendants(request, tempResult, layerBounds, + localHitTestLocation) && + isHitCandidate(false, zOffsetForContentsPtr, + unflattenedTransformState.get())) { + if (result.isRectBasedTest()) + result.append(tempResult); + else + result = tempResult; + if (!depthSortDescendants) + return true; + // Foreground can depth-sort with descendant layers, so keep this as a + // candidate. + hitLayer = true; + } else if (result.isRectBasedTest()) { + result.append(tempResult); } + } - return hitLayer; + return hitLayer; } -bool RenderBox::hitTestNonLayerDescendants(const HitTestRequest& request, HitTestResult& result, - const LayoutRect& layerBounds, const HitTestLocation& hitTestLocation) -{ - return hitTest(request, result, hitTestLocation, toLayoutPoint(layerBounds.location() - location())); +bool RenderBox::hitTestNonLayerDescendants( + const HitTestRequest& request, + HitTestResult& result, + const LayoutRect& layerBounds, + const HitTestLocation& hitTestLocation) { + return hitTest(request, result, hitTestLocation, + toLayoutPoint(layerBounds.location() - location())); } // --------------------- painting stuff ------------------------------- -void RenderBox::paintLayer(GraphicsContext* context, const LayerPaintingInfo& paintingInfo) -{ - // If this layer is totally invisible then there is nothing to paint. - // TODO(ojan): Return false from isSelfPainting and then ASSERT(!opacity()) here. - if (!opacity()) - return; - - if (!transform()) { - paintLayerContents(context, paintingInfo); - return; - } - - // The RenderView can't be transformed in Sky. - ASSERT(layer()->parent()); - - // If the transform can't be inverted, then don't paint anything. - if (!transform()->isInvertible()) - return; - - // Make sure the parent's clip rects have been calculated. - ClipRectsContext clipRectsContext(paintingInfo.rootLayer, PaintingClipRects); - ClipRect clipRect = layer()->clipper().backgroundClipRect(clipRectsContext); - clipRect.intersect(paintingInfo.paintDirtyRect); - - // Push the parent coordinate space's clip. - layer()->parent()->clipToRect(paintingInfo, context, clipRect); - - // This involves subtracting out the position of the layer in our current coordinate space, but preserving - // the accumulated error for sub-pixel layout. +void RenderBox::paintLayer(GraphicsContext* context, + const LayerPaintingInfo& paintingInfo) { + // If this layer is totally invisible then there is nothing to paint. + // TODO(ojan): Return false from isSelfPainting and then ASSERT(!opacity()) + // here. + if (!opacity()) + return; + + if (!transform()) { + paintLayerContents(context, paintingInfo); + return; + } + + // The RenderView can't be transformed in Sky. + ASSERT(layer()->parent()); + + // If the transform can't be inverted, then don't paint anything. + if (!transform()->isInvertible()) + return; + + // Make sure the parent's clip rects have been calculated. + ClipRectsContext clipRectsContext(paintingInfo.rootLayer, PaintingClipRects); + ClipRect clipRect = layer()->clipper().backgroundClipRect(clipRectsContext); + clipRect.intersect(paintingInfo.paintDirtyRect); + + // Push the parent coordinate space's clip. + layer()->parent()->clipToRect(paintingInfo, context, clipRect); + + // This involves subtracting out the position of the layer in our current + // coordinate space, but preserving the accumulated error for sub-pixel + // layout. + LayoutPoint delta; + layer()->convertToLayerCoords(paintingInfo.rootLayer, delta); + TransformationMatrix localTransform(*transform()); + IntPoint roundedDelta = roundedIntPoint(delta); + localTransform.translateRight(roundedDelta.x(), roundedDelta.y()); + LayoutSize adjustedSubPixelAccumulation = + paintingInfo.subPixelAccumulation + (delta - roundedDelta); + + // Apply the transform. + GraphicsContextStateSaver stateSaver(*context, false); + if (!localTransform.isIdentity()) { + stateSaver.save(); + context->concatCTM(localTransform.toAffineTransform()); + } + + // Now do a paint with the root layer shifted to be us. + LayerPaintingInfo transformedPaintingInfo( + layer(), + enclosingIntRect( + localTransform.inverse().mapRect(paintingInfo.paintDirtyRect)), + adjustedSubPixelAccumulation); + paintLayerContents(context, transformedPaintingInfo); + + // Restore the clip. + layer()->parent()->restoreClip(context, paintingInfo.paintDirtyRect, + clipRect); +} + +static LayoutRect transparencyClipBox(const RenderLayer*, + const RenderLayer* rootLayer, + const LayoutSize& subPixelAccumulation); + +static void expandClipRectForDescendantsAndReflection( + LayoutRect& clipRect, + const RenderLayer* layer, + const RenderLayer* rootLayer, + const LayoutSize& subPixelAccumulation) { + // Note: we don't have to walk z-order lists since transparent elements always + // establish a stacking container. This means we can just walk the layer tree + // directly. + for (RenderLayer* curr = layer->firstChild(); curr; + curr = curr->nextSibling()) + clipRect.unite(transparencyClipBox(curr, rootLayer, subPixelAccumulation)); +} + +static LayoutRect transparencyClipBox(const RenderLayer* layer, + const RenderLayer* rootLayer, + const LayoutSize& subPixelAccumulation) { + // FIXME: Although this function completely ignores CSS-imposed clipping, we + // did already intersect with the paintDirtyRect, and that should cut down on + // the amount we have to paint. Still it would be better to respect clips. + + if (rootLayer != layer && layer->renderer()->transform()) { + // The best we can do here is to use enclosed bounding boxes to establish a + // "fuzzy" enough clip to encompass the transformed layer and all of its + // children. + const RenderLayer* rootLayerForTransform = rootLayer; LayoutPoint delta; - layer()->convertToLayerCoords(paintingInfo.rootLayer, delta); - TransformationMatrix localTransform(*transform()); - IntPoint roundedDelta = roundedIntPoint(delta); - localTransform.translateRight(roundedDelta.x(), roundedDelta.y()); - LayoutSize adjustedSubPixelAccumulation = paintingInfo.subPixelAccumulation + (delta - roundedDelta); - - // Apply the transform. - GraphicsContextStateSaver stateSaver(*context, false); - if (!localTransform.isIdentity()) { - stateSaver.save(); - context->concatCTM(localTransform.toAffineTransform()); - } - - // Now do a paint with the root layer shifted to be us. - LayerPaintingInfo transformedPaintingInfo(layer(), enclosingIntRect(localTransform.inverse().mapRect(paintingInfo.paintDirtyRect)), - adjustedSubPixelAccumulation); - paintLayerContents(context, transformedPaintingInfo); - - // Restore the clip. - layer()->parent()->restoreClip(context, paintingInfo.paintDirtyRect, clipRect); -} - -static LayoutRect transparencyClipBox(const RenderLayer*, const RenderLayer* rootLayer, const LayoutSize& subPixelAccumulation); - -static void expandClipRectForDescendantsAndReflection(LayoutRect& clipRect, const RenderLayer* layer, const RenderLayer* rootLayer, - const LayoutSize& subPixelAccumulation) -{ - // Note: we don't have to walk z-order lists since transparent elements always establish - // a stacking container. This means we can just walk the layer tree directly. - for (RenderLayer* curr = layer->firstChild(); curr; curr = curr->nextSibling()) - clipRect.unite(transparencyClipBox(curr, rootLayer, subPixelAccumulation)); -} - -static LayoutRect transparencyClipBox(const RenderLayer* layer, const RenderLayer* rootLayer, - const LayoutSize& subPixelAccumulation) -{ - // FIXME: Although this function completely ignores CSS-imposed clipping, we did already intersect with the - // paintDirtyRect, and that should cut down on the amount we have to paint. Still it - // would be better to respect clips. - - if (rootLayer != layer && layer->renderer()->transform()) { - // The best we can do here is to use enclosed bounding boxes to establish a "fuzzy" enough clip to encompass - // the transformed layer and all of its children. - const RenderLayer* rootLayerForTransform = rootLayer; - LayoutPoint delta; - layer->convertToLayerCoords(rootLayerForTransform, delta); - - delta.move(subPixelAccumulation); - IntPoint pixelSnappedDelta = roundedIntPoint(delta); - TransformationMatrix transform; - transform.translate(pixelSnappedDelta.x(), pixelSnappedDelta.y()); - transform = transform * *layer->renderer()->transform(); - - // We don't use fragment boxes when collecting a transformed layer's bounding box, since it always - // paints unfragmented.y - LayoutRect clipRect = layer->physicalBoundingBox(layer); - expandClipRectForDescendantsAndReflection(clipRect, layer, layer, subPixelAccumulation); - LayoutRect result = transform.mapRect(clipRect); - return result; - } - - LayoutRect clipRect = layer->physicalBoundingBox(rootLayer); - expandClipRectForDescendantsAndReflection(clipRect, layer, rootLayer, subPixelAccumulation); - clipRect.move(subPixelAccumulation); - return clipRect; -} - -void RenderBox::paintLayerContents(GraphicsContext* context, const LayerPaintingInfo& paintingInfo) -{ - float deviceScaleFactor = 1.0f; - context->setDeviceScaleFactor(deviceScaleFactor); - - LayoutPoint offsetFromRoot; - layer()->convertToLayerCoords(paintingInfo.rootLayer, offsetFromRoot); - - LayerPaintingInfo localPaintingInfo(paintingInfo); - - LayoutRect layerBounds; - ClipRect contentRect; - ClipRectsContext clipRectsContext(localPaintingInfo.rootLayer, PaintingClipRects, localPaintingInfo.subPixelAccumulation); - layer()->clipper().calculateRects(clipRectsContext, localPaintingInfo.paintDirtyRect, - layerBounds, contentRect, &offsetFromRoot); - - if (!layer()->intersectsDamageRect(layerBounds, contentRect.rect(), localPaintingInfo.rootLayer, &offsetFromRoot)) - return; - - LayoutRect rootRelativeBounds; - - // Apply clip-path to context. - GraphicsContextStateSaver clipStateSaver(*context, false); - - // Clip-path, like border radius, must not be applied to the contents of a composited-scrolling container. - // It must, however, still be applied to the mask layer, so that the compositor can properly mask the - // scrolling contents and scrollbars. - if (hasClipPath()) { - ASSERT(style()->clipPath()); - if (style()->clipPath()->type() == ClipPathOperation::SHAPE) { - // Removed. - } - } - - if (isTransparent()) { - context->save(); - LayoutRect clipRect = intersection(paintingInfo.paintDirtyRect, - transparencyClipBox(layer(), localPaintingInfo.rootLayer, localPaintingInfo.subPixelAccumulation)); - context->clip(clipRect); - context->beginTransparencyLayer(opacity()); - } - - layer()->clipToRect(localPaintingInfo, context, contentRect); - - LayoutPoint layerLocation = toPoint(layerBounds.location() - location() + localPaintingInfo.subPixelAccumulation); - - Vector layers; - PaintInfo paintInfo(context, pixelSnappedIntRect(contentRect.rect()), localPaintingInfo.rootLayer->renderer()); - paint(paintInfo, layerLocation, layers); - - std::stable_sort(layers.begin(), layers.end(), forwardCompareZIndex); - for (auto& box : layers) { - box->paintLayer(context, paintingInfo); - } - - layer()->restoreClip(context, localPaintingInfo.paintDirtyRect, contentRect); - - if (isTransparent()) { - context->endLayer(); - context->restore(); - } -} - -void RenderBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, Vector& layers) -{ - LayoutPoint adjustedPaintOffset = paintOffset + location(); - for (RenderObject* child = slowFirstChild(); child; child = child->nextSibling()) - child->paint(paintInfo, adjustedPaintOffset, layers); -} - -BackgroundBleedAvoidance RenderBox::determineBackgroundBleedAvoidance(GraphicsContext* context, const BoxDecorationData& boxDecorationData) const -{ - if (!boxDecorationData.hasBackground || !boxDecorationData.hasBorder || !style()->hasBorderRadius()) - return BackgroundBleedNone; - - // FIXME: See crbug.com/382491. getCTM does not accurately reflect the scale at the time content is - // rasterized, and should not be relied on to make decisions about bleeding. - AffineTransform ctm = context->getCTM(); - FloatSize contextScaling(static_cast(ctm.xScale()), static_cast(ctm.yScale())); - - // Because RoundedRect uses IntRect internally the inset applied by the - // BackgroundBleedShrinkBackground strategy cannot be less than one integer - // layout coordinate, even with subpixel layout enabled. To take that into - // account, we clamp the contextScaling to 1.0 for the following test so - // that borderObscuresBackgroundEdge can only return true if the border - // widths are greater than 2 in both layout coordinates and screen - // coordinates. - // This precaution will become obsolete if RoundedRect is ever promoted to - // a sub-pixel representation. - if (contextScaling.width() > 1) - contextScaling.setWidth(1); - if (contextScaling.height() > 1) - contextScaling.setHeight(1); - - if (borderObscuresBackgroundEdge(contextScaling)) - return BackgroundBleedShrinkBackground; - if (borderObscuresBackground() && backgroundHasOpaqueTopLayer()) - return BackgroundBleedBackgroundOverBorder; - - return BackgroundBleedClipBackground; -} - -void RenderBox::paintBoxDecorationBackground(PaintInfo& paintInfo, const LayoutPoint& paintOffset) -{ - LayoutRect paintRect = borderBoxRect(); - paintRect.moveBy(paintOffset); - paintBoxDecorationBackgroundWithRect(paintInfo, paintOffset, paintRect); -} - -void RenderBox::paintBoxDecorationBackgroundWithRect(PaintInfo& paintInfo, const LayoutPoint& paintOffset, const LayoutRect& paintRect) -{ - RenderStyle* style = this->style(); - BoxDecorationData boxDecorationData(*style); - BackgroundBleedAvoidance bleedAvoidance = determineBackgroundBleedAvoidance(paintInfo.context, boxDecorationData); - - // FIXME: Should eventually give the theme control over whether the box shadow should paint, since controls could have - // custom shadows of their own. - if (!boxShadowShouldBeAppliedToBackground(bleedAvoidance)) - paintBoxShadow(paintInfo, paintRect, style, Normal); - - GraphicsContextStateSaver stateSaver(*paintInfo.context, false); - if (bleedAvoidance == BackgroundBleedClipBackground) { - stateSaver.save(); - RoundedRect border = style->getRoundedBorderFor(paintRect); - paintInfo.context->clipRoundedRect(border); - } - - if (bleedAvoidance == BackgroundBleedBackgroundOverBorder) - paintBorder(paintInfo, paintRect, style, bleedAvoidance); - - paintBackground(paintInfo, paintRect, boxDecorationData.backgroundColor, bleedAvoidance); - paintBoxShadow(paintInfo, paintRect, style, Inset); - - // The theme will tell us whether or not we should also paint the CSS border. - if (boxDecorationData.hasBorder && bleedAvoidance != BackgroundBleedBackgroundOverBorder) - paintBorder(paintInfo, paintRect, style, bleedAvoidance); -} - -void RenderBox::paintBackground(const PaintInfo& paintInfo, const LayoutRect& paintRect, const Color& backgroundColor, BackgroundBleedAvoidance bleedAvoidance) -{ - paintFillLayers(paintInfo, backgroundColor, style()->backgroundLayers(), paintRect, bleedAvoidance); -} - -bool RenderBox::backgroundHasOpaqueTopLayer() const -{ - const FillLayer& fillLayer = style()->backgroundLayers(); - if (fillLayer.clip() != BorderFillBox) - return false; - - if (fillLayer.hasOpaqueImage(this) && fillLayer.hasRepeatXY() && fillLayer.image()->canRender(*this)) - return true; - - // If there is only one layer and no image, check whether the background color is opaque - if (!fillLayer.next() && !fillLayer.hasImage()) { - Color bgColor = style()->resolveColor(style()->backgroundColor()); - if (bgColor.alpha() == 255) - return true; - } - + layer->convertToLayerCoords(rootLayerForTransform, delta); + + delta.move(subPixelAccumulation); + IntPoint pixelSnappedDelta = roundedIntPoint(delta); + TransformationMatrix transform; + transform.translate(pixelSnappedDelta.x(), pixelSnappedDelta.y()); + transform = transform * *layer->renderer()->transform(); + + // We don't use fragment boxes when collecting a transformed layer's + // bounding box, since it always paints unfragmented.y + LayoutRect clipRect = layer->physicalBoundingBox(layer); + expandClipRectForDescendantsAndReflection(clipRect, layer, layer, + subPixelAccumulation); + LayoutRect result = transform.mapRect(clipRect); + return result; + } + + LayoutRect clipRect = layer->physicalBoundingBox(rootLayer); + expandClipRectForDescendantsAndReflection(clipRect, layer, rootLayer, + subPixelAccumulation); + clipRect.move(subPixelAccumulation); + return clipRect; +} + +void RenderBox::paintLayerContents(GraphicsContext* context, + const LayerPaintingInfo& paintingInfo) { + float deviceScaleFactor = 1.0f; + context->setDeviceScaleFactor(deviceScaleFactor); + + LayoutPoint offsetFromRoot; + layer()->convertToLayerCoords(paintingInfo.rootLayer, offsetFromRoot); + + LayerPaintingInfo localPaintingInfo(paintingInfo); + + LayoutRect layerBounds; + ClipRect contentRect; + ClipRectsContext clipRectsContext(localPaintingInfo.rootLayer, + PaintingClipRects, + localPaintingInfo.subPixelAccumulation); + layer()->clipper().calculateRects(clipRectsContext, + localPaintingInfo.paintDirtyRect, + layerBounds, contentRect, &offsetFromRoot); + + if (!layer()->intersectsDamageRect(layerBounds, contentRect.rect(), + localPaintingInfo.rootLayer, + &offsetFromRoot)) + return; + + LayoutRect rootRelativeBounds; + + // Apply clip-path to context. + GraphicsContextStateSaver clipStateSaver(*context, false); + + // Clip-path, like border radius, must not be applied to the contents of a + // composited-scrolling container. It must, however, still be applied to the + // mask layer, so that the compositor can properly mask the scrolling contents + // and scrollbars. + if (hasClipPath()) { + ASSERT(style()->clipPath()); + if (style()->clipPath()->type() == ClipPathOperation::SHAPE) { + // Removed. + } + } + + if (isTransparent()) { + context->save(); + LayoutRect clipRect = intersection( + paintingInfo.paintDirtyRect, + transparencyClipBox(layer(), localPaintingInfo.rootLayer, + localPaintingInfo.subPixelAccumulation)); + context->clip(clipRect); + context->beginTransparencyLayer(opacity()); + } + + layer()->clipToRect(localPaintingInfo, context, contentRect); + + LayoutPoint layerLocation = toPoint(layerBounds.location() - location() + + localPaintingInfo.subPixelAccumulation); + + Vector layers; + PaintInfo paintInfo(context, pixelSnappedIntRect(contentRect.rect()), + localPaintingInfo.rootLayer->renderer()); + paint(paintInfo, layerLocation, layers); + + std::stable_sort(layers.begin(), layers.end(), forwardCompareZIndex); + for (auto& box : layers) { + box->paintLayer(context, paintingInfo); + } + + layer()->restoreClip(context, localPaintingInfo.paintDirtyRect, contentRect); + + if (isTransparent()) { + context->endLayer(); + context->restore(); + } +} + +void RenderBox::paint(PaintInfo& paintInfo, + const LayoutPoint& paintOffset, + Vector& layers) { + LayoutPoint adjustedPaintOffset = paintOffset + location(); + for (RenderObject* child = slowFirstChild(); child; + child = child->nextSibling()) + child->paint(paintInfo, adjustedPaintOffset, layers); +} + +BackgroundBleedAvoidance RenderBox::determineBackgroundBleedAvoidance( + GraphicsContext* context, + const BoxDecorationData& boxDecorationData) const { + if (!boxDecorationData.hasBackground || !boxDecorationData.hasBorder || + !style()->hasBorderRadius()) + return BackgroundBleedNone; + + // FIXME: See crbug.com/382491. getCTM does not accurately reflect the scale + // at the time content is rasterized, and should not be relied on to make + // decisions about bleeding. + AffineTransform ctm = context->getCTM(); + FloatSize contextScaling(static_cast(ctm.xScale()), + static_cast(ctm.yScale())); + + // Because RoundedRect uses IntRect internally the inset applied by the + // BackgroundBleedShrinkBackground strategy cannot be less than one integer + // layout coordinate, even with subpixel layout enabled. To take that into + // account, we clamp the contextScaling to 1.0 for the following test so + // that borderObscuresBackgroundEdge can only return true if the border + // widths are greater than 2 in both layout coordinates and screen + // coordinates. + // This precaution will become obsolete if RoundedRect is ever promoted to + // a sub-pixel representation. + if (contextScaling.width() > 1) + contextScaling.setWidth(1); + if (contextScaling.height() > 1) + contextScaling.setHeight(1); + + if (borderObscuresBackgroundEdge(contextScaling)) + return BackgroundBleedShrinkBackground; + if (borderObscuresBackground() && backgroundHasOpaqueTopLayer()) + return BackgroundBleedBackgroundOverBorder; + + return BackgroundBleedClipBackground; +} + +void RenderBox::paintBoxDecorationBackground(PaintInfo& paintInfo, + const LayoutPoint& paintOffset) { + LayoutRect paintRect = borderBoxRect(); + paintRect.moveBy(paintOffset); + paintBoxDecorationBackgroundWithRect(paintInfo, paintOffset, paintRect); +} + +void RenderBox::paintBoxDecorationBackgroundWithRect( + PaintInfo& paintInfo, + const LayoutPoint& paintOffset, + const LayoutRect& paintRect) { + RenderStyle* style = this->style(); + BoxDecorationData boxDecorationData(*style); + BackgroundBleedAvoidance bleedAvoidance = + determineBackgroundBleedAvoidance(paintInfo.context, boxDecorationData); + + // FIXME: Should eventually give the theme control over whether the box shadow + // should paint, since controls could have custom shadows of their own. + if (!boxShadowShouldBeAppliedToBackground(bleedAvoidance)) + paintBoxShadow(paintInfo, paintRect, style, Normal); + + GraphicsContextStateSaver stateSaver(*paintInfo.context, false); + if (bleedAvoidance == BackgroundBleedClipBackground) { + stateSaver.save(); + RoundedRect border = style->getRoundedBorderFor(paintRect); + paintInfo.context->clipRoundedRect(border); + } + + if (bleedAvoidance == BackgroundBleedBackgroundOverBorder) + paintBorder(paintInfo, paintRect, style, bleedAvoidance); + + paintBackground(paintInfo, paintRect, boxDecorationData.backgroundColor, + bleedAvoidance); + paintBoxShadow(paintInfo, paintRect, style, Inset); + + // The theme will tell us whether or not we should also paint the CSS border. + if (boxDecorationData.hasBorder && + bleedAvoidance != BackgroundBleedBackgroundOverBorder) + paintBorder(paintInfo, paintRect, style, bleedAvoidance); +} + +void RenderBox::paintBackground(const PaintInfo& paintInfo, + const LayoutRect& paintRect, + const Color& backgroundColor, + BackgroundBleedAvoidance bleedAvoidance) { + paintFillLayers(paintInfo, backgroundColor, style()->backgroundLayers(), + paintRect, bleedAvoidance); +} + +bool RenderBox::backgroundHasOpaqueTopLayer() const { + const FillLayer& fillLayer = style()->backgroundLayers(); + if (fillLayer.clip() != BorderFillBox) return false; -} - -void RenderBox::paintFillLayers(const PaintInfo& paintInfo, const Color& c, const FillLayer& fillLayer, const LayoutRect& rect, - BackgroundBleedAvoidance bleedAvoidance, RenderObject* backgroundObject) -{ - Vector layers; - const FillLayer* curLayer = &fillLayer; - bool shouldDrawBackgroundInSeparateBuffer = false; - while (curLayer) { - layers.append(curLayer); - // Stop traversal when an opaque layer is encountered. - // FIXME : It would be possible for the following occlusion culling test to be more aggressive - // on layers with no repeat by testing whether the image covers the layout rect. - // Testing that here would imply duplicating a lot of calculations that are currently done in - // RenderBoxModelObject::paintFillLayerExtended. A more efficient solution might be to move - // the layer recursion into paintFillLayerExtended, or to compute the layer geometry here - // and pass it down. - - if (!shouldDrawBackgroundInSeparateBuffer && curLayer->blendMode() != WebBlendModeNormal) - shouldDrawBackgroundInSeparateBuffer = true; - - // The clipOccludesNextLayers condition must be evaluated first to avoid short-circuiting. - if (curLayer->clipOccludesNextLayers(curLayer == &fillLayer) && curLayer->hasOpaqueImage(this) && curLayer->image()->canRender(*this) && curLayer->hasRepeatXY() && curLayer->blendMode() == WebBlendModeNormal && !boxShadowShouldBeAppliedToBackground(bleedAvoidance)) - break; - curLayer = curLayer->next(); - } - - GraphicsContext* context = paintInfo.context; - if (!context) - shouldDrawBackgroundInSeparateBuffer = false; - - // FIXME(sky): Propagate this constant. - bool skipBaseColor = false; - if (shouldDrawBackgroundInSeparateBuffer) - context->beginTransparencyLayer(1); - - Vector::const_reverse_iterator topLayer = layers.rend(); - for (Vector::const_reverse_iterator it = layers.rbegin(); it != topLayer; ++it) - paintFillLayer(paintInfo, c, **it, rect, bleedAvoidance, backgroundObject, skipBaseColor); - - if (shouldDrawBackgroundInSeparateBuffer) - context->endLayer(); -} -void RenderBox::paintFillLayer(const PaintInfo& paintInfo, const Color& c, const FillLayer& fillLayer, const LayoutRect& rect, - BackgroundBleedAvoidance bleedAvoidance, RenderObject* backgroundObject, bool skipBaseColor) -{ - paintFillLayerExtended(paintInfo, c, fillLayer, rect, bleedAvoidance, 0, LayoutSize(), backgroundObject, skipBaseColor); -} - -bool RenderBox::pushContentsClip(PaintInfo& paintInfo, const LayoutPoint& accumulatedOffset, ContentsClipBehavior contentsClipBehavior) -{ - bool isOverflowClip = hasOverflowClip() && !layer()->isSelfPaintingLayer(); - if (!isOverflowClip) - return false; - - LayoutRect clipRect = overflowClipRect(accumulatedOffset); - RoundedRect clipRoundedRect(0, 0, 0, 0); - bool hasBorderRadius = style()->hasBorderRadius(); - if (hasBorderRadius) - clipRoundedRect = style()->getRoundedInnerBorderFor(LayoutRect(accumulatedOffset, size())); - - if (contentsClipBehavior == SkipContentsClipIfPossible) { - LayoutRect contentsVisualOverflow = contentsVisualOverflowRect(); - if (contentsVisualOverflow.isEmpty()) - return false; - - LayoutRect conservativeClipRect = clipRect; - if (hasBorderRadius) - conservativeClipRect.intersect(clipRoundedRect.radiusCenterRect()); - conservativeClipRect.moveBy(-accumulatedOffset); - if (conservativeClipRect.contains(contentsVisualOverflow)) - return false; - } - - paintInfo.context->save(); - if (hasBorderRadius) - paintInfo.context->clipRoundedRect(clipRoundedRect); - paintInfo.context->clip(pixelSnappedIntRect(clipRect)); + if (fillLayer.hasOpaqueImage(this) && fillLayer.hasRepeatXY() && + fillLayer.image()->canRender(*this)) return true; -} - -void RenderBox::popContentsClip(PaintInfo& paintInfo, const LayoutPoint& accumulatedOffset) -{ - ASSERT(hasOverflowClip() && !layer()->isSelfPaintingLayer()); - paintInfo.context->restore(); -} - -LayoutRect RenderBox::overflowClipRect(const LayoutPoint& location) -{ - LayoutRect clipRect = borderBoxRect(); - clipRect.setLocation(location + clipRect.location() + LayoutSize(borderLeft(), borderTop())); - clipRect.setSize(clipRect.size() - LayoutSize(borderLeft() + borderRight(), borderTop() + borderBottom())); - return clipRect; -} - -LayoutRect RenderBox::clipRect(const LayoutPoint& location) -{ - LayoutRect borderBoxRect = this->borderBoxRect(); - LayoutRect clipRect = LayoutRect(borderBoxRect.location() + location, borderBoxRect.size()); - - if (!style()->clipLeft().isAuto()) { - LayoutUnit c = valueForLength(style()->clipLeft(), borderBoxRect.width()); - clipRect.move(c, 0); - clipRect.contract(c, 0); - } - - if (!style()->clipRight().isAuto()) - clipRect.contract(width() - valueForLength(style()->clipRight(), width()), 0); - - if (!style()->clipTop().isAuto()) { - LayoutUnit c = valueForLength(style()->clipTop(), borderBoxRect.height()); - clipRect.move(0, c); - clipRect.contract(0, c); - } - - if (!style()->clipBottom().isAuto()) - clipRect.contract(0, height() - valueForLength(style()->clipBottom(), height())); - - return clipRect; -} -LayoutUnit RenderBox::containingBlockLogicalHeightForContent(AvailableLogicalHeightType heightType) const -{ - return containingBlock()->availableLogicalHeight(heightType); -} - -void RenderBox::mapLocalToContainer(const RenderBox* paintInvalidationContainer, TransformState& transformState, MapCoordinatesFlags mode) const -{ - if (paintInvalidationContainer == this) - return; - - bool containerSkipped; - RenderObject* o = container(paintInvalidationContainer, &containerSkipped); - if (!o) - return; + // If there is only one layer and no image, check whether the background color + // is opaque + if (!fillLayer.next() && !fillLayer.hasImage()) { + Color bgColor = style()->resolveColor(style()->backgroundColor()); + if (bgColor.alpha() == 255) + return true; + } + + return false; +} + +void RenderBox::paintFillLayers(const PaintInfo& paintInfo, + const Color& c, + const FillLayer& fillLayer, + const LayoutRect& rect, + BackgroundBleedAvoidance bleedAvoidance, + RenderObject* backgroundObject) { + Vector layers; + const FillLayer* curLayer = &fillLayer; + bool shouldDrawBackgroundInSeparateBuffer = false; + while (curLayer) { + layers.append(curLayer); + // Stop traversal when an opaque layer is encountered. + // FIXME : It would be possible for the following occlusion culling test to + // be more aggressive on layers with no repeat by testing whether the image + // covers the layout rect. Testing that here would imply duplicating a lot + // of calculations that are currently done in + // RenderBoxModelObject::paintFillLayerExtended. A more efficient solution + // might be to move the layer recursion into paintFillLayerExtended, or to + // compute the layer geometry here and pass it down. + + if (!shouldDrawBackgroundInSeparateBuffer && + curLayer->blendMode() != WebBlendModeNormal) + shouldDrawBackgroundInSeparateBuffer = true; + + // The clipOccludesNextLayers condition must be evaluated first to avoid + // short-circuiting. + if (curLayer->clipOccludesNextLayers(curLayer == &fillLayer) && + curLayer->hasOpaqueImage(this) && curLayer->image()->canRender(*this) && + curLayer->hasRepeatXY() && + curLayer->blendMode() == WebBlendModeNormal && + !boxShadowShouldBeAppliedToBackground(bleedAvoidance)) + break; + curLayer = curLayer->next(); + } + + GraphicsContext* context = paintInfo.context; + if (!context) + shouldDrawBackgroundInSeparateBuffer = false; + + // FIXME(sky): Propagate this constant. + bool skipBaseColor = false; + if (shouldDrawBackgroundInSeparateBuffer) + context->beginTransparencyLayer(1); + + Vector::const_reverse_iterator topLayer = layers.rend(); + for (Vector::const_reverse_iterator it = layers.rbegin(); + it != topLayer; ++it) + paintFillLayer(paintInfo, c, **it, rect, bleedAvoidance, backgroundObject, + skipBaseColor); + + if (shouldDrawBackgroundInSeparateBuffer) + context->endLayer(); +} + +void RenderBox::paintFillLayer(const PaintInfo& paintInfo, + const Color& c, + const FillLayer& fillLayer, + const LayoutRect& rect, + BackgroundBleedAvoidance bleedAvoidance, + RenderObject* backgroundObject, + bool skipBaseColor) { + paintFillLayerExtended(paintInfo, c, fillLayer, rect, bleedAvoidance, 0, + LayoutSize(), backgroundObject, skipBaseColor); +} + +bool RenderBox::pushContentsClip(PaintInfo& paintInfo, + const LayoutPoint& accumulatedOffset, + ContentsClipBehavior contentsClipBehavior) { + bool isOverflowClip = hasOverflowClip() && !layer()->isSelfPaintingLayer(); + if (!isOverflowClip) + return false; - LayoutSize containerOffset = offsetFromContainer(o, roundedLayoutPoint(transformState.mappedPoint())); + LayoutRect clipRect = overflowClipRect(accumulatedOffset); + RoundedRect clipRoundedRect(0, 0, 0, 0); + bool hasBorderRadius = style()->hasBorderRadius(); + if (hasBorderRadius) + clipRoundedRect = style()->getRoundedInnerBorderFor( + LayoutRect(accumulatedOffset, size())); - bool preserve3D = mode & UseTransforms && (o->style()->preserves3D() || style()->preserves3D()); - if (mode & UseTransforms && shouldUseTransformFromContainer(o)) { - TransformationMatrix t; - getTransformFromContainer(o, containerOffset, t); - transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); - } else - transformState.move(containerOffset.width(), containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); + if (contentsClipBehavior == SkipContentsClipIfPossible) { + LayoutRect contentsVisualOverflow = contentsVisualOverflowRect(); + if (contentsVisualOverflow.isEmpty()) + return false; - if (containerSkipped) { - // There can't be a transform between paintInvalidationContainer and o, because transforms create containers, so it should be safe - // to just subtract the delta between the paintInvalidationContainer and o. - LayoutSize containerOffset = paintInvalidationContainer->offsetFromAncestorContainer(o); - transformState.move(-containerOffset.width(), -containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); - return; + LayoutRect conservativeClipRect = clipRect; + if (hasBorderRadius) + conservativeClipRect.intersect(clipRoundedRect.radiusCenterRect()); + conservativeClipRect.moveBy(-accumulatedOffset); + if (conservativeClipRect.contains(contentsVisualOverflow)) + return false; + } + + paintInfo.context->save(); + if (hasBorderRadius) + paintInfo.context->clipRoundedRect(clipRoundedRect); + paintInfo.context->clip(pixelSnappedIntRect(clipRect)); + return true; +} + +void RenderBox::popContentsClip(PaintInfo& paintInfo, + const LayoutPoint& accumulatedOffset) { + ASSERT(hasOverflowClip() && !layer()->isSelfPaintingLayer()); + paintInfo.context->restore(); +} + +LayoutRect RenderBox::overflowClipRect(const LayoutPoint& location) { + LayoutRect clipRect = borderBoxRect(); + clipRect.setLocation(location + clipRect.location() + + LayoutSize(borderLeft(), borderTop())); + clipRect.setSize(clipRect.size() - LayoutSize(borderLeft() + borderRight(), + borderTop() + borderBottom())); + return clipRect; +} + +LayoutRect RenderBox::clipRect(const LayoutPoint& location) { + LayoutRect borderBoxRect = this->borderBoxRect(); + LayoutRect clipRect = + LayoutRect(borderBoxRect.location() + location, borderBoxRect.size()); + + if (!style()->clipLeft().isAuto()) { + LayoutUnit c = valueForLength(style()->clipLeft(), borderBoxRect.width()); + clipRect.move(c, 0); + clipRect.contract(c, 0); + } + + if (!style()->clipRight().isAuto()) + clipRect.contract(width() - valueForLength(style()->clipRight(), width()), + 0); + + if (!style()->clipTop().isAuto()) { + LayoutUnit c = valueForLength(style()->clipTop(), borderBoxRect.height()); + clipRect.move(0, c); + clipRect.contract(0, c); + } + + if (!style()->clipBottom().isAuto()) + clipRect.contract( + 0, height() - valueForLength(style()->clipBottom(), height())); + + return clipRect; +} + +LayoutUnit RenderBox::containingBlockLogicalHeightForContent( + AvailableLogicalHeightType heightType) const { + return containingBlock()->availableLogicalHeight(heightType); +} + +void RenderBox::mapLocalToContainer(const RenderBox* paintInvalidationContainer, + TransformState& transformState, + MapCoordinatesFlags mode) const { + if (paintInvalidationContainer == this) + return; + + bool containerSkipped; + RenderObject* o = container(paintInvalidationContainer, &containerSkipped); + if (!o) + return; + + LayoutSize containerOffset = + offsetFromContainer(o, roundedLayoutPoint(transformState.mappedPoint())); + + bool preserve3D = mode & UseTransforms && + (o->style()->preserves3D() || style()->preserves3D()); + if (mode & UseTransforms && shouldUseTransformFromContainer(o)) { + TransformationMatrix t; + getTransformFromContainer(o, containerOffset, t); + transformState.applyTransform(t, preserve3D + ? TransformState::AccumulateTransform + : TransformState::FlattenTransform); + } else + transformState.move(containerOffset.width(), containerOffset.height(), + preserve3D ? TransformState::AccumulateTransform + : TransformState::FlattenTransform); + + if (containerSkipped) { + // There can't be a transform between paintInvalidationContainer and o, + // because transforms create containers, so it should be safe to just + // subtract the delta between the paintInvalidationContainer and o. + LayoutSize containerOffset = + paintInvalidationContainer->offsetFromAncestorContainer(o); + transformState.move(-containerOffset.width(), -containerOffset.height(), + preserve3D ? TransformState::AccumulateTransform + : TransformState::FlattenTransform); + return; + } + + mode &= ~ApplyContainerFlip; + + o->mapLocalToContainer(paintInvalidationContainer, transformState, mode); +} + +LayoutSize RenderBox::offsetFromContainer(const RenderObject* o, + const LayoutPoint& point, + bool* offsetDependsOnPoint) const { + ASSERT(o == container()); + if (!isInline() || isReplaced()) + return locationOffset(); + return LayoutSize(); +} + +InlineBox* RenderBox::createInlineBox() { + return new InlineBox(*this); +} + +void RenderBox::dirtyLineBoxes(bool fullLayout) { + if (inlineBoxWrapper()) { + if (fullLayout) { + inlineBoxWrapper()->destroy(); + ASSERT(m_rareData); + m_rareData->m_inlineBoxWrapper = 0; + } else { + inlineBoxWrapper()->dirtyLineBoxes(); } - - mode &= ~ApplyContainerFlip; - - o->mapLocalToContainer(paintInvalidationContainer, transformState, mode); + } } -LayoutSize RenderBox::offsetFromContainer(const RenderObject* o, const LayoutPoint& point, bool* offsetDependsOnPoint) const -{ - ASSERT(o == container()); - if (!isInline() || isReplaced()) - return locationOffset(); - return LayoutSize(); +void RenderBox::positionLineBox(InlineBox* box) { + if (isOutOfFlowPositioned()) { + box->remove(DontMarkLineBoxes); + box->destroy(); + } else if (isReplaced()) { + setLocation(roundedLayoutPoint(box->topLeft())); + setInlineBoxWrapper(box); + } } -InlineBox* RenderBox::createInlineBox() -{ - return new InlineBox(*this); +void RenderBox::deleteLineBoxWrapper() { + if (inlineBoxWrapper()) { + if (!documentBeingDestroyed()) + inlineBoxWrapper()->remove(); + inlineBoxWrapper()->destroy(); + ASSERT(m_rareData); + m_rareData->m_inlineBoxWrapper = 0; + } } -void RenderBox::dirtyLineBoxes(bool fullLayout) -{ - if (inlineBoxWrapper()) { - if (fullLayout) { - inlineBoxWrapper()->destroy(); - ASSERT(m_rareData); - m_rareData->m_inlineBoxWrapper = 0; - } else { - inlineBoxWrapper()->dirtyLineBoxes(); - } - } -} - -void RenderBox::positionLineBox(InlineBox* box) -{ - if (isOutOfFlowPositioned()) { - box->remove(DontMarkLineBoxes); - box->destroy(); - } else if (isReplaced()) { - setLocation(roundedLayoutPoint(box->topLeft())); - setInlineBoxWrapper(box); - } -} +void RenderBox::updateLogicalWidth() { + LogicalExtentComputedValues computedValues; + computeLogicalWidth(computedValues); -void RenderBox::deleteLineBoxWrapper() -{ - if (inlineBoxWrapper()) { - if (!documentBeingDestroyed()) - inlineBoxWrapper()->remove(); - inlineBoxWrapper()->destroy(); - ASSERT(m_rareData); - m_rareData->m_inlineBoxWrapper = 0; - } + setLogicalWidth(computedValues.m_extent); + setLogicalLeft(computedValues.m_position); + setMarginStart(computedValues.m_margins.m_start); + setMarginEnd(computedValues.m_margins.m_end); } -void RenderBox::updateLogicalWidth() -{ - LogicalExtentComputedValues computedValues; - computeLogicalWidth(computedValues); - - setLogicalWidth(computedValues.m_extent); - setLogicalLeft(computedValues.m_position); - setMarginStart(computedValues.m_margins.m_start); - setMarginEnd(computedValues.m_margins.m_end); -} - -void RenderBox::computeLogicalWidth(LogicalExtentComputedValues& computedValues) const -{ - computedValues.m_extent = logicalWidth(); - computedValues.m_position = logicalLeft(); - computedValues.m_margins.m_start = marginStart(); - computedValues.m_margins.m_end = marginEnd(); - - if (isOutOfFlowPositioned()) { - // FIXME: This calculation is not patched for block-flow yet. - // https://bugs.webkit.org/show_bug.cgi?id=46500 - computePositionedLogicalWidth(computedValues); - return; - } - - if (hasOverrideWidth()) { - computedValues.m_extent = overrideLogicalContentWidth() + borderAndPaddingLogicalWidth(); - return; - } - - bool treatAsReplaced = shouldComputeSizeAsReplaced(); - - RenderStyle* styleToUse = style(); - Length logicalWidthLength = treatAsReplaced ? Length(computeReplacedLogicalWidth(), Fixed) : styleToUse->logicalWidth(); +void RenderBox::computeLogicalWidth( + LogicalExtentComputedValues& computedValues) const { + computedValues.m_extent = logicalWidth(); + computedValues.m_position = logicalLeft(); + computedValues.m_margins.m_start = marginStart(); + computedValues.m_margins.m_end = marginEnd(); - RenderBlock* cb = containingBlock(); - LayoutUnit containerLogicalWidth = std::max(0, containingBlockLogicalWidthForContent()); - - if (isInline() && !isInlineBlock()) { - // just calculate margins - computedValues.m_margins.m_start = minimumValueForLength(styleToUse->marginStart(), containerLogicalWidth); - computedValues.m_margins.m_end = minimumValueForLength(styleToUse->marginEnd(), containerLogicalWidth); - if (treatAsReplaced) - computedValues.m_extent = std::max(floatValueForLength(logicalWidthLength, 0) + borderAndPaddingLogicalWidth(), minPreferredLogicalWidth()); - return; - } - - // Width calculations + if (isOutOfFlowPositioned()) { + // FIXME: This calculation is not patched for block-flow yet. + // https://bugs.webkit.org/show_bug.cgi?id=46500 + computePositionedLogicalWidth(computedValues); + return; + } + + if (hasOverrideWidth()) { + computedValues.m_extent = + overrideLogicalContentWidth() + borderAndPaddingLogicalWidth(); + return; + } + + bool treatAsReplaced = shouldComputeSizeAsReplaced(); + + RenderStyle* styleToUse = style(); + Length logicalWidthLength = treatAsReplaced + ? Length(computeReplacedLogicalWidth(), Fixed) + : styleToUse->logicalWidth(); + + RenderBlock* cb = containingBlock(); + LayoutUnit containerLogicalWidth = + std::max(0, containingBlockLogicalWidthForContent()); + + if (isInline() && !isInlineBlock()) { + // just calculate margins + computedValues.m_margins.m_start = + minimumValueForLength(styleToUse->marginStart(), containerLogicalWidth); + computedValues.m_margins.m_end = + minimumValueForLength(styleToUse->marginEnd(), containerLogicalWidth); if (treatAsReplaced) - computedValues.m_extent = logicalWidthLength.value() + borderAndPaddingLogicalWidth(); - else { - LayoutUnit preferredWidth = computeLogicalWidthUsing(MainOrPreferredSize, styleToUse->logicalWidth(), containerLogicalWidth, cb); - computedValues.m_extent = constrainLogicalWidthByMinMax(preferredWidth, containerLogicalWidth, cb); - } - - // Margin calculations. - computeMarginsForDirection(InlineDirection, cb, containerLogicalWidth, computedValues.m_extent, computedValues.m_margins.m_start, - computedValues.m_margins.m_end, style()->marginStart(), style()->marginEnd()); - - if (containerLogicalWidth && containerLogicalWidth != (computedValues.m_extent + computedValues.m_margins.m_start + computedValues.m_margins.m_end) - && !isInline() && !cb->isFlexibleBox()) { - LayoutUnit newMargin = containerLogicalWidth - computedValues.m_extent - cb->marginStartForChild(this); - bool hasInvertedDirection = cb->style()->isLeftToRightDirection() != style()->isLeftToRightDirection(); - if (hasInvertedDirection) - computedValues.m_margins.m_start = newMargin; - else - computedValues.m_margins.m_end = newMargin; - } -} - -LayoutUnit RenderBox::fillAvailableMeasure(LayoutUnit availableLogicalWidth) const -{ - LayoutUnit marginStart = minimumValueForLength(style()->marginStart(), availableLogicalWidth); - LayoutUnit marginEnd = minimumValueForLength(style()->marginEnd(), availableLogicalWidth); - return availableLogicalWidth - marginStart - marginEnd; -} - -LayoutUnit RenderBox::computeIntrinsicLogicalWidthUsing(const Length& logicalWidthLength, LayoutUnit availableLogicalWidth, LayoutUnit borderAndPadding) const -{ - if (logicalWidthLength.type() == FillAvailable) - return fillAvailableMeasure(availableLogicalWidth); - - LayoutUnit minLogicalWidth = 0; - LayoutUnit maxLogicalWidth = 0; - computeIntrinsicLogicalWidths(minLogicalWidth, maxLogicalWidth); - - if (logicalWidthLength.type() == MinContent) - return minLogicalWidth + borderAndPadding; - - if (logicalWidthLength.type() == MaxContent) - return maxLogicalWidth + borderAndPadding; - - if (logicalWidthLength.type() == FitContent) { - minLogicalWidth += borderAndPadding; - maxLogicalWidth += borderAndPadding; - return std::max(minLogicalWidth, std::min(maxLogicalWidth, fillAvailableMeasure(availableLogicalWidth))); - } - - ASSERT_NOT_REACHED(); - return 0; -} - -LayoutUnit RenderBox::computeLogicalWidthUsing(SizeType widthType, const Length& logicalWidth, LayoutUnit availableLogicalWidth, const RenderBlock* cb) const -{ - if (!logicalWidth.isIntrinsicOrAuto()) { - // FIXME: If the containing block flow is perpendicular to our direction we need to use the available logical height instead. - return adjustBorderBoxLogicalWidthForBoxSizing(valueForLength(logicalWidth, availableLogicalWidth)); - } - - if (logicalWidth.isIntrinsic()) - return computeIntrinsicLogicalWidthUsing(logicalWidth, availableLogicalWidth, borderAndPaddingLogicalWidth()); - - LayoutUnit logicalWidthResult = fillAvailableMeasure(availableLogicalWidth); - - if (widthType == MainOrPreferredSize && sizesLogicalWidthToFitContent(logicalWidth)) - return std::max(minPreferredLogicalWidth(), std::min(maxPreferredLogicalWidth(), logicalWidthResult)); - return logicalWidthResult; -} - -static bool columnFlexItemHasStretchAlignment(const RenderObject* flexitem) -{ - RenderObject* parent = flexitem->parent(); - // auto margins mean we don't stretch. Note that this function will only be used for - // widths, so we don't have to check marginBefore/marginAfter. - ASSERT(parent->style()->isColumnFlexDirection()); - if (flexitem->style()->marginStart().isAuto() || flexitem->style()->marginEnd().isAuto()) - return false; - return flexitem->style()->alignSelf() == ItemPositionStretch || (flexitem->style()->alignSelf() == ItemPositionAuto && parent->style()->alignItems() == ItemPositionStretch); -} - -bool RenderBox::sizesLogicalWidthToFitContent(const Length& logicalWidth) const -{ - if (isInlineBlock()) - return true; - - if (logicalWidth.type() == Intrinsic) - return true; - - // Flexible box items should shrink wrap, so we lay them out at their intrinsic widths. - // In the case of columns that have a stretch alignment, we go ahead and layout at the - // stretched size to avoid an extra layout when applying alignment. - if (parent()->isFlexibleBox()) { - // For multiline columns, we need to apply align-content first, so we can't stretch now. - if (!parent()->style()->isColumnFlexDirection() || parent()->style()->flexWrap() != FlexNoWrap) - return true; - if (!columnFlexItemHasStretchAlignment(this)) - return true; - } - + computedValues.m_extent = + std::max(floatValueForLength(logicalWidthLength, 0) + + borderAndPaddingLogicalWidth(), + minPreferredLogicalWidth()); + return; + } + + // Width calculations + if (treatAsReplaced) + computedValues.m_extent = + logicalWidthLength.value() + borderAndPaddingLogicalWidth(); + else { + LayoutUnit preferredWidth = computeLogicalWidthUsing( + MainOrPreferredSize, styleToUse->logicalWidth(), containerLogicalWidth, + cb); + computedValues.m_extent = constrainLogicalWidthByMinMax( + preferredWidth, containerLogicalWidth, cb); + } + + // Margin calculations. + computeMarginsForDirection( + InlineDirection, cb, containerLogicalWidth, computedValues.m_extent, + computedValues.m_margins.m_start, computedValues.m_margins.m_end, + style()->marginStart(), style()->marginEnd()); + + if (containerLogicalWidth && + containerLogicalWidth != + (computedValues.m_extent + computedValues.m_margins.m_start + + computedValues.m_margins.m_end) && + !isInline() && !cb->isFlexibleBox()) { + LayoutUnit newMargin = containerLogicalWidth - computedValues.m_extent - + cb->marginStartForChild(this); + bool hasInvertedDirection = cb->style()->isLeftToRightDirection() != + style()->isLeftToRightDirection(); + if (hasInvertedDirection) + computedValues.m_margins.m_start = newMargin; + else + computedValues.m_margins.m_end = newMargin; + } +} + +LayoutUnit RenderBox::fillAvailableMeasure( + LayoutUnit availableLogicalWidth) const { + LayoutUnit marginStart = + minimumValueForLength(style()->marginStart(), availableLogicalWidth); + LayoutUnit marginEnd = + minimumValueForLength(style()->marginEnd(), availableLogicalWidth); + return availableLogicalWidth - marginStart - marginEnd; +} + +LayoutUnit RenderBox::computeIntrinsicLogicalWidthUsing( + const Length& logicalWidthLength, + LayoutUnit availableLogicalWidth, + LayoutUnit borderAndPadding) const { + if (logicalWidthLength.type() == FillAvailable) + return fillAvailableMeasure(availableLogicalWidth); + + LayoutUnit minLogicalWidth = 0; + LayoutUnit maxLogicalWidth = 0; + computeIntrinsicLogicalWidths(minLogicalWidth, maxLogicalWidth); + + if (logicalWidthLength.type() == MinContent) + return minLogicalWidth + borderAndPadding; + + if (logicalWidthLength.type() == MaxContent) + return maxLogicalWidth + borderAndPadding; + + if (logicalWidthLength.type() == FitContent) { + minLogicalWidth += borderAndPadding; + maxLogicalWidth += borderAndPadding; + return std::max( + minLogicalWidth, + std::min(maxLogicalWidth, fillAvailableMeasure(availableLogicalWidth))); + } + + ASSERT_NOT_REACHED(); + return 0; +} + +LayoutUnit RenderBox::computeLogicalWidthUsing(SizeType widthType, + const Length& logicalWidth, + LayoutUnit availableLogicalWidth, + const RenderBlock* cb) const { + if (!logicalWidth.isIntrinsicOrAuto()) { + // FIXME: If the containing block flow is perpendicular to our direction we + // need to use the available logical height instead. + return adjustBorderBoxLogicalWidthForBoxSizing( + valueForLength(logicalWidth, availableLogicalWidth)); + } + + if (logicalWidth.isIntrinsic()) + return computeIntrinsicLogicalWidthUsing( + logicalWidth, availableLogicalWidth, borderAndPaddingLogicalWidth()); + + LayoutUnit logicalWidthResult = fillAvailableMeasure(availableLogicalWidth); + + if (widthType == MainOrPreferredSize && + sizesLogicalWidthToFitContent(logicalWidth)) + return std::max(minPreferredLogicalWidth(), + std::min(maxPreferredLogicalWidth(), logicalWidthResult)); + return logicalWidthResult; +} + +static bool columnFlexItemHasStretchAlignment(const RenderObject* flexitem) { + RenderObject* parent = flexitem->parent(); + // auto margins mean we don't stretch. Note that this function will only be + // used for widths, so we don't have to check marginBefore/marginAfter. + ASSERT(parent->style()->isColumnFlexDirection()); + if (flexitem->style()->marginStart().isAuto() || + flexitem->style()->marginEnd().isAuto()) return false; + return flexitem->style()->alignSelf() == ItemPositionStretch || + (flexitem->style()->alignSelf() == ItemPositionAuto && + parent->style()->alignItems() == ItemPositionStretch); } -void RenderBox::computeMarginsForDirection(MarginDirection flowDirection, const RenderBlock* containingBlock, LayoutUnit containerWidth, LayoutUnit childWidth, LayoutUnit& marginStart, LayoutUnit& marginEnd, Length marginStartLength, Length marginEndLength) const -{ - if (flowDirection == BlockDirection || isInline()) { - // Margins are calculated with respect to the logical width of - // the containing block (8.3) - // Inline blocks/tables and floats don't have their margins increased. - marginStart = minimumValueForLength(marginStartLength, containerWidth); - marginEnd = minimumValueForLength(marginEndLength, containerWidth); - return; - } - - if (containingBlock->isFlexibleBox()) { - // We need to let flexbox handle the margin adjustment - otherwise, flexbox - // will think we're wider than we actually are and calculate line sizes wrong. - // See also http://dev.w3.org/csswg/css-flexbox/#auto-margins - if (marginStartLength.isAuto()) - marginStartLength.setValue(0); - if (marginEndLength.isAuto()) - marginEndLength.setValue(0); - } - - LayoutUnit marginStartWidth = minimumValueForLength(marginStartLength, containerWidth); - LayoutUnit marginEndWidth = minimumValueForLength(marginEndLength, containerWidth); - - // CSS 2.1 (10.3.3): "If 'width' is not 'auto' and 'border-left-width' + 'padding-left' + 'width' + 'padding-right' + 'border-right-width' - // (plus any of 'margin-left' or 'margin-right' that are not 'auto') is larger than the width of the containing block, then any 'auto' - // values for 'margin-left' or 'margin-right' are, for the following rules, treated as zero. - LayoutUnit marginBoxWidth = childWidth + (!style()->width().isAuto() ? marginStartWidth + marginEndWidth : LayoutUnit()); - - // CSS 2.1: "If both 'margin-left' and 'margin-right' are 'auto', their used values are equal. This horizontally centers the element - // with respect to the edges of the containing block." - if (marginStartLength.isAuto() && marginEndLength.isAuto() && marginBoxWidth < containerWidth) { - // Other browsers center the margin box for align=center elements so we match them here. - LayoutUnit centeredMarginBoxStart = std::max(0, (containerWidth - childWidth - marginStartWidth - marginEndWidth) / 2); - marginStart = centeredMarginBoxStart + marginStartWidth; - marginEnd = containerWidth - childWidth - marginStart + marginEndWidth; - return; - } - - // CSS 2.1: "If there is exactly one value specified as 'auto', its used value follows from the equality." - if (marginEndLength.isAuto() && marginBoxWidth < containerWidth) { - marginStart = marginStartWidth; - marginEnd = containerWidth - childWidth - marginStart; - return; - } +bool RenderBox::sizesLogicalWidthToFitContent( + const Length& logicalWidth) const { + if (isInlineBlock()) + return true; - if (marginStartLength.isAuto() && marginBoxWidth < containerWidth) { - marginEnd = marginEndWidth; - marginStart = containerWidth - childWidth - marginEnd; - return; - } + if (logicalWidth.type() == Intrinsic) + return true; - // Either no auto margins, or our margin box width is >= the container width, auto margins will just turn into 0. + // Flexible box items should shrink wrap, so we lay them out at their + // intrinsic widths. In the case of columns that have a stretch alignment, we + // go ahead and layout at the stretched size to avoid an extra layout when + // applying alignment. + if (parent()->isFlexibleBox()) { + // For multiline columns, we need to apply align-content first, so we can't + // stretch now. + if (!parent()->style()->isColumnFlexDirection() || + parent()->style()->flexWrap() != FlexNoWrap) + return true; + if (!columnFlexItemHasStretchAlignment(this)) + return true; + } + + return false; +} + +void RenderBox::computeMarginsForDirection(MarginDirection flowDirection, + const RenderBlock* containingBlock, + LayoutUnit containerWidth, + LayoutUnit childWidth, + LayoutUnit& marginStart, + LayoutUnit& marginEnd, + Length marginStartLength, + Length marginEndLength) const { + if (flowDirection == BlockDirection || isInline()) { + // Margins are calculated with respect to the logical width of + // the containing block (8.3) + // Inline blocks/tables and floats don't have their margins increased. + marginStart = minimumValueForLength(marginStartLength, containerWidth); + marginEnd = minimumValueForLength(marginEndLength, containerWidth); + return; + } + + if (containingBlock->isFlexibleBox()) { + // We need to let flexbox handle the margin adjustment - otherwise, flexbox + // will think we're wider than we actually are and calculate line sizes + // wrong. See also http://dev.w3.org/csswg/css-flexbox/#auto-margins + if (marginStartLength.isAuto()) + marginStartLength.setValue(0); + if (marginEndLength.isAuto()) + marginEndLength.setValue(0); + } + + LayoutUnit marginStartWidth = + minimumValueForLength(marginStartLength, containerWidth); + LayoutUnit marginEndWidth = + minimumValueForLength(marginEndLength, containerWidth); + + // CSS 2.1 (10.3.3): "If 'width' is not 'auto' and 'border-left-width' + + // 'padding-left' + 'width' + 'padding-right' + 'border-right-width' (plus any + // of 'margin-left' or 'margin-right' that are not 'auto') is larger than the + // width of the containing block, then any 'auto' values for 'margin-left' or + // 'margin-right' are, for the following rules, treated as zero. + LayoutUnit marginBoxWidth = + childWidth + (!style()->width().isAuto() + ? marginStartWidth + marginEndWidth + : LayoutUnit()); + + // CSS 2.1: "If both 'margin-left' and 'margin-right' are 'auto', their used + // values are equal. This horizontally centers the element with respect to the + // edges of the containing block." + if (marginStartLength.isAuto() && marginEndLength.isAuto() && + marginBoxWidth < containerWidth) { + // Other browsers center the margin box for align=center elements so we + // match them here. + LayoutUnit centeredMarginBoxStart = std::max( + 0, + (containerWidth - childWidth - marginStartWidth - marginEndWidth) / 2); + marginStart = centeredMarginBoxStart + marginStartWidth; + marginEnd = containerWidth - childWidth - marginStart + marginEndWidth; + return; + } + + // CSS 2.1: "If there is exactly one value specified as 'auto', its used value + // follows from the equality." + if (marginEndLength.isAuto() && marginBoxWidth < containerWidth) { marginStart = marginStartWidth; + marginEnd = containerWidth - childWidth - marginStart; + return; + } + + if (marginStartLength.isAuto() && marginBoxWidth < containerWidth) { marginEnd = marginEndWidth; + marginStart = containerWidth - childWidth - marginEnd; + return; + } + + // Either no auto margins, or our margin box width is >= the container width, + // auto margins will just turn into 0. + marginStart = marginStartWidth; + marginEnd = marginEndWidth; } -void RenderBox::updateLogicalHeight() -{ - m_intrinsicContentLogicalHeight = contentLogicalHeight(); +void RenderBox::updateLogicalHeight() { + m_intrinsicContentLogicalHeight = contentLogicalHeight(); - LogicalExtentComputedValues computedValues; - computeLogicalHeight(logicalHeight(), logicalTop(), computedValues); + LogicalExtentComputedValues computedValues; + computeLogicalHeight(logicalHeight(), logicalTop(), computedValues); - setLogicalHeight(computedValues.m_extent); - setLogicalTop(computedValues.m_position); - setMarginBefore(computedValues.m_margins.m_before); - setMarginAfter(computedValues.m_margins.m_after); + setLogicalHeight(computedValues.m_extent); + setLogicalTop(computedValues.m_position); + setMarginBefore(computedValues.m_margins.m_before); + setMarginAfter(computedValues.m_margins.m_after); } -void RenderBox::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const -{ - computedValues.m_extent = logicalHeight; - computedValues.m_position = logicalTop; - - // Cell height is managed by the table and inline non-replaced elements do not support a height property. - if (isInline() && !isReplaced()) - return; - - Length h; - if (isOutOfFlowPositioned()) - computePositionedLogicalHeight(computedValues); - else { - RenderBlock* cb = containingBlock(); - - // If we are perpendicular to our containing block then we need to resolve our block-start and block-end margins so that if they - // are 'auto' we are centred or aligned within the inline flow containing block: this is done by computing the margins as though they are inline. - // Note that as this is the 'sizing phase' we are using our own writing mode rather than the containing block's. We use the containing block's - // writing mode when figuring out the block-direction margins for positioning in |computeAndSetBlockDirectionMargins| (i.e. margin collapsing etc.). - // See http://www.w3.org/TR/2014/CR-css-writing-modes-3-20140320/#orthogonal-flows - // FIXME(sky): Remove MarginDirection enum. - MarginDirection flowDirection = BlockDirection; - - bool treatAsReplaced = shouldComputeSizeAsReplaced(); - bool checkMinMaxHeight = false; - - // The parent box is flexing us, so it has increased or decreased our height. We have to - // grab our cached flexible height. - // FIXME: Account for block-flow in flexible boxes. - // https://bugs.webkit.org/show_bug.cgi?id=46418 - if (hasOverrideHeight() && parent()->isFlexibleBox()) - h = Length(overrideLogicalContentHeight(), Fixed); - else if (treatAsReplaced) - h = Length(computeReplacedLogicalHeight(), Fixed); - else { - h = style()->logicalHeight(); - checkMinMaxHeight = true; - } +void RenderBox::computeLogicalHeight( + LayoutUnit logicalHeight, + LayoutUnit logicalTop, + LogicalExtentComputedValues& computedValues) const { + computedValues.m_extent = logicalHeight; + computedValues.m_position = logicalTop; - LayoutUnit heightResult; - if (checkMinMaxHeight) { - heightResult = computeLogicalHeightUsing(style()->logicalHeight(), computedValues.m_extent - borderAndPaddingLogicalHeight()); - if (heightResult == -1) - heightResult = computedValues.m_extent; - heightResult = constrainLogicalHeightByMinMax(heightResult, computedValues.m_extent - borderAndPaddingLogicalHeight()); - } else { - // The only times we don't check min/max height are when a fixed length has - // been given as an override. Just use that. The value has already been adjusted - // for box-sizing. - ASSERT(h.isFixed()); - heightResult = h.value() + borderAndPaddingLogicalHeight(); - } + // Cell height is managed by the table and inline non-replaced elements do not + // support a height property. + if (isInline() && !isReplaced()) + return; - computedValues.m_extent = heightResult; - computeMarginsForDirection(flowDirection, cb, containingBlockLogicalWidthForContent(), computedValues.m_extent, computedValues.m_margins.m_before, - computedValues.m_margins.m_after, style()->marginBefore(), style()->marginAfter()); - } -} + Length h; + if (isOutOfFlowPositioned()) + computePositionedLogicalHeight(computedValues); + else { + RenderBlock* cb = containingBlock(); -LayoutUnit RenderBox::computeLogicalHeightUsing(const Length& height, LayoutUnit intrinsicContentHeight) const -{ - LayoutUnit logicalHeight = computeContentLogicalHeightUsing(height, intrinsicContentHeight); - if (logicalHeight != -1) - logicalHeight = adjustBorderBoxLogicalHeightForBoxSizing(logicalHeight); - return logicalHeight; -} - -LayoutUnit RenderBox::computeContentLogicalHeight(const Length& height, LayoutUnit intrinsicContentHeight) const -{ - LayoutUnit heightIncludingScrollbar = computeContentLogicalHeightUsing(height, intrinsicContentHeight); - if (heightIncludingScrollbar == -1) - return -1; - return std::max(0, adjustContentBoxLogicalHeightForBoxSizing(heightIncludingScrollbar)); -} - -LayoutUnit RenderBox::computeIntrinsicLogicalContentHeightUsing(const Length& logicalHeightLength, LayoutUnit intrinsicContentHeight, LayoutUnit borderAndPadding) const -{ - // FIXME(cbiesinger): The css-sizing spec is considering changing what min-content/max-content should resolve to. - // If that happens, this code will have to change. - if (logicalHeightLength.isMinContent() || logicalHeightLength.isMaxContent() || logicalHeightLength.isFitContent()) { - if (isReplaced()) - return intrinsicSize().height(); - if (m_intrinsicContentLogicalHeight != -1) - return m_intrinsicContentLogicalHeight; - return intrinsicContentHeight; - } - if (logicalHeightLength.isFillAvailable()) - return containingBlock()->availableLogicalHeight(ExcludeMarginBorderPadding) - borderAndPadding; - ASSERT_NOT_REACHED(); - return 0; -} + // If we are perpendicular to our containing block then we need to resolve + // our block-start and block-end margins so that if they are 'auto' we are + // centred or aligned within the inline flow containing block: this is done + // by computing the margins as though they are inline. Note that as this is + // the 'sizing phase' we are using our own writing mode rather than the + // containing block's. We use the containing block's writing mode when + // figuring out the block-direction margins for positioning in + // |computeAndSetBlockDirectionMargins| (i.e. margin collapsing etc.). See + // http://www.w3.org/TR/2014/CR-css-writing-modes-3-20140320/#orthogonal-flows + // FIXME(sky): Remove MarginDirection enum. + MarginDirection flowDirection = BlockDirection; -LayoutUnit RenderBox::computeContentLogicalHeightUsing(const Length& height, LayoutUnit intrinsicContentHeight) const -{ - // FIXME(cbiesinger): The css-sizing spec is considering changing what min-content/max-content should resolve to. - // If that happens, this code will have to change. - if (height.isIntrinsic()) { - if (intrinsicContentHeight == -1) - return -1; // Intrinsic height isn't available. - return computeIntrinsicLogicalContentHeightUsing(height, intrinsicContentHeight, borderAndPaddingLogicalHeight()); - } - if (height.isFixed()) - return height.value(); + bool treatAsReplaced = shouldComputeSizeAsReplaced(); + bool checkMinMaxHeight = false; + + // The parent box is flexing us, so it has increased or decreased our + // height. We have to grab our cached flexible height. + // FIXME: Account for block-flow in flexible boxes. + // https://bugs.webkit.org/show_bug.cgi?id=46418 + if (hasOverrideHeight() && parent()->isFlexibleBox()) + h = Length(overrideLogicalContentHeight(), Fixed); + else if (treatAsReplaced) + h = Length(computeReplacedLogicalHeight(), Fixed); + else { + h = style()->logicalHeight(); + checkMinMaxHeight = true; + } + + LayoutUnit heightResult; + if (checkMinMaxHeight) { + heightResult = computeLogicalHeightUsing( + style()->logicalHeight(), + computedValues.m_extent - borderAndPaddingLogicalHeight()); + if (heightResult == -1) + heightResult = computedValues.m_extent; + heightResult = constrainLogicalHeightByMinMax( + heightResult, + computedValues.m_extent - borderAndPaddingLogicalHeight()); + } else { + // The only times we don't check min/max height are when a fixed length + // has been given as an override. Just use that. The value has already + // been adjusted for box-sizing. + ASSERT(h.isFixed()); + heightResult = h.value() + borderAndPaddingLogicalHeight(); + } + + computedValues.m_extent = heightResult; + computeMarginsForDirection( + flowDirection, cb, containingBlockLogicalWidthForContent(), + computedValues.m_extent, computedValues.m_margins.m_before, + computedValues.m_margins.m_after, style()->marginBefore(), + style()->marginAfter()); + } +} + +LayoutUnit RenderBox::computeLogicalHeightUsing( + const Length& height, + LayoutUnit intrinsicContentHeight) const { + LayoutUnit logicalHeight = + computeContentLogicalHeightUsing(height, intrinsicContentHeight); + if (logicalHeight != -1) + logicalHeight = adjustBorderBoxLogicalHeightForBoxSizing(logicalHeight); + return logicalHeight; +} + +LayoutUnit RenderBox::computeContentLogicalHeight( + const Length& height, + LayoutUnit intrinsicContentHeight) const { + LayoutUnit heightIncludingScrollbar = + computeContentLogicalHeightUsing(height, intrinsicContentHeight); + if (heightIncludingScrollbar == -1) return -1; + return std::max( + 0, adjustContentBoxLogicalHeightForBoxSizing(heightIncludingScrollbar)); +} + +LayoutUnit RenderBox::computeIntrinsicLogicalContentHeightUsing( + const Length& logicalHeightLength, + LayoutUnit intrinsicContentHeight, + LayoutUnit borderAndPadding) const { + // FIXME(cbiesinger): The css-sizing spec is considering changing what + // min-content/max-content should resolve to. If that happens, this code will + // have to change. + if (logicalHeightLength.isMinContent() || + logicalHeightLength.isMaxContent() || + logicalHeightLength.isFitContent()) { + if (isReplaced()) + return intrinsicSize().height(); + if (m_intrinsicContentLogicalHeight != -1) + return m_intrinsicContentLogicalHeight; + return intrinsicContentHeight; + } + if (logicalHeightLength.isFillAvailable()) + return containingBlock()->availableLogicalHeight( + ExcludeMarginBorderPadding) - + borderAndPadding; + ASSERT_NOT_REACHED(); + return 0; +} + +LayoutUnit RenderBox::computeContentLogicalHeightUsing( + const Length& height, + LayoutUnit intrinsicContentHeight) const { + // FIXME(cbiesinger): The css-sizing spec is considering changing what + // min-content/max-content should resolve to. If that happens, this code will + // have to change. + if (height.isIntrinsic()) { + if (intrinsicContentHeight == -1) + return -1; // Intrinsic height isn't available. + return computeIntrinsicLogicalContentHeightUsing( + height, intrinsicContentHeight, borderAndPaddingLogicalHeight()); + } + if (height.isFixed()) + return height.value(); + return -1; } // FIXME(sky): Remove -bool RenderBox::skipContainingBlockForPercentHeightCalculation(const RenderBox* containingBlock) const -{ - return false; -} - -LayoutUnit RenderBox::computeReplacedLogicalWidth(ShouldComputePreferred shouldComputePreferred) const -{ - return computeReplacedLogicalWidthRespectingMinMaxWidth(computeReplacedLogicalWidthUsing(style()->logicalWidth()), shouldComputePreferred); -} - -LayoutUnit RenderBox::computeReplacedLogicalWidthRespectingMinMaxWidth(LayoutUnit logicalWidth, ShouldComputePreferred shouldComputePreferred) const -{ - LayoutUnit minLogicalWidth = (shouldComputePreferred == ComputePreferred && style()->logicalMinWidth().isPercent()) || style()->logicalMinWidth().isMaxSizeNone() ? logicalWidth : computeReplacedLogicalWidthUsing(style()->logicalMinWidth()); - LayoutUnit maxLogicalWidth = (shouldComputePreferred == ComputePreferred && style()->logicalMaxWidth().isPercent()) || style()->logicalMaxWidth().isMaxSizeNone() ? logicalWidth : computeReplacedLogicalWidthUsing(style()->logicalMaxWidth()); - return std::max(minLogicalWidth, std::min(logicalWidth, maxLogicalWidth)); -} - -LayoutUnit RenderBox::computeReplacedLogicalWidthUsing(const Length& logicalWidth) const -{ - switch (logicalWidth.type()) { - case Fixed: - return adjustContentBoxLogicalWidthForBoxSizing(logicalWidth.value()); - case MinContent: - case MaxContent: { - // MinContent/MaxContent don't need the availableLogicalWidth argument. - LayoutUnit availableLogicalWidth = 0; - return computeIntrinsicLogicalWidthUsing(logicalWidth, availableLogicalWidth, borderAndPaddingLogicalWidth()) - borderAndPaddingLogicalWidth(); - } - case FitContent: - case FillAvailable: - case Percent: - case Calculated: { - // FIXME: containingBlockLogicalWidthForContent() is wrong if the replaced element's block-flow is perpendicular to the - // containing block's block-flow. - // https://bugs.webkit.org/show_bug.cgi?id=46496 - const LayoutUnit cw = isOutOfFlowPositioned() ? containingBlockLogicalWidthForPositioned(toRenderBoxModelObject(container())) : containingBlockLogicalWidthForContent(); - Length containerLogicalWidth = containingBlock()->style()->logicalWidth(); - // FIXME: Handle cases when containing block width is calculated or viewport percent. - // https://bugs.webkit.org/show_bug.cgi?id=91071 - if (logicalWidth.isIntrinsic()) - return computeIntrinsicLogicalWidthUsing(logicalWidth, cw, borderAndPaddingLogicalWidth()) - borderAndPaddingLogicalWidth(); - if (cw > 0 || (!cw && (containerLogicalWidth.isFixed() || containerLogicalWidth.isPercent()))) - return adjustContentBoxLogicalWidthForBoxSizing(minimumValueForLength(logicalWidth, cw)); - return 0; - } - case Intrinsic: - case MinIntrinsic: - case Auto: - case MaxSizeNone: - return intrinsicLogicalWidth(); - case DeviceWidth: - case DeviceHeight: - break; - } - - ASSERT_NOT_REACHED(); - return 0; -} - -LayoutUnit RenderBox::computeReplacedLogicalHeight() const -{ - return computeReplacedLogicalHeightRespectingMinMaxHeight(computeReplacedLogicalHeightUsing(style()->logicalHeight())); -} - -bool RenderBox::logicalHeightComputesAsNone(SizeType sizeType) const -{ - ASSERT(sizeType == MinSize || sizeType == MaxSize); - Length logicalHeight = sizeType == MinSize ? style()->logicalMinHeight() : style()->logicalMaxHeight(); - Length initialLogicalHeight = sizeType == MinSize ? RenderStyle::initialMinSize() : RenderStyle::initialMaxSize(); - - if (logicalHeight == initialLogicalHeight) - return true; - - if (!logicalHeight.isPercent() || isOutOfFlowPositioned()) - return false; - - return containingBlock()->hasAutoHeightOrContainingBlockWithAutoHeight(); -} - -LayoutUnit RenderBox::computeReplacedLogicalHeightRespectingMinMaxHeight(LayoutUnit logicalHeight) const -{ - // If the height of the containing block is not specified explicitly (i.e., it depends on content height), and this element is not absolutely positioned, - // the percentage value is treated as '0' (for 'min-height') or 'none' (for 'max-height'). - LayoutUnit minLogicalHeight; - if (!logicalHeightComputesAsNone(MinSize)) - minLogicalHeight = computeReplacedLogicalHeightUsing(style()->logicalMinHeight()); - LayoutUnit maxLogicalHeight = logicalHeight; - if (!logicalHeightComputesAsNone(MaxSize)) - maxLogicalHeight = computeReplacedLogicalHeightUsing(style()->logicalMaxHeight()); - return std::max(minLogicalHeight, std::min(logicalHeight, maxLogicalHeight)); -} - -LayoutUnit RenderBox::computeReplacedLogicalHeightUsing(const Length& logicalHeight) const -{ - switch (logicalHeight.type()) { - case Fixed: - return adjustContentBoxLogicalHeightForBoxSizing(logicalHeight.value()); - case Percent: - case Calculated: - { - RenderObject* cb = isOutOfFlowPositioned() ? container() : containingBlock(); - if (cb->isRenderBlock()) - toRenderBlock(cb)->addPercentHeightDescendant(const_cast(this)); - - // FIXME: This calculation is not patched for block-flow yet. - // https://bugs.webkit.org/show_bug.cgi?id=46500 - if (cb->isOutOfFlowPositioned() && cb->style()->height().isAuto() && !(cb->style()->top().isAuto() || cb->style()->bottom().isAuto())) { - ASSERT_WITH_SECURITY_IMPLICATION(cb->isRenderBlock()); - RenderBlock* block = toRenderBlock(cb); - LogicalExtentComputedValues computedValues; - block->computeLogicalHeight(block->logicalHeight(), 0, computedValues); - LayoutUnit newContentHeight = computedValues.m_extent - block->borderAndPaddingLogicalHeight(); - LayoutUnit newHeight = block->adjustContentBoxLogicalHeightForBoxSizing(newContentHeight); - return adjustContentBoxLogicalHeightForBoxSizing(valueForLength(logicalHeight, newHeight)); - } - - // FIXME: availableLogicalHeight() is wrong if the replaced element's block-flow is perpendicular to the - // containing block's block-flow. - // https://bugs.webkit.org/show_bug.cgi?id=46496 - LayoutUnit availableHeight; - if (isOutOfFlowPositioned()) - availableHeight = containingBlockLogicalHeightForPositioned(toRenderBoxModelObject(cb)); - else { - availableHeight = containingBlockLogicalHeightForContent(IncludeMarginBorderPadding); - // It is necessary to use the border-box to match WinIE's broken - // box model. This is essential for sizing inside - // table cells using percentage heights. - // FIXME: This needs to be made block-flow-aware. If the cell and image are perpendicular block-flows, this isn't right. - // https://bugs.webkit.org/show_bug.cgi?id=46997 - while (cb && !cb->isRenderView() && (cb->style()->logicalHeight().isAuto() || cb->style()->logicalHeight().isPercent())) { - toRenderBlock(cb)->addPercentHeightDescendant(const_cast(this)); - cb = cb->containingBlock(); - } - } - return adjustContentBoxLogicalHeightForBoxSizing(valueForLength(logicalHeight, availableHeight)); - } - case MinContent: - case MaxContent: - case FitContent: - case FillAvailable: - return adjustContentBoxLogicalHeightForBoxSizing(computeIntrinsicLogicalContentHeightUsing(logicalHeight, intrinsicLogicalHeight(), borderAndPaddingHeight())); - default: - return intrinsicLogicalHeight(); - } -} - -LayoutUnit RenderBox::availableLogicalHeight(AvailableLogicalHeightType heightType) const -{ - // http://www.w3.org/TR/CSS2/visudet.html#propdef-height - We are interested in the content height. - return constrainContentBoxLogicalHeightByMinMax(availableLogicalHeightUsing(style()->logicalHeight(), heightType), -1); -} - -LayoutUnit RenderBox::availableLogicalHeightUsing(const Length& h, AvailableLogicalHeightType heightType) const -{ - if (isRenderView()) { - ASSERT_NOT_REACHED(); - return LayoutUnit(); - } - - if (h.isPercent() && isOutOfFlowPositioned()) { - // FIXME: This is wrong if the containingBlock has a perpendicular writing mode. - LayoutUnit availableHeight = containingBlockLogicalHeightForPositioned(containingBlock()); - return adjustContentBoxLogicalHeightForBoxSizing(valueForLength(h, availableHeight)); - } +bool RenderBox::skipContainingBlockForPercentHeightCalculation( + const RenderBox* containingBlock) const { + return false; +} + +LayoutUnit RenderBox::computeReplacedLogicalWidth( + ShouldComputePreferred shouldComputePreferred) const { + return computeReplacedLogicalWidthRespectingMinMaxWidth( + computeReplacedLogicalWidthUsing(style()->logicalWidth()), + shouldComputePreferred); +} + +LayoutUnit RenderBox::computeReplacedLogicalWidthRespectingMinMaxWidth( + LayoutUnit logicalWidth, + ShouldComputePreferred shouldComputePreferred) const { + LayoutUnit minLogicalWidth = + (shouldComputePreferred == ComputePreferred && + style()->logicalMinWidth().isPercent()) || + style()->logicalMinWidth().isMaxSizeNone() + ? logicalWidth + : computeReplacedLogicalWidthUsing(style()->logicalMinWidth()); + LayoutUnit maxLogicalWidth = + (shouldComputePreferred == ComputePreferred && + style()->logicalMaxWidth().isPercent()) || + style()->logicalMaxWidth().isMaxSizeNone() + ? logicalWidth + : computeReplacedLogicalWidthUsing(style()->logicalMaxWidth()); + return std::max(minLogicalWidth, std::min(logicalWidth, maxLogicalWidth)); +} + +LayoutUnit RenderBox::computeReplacedLogicalWidthUsing( + const Length& logicalWidth) const { + switch (logicalWidth.type()) { + case Fixed: + return adjustContentBoxLogicalWidthForBoxSizing(logicalWidth.value()); + case MinContent: + case MaxContent: { + // MinContent/MaxContent don't need the availableLogicalWidth argument. + LayoutUnit availableLogicalWidth = 0; + return computeIntrinsicLogicalWidthUsing(logicalWidth, + availableLogicalWidth, + borderAndPaddingLogicalWidth()) - + borderAndPaddingLogicalWidth(); + } + case FitContent: + case FillAvailable: + case Percent: + case Calculated: { + // FIXME: containingBlockLogicalWidthForContent() is wrong if the replaced + // element's block-flow is perpendicular to the containing block's + // block-flow. https://bugs.webkit.org/show_bug.cgi?id=46496 + const LayoutUnit cw = isOutOfFlowPositioned() + ? containingBlockLogicalWidthForPositioned( + toRenderBoxModelObject(container())) + : containingBlockLogicalWidthForContent(); + Length containerLogicalWidth = containingBlock()->style()->logicalWidth(); + // FIXME: Handle cases when containing block width is calculated or + // viewport percent. https://bugs.webkit.org/show_bug.cgi?id=91071 + if (logicalWidth.isIntrinsic()) + return computeIntrinsicLogicalWidthUsing( + logicalWidth, cw, borderAndPaddingLogicalWidth()) - + borderAndPaddingLogicalWidth(); + if (cw > 0 || (!cw && (containerLogicalWidth.isFixed() || + containerLogicalWidth.isPercent()))) + return adjustContentBoxLogicalWidthForBoxSizing( + minimumValueForLength(logicalWidth, cw)); + return 0; + } + case Intrinsic: + case MinIntrinsic: + case Auto: + case MaxSizeNone: + return intrinsicLogicalWidth(); + case DeviceWidth: + case DeviceHeight: + break; + } + + ASSERT_NOT_REACHED(); + return 0; +} + +LayoutUnit RenderBox::computeReplacedLogicalHeight() const { + return computeReplacedLogicalHeightRespectingMinMaxHeight( + computeReplacedLogicalHeightUsing(style()->logicalHeight())); +} + +bool RenderBox::logicalHeightComputesAsNone(SizeType sizeType) const { + ASSERT(sizeType == MinSize || sizeType == MaxSize); + Length logicalHeight = sizeType == MinSize ? style()->logicalMinHeight() + : style()->logicalMaxHeight(); + Length initialLogicalHeight = sizeType == MinSize + ? RenderStyle::initialMinSize() + : RenderStyle::initialMaxSize(); + + if (logicalHeight == initialLogicalHeight) + return true; - LayoutUnit heightIncludingScrollbar = computeContentLogicalHeightUsing(h, -1); - if (heightIncludingScrollbar != -1) - return std::max(0, adjustContentBoxLogicalHeightForBoxSizing(heightIncludingScrollbar)); + if (!logicalHeight.isPercent() || isOutOfFlowPositioned()) + return false; - // FIXME: Check logicalTop/logicalBottom here to correctly handle vertical writing-mode. - // https://bugs.webkit.org/show_bug.cgi?id=46500 - if (isRenderBlock() && isOutOfFlowPositioned() && style()->height().isAuto() && !(style()->top().isAuto() || style()->bottom().isAuto())) { - RenderBlock* block = const_cast(toRenderBlock(this)); + return containingBlock()->hasAutoHeightOrContainingBlockWithAutoHeight(); +} + +LayoutUnit RenderBox::computeReplacedLogicalHeightRespectingMinMaxHeight( + LayoutUnit logicalHeight) const { + // If the height of the containing block is not specified explicitly (i.e., it + // depends on content height), and this element is not absolutely positioned, + // the percentage value is treated as '0' (for 'min-height') or 'none' (for + // 'max-height'). + LayoutUnit minLogicalHeight; + if (!logicalHeightComputesAsNone(MinSize)) + minLogicalHeight = + computeReplacedLogicalHeightUsing(style()->logicalMinHeight()); + LayoutUnit maxLogicalHeight = logicalHeight; + if (!logicalHeightComputesAsNone(MaxSize)) + maxLogicalHeight = + computeReplacedLogicalHeightUsing(style()->logicalMaxHeight()); + return std::max(minLogicalHeight, std::min(logicalHeight, maxLogicalHeight)); +} + +LayoutUnit RenderBox::computeReplacedLogicalHeightUsing( + const Length& logicalHeight) const { + switch (logicalHeight.type()) { + case Fixed: + return adjustContentBoxLogicalHeightForBoxSizing(logicalHeight.value()); + case Percent: + case Calculated: { + RenderObject* cb = + isOutOfFlowPositioned() ? container() : containingBlock(); + if (cb->isRenderBlock()) + toRenderBlock(cb)->addPercentHeightDescendant( + const_cast(this)); + + // FIXME: This calculation is not patched for block-flow yet. + // https://bugs.webkit.org/show_bug.cgi?id=46500 + if (cb->isOutOfFlowPositioned() && cb->style()->height().isAuto() && + !(cb->style()->top().isAuto() || cb->style()->bottom().isAuto())) { + ASSERT_WITH_SECURITY_IMPLICATION(cb->isRenderBlock()); + RenderBlock* block = toRenderBlock(cb); LogicalExtentComputedValues computedValues; block->computeLogicalHeight(block->logicalHeight(), 0, computedValues); - LayoutUnit newContentHeight = computedValues.m_extent - block->borderAndPaddingLogicalHeight(); - return adjustContentBoxLogicalHeightForBoxSizing(newContentHeight); - } - - // FIXME: This is wrong if the containingBlock has a perpendicular writing mode. - LayoutUnit availableHeight = containingBlockLogicalHeightForContent(heightType); - if (heightType == ExcludeMarginBorderPadding) { - // FIXME: Margin collapsing hasn't happened yet, so this incorrectly removes collapsed margins. - availableHeight -= marginBefore() + marginAfter() + borderAndPaddingLogicalHeight(); - } - return availableHeight; -} - -void RenderBox::computeAndSetBlockDirectionMargins(const RenderBlock* containingBlock) -{ - LayoutUnit marginBefore; - LayoutUnit marginAfter; - computeMarginsForDirection(BlockDirection, containingBlock, containingBlockLogicalWidthForContent(), logicalHeight(), marginBefore, marginAfter, - style()->marginBeforeUsing(containingBlock->style()), - style()->marginAfterUsing(containingBlock->style())); - // Note that in this 'positioning phase' of the layout we are using the containing block's writing mode rather than our own when calculating margins. - // See http://www.w3.org/TR/2014/CR-css-writing-modes-3-20140320/#orthogonal-flows - containingBlock->setMarginBeforeForChild(this, marginBefore); - containingBlock->setMarginAfterForChild(this, marginAfter); -} - -LayoutUnit RenderBox::containingBlockLogicalWidthForPositioned(const RenderBoxModelObject* containingBlock) const -{ - ASSERT(containingBlock->isBox()); - return toRenderBox(containingBlock)->clientLogicalWidth(); -} - -LayoutUnit RenderBox::containingBlockLogicalHeightForPositioned(const RenderBoxModelObject* containingBlock) const -{ - ASSERT(containingBlock->isBox()); - const RenderBlock* cb = containingBlock->isRenderBlock() ? - toRenderBlock(containingBlock) : containingBlock->containingBlock(); - return cb->clientLogicalHeight(); -} - -static void computePositionedStaticDistance(Length& leftOrTop, Length& rightOrBottom) -{ - if (!leftOrTop.isAuto() || !rightOrBottom.isAuto()) - return; - leftOrTop.setValue(Fixed, 0); -} - -void RenderBox::computePositionedLogicalWidth(LogicalExtentComputedValues& computedValues) const -{ - if (isReplaced()) { - computePositionedLogicalWidthReplaced(computedValues); - return; - } - - // QUESTIONS - // FIXME 1: Should we still deal with these the cases of 'left' or 'right' having - // the type 'static' in determining whether to calculate the static distance? - // NOTE: 'static' is not a legal value for 'left' or 'right' as of CSS 2.1. - - // FIXME 2: Can perhaps optimize out cases when max-width/min-width are greater - // than or less than the computed width(). Be careful of box-sizing and - // percentage issues. - - // The following is based off of the W3C Working Draft from April 11, 2006 of - // CSS 2.1: Section 10.3.7 "Absolutely positioned, non-replaced elements" - // - // (block-style-comments in this function and in computePositionedLogicalWidthUsing() - // correspond to text from the spec) - - - // We don't use containingBlock(), since we may be positioned by an enclosing - // relative positioned inline. - const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container()); - - const LayoutUnit containerLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock); - - // Use the container block's direction except when calculating the static distance - // This conforms with the reference results for abspos-replaced-width-margin-000.htm - // of the CSS 2.1 test suite - TextDirection containerDirection = containerBlock->style()->direction(); - - const LayoutUnit bordersPlusPadding = borderAndPaddingLogicalWidth(); - const Length marginLogicalLeft = style()->marginLeft(); - const Length marginLogicalRight = style()->marginRight(); - - Length logicalLeftLength = style()->logicalLeft(); - Length logicalRightLength = style()->logicalRight(); - - /*---------------------------------------------------------------------------*\ - * For the purposes of this section and the next, the term "static position" - * (of an element) refers, roughly, to the position an element would have had - * in the normal flow. More precisely: - * - * * The static position for 'left' is the distance from the left edge of the - * containing block to the left margin edge of a hypothetical box that would - * have been the first box of the element if its 'position' property had - * been 'static' and 'float' had been 'none'. The value is negative if the - * hypothetical box is to the left of the containing block. - * * The static position for 'right' is the distance from the right edge of the - * containing block to the right margin edge of the same hypothetical box as - * above. The value is positive if the hypothetical box is to the left of the - * containing block's edge. - * - * But rather than actually calculating the dimensions of that hypothetical box, - * user agents are free to make a guess at its probable position. - * - * For the purposes of calculating the static position, the containing block of - * fixed positioned elements is the initial containing block instead of the - * viewport, and all scrollable boxes should be assumed to be scrolled to their - * origin. - \*---------------------------------------------------------------------------*/ - - // see FIXME 1 - // Calculate the static distance if needed. - computePositionedStaticDistance(logicalLeftLength, logicalRightLength); - - // Calculate constraint equation values for 'width' case. - computePositionedLogicalWidthUsing(style()->logicalWidth(), containerBlock, containerDirection, - containerLogicalWidth, bordersPlusPadding, - logicalLeftLength, logicalRightLength, marginLogicalLeft, marginLogicalRight, - computedValues); - - // Calculate constraint equation values for 'max-width' case. - if (!style()->logicalMaxWidth().isMaxSizeNone()) { - LogicalExtentComputedValues maxValues; - - computePositionedLogicalWidthUsing(style()->logicalMaxWidth(), containerBlock, containerDirection, - containerLogicalWidth, bordersPlusPadding, - logicalLeftLength, logicalRightLength, marginLogicalLeft, marginLogicalRight, - maxValues); - - if (computedValues.m_extent > maxValues.m_extent) { - computedValues.m_extent = maxValues.m_extent; - computedValues.m_position = maxValues.m_position; - computedValues.m_margins.m_start = maxValues.m_margins.m_start; - computedValues.m_margins.m_end = maxValues.m_margins.m_end; - } - } - - // Calculate constraint equation values for 'min-width' case. - if (!style()->logicalMinWidth().isZero() || style()->logicalMinWidth().isIntrinsic()) { - LogicalExtentComputedValues minValues; - - computePositionedLogicalWidthUsing(style()->logicalMinWidth(), containerBlock, containerDirection, - containerLogicalWidth, bordersPlusPadding, - logicalLeftLength, logicalRightLength, marginLogicalLeft, marginLogicalRight, - minValues); - - if (computedValues.m_extent < minValues.m_extent) { - computedValues.m_extent = minValues.m_extent; - computedValues.m_position = minValues.m_position; - computedValues.m_margins.m_start = minValues.m_margins.m_start; - computedValues.m_margins.m_end = minValues.m_margins.m_end; - } - } - - computedValues.m_extent += bordersPlusPadding; -} - -static void computeLogicalLeftPositionedOffset(LayoutUnit& logicalLeftPos, const RenderBox* child, LayoutUnit logicalWidthValue, const RenderBoxModelObject* containerBlock, LayoutUnit containerLogicalWidth) -{ - // FIXME(sky): Remove - logicalLeftPos += containerBlock->borderLeft(); -} - -void RenderBox::shrinkToFitWidth(const LayoutUnit availableSpace, const LayoutUnit logicalLeftValue, const LayoutUnit bordersPlusPadding, LogicalExtentComputedValues& computedValues) const -{ - // FIXME: would it be better to have shrink-to-fit in one step? - LayoutUnit preferredWidth = maxPreferredLogicalWidth() - bordersPlusPadding; - LayoutUnit preferredMinWidth = minPreferredLogicalWidth() - bordersPlusPadding; - LayoutUnit availableWidth = availableSpace - logicalLeftValue; - computedValues.m_extent = std::min(std::max(preferredMinWidth, availableWidth), preferredWidth); -} - -void RenderBox::computePositionedLogicalWidthUsing(Length logicalWidth, const RenderBoxModelObject* containerBlock, TextDirection containerDirection, - LayoutUnit containerLogicalWidth, LayoutUnit bordersPlusPadding, - const Length& logicalLeft, const Length& logicalRight, const Length& marginLogicalLeft, - const Length& marginLogicalRight, LogicalExtentComputedValues& computedValues) const -{ - if (logicalWidth.isIntrinsic()) - logicalWidth = Length(computeIntrinsicLogicalWidthUsing(logicalWidth, containerLogicalWidth, bordersPlusPadding) - bordersPlusPadding, Fixed); - - // 'left' and 'right' cannot both be 'auto' because one would of been - // converted to the static position already - ASSERT(!(logicalLeft.isAuto() && logicalRight.isAuto())); - - LayoutUnit logicalLeftValue = 0; - - const LayoutUnit containerRelativeLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock); - - bool logicalWidthIsAuto = logicalWidth.isIntrinsicOrAuto(); - bool logicalLeftIsAuto = logicalLeft.isAuto(); - bool logicalRightIsAuto = logicalRight.isAuto(); - LayoutUnit& marginLogicalLeftValue = style()->isLeftToRightDirection() ? computedValues.m_margins.m_start : computedValues.m_margins.m_end; - LayoutUnit& marginLogicalRightValue = style()->isLeftToRightDirection() ? computedValues.m_margins.m_end : computedValues.m_margins.m_start; - if (!logicalLeftIsAuto && !logicalWidthIsAuto && !logicalRightIsAuto) { - /*-----------------------------------------------------------------------*\ - * If none of the three is 'auto': If both 'margin-left' and 'margin- - * right' are 'auto', solve the equation under the extra constraint that - * the two margins get equal values, unless this would make them negative, - * in which case when direction of the containing block is 'ltr' ('rtl'), - * set 'margin-left' ('margin-right') to zero and solve for 'margin-right' - * ('margin-left'). If one of 'margin-left' or 'margin-right' is 'auto', - * solve the equation for that value. If the values are over-constrained, - * ignore the value for 'left' (in case the 'direction' property of the - * containing block is 'rtl') or 'right' (in case 'direction' is 'ltr') - * and solve for that value. - \*-----------------------------------------------------------------------*/ - // NOTE: It is not necessary to solve for 'right' in the over constrained - // case because the value is not used for any further calculations. - - logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth); - computedValues.m_extent = adjustContentBoxLogicalWidthForBoxSizing(valueForLength(logicalWidth, containerLogicalWidth)); - - const LayoutUnit availableSpace = containerLogicalWidth - (logicalLeftValue + computedValues.m_extent + valueForLength(logicalRight, containerLogicalWidth) + bordersPlusPadding); - - // Margins are now the only unknown - if (marginLogicalLeft.isAuto() && marginLogicalRight.isAuto()) { - // Both margins auto, solve for equality - if (availableSpace >= 0) { - marginLogicalLeftValue = availableSpace / 2; // split the difference - marginLogicalRightValue = availableSpace - marginLogicalLeftValue; // account for odd valued differences - } else { - // Use the containing block's direction rather than the parent block's - // per CSS 2.1 reference test abspos-non-replaced-width-margin-000. - if (containerDirection == LTR) { - marginLogicalLeftValue = 0; - marginLogicalRightValue = availableSpace; // will be negative - } else { - marginLogicalLeftValue = availableSpace; // will be negative - marginLogicalRightValue = 0; - } - } - } else if (marginLogicalLeft.isAuto()) { - // Solve for left margin - marginLogicalRightValue = valueForLength(marginLogicalRight, containerRelativeLogicalWidth); - marginLogicalLeftValue = availableSpace - marginLogicalRightValue; - } else if (marginLogicalRight.isAuto()) { - // Solve for right margin - marginLogicalLeftValue = valueForLength(marginLogicalLeft, containerRelativeLogicalWidth); - marginLogicalRightValue = availableSpace - marginLogicalLeftValue; - } else { - // Over-constrained, solve for left if direction is RTL - marginLogicalLeftValue = valueForLength(marginLogicalLeft, containerRelativeLogicalWidth); - marginLogicalRightValue = valueForLength(marginLogicalRight, containerRelativeLogicalWidth); - - // Use the containing block's direction rather than the parent block's - // per CSS 2.1 reference test abspos-non-replaced-width-margin-000. - if (containerDirection == RTL) - logicalLeftValue = (availableSpace + logicalLeftValue) - marginLogicalLeftValue - marginLogicalRightValue; - } - } else { - /*--------------------------------------------------------------------*\ - * Otherwise, set 'auto' values for 'margin-left' and 'margin-right' - * to 0, and pick the one of the following six rules that applies. - * - * 1. 'left' and 'width' are 'auto' and 'right' is not 'auto', then the - * width is shrink-to-fit. Then solve for 'left' - * - * OMIT RULE 2 AS IT SHOULD NEVER BE HIT - * ------------------------------------------------------------------ - * 2. 'left' and 'right' are 'auto' and 'width' is not 'auto', then if - * the 'direction' property of the containing block is 'ltr' set - * 'left' to the static position, otherwise set 'right' to the - * static position. Then solve for 'left' (if 'direction is 'rtl') - * or 'right' (if 'direction' is 'ltr'). - * ------------------------------------------------------------------ - * - * 3. 'width' and 'right' are 'auto' and 'left' is not 'auto', then the - * width is shrink-to-fit . Then solve for 'right' - * 4. 'left' is 'auto', 'width' and 'right' are not 'auto', then solve - * for 'left' - * 5. 'width' is 'auto', 'left' and 'right' are not 'auto', then solve - * for 'width' - * 6. 'right' is 'auto', 'left' and 'width' are not 'auto', then solve - * for 'right' - * - * Calculation of the shrink-to-fit width is similar to calculating the - * width of a table cell using the automatic table layout algorithm. - * Roughly: calculate the preferred width by formatting the content - * without breaking lines other than where explicit line breaks occur, - * and also calculate the preferred minimum width, e.g., by trying all - * possible line breaks. CSS 2.1 does not define the exact algorithm. - * Thirdly, calculate the available width: this is found by solving - * for 'width' after setting 'left' (in case 1) or 'right' (in case 3) - * to 0. - * - * Then the shrink-to-fit width is: - * min(max(preferred minimum width, available width), preferred width). - \*--------------------------------------------------------------------*/ - // NOTE: For rules 3 and 6 it is not necessary to solve for 'right' - // because the value is not used for any further calculations. - - // Calculate margins, 'auto' margins are ignored. - marginLogicalLeftValue = minimumValueForLength(marginLogicalLeft, containerRelativeLogicalWidth); - marginLogicalRightValue = minimumValueForLength(marginLogicalRight, containerRelativeLogicalWidth); - - const LayoutUnit availableSpace = containerLogicalWidth - (marginLogicalLeftValue + marginLogicalRightValue + bordersPlusPadding); - - // FIXME: Is there a faster way to find the correct case? - // Use rule/case that applies. - if (logicalLeftIsAuto && logicalWidthIsAuto && !logicalRightIsAuto) { - // RULE 1: (use shrink-to-fit for width, and solve of left) - LayoutUnit logicalRightValue = valueForLength(logicalRight, containerLogicalWidth); - - // FIXME: would it be better to have shrink-to-fit in one step? - LayoutUnit preferredWidth = maxPreferredLogicalWidth() - bordersPlusPadding; - LayoutUnit preferredMinWidth = minPreferredLogicalWidth() - bordersPlusPadding; - LayoutUnit availableWidth = availableSpace - logicalRightValue; - computedValues.m_extent = std::min(std::max(preferredMinWidth, availableWidth), preferredWidth); - logicalLeftValue = availableSpace - (computedValues.m_extent + logicalRightValue); - } else if (!logicalLeftIsAuto && logicalWidthIsAuto && logicalRightIsAuto) { - // RULE 3: (use shrink-to-fit for width, and no need solve of right) - logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth); - - shrinkToFitWidth(availableSpace, logicalLeftValue, bordersPlusPadding, computedValues); - } else if (logicalLeftIsAuto && !logicalWidthIsAuto && !logicalRightIsAuto) { - // RULE 4: (solve for left) - computedValues.m_extent = adjustContentBoxLogicalWidthForBoxSizing(valueForLength(logicalWidth, containerLogicalWidth)); - logicalLeftValue = availableSpace - (computedValues.m_extent + valueForLength(logicalRight, containerLogicalWidth)); - } else if (!logicalLeftIsAuto && logicalWidthIsAuto && !logicalRightIsAuto) { - // RULE 5: (solve for width) - logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth); - computedValues.m_extent = availableSpace - (logicalLeftValue + valueForLength(logicalRight, containerLogicalWidth)); - } else if (!logicalLeftIsAuto && !logicalWidthIsAuto && logicalRightIsAuto) { - // RULE 6: (no need solve for right) - logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth); - computedValues.m_extent = adjustContentBoxLogicalWidthForBoxSizing(valueForLength(logicalWidth, containerLogicalWidth)); - } - } - - // Use computed values to calculate the horizontal position. - - // FIXME: This hack is needed to calculate the logical left position for a 'rtl' relatively - // positioned, inline because right now, it is using the logical left position - // of the first line box when really it should use the last line box. When - // this is fixed elsewhere, this block should be removed. - if (containerBlock->isRenderInline() && !containerBlock->style()->isLeftToRightDirection()) { - const RenderInline* flow = toRenderInline(containerBlock); - InlineFlowBox* firstLine = flow->firstLineBox(); - InlineFlowBox* lastLine = flow->lastLineBox(); - if (firstLine && lastLine && firstLine != lastLine) { - computedValues.m_position = logicalLeftValue + marginLogicalLeftValue + lastLine->borderLogicalLeft() + (lastLine->logicalLeft() - firstLine->logicalLeft()); - return; - } - } - - computedValues.m_position = logicalLeftValue + marginLogicalLeftValue; - computeLogicalLeftPositionedOffset(computedValues.m_position, this, computedValues.m_extent, containerBlock, containerLogicalWidth); -} - -void RenderBox::computePositionedLogicalHeight(LogicalExtentComputedValues& computedValues) const -{ - if (isReplaced()) { - computePositionedLogicalHeightReplaced(computedValues); - return; - } - - // The following is based off of the W3C Working Draft from April 11, 2006 of - // CSS 2.1: Section 10.6.4 "Absolutely positioned, non-replaced elements" - // - // (block-style-comments in this function and in computePositionedLogicalHeightUsing() - // correspond to text from the spec) - - - // We don't use containingBlock(), since we may be positioned by an enclosing relpositioned inline. - const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container()); - - const LayoutUnit containerLogicalHeight = containingBlockLogicalHeightForPositioned(containerBlock); - - RenderStyle* styleToUse = style(); - const LayoutUnit bordersPlusPadding = borderAndPaddingLogicalHeight(); - const Length marginBefore = styleToUse->marginBefore(); - const Length marginAfter = styleToUse->marginAfter(); - Length logicalTopLength = styleToUse->logicalTop(); - Length logicalBottomLength = styleToUse->logicalBottom(); - - /*---------------------------------------------------------------------------*\ - * For the purposes of this section and the next, the term "static position" - * (of an element) refers, roughly, to the position an element would have had - * in the normal flow. More precisely, the static position for 'top' is the - * distance from the top edge of the containing block to the top margin edge - * of a hypothetical box that would have been the first box of the element if - * its 'position' property had been 'static' and 'float' had been 'none'. The - * value is negative if the hypothetical box is above the containing block. - * - * But rather than actually calculating the dimensions of that hypothetical - * box, user agents are free to make a guess at its probable position. - * - * For the purposes of calculating the static position, the containing block - * of fixed positioned elements is the initial containing block instead of - * the viewport. - \*---------------------------------------------------------------------------*/ - - // see FIXME 1 - // Calculate the static distance if needed. - computePositionedStaticDistance(logicalTopLength, logicalBottomLength); - - // Calculate constraint equation values for 'height' case. - LayoutUnit logicalHeight = computedValues.m_extent; - computePositionedLogicalHeightUsing(styleToUse->logicalHeight(), containerBlock, containerLogicalHeight, bordersPlusPadding, logicalHeight, - logicalTopLength, logicalBottomLength, marginBefore, marginAfter, - computedValues); - - // Avoid doing any work in the common case (where the values of min-height and max-height are their defaults). - // see FIXME 2 - - // Calculate constraint equation values for 'max-height' case. - if (!styleToUse->logicalMaxHeight().isMaxSizeNone()) { - LogicalExtentComputedValues maxValues; - - computePositionedLogicalHeightUsing(styleToUse->logicalMaxHeight(), containerBlock, containerLogicalHeight, bordersPlusPadding, logicalHeight, - logicalTopLength, logicalBottomLength, marginBefore, marginAfter, - maxValues); - - if (computedValues.m_extent > maxValues.m_extent) { - computedValues.m_extent = maxValues.m_extent; - computedValues.m_position = maxValues.m_position; - computedValues.m_margins.m_before = maxValues.m_margins.m_before; - computedValues.m_margins.m_after = maxValues.m_margins.m_after; - } - } - - // Calculate constraint equation values for 'min-height' case. - if (!styleToUse->logicalMinHeight().isZero() || styleToUse->logicalMinHeight().isIntrinsic()) { - LogicalExtentComputedValues minValues; - - computePositionedLogicalHeightUsing(styleToUse->logicalMinHeight(), containerBlock, containerLogicalHeight, bordersPlusPadding, logicalHeight, - logicalTopLength, logicalBottomLength, marginBefore, marginAfter, - minValues); - - if (computedValues.m_extent < minValues.m_extent) { - computedValues.m_extent = minValues.m_extent; - computedValues.m_position = minValues.m_position; - computedValues.m_margins.m_before = minValues.m_margins.m_before; - computedValues.m_margins.m_after = minValues.m_margins.m_after; - } - } - - // Set final height value. - computedValues.m_extent += bordersPlusPadding; -} - -static void computeLogicalTopPositionedOffset(LayoutUnit& logicalTopPos, const RenderBox* child, LayoutUnit logicalHeightValue, const RenderBoxModelObject* containerBlock, LayoutUnit containerLogicalHeight) -{ - // FIXME(sky): Remove - logicalTopPos += containerBlock->borderTop(); -} - -void RenderBox::computePositionedLogicalHeightUsing(Length logicalHeightLength, const RenderBoxModelObject* containerBlock, - LayoutUnit containerLogicalHeight, LayoutUnit bordersPlusPadding, LayoutUnit logicalHeight, - const Length& logicalTop, const Length& logicalBottom, const Length& marginBefore, - const Length& marginAfter, LogicalExtentComputedValues& computedValues) const -{ - // 'top' and 'bottom' cannot both be 'auto' because 'top would of been - // converted to the static position in computePositionedLogicalHeight() - ASSERT(!(logicalTop.isAuto() && logicalBottom.isAuto())); - - LayoutUnit logicalHeightValue; - LayoutUnit contentLogicalHeight = logicalHeight - bordersPlusPadding; - - const LayoutUnit containerRelativeLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock); - - LayoutUnit logicalTopValue = 0; - - bool logicalHeightIsAuto = logicalHeightLength.isAuto(); - bool logicalTopIsAuto = logicalTop.isAuto(); - bool logicalBottomIsAuto = logicalBottom.isAuto(); - - LayoutUnit resolvedLogicalHeight; - if (logicalHeightLength.isIntrinsic()) - resolvedLogicalHeight = computeIntrinsicLogicalContentHeightUsing(logicalHeightLength, contentLogicalHeight, bordersPlusPadding); - else - resolvedLogicalHeight = adjustContentBoxLogicalHeightForBoxSizing(valueForLength(logicalHeightLength, containerLogicalHeight)); - - if (!logicalTopIsAuto && !logicalHeightIsAuto && !logicalBottomIsAuto) { - /*-----------------------------------------------------------------------*\ - * If none of the three are 'auto': If both 'margin-top' and 'margin- - * bottom' are 'auto', solve the equation under the extra constraint that - * the two margins get equal values. If one of 'margin-top' or 'margin- - * bottom' is 'auto', solve the equation for that value. If the values - * are over-constrained, ignore the value for 'bottom' and solve for that - * value. - \*-----------------------------------------------------------------------*/ - // NOTE: It is not necessary to solve for 'bottom' in the over constrained - // case because the value is not used for any further calculations. - - logicalHeightValue = resolvedLogicalHeight; - logicalTopValue = valueForLength(logicalTop, containerLogicalHeight); - - const LayoutUnit availableSpace = containerLogicalHeight - (logicalTopValue + logicalHeightValue + valueForLength(logicalBottom, containerLogicalHeight) + bordersPlusPadding); - - // Margins are now the only unknown - if (marginBefore.isAuto() && marginAfter.isAuto()) { - // Both margins auto, solve for equality - // NOTE: This may result in negative values. - computedValues.m_margins.m_before = availableSpace / 2; // split the difference - computedValues.m_margins.m_after = availableSpace - computedValues.m_margins.m_before; // account for odd valued differences - } else if (marginBefore.isAuto()) { - // Solve for top margin - computedValues.m_margins.m_after = valueForLength(marginAfter, containerRelativeLogicalWidth); - computedValues.m_margins.m_before = availableSpace - computedValues.m_margins.m_after; - } else if (marginAfter.isAuto()) { - // Solve for bottom margin - computedValues.m_margins.m_before = valueForLength(marginBefore, containerRelativeLogicalWidth); - computedValues.m_margins.m_after = availableSpace - computedValues.m_margins.m_before; - } else { - // Over-constrained, (no need solve for bottom) - computedValues.m_margins.m_before = valueForLength(marginBefore, containerRelativeLogicalWidth); - computedValues.m_margins.m_after = valueForLength(marginAfter, containerRelativeLogicalWidth); - } - } else { - /*--------------------------------------------------------------------*\ - * Otherwise, set 'auto' values for 'margin-top' and 'margin-bottom' - * to 0, and pick the one of the following six rules that applies. - * - * 1. 'top' and 'height' are 'auto' and 'bottom' is not 'auto', then - * the height is based on the content, and solve for 'top'. - * - * OMIT RULE 2 AS IT SHOULD NEVER BE HIT - * ------------------------------------------------------------------ - * 2. 'top' and 'bottom' are 'auto' and 'height' is not 'auto', then - * set 'top' to the static position, and solve for 'bottom'. - * ------------------------------------------------------------------ - * - * 3. 'height' and 'bottom' are 'auto' and 'top' is not 'auto', then - * the height is based on the content, and solve for 'bottom'. - * 4. 'top' is 'auto', 'height' and 'bottom' are not 'auto', and - * solve for 'top'. - * 5. 'height' is 'auto', 'top' and 'bottom' are not 'auto', and - * solve for 'height'. - * 6. 'bottom' is 'auto', 'top' and 'height' are not 'auto', and - * solve for 'bottom'. - \*--------------------------------------------------------------------*/ - // NOTE: For rules 3 and 6 it is not necessary to solve for 'bottom' - // because the value is not used for any further calculations. - - // Calculate margins, 'auto' margins are ignored. - computedValues.m_margins.m_before = minimumValueForLength(marginBefore, containerRelativeLogicalWidth); - computedValues.m_margins.m_after = minimumValueForLength(marginAfter, containerRelativeLogicalWidth); - - const LayoutUnit availableSpace = containerLogicalHeight - (computedValues.m_margins.m_before + computedValues.m_margins.m_after + bordersPlusPadding); - - // Use rule/case that applies. - if (logicalTopIsAuto && logicalHeightIsAuto && !logicalBottomIsAuto) { - // RULE 1: (height is content based, solve of top) - logicalHeightValue = contentLogicalHeight; - logicalTopValue = availableSpace - (logicalHeightValue + valueForLength(logicalBottom, containerLogicalHeight)); - } else if (!logicalTopIsAuto && logicalHeightIsAuto && logicalBottomIsAuto) { - // RULE 3: (height is content based, no need solve of bottom) - logicalTopValue = valueForLength(logicalTop, containerLogicalHeight); - logicalHeightValue = contentLogicalHeight; - } else if (logicalTopIsAuto && !logicalHeightIsAuto && !logicalBottomIsAuto) { - // RULE 4: (solve of top) - logicalHeightValue = resolvedLogicalHeight; - logicalTopValue = availableSpace - (logicalHeightValue + valueForLength(logicalBottom, containerLogicalHeight)); - } else if (!logicalTopIsAuto && logicalHeightIsAuto && !logicalBottomIsAuto) { - // RULE 5: (solve of height) - logicalTopValue = valueForLength(logicalTop, containerLogicalHeight); - logicalHeightValue = std::max(0, availableSpace - (logicalTopValue + valueForLength(logicalBottom, containerLogicalHeight))); - } else if (!logicalTopIsAuto && !logicalHeightIsAuto && logicalBottomIsAuto) { - // RULE 6: (no need solve of bottom) - logicalHeightValue = resolvedLogicalHeight; - logicalTopValue = valueForLength(logicalTop, containerLogicalHeight); + LayoutUnit newContentHeight = + computedValues.m_extent - block->borderAndPaddingLogicalHeight(); + LayoutUnit newHeight = + block->adjustContentBoxLogicalHeightForBoxSizing(newContentHeight); + return adjustContentBoxLogicalHeightForBoxSizing( + valueForLength(logicalHeight, newHeight)); + } + + // FIXME: availableLogicalHeight() is wrong if the replaced element's + // block-flow is perpendicular to the containing block's block-flow. + // https://bugs.webkit.org/show_bug.cgi?id=46496 + LayoutUnit availableHeight; + if (isOutOfFlowPositioned()) + availableHeight = containingBlockLogicalHeightForPositioned( + toRenderBoxModelObject(cb)); + else { + availableHeight = + containingBlockLogicalHeightForContent(IncludeMarginBorderPadding); + // It is necessary to use the border-box to match WinIE's broken + // box model. This is essential for sizing inside + // table cells using percentage heights. + // FIXME: This needs to be made block-flow-aware. If the cell and image + // are perpendicular block-flows, this isn't right. + // https://bugs.webkit.org/show_bug.cgi?id=46997 + while (cb && !cb->isRenderView() && + (cb->style()->logicalHeight().isAuto() || + cb->style()->logicalHeight().isPercent())) { + toRenderBlock(cb)->addPercentHeightDescendant( + const_cast(this)); + cb = cb->containingBlock(); } - } - computedValues.m_extent = logicalHeightValue; - - // Use computed values to calculate the vertical position. - computedValues.m_position = logicalTopValue + computedValues.m_margins.m_before; - computeLogicalTopPositionedOffset(computedValues.m_position, this, logicalHeightValue, containerBlock, containerLogicalHeight); -} - -void RenderBox::computePositionedLogicalWidthReplaced(LogicalExtentComputedValues& computedValues) const -{ - // The following is based off of the W3C Working Draft from April 11, 2006 of - // CSS 2.1: Section 10.3.8 "Absolutely positioned, replaced elements" - // - // (block-style-comments in this function correspond to text from the spec and - // the numbers correspond to numbers in spec) - - // We don't use containingBlock(), since we may be positioned by an enclosing - // relative positioned inline. - const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container()); - - const LayoutUnit containerLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock); - const LayoutUnit containerRelativeLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock); - - // To match WinIE, in quirks mode use the parent's 'direction' property - // instead of the the container block's. - TextDirection containerDirection = containerBlock->style()->direction(); - - // Variables to solve. - Length logicalLeft = style()->logicalLeft(); - Length logicalRight = style()->logicalRight(); - Length marginLogicalLeft = style()->marginLeft(); - Length marginLogicalRight = style()->marginRight(); - LayoutUnit& marginLogicalLeftAlias = style()->isLeftToRightDirection() ? computedValues.m_margins.m_start : computedValues.m_margins.m_end; - LayoutUnit& marginLogicalRightAlias = style()->isLeftToRightDirection() ? computedValues.m_margins.m_end : computedValues.m_margins.m_start; - - /*-----------------------------------------------------------------------*\ - * 1. The used value of 'width' is determined as for inline replaced - * elements. - \*-----------------------------------------------------------------------*/ - // NOTE: This value of width is final in that the min/max width calculations - // are dealt with in computeReplacedWidth(). This means that the steps to produce - // correct max/min in the non-replaced version, are not necessary. - computedValues.m_extent = computeReplacedLogicalWidth() + borderAndPaddingLogicalWidth(); - - const LayoutUnit availableSpace = containerLogicalWidth - computedValues.m_extent; - + } + return adjustContentBoxLogicalHeightForBoxSizing( + valueForLength(logicalHeight, availableHeight)); + } + case MinContent: + case MaxContent: + case FitContent: + case FillAvailable: + return adjustContentBoxLogicalHeightForBoxSizing( + computeIntrinsicLogicalContentHeightUsing(logicalHeight, + intrinsicLogicalHeight(), + borderAndPaddingHeight())); + default: + return intrinsicLogicalHeight(); + } +} + +LayoutUnit RenderBox::availableLogicalHeight( + AvailableLogicalHeightType heightType) const { + // http://www.w3.org/TR/CSS2/visudet.html#propdef-height - We are interested + // in the content height. + return constrainContentBoxLogicalHeightByMinMax( + availableLogicalHeightUsing(style()->logicalHeight(), heightType), -1); +} + +LayoutUnit RenderBox::availableLogicalHeightUsing( + const Length& h, + AvailableLogicalHeightType heightType) const { + if (isRenderView()) { + ASSERT_NOT_REACHED(); + return LayoutUnit(); + } + + if (h.isPercent() && isOutOfFlowPositioned()) { + // FIXME: This is wrong if the containingBlock has a perpendicular writing + // mode. + LayoutUnit availableHeight = + containingBlockLogicalHeightForPositioned(containingBlock()); + return adjustContentBoxLogicalHeightForBoxSizing( + valueForLength(h, availableHeight)); + } + + LayoutUnit heightIncludingScrollbar = computeContentLogicalHeightUsing(h, -1); + if (heightIncludingScrollbar != -1) + return std::max( + 0, adjustContentBoxLogicalHeightForBoxSizing(heightIncludingScrollbar)); + + // FIXME: Check logicalTop/logicalBottom here to correctly handle vertical + // writing-mode. https://bugs.webkit.org/show_bug.cgi?id=46500 + if (isRenderBlock() && isOutOfFlowPositioned() && + style()->height().isAuto() && + !(style()->top().isAuto() || style()->bottom().isAuto())) { + RenderBlock* block = const_cast(toRenderBlock(this)); + LogicalExtentComputedValues computedValues; + block->computeLogicalHeight(block->logicalHeight(), 0, computedValues); + LayoutUnit newContentHeight = + computedValues.m_extent - block->borderAndPaddingLogicalHeight(); + return adjustContentBoxLogicalHeightForBoxSizing(newContentHeight); + } + + // FIXME: This is wrong if the containingBlock has a perpendicular writing + // mode. + LayoutUnit availableHeight = + containingBlockLogicalHeightForContent(heightType); + if (heightType == ExcludeMarginBorderPadding) { + // FIXME: Margin collapsing hasn't happened yet, so this incorrectly removes + // collapsed margins. + availableHeight -= + marginBefore() + marginAfter() + borderAndPaddingLogicalHeight(); + } + return availableHeight; +} + +void RenderBox::computeAndSetBlockDirectionMargins( + const RenderBlock* containingBlock) { + LayoutUnit marginBefore; + LayoutUnit marginAfter; + computeMarginsForDirection( + BlockDirection, containingBlock, containingBlockLogicalWidthForContent(), + logicalHeight(), marginBefore, marginAfter, + style()->marginBeforeUsing(containingBlock->style()), + style()->marginAfterUsing(containingBlock->style())); + // Note that in this 'positioning phase' of the layout we are using the + // containing block's writing mode rather than our own when calculating + // margins. See + // http://www.w3.org/TR/2014/CR-css-writing-modes-3-20140320/#orthogonal-flows + containingBlock->setMarginBeforeForChild(this, marginBefore); + containingBlock->setMarginAfterForChild(this, marginAfter); +} + +LayoutUnit RenderBox::containingBlockLogicalWidthForPositioned( + const RenderBoxModelObject* containingBlock) const { + ASSERT(containingBlock->isBox()); + return toRenderBox(containingBlock)->clientLogicalWidth(); +} + +LayoutUnit RenderBox::containingBlockLogicalHeightForPositioned( + const RenderBoxModelObject* containingBlock) const { + ASSERT(containingBlock->isBox()); + const RenderBlock* cb = containingBlock->isRenderBlock() + ? toRenderBlock(containingBlock) + : containingBlock->containingBlock(); + return cb->clientLogicalHeight(); +} + +static void computePositionedStaticDistance(Length& leftOrTop, + Length& rightOrBottom) { + if (!leftOrTop.isAuto() || !rightOrBottom.isAuto()) + return; + leftOrTop.setValue(Fixed, 0); +} + +void RenderBox::computePositionedLogicalWidth( + LogicalExtentComputedValues& computedValues) const { + if (isReplaced()) { + computePositionedLogicalWidthReplaced(computedValues); + return; + } + + // QUESTIONS + // FIXME 1: Should we still deal with these the cases of 'left' or 'right' + // having the type 'static' in determining whether to calculate the static + // distance? NOTE: 'static' is not a legal value for 'left' or 'right' as of + // CSS 2.1. + + // FIXME 2: Can perhaps optimize out cases when max-width/min-width are + // greater than or less than the computed width(). Be careful of box-sizing + // and percentage issues. + + // The following is based off of the W3C Working Draft from April 11, 2006 of + // CSS 2.1: Section 10.3.7 "Absolutely positioned, non-replaced elements" + // + // (block-style-comments in this function and in + // computePositionedLogicalWidthUsing() correspond to text from the spec) + + // We don't use containingBlock(), since we may be positioned by an enclosing + // relative positioned inline. + const RenderBoxModelObject* containerBlock = + toRenderBoxModelObject(container()); + + const LayoutUnit containerLogicalWidth = + containingBlockLogicalWidthForPositioned(containerBlock); + + // Use the container block's direction except when calculating the static + // distance This conforms with the reference results for + // abspos-replaced-width-margin-000.htm of the CSS 2.1 test suite + TextDirection containerDirection = containerBlock->style()->direction(); + + const LayoutUnit bordersPlusPadding = borderAndPaddingLogicalWidth(); + const Length marginLogicalLeft = style()->marginLeft(); + const Length marginLogicalRight = style()->marginRight(); + + Length logicalLeftLength = style()->logicalLeft(); + Length logicalRightLength = style()->logicalRight(); + + /*---------------------------------------------------------------------------*\ + * For the purposes of this section and the next, the term "static position" + * (of an element) refers, roughly, to the position an element would have had + * in the normal flow. More precisely: + * + * * The static position for 'left' is the distance from the left edge of the + * containing block to the left margin edge of a hypothetical box that would + * have been the first box of the element if its 'position' property had + * been 'static' and 'float' had been 'none'. The value is negative if the + * hypothetical box is to the left of the containing block. + * * The static position for 'right' is the distance from the right edge of + the + * containing block to the right margin edge of the same hypothetical box as + * above. The value is positive if the hypothetical box is to the left of + the + * containing block's edge. + * + * But rather than actually calculating the dimensions of that hypothetical + box, + * user agents are free to make a guess at its probable position. + * + * For the purposes of calculating the static position, the containing block + of + * fixed positioned elements is the initial containing block instead of the + * viewport, and all scrollable boxes should be assumed to be scrolled to + their + * origin. + \*---------------------------------------------------------------------------*/ + + // see FIXME 1 + // Calculate the static distance if needed. + computePositionedStaticDistance(logicalLeftLength, logicalRightLength); + + // Calculate constraint equation values for 'width' case. + computePositionedLogicalWidthUsing(style()->logicalWidth(), containerBlock, + containerDirection, containerLogicalWidth, + bordersPlusPadding, logicalLeftLength, + logicalRightLength, marginLogicalLeft, + marginLogicalRight, computedValues); + + // Calculate constraint equation values for 'max-width' case. + if (!style()->logicalMaxWidth().isMaxSizeNone()) { + LogicalExtentComputedValues maxValues; + + computePositionedLogicalWidthUsing( + style()->logicalMaxWidth(), containerBlock, containerDirection, + containerLogicalWidth, bordersPlusPadding, logicalLeftLength, + logicalRightLength, marginLogicalLeft, marginLogicalRight, maxValues); + + if (computedValues.m_extent > maxValues.m_extent) { + computedValues.m_extent = maxValues.m_extent; + computedValues.m_position = maxValues.m_position; + computedValues.m_margins.m_start = maxValues.m_margins.m_start; + computedValues.m_margins.m_end = maxValues.m_margins.m_end; + } + } + + // Calculate constraint equation values for 'min-width' case. + if (!style()->logicalMinWidth().isZero() || + style()->logicalMinWidth().isIntrinsic()) { + LogicalExtentComputedValues minValues; + + computePositionedLogicalWidthUsing( + style()->logicalMinWidth(), containerBlock, containerDirection, + containerLogicalWidth, bordersPlusPadding, logicalLeftLength, + logicalRightLength, marginLogicalLeft, marginLogicalRight, minValues); + + if (computedValues.m_extent < minValues.m_extent) { + computedValues.m_extent = minValues.m_extent; + computedValues.m_position = minValues.m_position; + computedValues.m_margins.m_start = minValues.m_margins.m_start; + computedValues.m_margins.m_end = minValues.m_margins.m_end; + } + } + + computedValues.m_extent += bordersPlusPadding; +} + +static void computeLogicalLeftPositionedOffset( + LayoutUnit& logicalLeftPos, + const RenderBox* child, + LayoutUnit logicalWidthValue, + const RenderBoxModelObject* containerBlock, + LayoutUnit containerLogicalWidth) { + // FIXME(sky): Remove + logicalLeftPos += containerBlock->borderLeft(); +} + +void RenderBox::shrinkToFitWidth( + const LayoutUnit availableSpace, + const LayoutUnit logicalLeftValue, + const LayoutUnit bordersPlusPadding, + LogicalExtentComputedValues& computedValues) const { + // FIXME: would it be better to have shrink-to-fit in one step? + LayoutUnit preferredWidth = maxPreferredLogicalWidth() - bordersPlusPadding; + LayoutUnit preferredMinWidth = + minPreferredLogicalWidth() - bordersPlusPadding; + LayoutUnit availableWidth = availableSpace - logicalLeftValue; + computedValues.m_extent = + std::min(std::max(preferredMinWidth, availableWidth), preferredWidth); +} + +void RenderBox::computePositionedLogicalWidthUsing( + Length logicalWidth, + const RenderBoxModelObject* containerBlock, + TextDirection containerDirection, + LayoutUnit containerLogicalWidth, + LayoutUnit bordersPlusPadding, + const Length& logicalLeft, + const Length& logicalRight, + const Length& marginLogicalLeft, + const Length& marginLogicalRight, + LogicalExtentComputedValues& computedValues) const { + if (logicalWidth.isIntrinsic()) + logicalWidth = + Length(computeIntrinsicLogicalWidthUsing( + logicalWidth, containerLogicalWidth, bordersPlusPadding) - + bordersPlusPadding, + Fixed); + + // 'left' and 'right' cannot both be 'auto' because one would of been + // converted to the static position already + ASSERT(!(logicalLeft.isAuto() && logicalRight.isAuto())); + + LayoutUnit logicalLeftValue = 0; + + const LayoutUnit containerRelativeLogicalWidth = + containingBlockLogicalWidthForPositioned(containerBlock); + + bool logicalWidthIsAuto = logicalWidth.isIntrinsicOrAuto(); + bool logicalLeftIsAuto = logicalLeft.isAuto(); + bool logicalRightIsAuto = logicalRight.isAuto(); + LayoutUnit& marginLogicalLeftValue = style()->isLeftToRightDirection() + ? computedValues.m_margins.m_start + : computedValues.m_margins.m_end; + LayoutUnit& marginLogicalRightValue = style()->isLeftToRightDirection() + ? computedValues.m_margins.m_end + : computedValues.m_margins.m_start; + if (!logicalLeftIsAuto && !logicalWidthIsAuto && !logicalRightIsAuto) { /*-----------------------------------------------------------------------*\ - * 2. If both 'left' and 'right' have the value 'auto', then if 'direction' - * of the containing block is 'ltr', set 'left' to the static position; - * else if 'direction' is 'rtl', set 'right' to the static position. + * If none of the three is 'auto': If both 'margin-left' and 'margin- + * right' are 'auto', solve the equation under the extra constraint that + * the two margins get equal values, unless this would make them negative, + * in which case when direction of the containing block is 'ltr' ('rtl'), + * set 'margin-left' ('margin-right') to zero and solve for 'margin-right' + * ('margin-left'). If one of 'margin-left' or 'margin-right' is 'auto', + * solve the equation for that value. If the values are over-constrained, + * ignore the value for 'left' (in case the 'direction' property of the + * containing block is 'rtl') or 'right' (in case 'direction' is 'ltr') + * and solve for that value. \*-----------------------------------------------------------------------*/ - // see FIXME 1 - computePositionedStaticDistance(logicalLeft, logicalRight); + // NOTE: It is not necessary to solve for 'right' in the over constrained + // case because the value is not used for any further calculations. - /*-----------------------------------------------------------------------*\ - * 3. If 'left' or 'right' are 'auto', replace any 'auto' on 'margin-left' - * or 'margin-right' with '0'. - \*-----------------------------------------------------------------------*/ - if (logicalLeft.isAuto() || logicalRight.isAuto()) { - if (marginLogicalLeft.isAuto()) - marginLogicalLeft.setValue(Fixed, 0); - if (marginLogicalRight.isAuto()) - marginLogicalRight.setValue(Fixed, 0); - } + logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth); + computedValues.m_extent = adjustContentBoxLogicalWidthForBoxSizing( + valueForLength(logicalWidth, containerLogicalWidth)); - /*-----------------------------------------------------------------------*\ - * 4. If at this point both 'margin-left' and 'margin-right' are still - * 'auto', solve the equation under the extra constraint that the two - * margins must get equal values, unless this would make them negative, - * in which case when the direction of the containing block is 'ltr' - * ('rtl'), set 'margin-left' ('margin-right') to zero and solve for - * 'margin-right' ('margin-left'). - \*-----------------------------------------------------------------------*/ - LayoutUnit logicalLeftValue = 0; - LayoutUnit logicalRightValue = 0; + const LayoutUnit availableSpace = + containerLogicalWidth - + (logicalLeftValue + computedValues.m_extent + + valueForLength(logicalRight, containerLogicalWidth) + + bordersPlusPadding); + // Margins are now the only unknown if (marginLogicalLeft.isAuto() && marginLogicalRight.isAuto()) { - // 'left' and 'right' cannot be 'auto' due to step 3 - ASSERT(!(logicalLeft.isAuto() && logicalRight.isAuto())); - - logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth); - logicalRightValue = valueForLength(logicalRight, containerLogicalWidth); - - LayoutUnit difference = availableSpace - (logicalLeftValue + logicalRightValue); - if (difference > 0) { - marginLogicalLeftAlias = difference / 2; // split the difference - marginLogicalRightAlias = difference - marginLogicalLeftAlias; // account for odd valued differences + // Both margins auto, solve for equality + if (availableSpace >= 0) { + marginLogicalLeftValue = availableSpace / 2; // split the difference + marginLogicalRightValue = + availableSpace - + marginLogicalLeftValue; // account for odd valued differences + } else { + // Use the containing block's direction rather than the parent block's + // per CSS 2.1 reference test abspos-non-replaced-width-margin-000. + if (containerDirection == LTR) { + marginLogicalLeftValue = 0; + marginLogicalRightValue = availableSpace; // will be negative } else { - // Use the containing block's direction rather than the parent block's - // per CSS 2.1 reference test abspos-replaced-width-margin-000. - if (containerDirection == LTR) { - marginLogicalLeftAlias = 0; - marginLogicalRightAlias = difference; // will be negative - } else { - marginLogicalLeftAlias = difference; // will be negative - marginLogicalRightAlias = 0; - } + marginLogicalLeftValue = availableSpace; // will be negative + marginLogicalRightValue = 0; } - - /*-----------------------------------------------------------------------*\ - * 5. If at this point there is an 'auto' left, solve the equation for - * that value. - \*-----------------------------------------------------------------------*/ - } else if (logicalLeft.isAuto()) { - marginLogicalLeftAlias = valueForLength(marginLogicalLeft, containerRelativeLogicalWidth); - marginLogicalRightAlias = valueForLength(marginLogicalRight, containerRelativeLogicalWidth); - logicalRightValue = valueForLength(logicalRight, containerLogicalWidth); - - // Solve for 'left' - logicalLeftValue = availableSpace - (logicalRightValue + marginLogicalLeftAlias + marginLogicalRightAlias); - } else if (logicalRight.isAuto()) { - marginLogicalLeftAlias = valueForLength(marginLogicalLeft, containerRelativeLogicalWidth); - marginLogicalRightAlias = valueForLength(marginLogicalRight, containerRelativeLogicalWidth); - logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth); - - // Solve for 'right' - logicalRightValue = availableSpace - (logicalLeftValue + marginLogicalLeftAlias + marginLogicalRightAlias); + } } else if (marginLogicalLeft.isAuto()) { - marginLogicalRightAlias = valueForLength(marginLogicalRight, containerRelativeLogicalWidth); - logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth); - logicalRightValue = valueForLength(logicalRight, containerLogicalWidth); - - // Solve for 'margin-left' - marginLogicalLeftAlias = availableSpace - (logicalLeftValue + logicalRightValue + marginLogicalRightAlias); + // Solve for left margin + marginLogicalRightValue = + valueForLength(marginLogicalRight, containerRelativeLogicalWidth); + marginLogicalLeftValue = availableSpace - marginLogicalRightValue; } else if (marginLogicalRight.isAuto()) { - marginLogicalLeftAlias = valueForLength(marginLogicalLeft, containerRelativeLogicalWidth); - logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth); - logicalRightValue = valueForLength(logicalRight, containerLogicalWidth); - - // Solve for 'margin-right' - marginLogicalRightAlias = availableSpace - (logicalLeftValue + logicalRightValue + marginLogicalLeftAlias); + // Solve for right margin + marginLogicalLeftValue = + valueForLength(marginLogicalLeft, containerRelativeLogicalWidth); + marginLogicalRightValue = availableSpace - marginLogicalLeftValue; } else { - // Nothing is 'auto', just calculate the values. - marginLogicalLeftAlias = valueForLength(marginLogicalLeft, containerRelativeLogicalWidth); - marginLogicalRightAlias = valueForLength(marginLogicalRight, containerRelativeLogicalWidth); - logicalRightValue = valueForLength(logicalRight, containerLogicalWidth); - logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth); - // If the containing block is right-to-left, then push the left position as far to the right as possible - if (containerDirection == RTL) { - int totalLogicalWidth = computedValues.m_extent + logicalLeftValue + logicalRightValue + marginLogicalLeftAlias + marginLogicalRightAlias; - logicalLeftValue = containerLogicalWidth - (totalLogicalWidth - logicalLeftValue); - } - } - + // Over-constrained, solve for left if direction is RTL + marginLogicalLeftValue = + valueForLength(marginLogicalLeft, containerRelativeLogicalWidth); + marginLogicalRightValue = + valueForLength(marginLogicalRight, containerRelativeLogicalWidth); + + // Use the containing block's direction rather than the parent block's + // per CSS 2.1 reference test abspos-non-replaced-width-margin-000. + if (containerDirection == RTL) + logicalLeftValue = (availableSpace + logicalLeftValue) - + marginLogicalLeftValue - marginLogicalRightValue; + } + } else { + /*--------------------------------------------------------------------*\ + * Otherwise, set 'auto' values for 'margin-left' and 'margin-right' + * to 0, and pick the one of the following six rules that applies. + * + * 1. 'left' and 'width' are 'auto' and 'right' is not 'auto', then the + * width is shrink-to-fit. Then solve for 'left' + * + * OMIT RULE 2 AS IT SHOULD NEVER BE HIT + * ------------------------------------------------------------------ + * 2. 'left' and 'right' are 'auto' and 'width' is not 'auto', then if + * the 'direction' property of the containing block is 'ltr' set + * 'left' to the static position, otherwise set 'right' to the + * static position. Then solve for 'left' (if 'direction is 'rtl') + * or 'right' (if 'direction' is 'ltr'). + * ------------------------------------------------------------------ + * + * 3. 'width' and 'right' are 'auto' and 'left' is not 'auto', then the + * width is shrink-to-fit . Then solve for 'right' + * 4. 'left' is 'auto', 'width' and 'right' are not 'auto', then solve + * for 'left' + * 5. 'width' is 'auto', 'left' and 'right' are not 'auto', then solve + * for 'width' + * 6. 'right' is 'auto', 'left' and 'width' are not 'auto', then solve + * for 'right' + * + * Calculation of the shrink-to-fit width is similar to calculating the + * width of a table cell using the automatic table layout algorithm. + * Roughly: calculate the preferred width by formatting the content + * without breaking lines other than where explicit line breaks occur, + * and also calculate the preferred minimum width, e.g., by trying all + * possible line breaks. CSS 2.1 does not define the exact algorithm. + * Thirdly, calculate the available width: this is found by solving + * for 'width' after setting 'left' (in case 1) or 'right' (in case 3) + * to 0. + * + * Then the shrink-to-fit width is: + * min(max(preferred minimum width, available width), preferred width). + \*--------------------------------------------------------------------*/ + // NOTE: For rules 3 and 6 it is not necessary to solve for 'right' + // because the value is not used for any further calculations. + + // Calculate margins, 'auto' margins are ignored. + marginLogicalLeftValue = + minimumValueForLength(marginLogicalLeft, containerRelativeLogicalWidth); + marginLogicalRightValue = minimumValueForLength( + marginLogicalRight, containerRelativeLogicalWidth); + + const LayoutUnit availableSpace = + containerLogicalWidth - + (marginLogicalLeftValue + marginLogicalRightValue + bordersPlusPadding); + + // FIXME: Is there a faster way to find the correct case? + // Use rule/case that applies. + if (logicalLeftIsAuto && logicalWidthIsAuto && !logicalRightIsAuto) { + // RULE 1: (use shrink-to-fit for width, and solve of left) + LayoutUnit logicalRightValue = + valueForLength(logicalRight, containerLogicalWidth); + + // FIXME: would it be better to have shrink-to-fit in one step? + LayoutUnit preferredWidth = + maxPreferredLogicalWidth() - bordersPlusPadding; + LayoutUnit preferredMinWidth = + minPreferredLogicalWidth() - bordersPlusPadding; + LayoutUnit availableWidth = availableSpace - logicalRightValue; + computedValues.m_extent = + std::min(std::max(preferredMinWidth, availableWidth), preferredWidth); + logicalLeftValue = + availableSpace - (computedValues.m_extent + logicalRightValue); + } else if (!logicalLeftIsAuto && logicalWidthIsAuto && logicalRightIsAuto) { + // RULE 3: (use shrink-to-fit for width, and no need solve of right) + logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth); + + shrinkToFitWidth(availableSpace, logicalLeftValue, bordersPlusPadding, + computedValues); + } else if (logicalLeftIsAuto && !logicalWidthIsAuto && + !logicalRightIsAuto) { + // RULE 4: (solve for left) + computedValues.m_extent = adjustContentBoxLogicalWidthForBoxSizing( + valueForLength(logicalWidth, containerLogicalWidth)); + logicalLeftValue = availableSpace - + (computedValues.m_extent + + valueForLength(logicalRight, containerLogicalWidth)); + } else if (!logicalLeftIsAuto && logicalWidthIsAuto && + !logicalRightIsAuto) { + // RULE 5: (solve for width) + logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth); + computedValues.m_extent = + availableSpace - + (logicalLeftValue + + valueForLength(logicalRight, containerLogicalWidth)); + } else if (!logicalLeftIsAuto && !logicalWidthIsAuto && + logicalRightIsAuto) { + // RULE 6: (no need solve for right) + logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth); + computedValues.m_extent = adjustContentBoxLogicalWidthForBoxSizing( + valueForLength(logicalWidth, containerLogicalWidth)); + } + } + + // Use computed values to calculate the horizontal position. + + // FIXME: This hack is needed to calculate the logical left position for a + // 'rtl' relatively positioned, inline because right now, it is using the + // logical left position of the first line box when really it should use the + // last line box. When this is fixed elsewhere, this block should be removed. + if (containerBlock->isRenderInline() && + !containerBlock->style()->isLeftToRightDirection()) { + const RenderInline* flow = toRenderInline(containerBlock); + InlineFlowBox* firstLine = flow->firstLineBox(); + InlineFlowBox* lastLine = flow->lastLineBox(); + if (firstLine && lastLine && firstLine != lastLine) { + computedValues.m_position = + logicalLeftValue + marginLogicalLeftValue + + lastLine->borderLogicalLeft() + + (lastLine->logicalLeft() - firstLine->logicalLeft()); + return; + } + } + + computedValues.m_position = logicalLeftValue + marginLogicalLeftValue; + computeLogicalLeftPositionedOffset(computedValues.m_position, this, + computedValues.m_extent, containerBlock, + containerLogicalWidth); +} + +void RenderBox::computePositionedLogicalHeight( + LogicalExtentComputedValues& computedValues) const { + if (isReplaced()) { + computePositionedLogicalHeightReplaced(computedValues); + return; + } + + // The following is based off of the W3C Working Draft from April 11, 2006 of + // CSS 2.1: Section 10.6.4 "Absolutely positioned, non-replaced elements" + // + // (block-style-comments in this function and in + // computePositionedLogicalHeightUsing() correspond to text from the spec) + + // We don't use containingBlock(), since we may be positioned by an enclosing + // relpositioned inline. + const RenderBoxModelObject* containerBlock = + toRenderBoxModelObject(container()); + + const LayoutUnit containerLogicalHeight = + containingBlockLogicalHeightForPositioned(containerBlock); + + RenderStyle* styleToUse = style(); + const LayoutUnit bordersPlusPadding = borderAndPaddingLogicalHeight(); + const Length marginBefore = styleToUse->marginBefore(); + const Length marginAfter = styleToUse->marginAfter(); + Length logicalTopLength = styleToUse->logicalTop(); + Length logicalBottomLength = styleToUse->logicalBottom(); + + /*---------------------------------------------------------------------------*\ + * For the purposes of this section and the next, the term "static position" + * (of an element) refers, roughly, to the position an element would have had + * in the normal flow. More precisely, the static position for 'top' is the + * distance from the top edge of the containing block to the top margin edge + * of a hypothetical box that would have been the first box of the element if + * its 'position' property had been 'static' and 'float' had been 'none'. The + * value is negative if the hypothetical box is above the containing block. + * + * But rather than actually calculating the dimensions of that hypothetical + * box, user agents are free to make a guess at its probable position. + * + * For the purposes of calculating the static position, the containing block + * of fixed positioned elements is the initial containing block instead of + * the viewport. + \*---------------------------------------------------------------------------*/ + + // see FIXME 1 + // Calculate the static distance if needed. + computePositionedStaticDistance(logicalTopLength, logicalBottomLength); + + // Calculate constraint equation values for 'height' case. + LayoutUnit logicalHeight = computedValues.m_extent; + computePositionedLogicalHeightUsing( + styleToUse->logicalHeight(), containerBlock, containerLogicalHeight, + bordersPlusPadding, logicalHeight, logicalTopLength, logicalBottomLength, + marginBefore, marginAfter, computedValues); + + // Avoid doing any work in the common case (where the values of min-height and + // max-height are their defaults). see FIXME 2 + + // Calculate constraint equation values for 'max-height' case. + if (!styleToUse->logicalMaxHeight().isMaxSizeNone()) { + LogicalExtentComputedValues maxValues; + + computePositionedLogicalHeightUsing( + styleToUse->logicalMaxHeight(), containerBlock, containerLogicalHeight, + bordersPlusPadding, logicalHeight, logicalTopLength, + logicalBottomLength, marginBefore, marginAfter, maxValues); + + if (computedValues.m_extent > maxValues.m_extent) { + computedValues.m_extent = maxValues.m_extent; + computedValues.m_position = maxValues.m_position; + computedValues.m_margins.m_before = maxValues.m_margins.m_before; + computedValues.m_margins.m_after = maxValues.m_margins.m_after; + } + } + + // Calculate constraint equation values for 'min-height' case. + if (!styleToUse->logicalMinHeight().isZero() || + styleToUse->logicalMinHeight().isIntrinsic()) { + LogicalExtentComputedValues minValues; + + computePositionedLogicalHeightUsing( + styleToUse->logicalMinHeight(), containerBlock, containerLogicalHeight, + bordersPlusPadding, logicalHeight, logicalTopLength, + logicalBottomLength, marginBefore, marginAfter, minValues); + + if (computedValues.m_extent < minValues.m_extent) { + computedValues.m_extent = minValues.m_extent; + computedValues.m_position = minValues.m_position; + computedValues.m_margins.m_before = minValues.m_margins.m_before; + computedValues.m_margins.m_after = minValues.m_margins.m_after; + } + } + + // Set final height value. + computedValues.m_extent += bordersPlusPadding; +} + +static void computeLogicalTopPositionedOffset( + LayoutUnit& logicalTopPos, + const RenderBox* child, + LayoutUnit logicalHeightValue, + const RenderBoxModelObject* containerBlock, + LayoutUnit containerLogicalHeight) { + // FIXME(sky): Remove + logicalTopPos += containerBlock->borderTop(); +} + +void RenderBox::computePositionedLogicalHeightUsing( + Length logicalHeightLength, + const RenderBoxModelObject* containerBlock, + LayoutUnit containerLogicalHeight, + LayoutUnit bordersPlusPadding, + LayoutUnit logicalHeight, + const Length& logicalTop, + const Length& logicalBottom, + const Length& marginBefore, + const Length& marginAfter, + LogicalExtentComputedValues& computedValues) const { + // 'top' and 'bottom' cannot both be 'auto' because 'top would of been + // converted to the static position in computePositionedLogicalHeight() + ASSERT(!(logicalTop.isAuto() && logicalBottom.isAuto())); + + LayoutUnit logicalHeightValue; + LayoutUnit contentLogicalHeight = logicalHeight - bordersPlusPadding; + + const LayoutUnit containerRelativeLogicalWidth = + containingBlockLogicalWidthForPositioned(containerBlock); + + LayoutUnit logicalTopValue = 0; + + bool logicalHeightIsAuto = logicalHeightLength.isAuto(); + bool logicalTopIsAuto = logicalTop.isAuto(); + bool logicalBottomIsAuto = logicalBottom.isAuto(); + + LayoutUnit resolvedLogicalHeight; + if (logicalHeightLength.isIntrinsic()) + resolvedLogicalHeight = computeIntrinsicLogicalContentHeightUsing( + logicalHeightLength, contentLogicalHeight, bordersPlusPadding); + else + resolvedLogicalHeight = adjustContentBoxLogicalHeightForBoxSizing( + valueForLength(logicalHeightLength, containerLogicalHeight)); + + if (!logicalTopIsAuto && !logicalHeightIsAuto && !logicalBottomIsAuto) { /*-----------------------------------------------------------------------*\ - * 6. If at this point the values are over-constrained, ignore the value - * for either 'left' (in case the 'direction' property of the - * containing block is 'rtl') or 'right' (in case 'direction' is - * 'ltr') and solve for that value. + * If none of the three are 'auto': If both 'margin-top' and 'margin- + * bottom' are 'auto', solve the equation under the extra constraint that + * the two margins get equal values. If one of 'margin-top' or 'margin- + * bottom' is 'auto', solve the equation for that value. If the values + * are over-constrained, ignore the value for 'bottom' and solve for that + * value. \*-----------------------------------------------------------------------*/ - // NOTE: Constraints imposed by the width of the containing block and its content have already been accounted for above. - - // FIXME: Deal with differing writing modes here. Our offset needs to be in the containing block's coordinate space, so that - // can make the result here rather complicated to compute. - - // Use computed values to calculate the horizontal position. - - // FIXME: This hack is needed to calculate the logical left position for a 'rtl' relatively - // positioned, inline containing block because right now, it is using the logical left position - // of the first line box when really it should use the last line box. When - // this is fixed elsewhere, this block should be removed. - if (containerBlock->isRenderInline() && !containerBlock->style()->isLeftToRightDirection()) { - const RenderInline* flow = toRenderInline(containerBlock); - InlineFlowBox* firstLine = flow->firstLineBox(); - InlineFlowBox* lastLine = flow->lastLineBox(); - if (firstLine && lastLine && firstLine != lastLine) { - computedValues.m_position = logicalLeftValue + marginLogicalLeftAlias + lastLine->borderLogicalLeft() + (lastLine->logicalLeft() - firstLine->logicalLeft()); - return; - } - } - - LayoutUnit logicalLeftPos = logicalLeftValue + marginLogicalLeftAlias; - computeLogicalLeftPositionedOffset(logicalLeftPos, this, computedValues.m_extent, containerBlock, containerLogicalWidth); - computedValues.m_position = logicalLeftPos; -} - -void RenderBox::computePositionedLogicalHeightReplaced(LogicalExtentComputedValues& computedValues) const -{ - // The following is based off of the W3C Working Draft from April 11, 2006 of - // CSS 2.1: Section 10.6.5 "Absolutely positioned, replaced elements" - // - // (block-style-comments in this function correspond to text from the spec and - // the numbers correspond to numbers in spec) + // NOTE: It is not necessary to solve for 'bottom' in the over constrained + // case because the value is not used for any further calculations. - // We don't use containingBlock(), since we may be positioned by an enclosing relpositioned inline. - const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container()); + logicalHeightValue = resolvedLogicalHeight; + logicalTopValue = valueForLength(logicalTop, containerLogicalHeight); - const LayoutUnit containerLogicalHeight = containingBlockLogicalHeightForPositioned(containerBlock); - const LayoutUnit containerRelativeLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock); + const LayoutUnit availableSpace = + containerLogicalHeight - + (logicalTopValue + logicalHeightValue + + valueForLength(logicalBottom, containerLogicalHeight) + + bordersPlusPadding); - // Variables to solve. - Length marginBefore = style()->marginBefore(); - Length marginAfter = style()->marginAfter(); - LayoutUnit& marginBeforeAlias = computedValues.m_margins.m_before; - LayoutUnit& marginAfterAlias = computedValues.m_margins.m_after; - - Length logicalTop = style()->logicalTop(); - Length logicalBottom = style()->logicalBottom(); - - /*-----------------------------------------------------------------------*\ - * 1. The used value of 'height' is determined as for inline replaced - * elements. - \*-----------------------------------------------------------------------*/ - // NOTE: This value of height is final in that the min/max height calculations - // are dealt with in computeReplacedHeight(). This means that the steps to produce - // correct max/min in the non-replaced version, are not necessary. - computedValues.m_extent = computeReplacedLogicalHeight() + borderAndPaddingLogicalHeight(); - const LayoutUnit availableSpace = containerLogicalHeight - computedValues.m_extent; + // Margins are now the only unknown + if (marginBefore.isAuto() && marginAfter.isAuto()) { + // Both margins auto, solve for equality + // NOTE: This may result in negative values. + computedValues.m_margins.m_before = + availableSpace / 2; // split the difference + computedValues.m_margins.m_after = + availableSpace - computedValues.m_margins + .m_before; // account for odd valued differences + } else if (marginBefore.isAuto()) { + // Solve for top margin + computedValues.m_margins.m_after = + valueForLength(marginAfter, containerRelativeLogicalWidth); + computedValues.m_margins.m_before = + availableSpace - computedValues.m_margins.m_after; + } else if (marginAfter.isAuto()) { + // Solve for bottom margin + computedValues.m_margins.m_before = + valueForLength(marginBefore, containerRelativeLogicalWidth); + computedValues.m_margins.m_after = + availableSpace - computedValues.m_margins.m_before; + } else { + // Over-constrained, (no need solve for bottom) + computedValues.m_margins.m_before = + valueForLength(marginBefore, containerRelativeLogicalWidth); + computedValues.m_margins.m_after = + valueForLength(marginAfter, containerRelativeLogicalWidth); + } + } else { + /*--------------------------------------------------------------------*\ + * Otherwise, set 'auto' values for 'margin-top' and 'margin-bottom' + * to 0, and pick the one of the following six rules that applies. + * + * 1. 'top' and 'height' are 'auto' and 'bottom' is not 'auto', then + * the height is based on the content, and solve for 'top'. + * + * OMIT RULE 2 AS IT SHOULD NEVER BE HIT + * ------------------------------------------------------------------ + * 2. 'top' and 'bottom' are 'auto' and 'height' is not 'auto', then + * set 'top' to the static position, and solve for 'bottom'. + * ------------------------------------------------------------------ + * + * 3. 'height' and 'bottom' are 'auto' and 'top' is not 'auto', then + * the height is based on the content, and solve for 'bottom'. + * 4. 'top' is 'auto', 'height' and 'bottom' are not 'auto', and + * solve for 'top'. + * 5. 'height' is 'auto', 'top' and 'bottom' are not 'auto', and + * solve for 'height'. + * 6. 'bottom' is 'auto', 'top' and 'height' are not 'auto', and + * solve for 'bottom'. + \*--------------------------------------------------------------------*/ + // NOTE: For rules 3 and 6 it is not necessary to solve for 'bottom' + // because the value is not used for any further calculations. + + // Calculate margins, 'auto' margins are ignored. + computedValues.m_margins.m_before = + minimumValueForLength(marginBefore, containerRelativeLogicalWidth); + computedValues.m_margins.m_after = + minimumValueForLength(marginAfter, containerRelativeLogicalWidth); + + const LayoutUnit availableSpace = + containerLogicalHeight - + (computedValues.m_margins.m_before + computedValues.m_margins.m_after + + bordersPlusPadding); + + // Use rule/case that applies. + if (logicalTopIsAuto && logicalHeightIsAuto && !logicalBottomIsAuto) { + // RULE 1: (height is content based, solve of top) + logicalHeightValue = contentLogicalHeight; + logicalTopValue = availableSpace - + (logicalHeightValue + + valueForLength(logicalBottom, containerLogicalHeight)); + } else if (!logicalTopIsAuto && logicalHeightIsAuto && + logicalBottomIsAuto) { + // RULE 3: (height is content based, no need solve of bottom) + logicalTopValue = valueForLength(logicalTop, containerLogicalHeight); + logicalHeightValue = contentLogicalHeight; + } else if (logicalTopIsAuto && !logicalHeightIsAuto && + !logicalBottomIsAuto) { + // RULE 4: (solve of top) + logicalHeightValue = resolvedLogicalHeight; + logicalTopValue = availableSpace - + (logicalHeightValue + + valueForLength(logicalBottom, containerLogicalHeight)); + } else if (!logicalTopIsAuto && logicalHeightIsAuto && + !logicalBottomIsAuto) { + // RULE 5: (solve of height) + logicalTopValue = valueForLength(logicalTop, containerLogicalHeight); + logicalHeightValue = std::max( + 0, availableSpace - + (logicalTopValue + + valueForLength(logicalBottom, containerLogicalHeight))); + } else if (!logicalTopIsAuto && !logicalHeightIsAuto && + logicalBottomIsAuto) { + // RULE 6: (no need solve of bottom) + logicalHeightValue = resolvedLogicalHeight; + logicalTopValue = valueForLength(logicalTop, containerLogicalHeight); + } + } + computedValues.m_extent = logicalHeightValue; + + // Use computed values to calculate the vertical position. + computedValues.m_position = + logicalTopValue + computedValues.m_margins.m_before; + computeLogicalTopPositionedOffset(computedValues.m_position, this, + logicalHeightValue, containerBlock, + containerLogicalHeight); +} + +void RenderBox::computePositionedLogicalWidthReplaced( + LogicalExtentComputedValues& computedValues) const { + // The following is based off of the W3C Working Draft from April 11, 2006 of + // CSS 2.1: Section 10.3.8 "Absolutely positioned, replaced elements" + // + // (block-style-comments in this function correspond to text from the spec and + // the numbers correspond to numbers in spec) + + // We don't use containingBlock(), since we may be positioned by an enclosing + // relative positioned inline. + const RenderBoxModelObject* containerBlock = + toRenderBoxModelObject(container()); + + const LayoutUnit containerLogicalWidth = + containingBlockLogicalWidthForPositioned(containerBlock); + const LayoutUnit containerRelativeLogicalWidth = + containingBlockLogicalWidthForPositioned(containerBlock); + + // To match WinIE, in quirks mode use the parent's 'direction' property + // instead of the the container block's. + TextDirection containerDirection = containerBlock->style()->direction(); + + // Variables to solve. + Length logicalLeft = style()->logicalLeft(); + Length logicalRight = style()->logicalRight(); + Length marginLogicalLeft = style()->marginLeft(); + Length marginLogicalRight = style()->marginRight(); + LayoutUnit& marginLogicalLeftAlias = style()->isLeftToRightDirection() + ? computedValues.m_margins.m_start + : computedValues.m_margins.m_end; + LayoutUnit& marginLogicalRightAlias = style()->isLeftToRightDirection() + ? computedValues.m_margins.m_end + : computedValues.m_margins.m_start; + + /*-----------------------------------------------------------------------*\ + * 1. The used value of 'width' is determined as for inline replaced + * elements. + \*-----------------------------------------------------------------------*/ + // NOTE: This value of width is final in that the min/max width calculations + // are dealt with in computeReplacedWidth(). This means that the steps to + // produce correct max/min in the non-replaced version, are not necessary. + computedValues.m_extent = + computeReplacedLogicalWidth() + borderAndPaddingLogicalWidth(); + + const LayoutUnit availableSpace = + containerLogicalWidth - computedValues.m_extent; + + /*-----------------------------------------------------------------------*\ + * 2. If both 'left' and 'right' have the value 'auto', then if 'direction' + * of the containing block is 'ltr', set 'left' to the static position; + * else if 'direction' is 'rtl', set 'right' to the static position. + \*-----------------------------------------------------------------------*/ + // see FIXME 1 + computePositionedStaticDistance(logicalLeft, logicalRight); + + /*-----------------------------------------------------------------------*\ + * 3. If 'left' or 'right' are 'auto', replace any 'auto' on 'margin-left' + * or 'margin-right' with '0'. + \*-----------------------------------------------------------------------*/ + if (logicalLeft.isAuto() || logicalRight.isAuto()) { + if (marginLogicalLeft.isAuto()) + marginLogicalLeft.setValue(Fixed, 0); + if (marginLogicalRight.isAuto()) + marginLogicalRight.setValue(Fixed, 0); + } + + /*-----------------------------------------------------------------------*\ + * 4. If at this point both 'margin-left' and 'margin-right' are still + * 'auto', solve the equation under the extra constraint that the two + * margins must get equal values, unless this would make them negative, + * in which case when the direction of the containing block is 'ltr' + * ('rtl'), set 'margin-left' ('margin-right') to zero and solve for + * 'margin-right' ('margin-left'). + \*-----------------------------------------------------------------------*/ + LayoutUnit logicalLeftValue = 0; + LayoutUnit logicalRightValue = 0; + + if (marginLogicalLeft.isAuto() && marginLogicalRight.isAuto()) { + // 'left' and 'right' cannot be 'auto' due to step 3 + ASSERT(!(logicalLeft.isAuto() && logicalRight.isAuto())); - /*-----------------------------------------------------------------------*\ - * 2. If both 'top' and 'bottom' have the value 'auto', replace 'top' - * with the element's static position. - \*-----------------------------------------------------------------------*/ - // see FIXME 1 - computePositionedStaticDistance(logicalTop, logicalBottom); + logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth); + logicalRightValue = valueForLength(logicalRight, containerLogicalWidth); - /*-----------------------------------------------------------------------*\ - * 3. If 'bottom' is 'auto', replace any 'auto' on 'margin-top' or - * 'margin-bottom' with '0'. - \*-----------------------------------------------------------------------*/ - // FIXME: The spec. says that this step should only be taken when bottom is - // auto, but if only top is auto, this makes step 4 impossible. - if (logicalTop.isAuto() || logicalBottom.isAuto()) { - if (marginBefore.isAuto()) - marginBefore.setValue(Fixed, 0); - if (marginAfter.isAuto()) - marginAfter.setValue(Fixed, 0); + LayoutUnit difference = + availableSpace - (logicalLeftValue + logicalRightValue); + if (difference > 0) { + marginLogicalLeftAlias = difference / 2; // split the difference + marginLogicalRightAlias = + difference - + marginLogicalLeftAlias; // account for odd valued differences + } else { + // Use the containing block's direction rather than the parent block's + // per CSS 2.1 reference test abspos-replaced-width-margin-000. + if (containerDirection == LTR) { + marginLogicalLeftAlias = 0; + marginLogicalRightAlias = difference; // will be negative + } else { + marginLogicalLeftAlias = difference; // will be negative + marginLogicalRightAlias = 0; + } } /*-----------------------------------------------------------------------*\ - * 4. If at this point both 'margin-top' and 'margin-bottom' are still - * 'auto', solve the equation under the extra constraint that the two - * margins must get equal values. + * 5. If at this point there is an 'auto' left, solve the equation for + * that value. \*-----------------------------------------------------------------------*/ - LayoutUnit logicalTopValue = 0; - LayoutUnit logicalBottomValue = 0; - - if (marginBefore.isAuto() && marginAfter.isAuto()) { - // 'top' and 'bottom' cannot be 'auto' due to step 2 and 3 combined. - ASSERT(!(logicalTop.isAuto() || logicalBottom.isAuto())); - - logicalTopValue = valueForLength(logicalTop, containerLogicalHeight); - logicalBottomValue = valueForLength(logicalBottom, containerLogicalHeight); - - LayoutUnit difference = availableSpace - (logicalTopValue + logicalBottomValue); - // NOTE: This may result in negative values. - marginBeforeAlias = difference / 2; // split the difference - marginAfterAlias = difference - marginBeforeAlias; // account for odd valued differences + } else if (logicalLeft.isAuto()) { + marginLogicalLeftAlias = + valueForLength(marginLogicalLeft, containerRelativeLogicalWidth); + marginLogicalRightAlias = + valueForLength(marginLogicalRight, containerRelativeLogicalWidth); + logicalRightValue = valueForLength(logicalRight, containerLogicalWidth); + + // Solve for 'left' + logicalLeftValue = + availableSpace - + (logicalRightValue + marginLogicalLeftAlias + marginLogicalRightAlias); + } else if (logicalRight.isAuto()) { + marginLogicalLeftAlias = + valueForLength(marginLogicalLeft, containerRelativeLogicalWidth); + marginLogicalRightAlias = + valueForLength(marginLogicalRight, containerRelativeLogicalWidth); + logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth); + + // Solve for 'right' + logicalRightValue = + availableSpace - + (logicalLeftValue + marginLogicalLeftAlias + marginLogicalRightAlias); + } else if (marginLogicalLeft.isAuto()) { + marginLogicalRightAlias = + valueForLength(marginLogicalRight, containerRelativeLogicalWidth); + logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth); + logicalRightValue = valueForLength(logicalRight, containerLogicalWidth); + + // Solve for 'margin-left' + marginLogicalLeftAlias = + availableSpace - + (logicalLeftValue + logicalRightValue + marginLogicalRightAlias); + } else if (marginLogicalRight.isAuto()) { + marginLogicalLeftAlias = + valueForLength(marginLogicalLeft, containerRelativeLogicalWidth); + logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth); + logicalRightValue = valueForLength(logicalRight, containerLogicalWidth); + + // Solve for 'margin-right' + marginLogicalRightAlias = + availableSpace - + (logicalLeftValue + logicalRightValue + marginLogicalLeftAlias); + } else { + // Nothing is 'auto', just calculate the values. + marginLogicalLeftAlias = + valueForLength(marginLogicalLeft, containerRelativeLogicalWidth); + marginLogicalRightAlias = + valueForLength(marginLogicalRight, containerRelativeLogicalWidth); + logicalRightValue = valueForLength(logicalRight, containerLogicalWidth); + logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth); + // If the containing block is right-to-left, then push the left position as + // far to the right as possible + if (containerDirection == RTL) { + int totalLogicalWidth = computedValues.m_extent + logicalLeftValue + + logicalRightValue + marginLogicalLeftAlias + + marginLogicalRightAlias; + logicalLeftValue = + containerLogicalWidth - (totalLogicalWidth - logicalLeftValue); + } + } + + /*-----------------------------------------------------------------------*\ + * 6. If at this point the values are over-constrained, ignore the value + * for either 'left' (in case the 'direction' property of the + * containing block is 'rtl') or 'right' (in case 'direction' is + * 'ltr') and solve for that value. + \*-----------------------------------------------------------------------*/ + // NOTE: Constraints imposed by the width of the containing block and its + // content have already been accounted for above. + + // FIXME: Deal with differing writing modes here. Our offset needs to be in + // the containing block's coordinate space, so that can make the result here + // rather complicated to compute. + + // Use computed values to calculate the horizontal position. + + // FIXME: This hack is needed to calculate the logical left position for a + // 'rtl' relatively positioned, inline containing block because right now, it + // is using the logical left position of the first line box when really it + // should use the last line box. When this is fixed elsewhere, this block + // should be removed. + if (containerBlock->isRenderInline() && + !containerBlock->style()->isLeftToRightDirection()) { + const RenderInline* flow = toRenderInline(containerBlock); + InlineFlowBox* firstLine = flow->firstLineBox(); + InlineFlowBox* lastLine = flow->lastLineBox(); + if (firstLine && lastLine && firstLine != lastLine) { + computedValues.m_position = + logicalLeftValue + marginLogicalLeftAlias + + lastLine->borderLogicalLeft() + + (lastLine->logicalLeft() - firstLine->logicalLeft()); + return; + } + } + + LayoutUnit logicalLeftPos = logicalLeftValue + marginLogicalLeftAlias; + computeLogicalLeftPositionedOffset(logicalLeftPos, this, + computedValues.m_extent, containerBlock, + containerLogicalWidth); + computedValues.m_position = logicalLeftPos; +} + +void RenderBox::computePositionedLogicalHeightReplaced( + LogicalExtentComputedValues& computedValues) const { + // The following is based off of the W3C Working Draft from April 11, 2006 of + // CSS 2.1: Section 10.6.5 "Absolutely positioned, replaced elements" + // + // (block-style-comments in this function correspond to text from the spec and + // the numbers correspond to numbers in spec) + + // We don't use containingBlock(), since we may be positioned by an enclosing + // relpositioned inline. + const RenderBoxModelObject* containerBlock = + toRenderBoxModelObject(container()); + + const LayoutUnit containerLogicalHeight = + containingBlockLogicalHeightForPositioned(containerBlock); + const LayoutUnit containerRelativeLogicalWidth = + containingBlockLogicalWidthForPositioned(containerBlock); + + // Variables to solve. + Length marginBefore = style()->marginBefore(); + Length marginAfter = style()->marginAfter(); + LayoutUnit& marginBeforeAlias = computedValues.m_margins.m_before; + LayoutUnit& marginAfterAlias = computedValues.m_margins.m_after; + + Length logicalTop = style()->logicalTop(); + Length logicalBottom = style()->logicalBottom(); + + /*-----------------------------------------------------------------------*\ + * 1. The used value of 'height' is determined as for inline replaced + * elements. + \*-----------------------------------------------------------------------*/ + // NOTE: This value of height is final in that the min/max height calculations + // are dealt with in computeReplacedHeight(). This means that the steps to + // produce correct max/min in the non-replaced version, are not necessary. + computedValues.m_extent = + computeReplacedLogicalHeight() + borderAndPaddingLogicalHeight(); + const LayoutUnit availableSpace = + containerLogicalHeight - computedValues.m_extent; + + /*-----------------------------------------------------------------------*\ + * 2. If both 'top' and 'bottom' have the value 'auto', replace 'top' + * with the element's static position. + \*-----------------------------------------------------------------------*/ + // see FIXME 1 + computePositionedStaticDistance(logicalTop, logicalBottom); + + /*-----------------------------------------------------------------------*\ + * 3. If 'bottom' is 'auto', replace any 'auto' on 'margin-top' or + * 'margin-bottom' with '0'. + \*-----------------------------------------------------------------------*/ + // FIXME: The spec. says that this step should only be taken when bottom is + // auto, but if only top is auto, this makes step 4 impossible. + if (logicalTop.isAuto() || logicalBottom.isAuto()) { + if (marginBefore.isAuto()) + marginBefore.setValue(Fixed, 0); + if (marginAfter.isAuto()) + marginAfter.setValue(Fixed, 0); + } + + /*-----------------------------------------------------------------------*\ + * 4. If at this point both 'margin-top' and 'margin-bottom' are still + * 'auto', solve the equation under the extra constraint that the two + * margins must get equal values. + \*-----------------------------------------------------------------------*/ + LayoutUnit logicalTopValue = 0; + LayoutUnit logicalBottomValue = 0; + + if (marginBefore.isAuto() && marginAfter.isAuto()) { + // 'top' and 'bottom' cannot be 'auto' due to step 2 and 3 combined. + ASSERT(!(logicalTop.isAuto() || logicalBottom.isAuto())); + + logicalTopValue = valueForLength(logicalTop, containerLogicalHeight); + logicalBottomValue = valueForLength(logicalBottom, containerLogicalHeight); + + LayoutUnit difference = + availableSpace - (logicalTopValue + logicalBottomValue); + // NOTE: This may result in negative values. + marginBeforeAlias = difference / 2; // split the difference + marginAfterAlias = + difference - marginBeforeAlias; // account for odd valued differences /*-----------------------------------------------------------------------*\ * 5. If at this point there is only one 'auto' left, solve the equation * for that value. \*-----------------------------------------------------------------------*/ - } else if (logicalTop.isAuto()) { - marginBeforeAlias = valueForLength(marginBefore, containerRelativeLogicalWidth); - marginAfterAlias = valueForLength(marginAfter, containerRelativeLogicalWidth); - logicalBottomValue = valueForLength(logicalBottom, containerLogicalHeight); - - // Solve for 'top' - logicalTopValue = availableSpace - (logicalBottomValue + marginBeforeAlias + marginAfterAlias); - } else if (logicalBottom.isAuto()) { - marginBeforeAlias = valueForLength(marginBefore, containerRelativeLogicalWidth); - marginAfterAlias = valueForLength(marginAfter, containerRelativeLogicalWidth); - logicalTopValue = valueForLength(logicalTop, containerLogicalHeight); - - // Solve for 'bottom' - // NOTE: It is not necessary to solve for 'bottom' because we don't ever - // use the value. - } else if (marginBefore.isAuto()) { - marginAfterAlias = valueForLength(marginAfter, containerRelativeLogicalWidth); - logicalTopValue = valueForLength(logicalTop, containerLogicalHeight); - logicalBottomValue = valueForLength(logicalBottom, containerLogicalHeight); - - // Solve for 'margin-top' - marginBeforeAlias = availableSpace - (logicalTopValue + logicalBottomValue + marginAfterAlias); - } else if (marginAfter.isAuto()) { - marginBeforeAlias = valueForLength(marginBefore, containerRelativeLogicalWidth); - logicalTopValue = valueForLength(logicalTop, containerLogicalHeight); - logicalBottomValue = valueForLength(logicalBottom, containerLogicalHeight); - - // Solve for 'margin-bottom' - marginAfterAlias = availableSpace - (logicalTopValue + logicalBottomValue + marginBeforeAlias); - } else { - // Nothing is 'auto', just calculate the values. - marginBeforeAlias = valueForLength(marginBefore, containerRelativeLogicalWidth); - marginAfterAlias = valueForLength(marginAfter, containerRelativeLogicalWidth); - logicalTopValue = valueForLength(logicalTop, containerLogicalHeight); - // NOTE: It is not necessary to solve for 'bottom' because we don't ever - // use the value. - } - - /*-----------------------------------------------------------------------*\ - * 6. If at this point the values are over-constrained, ignore the value - * for 'bottom' and solve for that value. - \*-----------------------------------------------------------------------*/ - // NOTE: It is not necessary to do this step because we don't end up using - // the value of 'bottom' regardless of whether the values are over-constrained - // or not. - - // Use computed values to calculate the vertical position. - LayoutUnit logicalTopPos = logicalTopValue + marginBeforeAlias; - computeLogicalTopPositionedOffset(logicalTopPos, this, computedValues.m_extent, containerBlock, containerLogicalHeight); - computedValues.m_position = logicalTopPos; -} - -LayoutRect RenderBox::localCaretRect(InlineBox* box, int caretOffset, LayoutUnit* extraWidthToEndOfLine) -{ - // VisiblePositions at offsets inside containers either a) refer to the positions before/after - // those containers (tables and select elements) or b) refer to the position inside an empty block. - // They never refer to children. - // FIXME: Paint the carets inside empty blocks differently than the carets before/after elements. - - LayoutRect rect(location(), LayoutSize(caretWidth, height())); - bool ltr = box ? box->isLeftToRightDirection() : style()->isLeftToRightDirection(); - - if ((!caretOffset) ^ ltr) - rect.move(LayoutSize(width() - caretWidth, 0)); - - if (box) { - RootInlineBox& rootBox = box->root(); - LayoutUnit top = rootBox.lineTop(); - rect.setY(top); - rect.setHeight(rootBox.lineBottom() - top); - } - - // If height of box is smaller than font height, use the latter one, - // otherwise the caret might become invisible. - // - // Also, if the box is not a replaced element, always use the font height. - // This prevents the "big caret" bug described in: - // Deleting all content in a document can result in giant tall-as-window insertion point - // - // FIXME: ignoring :first-line, missing good reason to take care of - LayoutUnit fontHeight = style()->fontMetrics().height(); - if (fontHeight > rect.height() || !isReplaced()) - rect.setHeight(fontHeight); - - if (extraWidthToEndOfLine) - *extraWidthToEndOfLine = x() + width() - rect.maxX(); - - // Move to local coords - rect.moveBy(-location()); - - return rect; -} - -PositionWithAffinity RenderBox::positionForPoint(const LayoutPoint& point) -{ - // no children...return this render object's element, if there is one, and offset 0 - RenderObject* firstChild = slowFirstChild(); - if (!firstChild) - return createPositionWithAffinity(caretMinOffset(), DOWNSTREAM); - - // Pass off to the closest child. - LayoutUnit minDist = LayoutUnit::max(); - RenderBox* closestRenderer = 0; - LayoutPoint adjustedPoint = point; - - for (RenderObject* renderObject = firstChild; renderObject; renderObject = renderObject->nextSibling()) { - if (!renderObject->slowFirstChild() && !renderObject->isInline() && !renderObject->isRenderParagraph()) - continue; - - if (!renderObject->isBox()) - continue; - - RenderBox* renderer = toRenderBox(renderObject); - - LayoutUnit top = renderer->borderTop() + renderer->paddingTop() + renderer->y(); - LayoutUnit bottom = top + renderer->contentHeight(); - LayoutUnit left = renderer->borderLeft() + renderer->paddingLeft() + renderer->x(); - LayoutUnit right = left + renderer->contentWidth(); - - if (point.x() <= right && point.x() >= left && point.y() <= top && point.y() >= bottom) - return renderer->positionForPoint(point - renderer->locationOffset()); - - // Find the distance from (x, y) to the box. Split the space around the box into 8 pieces - // and use a different compare depending on which piece (x, y) is in. - LayoutPoint cmp; - if (point.x() > right) { - if (point.y() < top) - cmp = LayoutPoint(right, top); - else if (point.y() > bottom) - cmp = LayoutPoint(right, bottom); - else - cmp = LayoutPoint(right, point.y()); - } else if (point.x() < left) { - if (point.y() < top) - cmp = LayoutPoint(left, top); - else if (point.y() > bottom) - cmp = LayoutPoint(left, bottom); - else - cmp = LayoutPoint(left, point.y()); - } else { - if (point.y() < top) - cmp = LayoutPoint(point.x(), top); - else - cmp = LayoutPoint(point.x(), bottom); - } - - LayoutSize difference = cmp - point; - - LayoutUnit dist = difference.width() * difference.width() + difference.height() * difference.height(); - if (dist < minDist) { - closestRenderer = renderer; - minDist = dist; - } - } - - if (closestRenderer) - return closestRenderer->positionForPoint(adjustedPoint - closestRenderer->locationOffset()); + } else if (logicalTop.isAuto()) { + marginBeforeAlias = + valueForLength(marginBefore, containerRelativeLogicalWidth); + marginAfterAlias = + valueForLength(marginAfter, containerRelativeLogicalWidth); + logicalBottomValue = valueForLength(logicalBottom, containerLogicalHeight); + + // Solve for 'top' + logicalTopValue = availableSpace - (logicalBottomValue + marginBeforeAlias + + marginAfterAlias); + } else if (logicalBottom.isAuto()) { + marginBeforeAlias = + valueForLength(marginBefore, containerRelativeLogicalWidth); + marginAfterAlias = + valueForLength(marginAfter, containerRelativeLogicalWidth); + logicalTopValue = valueForLength(logicalTop, containerLogicalHeight); + + // Solve for 'bottom' + // NOTE: It is not necessary to solve for 'bottom' because we don't ever + // use the value. + } else if (marginBefore.isAuto()) { + marginAfterAlias = + valueForLength(marginAfter, containerRelativeLogicalWidth); + logicalTopValue = valueForLength(logicalTop, containerLogicalHeight); + logicalBottomValue = valueForLength(logicalBottom, containerLogicalHeight); + + // Solve for 'margin-top' + marginBeforeAlias = availableSpace - (logicalTopValue + logicalBottomValue + + marginAfterAlias); + } else if (marginAfter.isAuto()) { + marginBeforeAlias = + valueForLength(marginBefore, containerRelativeLogicalWidth); + logicalTopValue = valueForLength(logicalTop, containerLogicalHeight); + logicalBottomValue = valueForLength(logicalBottom, containerLogicalHeight); + + // Solve for 'margin-bottom' + marginAfterAlias = availableSpace - (logicalTopValue + logicalBottomValue + + marginBeforeAlias); + } else { + // Nothing is 'auto', just calculate the values. + marginBeforeAlias = + valueForLength(marginBefore, containerRelativeLogicalWidth); + marginAfterAlias = + valueForLength(marginAfter, containerRelativeLogicalWidth); + logicalTopValue = valueForLength(logicalTop, containerLogicalHeight); + // NOTE: It is not necessary to solve for 'bottom' because we don't ever + // use the value. + } + + /*-----------------------------------------------------------------------*\ + * 6. If at this point the values are over-constrained, ignore the value + * for 'bottom' and solve for that value. + \*-----------------------------------------------------------------------*/ + // NOTE: It is not necessary to do this step because we don't end up using + // the value of 'bottom' regardless of whether the values are over-constrained + // or not. + + // Use computed values to calculate the vertical position. + LayoutUnit logicalTopPos = logicalTopValue + marginBeforeAlias; + computeLogicalTopPositionedOffset(logicalTopPos, this, + computedValues.m_extent, containerBlock, + containerLogicalHeight); + computedValues.m_position = logicalTopPos; +} + +LayoutRect RenderBox::localCaretRect(InlineBox* box, + int caretOffset, + LayoutUnit* extraWidthToEndOfLine) { + // VisiblePositions at offsets inside containers either a) refer to the + // positions before/after those containers (tables and select elements) or b) + // refer to the position inside an empty block. They never refer to children. + // FIXME: Paint the carets inside empty blocks differently than the carets + // before/after elements. + + LayoutRect rect(location(), LayoutSize(caretWidth, height())); + bool ltr = + box ? box->isLeftToRightDirection() : style()->isLeftToRightDirection(); + + if ((!caretOffset) ^ ltr) + rect.move(LayoutSize(width() - caretWidth, 0)); + + if (box) { + RootInlineBox& rootBox = box->root(); + LayoutUnit top = rootBox.lineTop(); + rect.setY(top); + rect.setHeight(rootBox.lineBottom() - top); + } + + // If height of box is smaller than font height, use the latter one, + // otherwise the caret might become invisible. + // + // Also, if the box is not a replaced element, always use the font height. + // This prevents the "big caret" bug described in: + // Deleting all content in a document can result in + // giant tall-as-window insertion point + // + // FIXME: ignoring :first-line, missing good reason to take care of + LayoutUnit fontHeight = style()->fontMetrics().height(); + if (fontHeight > rect.height() || !isReplaced()) + rect.setHeight(fontHeight); + + if (extraWidthToEndOfLine) + *extraWidthToEndOfLine = x() + width() - rect.maxX(); + + // Move to local coords + rect.moveBy(-location()); + + return rect; +} + +PositionWithAffinity RenderBox::positionForPoint(const LayoutPoint& point) { + // no children...return this render object's element, if there is one, and + // offset 0 + RenderObject* firstChild = slowFirstChild(); + if (!firstChild) return createPositionWithAffinity(caretMinOffset(), DOWNSTREAM); -} - -void RenderBox::addVisualEffectOverflow() -{ - if (!style()->hasVisualOverflowingEffect()) - return; - - // Add in the final overflow with shadows, outsets and outline combined. - LayoutRect visualEffectOverflow = borderBoxRect(); - visualEffectOverflow.expand(computeVisualEffectOverflowExtent()); - addVisualOverflow(visualEffectOverflow); -} - -LayoutBoxExtent RenderBox::computeVisualEffectOverflowExtent() const -{ - ASSERT(style()->hasVisualOverflowingEffect()); - - LayoutUnit top; - LayoutUnit right; - LayoutUnit bottom; - LayoutUnit left; - - if (style()->boxShadow()) { - style()->getBoxShadowExtent(top, right, bottom, left); - // Box shadow extent's top and left are negative when extend to left and top direction, respectively. - // Negate to make them positive. - top = -top; - left = -left; - } - - if (style()->hasOutline()) { - if (style()->outlineStyleIsAuto()) { - // The result focus ring rects are in coordinates of this object's border box. - Vector focusRingRects; - addFocusRingRects(focusRingRects, LayoutPoint(), this); - IntRect rect = unionRect(focusRingRects); - - int outlineSize = GraphicsContext::focusRingOutsetExtent(style()->outlineOffset(), style()->outlineWidth()); - top = std::max(top, -rect.y() + outlineSize); - right = std::max(right, rect.maxX() - width() + outlineSize); - bottom = std::max(bottom, rect.maxY() - height() + outlineSize); - left = std::max(left, -rect.x() + outlineSize); - } else { - LayoutUnit outlineSize = style()->outlineSize(); - top = std::max(top, outlineSize); - right = std::max(right, outlineSize); - bottom = std::max(bottom, outlineSize); - left = std::max(left, outlineSize); - } - } - - return LayoutBoxExtent(top, right, bottom, left); -} - -void RenderBox::addOverflowFromChild(RenderBox* child, const LayoutSize& delta) -{ - // Only propagate layout overflow from the child if the child isn't clipping its overflow. If it is, then - // its overflow is internal to it, and we don't care about it. layoutOverflowRectForPropagation takes care of this - // and just propagates the border box rect instead. - LayoutRect childLayoutOverflowRect = child->layoutOverflowRectForPropagation(); - childLayoutOverflowRect.move(delta); - addLayoutOverflow(childLayoutOverflowRect); - - // Add in visual overflow from the child. Even if the child clips its overflow, it may still - // have visual overflow of its own set from box shadows or reflections. It is unnecessary to propagate this - // overflow if we are clipping our own overflow. - if (child->hasSelfPaintingLayer()) - return; - LayoutRect childVisualOverflowRect = child->visualOverflowRect(); - childVisualOverflowRect.move(delta); - addContentsVisualOverflow(childVisualOverflowRect); -} - -void RenderBox::addLayoutOverflow(const LayoutRect& rect) -{ - LayoutRect clientBox = paddingBoxRect(); - if (clientBox.contains(rect) || rect.isEmpty()) - return; - - // For overflow clip objects, we don't want to propagate overflow into unreachable areas. - LayoutRect overflowRect(rect); - if (hasOverflowClip() || isRenderView()) { - // Overflow is in the block's coordinate space and thus is flipped for horizontal-bt and vertical-rl - // writing modes. At this stage that is actually a simplification, since we can treat horizontal-tb/bt as the same - // and vertical-lr/rl as the same. - bool hasTopOverflow = false; - bool hasLeftOverflow = !style()->isLeftToRightDirection(); - if (isFlexibleBox() && style()->isReverseFlexDirection()) { - RenderFlexibleBox* flexibleBox = toRenderFlexibleBox(this); - if (flexibleBox->isHorizontalFlow()) - hasLeftOverflow = true; - else - hasTopOverflow = true; - } + // Pass off to the closest child. + LayoutUnit minDist = LayoutUnit::max(); + RenderBox* closestRenderer = 0; + LayoutPoint adjustedPoint = point; + + for (RenderObject* renderObject = firstChild; renderObject; + renderObject = renderObject->nextSibling()) { + if (!renderObject->slowFirstChild() && !renderObject->isInline() && + !renderObject->isRenderParagraph()) + continue; + + if (!renderObject->isBox()) + continue; + + RenderBox* renderer = toRenderBox(renderObject); + + LayoutUnit top = + renderer->borderTop() + renderer->paddingTop() + renderer->y(); + LayoutUnit bottom = top + renderer->contentHeight(); + LayoutUnit left = + renderer->borderLeft() + renderer->paddingLeft() + renderer->x(); + LayoutUnit right = left + renderer->contentWidth(); + + if (point.x() <= right && point.x() >= left && point.y() <= top && + point.y() >= bottom) + return renderer->positionForPoint(point - renderer->locationOffset()); + + // Find the distance from (x, y) to the box. Split the space around the box + // into 8 pieces and use a different compare depending on which piece (x, y) + // is in. + LayoutPoint cmp; + if (point.x() > right) { + if (point.y() < top) + cmp = LayoutPoint(right, top); + else if (point.y() > bottom) + cmp = LayoutPoint(right, bottom); + else + cmp = LayoutPoint(right, point.y()); + } else if (point.x() < left) { + if (point.y() < top) + cmp = LayoutPoint(left, top); + else if (point.y() > bottom) + cmp = LayoutPoint(left, bottom); + else + cmp = LayoutPoint(left, point.y()); + } else { + if (point.y() < top) + cmp = LayoutPoint(point.x(), top); + else + cmp = LayoutPoint(point.x(), bottom); + } + + LayoutSize difference = cmp - point; + + LayoutUnit dist = difference.width() * difference.width() + + difference.height() * difference.height(); + if (dist < minDist) { + closestRenderer = renderer; + minDist = dist; + } + } + + if (closestRenderer) + return closestRenderer->positionForPoint(adjustedPoint - + closestRenderer->locationOffset()); + return createPositionWithAffinity(caretMinOffset(), DOWNSTREAM); +} + +void RenderBox::addVisualEffectOverflow() { + if (!style()->hasVisualOverflowingEffect()) + return; + + // Add in the final overflow with shadows, outsets and outline combined. + LayoutRect visualEffectOverflow = borderBoxRect(); + visualEffectOverflow.expand(computeVisualEffectOverflowExtent()); + addVisualOverflow(visualEffectOverflow); +} + +LayoutBoxExtent RenderBox::computeVisualEffectOverflowExtent() const { + ASSERT(style()->hasVisualOverflowingEffect()); + + LayoutUnit top; + LayoutUnit right; + LayoutUnit bottom; + LayoutUnit left; + + if (style()->boxShadow()) { + style()->getBoxShadowExtent(top, right, bottom, left); + + // Box shadow extent's top and left are negative when extend to left and top + // direction, respectively. Negate to make them positive. + top = -top; + left = -left; + } + + if (style()->hasOutline()) { + if (style()->outlineStyleIsAuto()) { + // The result focus ring rects are in coordinates of this object's border + // box. + Vector focusRingRects; + addFocusRingRects(focusRingRects, LayoutPoint(), this); + IntRect rect = unionRect(focusRingRects); + + int outlineSize = GraphicsContext::focusRingOutsetExtent( + style()->outlineOffset(), style()->outlineWidth()); + top = std::max(top, -rect.y() + outlineSize); + right = std::max(right, rect.maxX() - width() + outlineSize); + bottom = + std::max(bottom, rect.maxY() - height() + outlineSize); + left = std::max(left, -rect.x() + outlineSize); + } else { + LayoutUnit outlineSize = style()->outlineSize(); + top = std::max(top, outlineSize); + right = std::max(right, outlineSize); + bottom = std::max(bottom, outlineSize); + left = std::max(left, outlineSize); + } + } + + return LayoutBoxExtent(top, right, bottom, left); +} + +void RenderBox::addOverflowFromChild(RenderBox* child, + const LayoutSize& delta) { + // Only propagate layout overflow from the child if the child isn't clipping + // its overflow. If it is, then its overflow is internal to it, and we don't + // care about it. layoutOverflowRectForPropagation takes care of this and + // just propagates the border box rect instead. + LayoutRect childLayoutOverflowRect = + child->layoutOverflowRectForPropagation(); + childLayoutOverflowRect.move(delta); + addLayoutOverflow(childLayoutOverflowRect); + + // Add in visual overflow from the child. Even if the child clips its + // overflow, it may still have visual overflow of its own set from box shadows + // or reflections. It is unnecessary to propagate this overflow if we are + // clipping our own overflow. + if (child->hasSelfPaintingLayer()) + return; + LayoutRect childVisualOverflowRect = child->visualOverflowRect(); + childVisualOverflowRect.move(delta); + addContentsVisualOverflow(childVisualOverflowRect); +} + +void RenderBox::addLayoutOverflow(const LayoutRect& rect) { + LayoutRect clientBox = paddingBoxRect(); + if (clientBox.contains(rect) || rect.isEmpty()) + return; + + // For overflow clip objects, we don't want to propagate overflow into + // unreachable areas. + LayoutRect overflowRect(rect); + if (hasOverflowClip() || isRenderView()) { + // Overflow is in the block's coordinate space and thus is flipped for + // horizontal-bt and vertical-rl writing modes. At this stage that is + // actually a simplification, since we can treat horizontal-tb/bt as the + // same and vertical-lr/rl as the same. + bool hasTopOverflow = false; + bool hasLeftOverflow = !style()->isLeftToRightDirection(); + if (isFlexibleBox() && style()->isReverseFlexDirection()) { + RenderFlexibleBox* flexibleBox = toRenderFlexibleBox(this); + if (flexibleBox->isHorizontalFlow()) + hasLeftOverflow = true; + else + hasTopOverflow = true; + } + + if (!hasTopOverflow) + overflowRect.shiftYEdgeTo(std::max(overflowRect.y(), clientBox.y())); + else + overflowRect.shiftMaxYEdgeTo( + std::min(overflowRect.maxY(), clientBox.maxY())); + if (!hasLeftOverflow) + overflowRect.shiftXEdgeTo(std::max(overflowRect.x(), clientBox.x())); + else + overflowRect.shiftMaxXEdgeTo( + std::min(overflowRect.maxX(), clientBox.maxX())); - if (!hasTopOverflow) - overflowRect.shiftYEdgeTo(std::max(overflowRect.y(), clientBox.y())); - else - overflowRect.shiftMaxYEdgeTo(std::min(overflowRect.maxY(), clientBox.maxY())); - if (!hasLeftOverflow) - overflowRect.shiftXEdgeTo(std::max(overflowRect.x(), clientBox.x())); - else - overflowRect.shiftMaxXEdgeTo(std::min(overflowRect.maxX(), clientBox.maxX())); - - // Now re-test with the adjusted rectangle and see if it has become unreachable or fully - // contained. - if (clientBox.contains(overflowRect) || overflowRect.isEmpty()) - return; - } + // Now re-test with the adjusted rectangle and see if it has become + // unreachable or fully contained. + if (clientBox.contains(overflowRect) || overflowRect.isEmpty()) + return; + } - if (!m_overflow) - m_overflow = adoptPtr(new RenderOverflow(clientBox, borderBoxRect())); + if (!m_overflow) + m_overflow = adoptPtr(new RenderOverflow(clientBox, borderBoxRect())); - m_overflow->addLayoutOverflow(overflowRect); + m_overflow->addLayoutOverflow(overflowRect); } -void RenderBox::addVisualOverflow(const LayoutRect& rect) -{ - LayoutRect borderBox = borderBoxRect(); - if (borderBox.contains(rect) || rect.isEmpty()) - return; +void RenderBox::addVisualOverflow(const LayoutRect& rect) { + LayoutRect borderBox = borderBoxRect(); + if (borderBox.contains(rect) || rect.isEmpty()) + return; - if (!m_overflow) - m_overflow = adoptPtr(new RenderOverflow(paddingBoxRect(), borderBox)); + if (!m_overflow) + m_overflow = adoptPtr(new RenderOverflow(paddingBoxRect(), borderBox)); - m_overflow->addVisualOverflow(rect); + m_overflow->addVisualOverflow(rect); } -void RenderBox::addContentsVisualOverflow(const LayoutRect& rect) -{ - if (!hasOverflowClip()) { - addVisualOverflow(rect); - return; - } +void RenderBox::addContentsVisualOverflow(const LayoutRect& rect) { + if (!hasOverflowClip()) { + addVisualOverflow(rect); + return; + } - if (!m_overflow) - m_overflow = adoptPtr(new RenderOverflow(paddingBoxRect(), borderBoxRect())); - m_overflow->addContentsVisualOverflow(rect); + if (!m_overflow) + m_overflow = + adoptPtr(new RenderOverflow(paddingBoxRect(), borderBoxRect())); + m_overflow->addContentsVisualOverflow(rect); } -void RenderBox::clearLayoutOverflow() -{ - if (!m_overflow) - return; +void RenderBox::clearLayoutOverflow() { + if (!m_overflow) + return; - if (!hasVisualOverflow() && contentsVisualOverflowRect().isEmpty()) { - clearAllOverflows(); - return; - } + if (!hasVisualOverflow() && contentsVisualOverflowRect().isEmpty()) { + clearAllOverflows(); + return; + } - m_overflow->setLayoutOverflow(paddingBoxRect()); + m_overflow->setLayoutOverflow(paddingBoxRect()); } -LayoutUnit RenderBox::lineHeight(bool /*firstLine*/, LineDirectionMode direction, LinePositionMode /*linePositionMode*/) const -{ - if (isReplaced()) - return direction == HorizontalLine ? m_marginBox.top() + height() + m_marginBox.bottom() : m_marginBox.right() + width() + m_marginBox.left(); - return 0; +LayoutUnit RenderBox::lineHeight(bool /*firstLine*/, + LineDirectionMode direction, + LinePositionMode /*linePositionMode*/) const { + if (isReplaced()) + return direction == HorizontalLine + ? m_marginBox.top() + height() + m_marginBox.bottom() + : m_marginBox.right() + width() + m_marginBox.left(); + return 0; } -int RenderBox::baselinePosition(FontBaseline baselineType, bool /*firstLine*/, LineDirectionMode direction, LinePositionMode linePositionMode) const -{ - ASSERT(linePositionMode == PositionOnContainingLine); - if (isReplaced()) { - int result = direction == HorizontalLine ? m_marginBox.top() + height() + m_marginBox.bottom() : m_marginBox.right() + width() + m_marginBox.left(); - if (baselineType == AlphabeticBaseline) - return result; - return result - result / 2; - } - return 0; +int RenderBox::baselinePosition(FontBaseline baselineType, + bool /*firstLine*/, + LineDirectionMode direction, + LinePositionMode linePositionMode) const { + ASSERT(linePositionMode == PositionOnContainingLine); + if (isReplaced()) { + int result = direction == HorizontalLine + ? m_marginBox.top() + height() + m_marginBox.bottom() + : m_marginBox.right() + width() + m_marginBox.left(); + if (baselineType == AlphabeticBaseline) + return result; + return result - result / 2; + } + return 0; } - -RenderLayer* RenderBox::enclosingFloatPaintingLayer() const -{ - const RenderObject* curr = this; - while (curr) { - RenderLayer* layer = curr->hasLayer() && curr->isBox() ? toRenderBox(curr)->layer() : 0; - if (layer && layer->isSelfPaintingLayer()) - return layer; - curr = curr->parent(); - } - return 0; +RenderLayer* RenderBox::enclosingFloatPaintingLayer() const { + const RenderObject* curr = this; + while (curr) { + RenderLayer* layer = + curr->hasLayer() && curr->isBox() ? toRenderBox(curr)->layer() : 0; + if (layer && layer->isSelfPaintingLayer()) + return layer; + curr = curr->parent(); + } + return 0; } -LayoutRect RenderBox::layoutOverflowRectForPropagation() const -{ - // Only propagate interior layout overflow if we don't clip it. - LayoutRect rect = borderBoxRect(); - rect.expand(LayoutSize(LayoutUnit(), marginAfter())); +LayoutRect RenderBox::layoutOverflowRectForPropagation() const { + // Only propagate interior layout overflow if we don't clip it. + LayoutRect rect = borderBoxRect(); + rect.expand(LayoutSize(LayoutUnit(), marginAfter())); - if (!hasOverflowClip()) - rect.unite(layoutOverflowRect()); + if (!hasOverflowClip()) + rect.unite(layoutOverflowRect()); - if (transform()) - rect = transform()->mapRect(rect); + if (transform()) + rect = transform()->mapRect(rect); - return rect; + return rect; } -LayoutUnit RenderBox::offsetLeft() const -{ - return adjustedPositionRelativeToOffsetParent(location()).x(); +LayoutUnit RenderBox::offsetLeft() const { + return adjustedPositionRelativeToOffsetParent(location()).x(); } -LayoutUnit RenderBox::offsetTop() const -{ - return adjustedPositionRelativeToOffsetParent(location()).y(); +LayoutUnit RenderBox::offsetTop() const { + return adjustedPositionRelativeToOffsetParent(location()).y(); } -bool RenderBox::hasRelativeLogicalHeight() const -{ - return style()->logicalHeight().isPercent() - || style()->logicalMinHeight().isPercent() - || style()->logicalMaxHeight().isPercent(); +bool RenderBox::hasRelativeLogicalHeight() const { + return style()->logicalHeight().isPercent() || + style()->logicalMinHeight().isPercent() || + style()->logicalMaxHeight().isPercent(); } -RenderBox::BoxDecorationData::BoxDecorationData(const RenderStyle& style) -{ - backgroundColor = style.resolveColor(style.backgroundColor()); - hasBackground = backgroundColor.alpha() || style.hasBackgroundImage(); - ASSERT(hasBackground == style.hasBackground()); - hasBorder = style.hasBorder(); +RenderBox::BoxDecorationData::BoxDecorationData(const RenderStyle& style) { + backgroundColor = style.resolveColor(style.backgroundColor()); + hasBackground = backgroundColor.alpha() || style.hasBackgroundImage(); + ASSERT(hasBackground == style.hasBackground()); + hasBorder = style.hasBorder(); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/core/rendering/RenderBox.h b/sky/engine/core/rendering/RenderBox.h index 09c8efc2ff5fa..52e9a6999311b 100644 --- a/sky/engine/core/rendering/RenderBox.h +++ b/sky/engine/core/rendering/RenderBox.h @@ -34,7 +34,10 @@ class HitTestingTransformState; class TransformationMatrix; enum SizeType { MainOrPreferredSize, MinSize, MaxSize }; -enum AvailableLogicalHeightType { ExcludeMarginBorderPadding, IncludeMarginBorderPadding }; +enum AvailableLogicalHeightType { + ExcludeMarginBorderPadding, + IncludeMarginBorderPadding +}; enum MarginDirection { BlockDirection, InlineDirection }; enum ShouldComputePreferred { ComputeActual, ComputePreferred }; @@ -42,586 +45,781 @@ enum ShouldComputePreferred { ComputeActual, ComputePreferred }; enum ContentsClipBehavior { ForceContentsClip, SkipContentsClipIfPossible }; struct RenderBoxRareData { - WTF_MAKE_NONCOPYABLE(RenderBoxRareData); WTF_MAKE_FAST_ALLOCATED; -public: - RenderBoxRareData() - : m_inlineBoxWrapper(0) - , m_overrideLogicalContentHeight(-1) - , m_overrideLogicalContentWidth(-1) - { - } - - // For inline replaced elements, the inline box that owns us. - InlineBox* m_inlineBoxWrapper; - LayoutUnit m_overrideLogicalContentHeight; - LayoutUnit m_overrideLogicalContentWidth; + WTF_MAKE_NONCOPYABLE(RenderBoxRareData); + WTF_MAKE_FAST_ALLOCATED; + + public: + RenderBoxRareData() + : m_inlineBoxWrapper(0), + m_overrideLogicalContentHeight(-1), + m_overrideLogicalContentWidth(-1) {} + + // For inline replaced elements, the inline box that owns us. + InlineBox* m_inlineBoxWrapper; + LayoutUnit m_overrideLogicalContentHeight; + LayoutUnit m_overrideLogicalContentWidth; }; enum LayerType { - NoLayer, - NormalLayer, - // An overflow clip layer is required for bookkeeping purposes, - // but does not force a layer to be self painting. - OverflowClipLayer, + NoLayer, + NormalLayer, + // An overflow clip layer is required for bookkeeping purposes, + // but does not force a layer to be self painting. + OverflowClipLayer, }; struct FontBaselineOrAuto { - FontBaselineOrAuto() - : m_auto(true) - , m_baseline(AlphabeticBaseline) - { - } - FontBaselineOrAuto(FontBaseline baseline) - : m_auto(false) - , m_baseline(baseline) - { - } - bool m_auto; - FontBaseline m_baseline; + FontBaselineOrAuto() : m_auto(true), m_baseline(AlphabeticBaseline) {} + FontBaselineOrAuto(FontBaseline baseline) + : m_auto(false), m_baseline(baseline) {} + bool m_auto; + FontBaseline m_baseline; }; class RenderBox : public RenderBoxModelObject { -public: - explicit RenderBox(); - - // hasAutoZIndex only returns true if the element is positioned or a flex-item since - // position:static elements that are not flex-items get their z-index coerced to auto. - virtual LayerType layerTypeRequired() const - { - if (isPositioned() || createsGroup() || hasClipPath() || hasTransform() || !style()->hasAutoZIndex()) - return NormalLayer; - if (hasOverflowClip()) - return OverflowClipLayer; - return NoLayer; - } - - void destroyLayer(); - void createLayer(LayerType); - bool hasSelfPaintingLayer() const; - RenderLayer* layer() const { return m_layer.get(); } - - // Use this with caution! No type checking is done! - RenderBox* firstChildBox() const; - RenderBox* lastChildBox() const; - - LayoutUnit x() const { return m_frameRect.x(); } - LayoutUnit y() const { return m_frameRect.y(); } - LayoutUnit width() const { return m_frameRect.width(); } - LayoutUnit height() const { return m_frameRect.height(); } - - int pixelSnappedWidth() const { return m_frameRect.pixelSnappedWidth(); } - int pixelSnappedHeight() const { return m_frameRect.pixelSnappedHeight(); } - - // These represent your location relative to your container as a physical offset. - // In layout related methods you almost always want the logical location (e.g. x() and y()). - LayoutUnit top() const { return location().y(); } - LayoutUnit left() const { return location().x(); } - - void setX(LayoutUnit x) { m_frameRect.setX(x); } - void setY(LayoutUnit y) { m_frameRect.setY(y); } - void setWidth(LayoutUnit width) { m_frameRect.setWidth(width); } - void setHeight(LayoutUnit height) { m_frameRect.setHeight(height); } - - LayoutUnit logicalLeft() const { return x(); } - LayoutUnit logicalRight() const { return logicalLeft() + logicalWidth(); } - LayoutUnit logicalTop() const { return y(); } - LayoutUnit logicalBottom() const { return logicalTop() + logicalHeight(); } - LayoutUnit logicalWidth() const { return width(); } - LayoutUnit logicalHeight() const { return height(); } - - LayoutUnit constrainLogicalWidthByMinMax(LayoutUnit, LayoutUnit, RenderBlock*) const; - LayoutUnit constrainLogicalHeightByMinMax(LayoutUnit logicalHeight, LayoutUnit intrinsicContentHeight) const; - LayoutUnit constrainContentBoxLogicalHeightByMinMax(LayoutUnit logicalHeight, LayoutUnit intrinsicContentHeight) const; - - int pixelSnappedLogicalHeight() const { return pixelSnappedHeight(); } - int pixelSnappedLogicalWidth() const { return pixelSnappedWidth(); } - - void setLogicalLeft(LayoutUnit left) - { - setX(left); - } - void setLogicalTop(LayoutUnit top) - { - setY(top); - } - void setLogicalLocation(const LayoutPoint& location) - { - setLocation(location); - } - void setLogicalWidth(LayoutUnit size) - { - setWidth(size); - } - void setLogicalHeight(LayoutUnit size) - { - setHeight(size); - } - void setLogicalSize(const LayoutSize& size) - { - setSize(size); - } - - LayoutPoint location() const { return m_frameRect.location(); } - LayoutSize locationOffset() const { return LayoutSize(x(), y()); } - LayoutSize size() const { return m_frameRect.size(); } - IntSize pixelSnappedSize() const { return m_frameRect.pixelSnappedSize(); } - - void setLocation(const LayoutPoint& location) { m_frameRect.setLocation(location); } - - void setSize(const LayoutSize& size) { m_frameRect.setSize(size); } - void move(LayoutUnit dx, LayoutUnit dy) { m_frameRect.move(dx, dy); } - - LayoutRect frameRect() const { return m_frameRect; } - IntRect pixelSnappedFrameRect() const { return pixelSnappedIntRect(m_frameRect); } - void setFrameRect(const LayoutRect& rect) { m_frameRect = rect; } - - LayoutRect borderBoxRect() const { return LayoutRect(LayoutPoint(), size()); } - LayoutRect paddingBoxRect() const { return LayoutRect(borderLeft(), borderTop(), clientWidth(), clientHeight()); } - IntRect pixelSnappedBorderBoxRect() const { return IntRect(IntPoint(), m_frameRect.pixelSnappedSize()); } - virtual IntRect borderBoundingBox() const override final { return pixelSnappedBorderBoxRect(); } - - // The content area of the box (excludes padding - and intrinsic padding for table cells, etc... - and border). - LayoutRect contentBoxRect() const { return LayoutRect(borderLeft() + paddingLeft(), borderTop() + paddingTop(), contentWidth(), contentHeight()); } - // The content box in absolute coords. Ignores transforms. - IntRect absoluteContentBox() const; - // The content box converted to absolute coords (taking transforms into account). - FloatQuad absoluteContentQuad() const; - - FloatPoint perspectiveOrigin() const; - - // This returns the content area of the box (excluding padding and border). The only difference with contentBoxRect is that computedCSSContentBoxRect - // does include the intrinsic padding in the content box as this is what some callers expect (like getComputedStyle). - LayoutRect computedCSSContentBoxRect() const { return LayoutRect(borderLeft() + computedCSSPaddingLeft(), borderTop() + computedCSSPaddingTop(), clientWidth() - computedCSSPaddingLeft() - computedCSSPaddingRight(), clientHeight() - computedCSSPaddingTop() - computedCSSPaddingBottom()); } - - virtual void addFocusRingRects(Vector&, const LayoutPoint& additionalOffset, const RenderBox* paintContainer = 0) const override; - - // Use this with caution! No type checking is done! - RenderBox* previousSiblingBox() const; - RenderBox* nextSiblingBox() const; - RenderBox* parentBox() const; - - // Visual and layout overflow are in the coordinate space of the box. This means that they aren't purely physical directions. - // For horizontal-tb and vertical-lr they will match physical directions, but for horizontal-bt and vertical-rl, the top/bottom and left/right - // respectively are flipped when compared to their physical counterparts. For example minX is on the left in vertical-lr, - // but it is on the right in vertical-rl. - LayoutRect layoutOverflowRect() const { return m_overflow ? m_overflow->layoutOverflowRect() : paddingBoxRect(); } - IntRect pixelSnappedLayoutOverflowRect() const { return pixelSnappedIntRect(layoutOverflowRect()); } - LayoutSize maxLayoutOverflow() const { return LayoutSize(layoutOverflowRect().maxX(), layoutOverflowRect().maxY()); } - LayoutUnit logicalLefxlayoutOverflow() const { return layoutOverflowRect().x(); } - LayoutUnit logicalRightLayoutOverflow() const { return layoutOverflowRect().maxX(); } - - virtual LayoutRect visualOverflowRect() const { return m_overflow ? m_overflow->visualOverflowRect() : borderBoxRect(); } - LayoutUnit logicalLeftVisualOverflow() const { return visualOverflowRect().x(); } - LayoutUnit logicalRightVisualOverflow() const { return visualOverflowRect().maxX(); } - - LayoutRect contentsVisualOverflowRect() const { return m_overflow ? m_overflow->contentsVisualOverflowRect() : LayoutRect(); } - - void addLayoutOverflow(const LayoutRect&); - void addVisualOverflow(const LayoutRect&); - - // Clipped by the contents clip, if one exists. - void addContentsVisualOverflow(const LayoutRect&); - - void addVisualEffectOverflow(); - LayoutBoxExtent computeVisualEffectOverflowExtent() const; - void addOverflowFromChild(RenderBox* child) { addOverflowFromChild(child, child->locationOffset()); } - void addOverflowFromChild(RenderBox* child, const LayoutSize& delta); - void clearLayoutOverflow(); - void clearAllOverflows() { m_overflow.clear(); } - - void updateLayerTransformAfterLayout(); - - // This transform has the transform-origin baked in. - TransformationMatrix* transform() const { return m_transform.get(); } - bool has3DTransform() const { return m_transform && !m_transform->isAffine(); } - - LayoutUnit contentWidth() const { return clientWidth() - paddingLeft() - paddingRight(); } - LayoutUnit contentHeight() const { return clientHeight() - paddingTop() - paddingBottom(); } - LayoutUnit contentLogicalWidth() const { return contentWidth(); } - LayoutUnit contentLogicalHeight() const { return contentHeight(); } - - // IE extensions. Used to calculate offsetWidth/Height. Overridden by inlines (RenderFlow) - // to return the remaining width on a given line (and the height of a single line). - virtual LayoutUnit offsetWidth() const override { return width(); } - virtual LayoutUnit offsetHeight() const override { return height(); } - - virtual int pixelSnappedOffsetWidth() const override final; - virtual int pixelSnappedOffsetHeight() const override final; - - // More IE extensions. clientWidth and clientHeight represent the interior of an object - // excluding border and scrollbar. clientLeft/Top are just the borderLeftWidth and borderTopWidth. - LayoutUnit clientLeft() const { return borderLeft(); } - LayoutUnit clientTop() const { return borderTop(); } - LayoutUnit clientWidth() const; - LayoutUnit clientHeight() const; - LayoutUnit clientLogicalWidth() const { return clientWidth(); } - LayoutUnit clientLogicalHeight() const { return clientHeight(); } - LayoutUnit clientLogicalBottom() const { return borderBefore() + clientLogicalHeight(); } - LayoutRect clientBoxRect() const { return LayoutRect(clientLeft(), clientTop(), clientWidth(), clientHeight()); } - - int pixelSnappedClientWidth() const; - int pixelSnappedClientHeight() const; - - virtual LayoutUnit marginTop() const override { return m_marginBox.top(); } - virtual LayoutUnit marginBottom() const override { return m_marginBox.bottom(); } - virtual LayoutUnit marginLeft() const override { return m_marginBox.left(); } - virtual LayoutUnit marginRight() const override { return m_marginBox.right(); } - void setMarginTop(LayoutUnit margin) { m_marginBox.setTop(margin); } - void setMarginBottom(LayoutUnit margin) { m_marginBox.setBottom(margin); } - void setMarginLeft(LayoutUnit margin) { m_marginBox.setLeft(margin); } - void setMarginRight(LayoutUnit margin) { m_marginBox.setRight(margin); } - - LayoutUnit marginLogicalLeft() const { return m_marginBox.logicalLeft(); } - LayoutUnit marginLogicalRight() const { return m_marginBox.logicalRight(); } - - virtual LayoutUnit marginBefore(const RenderStyle* overrideStyle = 0) const override final { return m_marginBox.before(); } - virtual LayoutUnit marginAfter(const RenderStyle* overrideStyle = 0) const override final { return m_marginBox.after(); } - virtual LayoutUnit marginStart(const RenderStyle* overrideStyle = 0) const override final - { - const RenderStyle* styleToUse = overrideStyle ? overrideStyle : style(); - return m_marginBox.start(styleToUse->direction()); - } - virtual LayoutUnit marginEnd(const RenderStyle* overrideStyle = 0) const override final - { - const RenderStyle* styleToUse = overrideStyle ? overrideStyle : style(); - return m_marginBox.end(styleToUse->direction()); - } - void setMarginBefore(LayoutUnit value, const RenderStyle* overrideStyle = 0) { m_marginBox.setBefore(value); } - void setMarginAfter(LayoutUnit value, const RenderStyle* overrideStyle = 0) { m_marginBox.setAfter(value); } - void setMarginStart(LayoutUnit value, const RenderStyle* overrideStyle = 0) - { - const RenderStyle* styleToUse = overrideStyle ? overrideStyle : style(); - m_marginBox.setStart(styleToUse->direction(), value); - } - void setMarginEnd(LayoutUnit value, const RenderStyle* overrideStyle = 0) - { - const RenderStyle* styleToUse = overrideStyle ? overrideStyle : style(); - m_marginBox.setEnd(styleToUse->direction(), value); - } - - virtual void absoluteQuads(Vector&) const override; - - bool hitTestLayer(RenderLayer* rootLayer, RenderLayer* containerLayer, - const HitTestRequest& request, HitTestResult& result, - const LayoutRect& hitTestRect, const HitTestLocation& hitTestLocation, - const HitTestingTransformState* transformState = 0, double* zOffset = 0); - - void paintLayer(GraphicsContext*, const LayerPaintingInfo&); - - virtual void layout() override; - virtual void paint(PaintInfo&, const LayoutPoint&, Vector& layers) override; - virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset) override; - - virtual LayoutUnit minPreferredLogicalWidth() const override; - virtual LayoutUnit maxPreferredLogicalWidth() const override; - - void setMinPreferredLogicalWidth(LayoutUnit); - void setMaxPreferredLogicalWidth(LayoutUnit); - - // FIXME: We should rename these back to overrideLogicalHeight/Width and have them store - // the border-box height/width like the regular height/width accessors on RenderBox. - // Right now, these are different than contentHeight/contentWidth because they still - // include the scrollbar height/width. - LayoutUnit overrideLogicalContentWidth() const; - LayoutUnit overrideLogicalContentHeight() const; - bool hasOverrideHeight() const; - bool hasOverrideWidth() const; - void setOverrideLogicalContentHeight(LayoutUnit); - void setOverrideLogicalContentWidth(LayoutUnit); - void clearOverrideSize(); - void clearOverrideLogicalContentHeight(); - void clearOverrideLogicalContentWidth(); - - virtual LayoutSize offsetFromContainer(const RenderObject*, const LayoutPoint&, bool* offsetDependsOnPoint = 0) const override; - - LayoutUnit adjustBorderBoxLogicalWidthForBoxSizing(LayoutUnit width) const; - LayoutUnit adjustBorderBoxLogicalHeightForBoxSizing(LayoutUnit height) const; - LayoutUnit adjustContentBoxLogicalWidthForBoxSizing(LayoutUnit width) const; - LayoutUnit adjustContentBoxLogicalHeightForBoxSizing(LayoutUnit height) const; - - struct ComputedMarginValues { - ComputedMarginValues() { } - - LayoutUnit m_before; - LayoutUnit m_after; - LayoutUnit m_start; - LayoutUnit m_end; - }; - struct LogicalExtentComputedValues { - LogicalExtentComputedValues() { } - - LayoutUnit m_extent; - LayoutUnit m_position; - ComputedMarginValues m_margins; - }; - // Resolve auto margins in the chosen direction of the containing block so that objects can be pushed to the start, middle or end - // of the containing block. - void computeMarginsForDirection(MarginDirection forDirection, const RenderBlock* containingBlock, LayoutUnit containerWidth, LayoutUnit childWidth, LayoutUnit& marginStart, LayoutUnit& marginEnd, Length marginStartLength, Length marginStartEnd) const; - - // Used to resolve margins in the containing block's block-flow direction. - void computeAndSetBlockDirectionMargins(const RenderBlock* containingBlock); - - void positionLineBox(InlineBox*); - - virtual InlineBox* createInlineBox(); - void dirtyLineBoxes(bool fullLayout); - - // For inline replaced elements, this function returns the inline box that owns us. Enables - // the replaced RenderObject to quickly determine what line it is contained on and to easily - // iterate over structures on the line. - InlineBox* inlineBoxWrapper() const { return m_rareData ? m_rareData->m_inlineBoxWrapper : 0; } - void setInlineBoxWrapper(InlineBox*); - void deleteLineBoxWrapper(); - - LayoutUnit containingBlockLogicalHeightForContent(AvailableLogicalHeightType) const; - - virtual void updateLogicalWidth(); - virtual void updateLogicalHeight(); - virtual void computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues&) const; - - void computeLogicalWidth(LogicalExtentComputedValues&) const; - - virtual LayoutSize intrinsicSize() const { return LayoutSize(); } - LayoutUnit intrinsicLogicalWidth() const { return intrinsicSize().width(); } - LayoutUnit intrinsicLogicalHeight() const { return intrinsicSize().height(); } - virtual LayoutUnit intrinsicContentLogicalHeight() const { return m_intrinsicContentLogicalHeight; } - - // Whether or not the element shrinks to its intrinsic width (rather than filling the width - // of a containing block). HTML4 buttons, s, legends, and floating/compact elements do this. - bool sizesLogicalWidthToFitContent(const Length& logicalWidth) const; - - LayoutUnit computeLogicalWidthUsing(SizeType, const Length& logicalWidth, LayoutUnit availableLogicalWidth, const RenderBlock* containingBlock) const; - LayoutUnit computeLogicalHeightUsing(const Length& height, LayoutUnit intrinsicContentHeight) const; - LayoutUnit computeContentLogicalHeight(const Length& height, LayoutUnit intrinsicContentHeight) const; - LayoutUnit computeContentLogicalHeightUsing(const Length& height, LayoutUnit intrinsicContentHeight) const; - LayoutUnit computeReplacedLogicalWidthUsing(const Length& width) const; - LayoutUnit computeReplacedLogicalWidthRespectingMinMaxWidth(LayoutUnit logicalWidth, ShouldComputePreferred = ComputeActual) const; - LayoutUnit computeReplacedLogicalHeightUsing(const Length& height) const; - LayoutUnit computeReplacedLogicalHeightRespectingMinMaxHeight(LayoutUnit logicalHeight) const; - - virtual LayoutUnit computeReplacedLogicalWidth(ShouldComputePreferred = ComputeActual) const; - virtual LayoutUnit computeReplacedLogicalHeight() const; - - // Block flows subclass availableWidth/Height to handle multi column layout (shrinking the width/height available to children when laying out.) - virtual LayoutUnit availableLogicalWidth() const { return contentLogicalWidth(); } - virtual LayoutUnit availableLogicalHeight(AvailableLogicalHeightType) const; - LayoutUnit availableLogicalHeightUsing(const Length&, AvailableLogicalHeightType) const; - - // There are a few cases where we need to refer specifically to the available physical width and available physical height. - // Relative positioning is one of those cases, since left/top offsets are physical. - LayoutUnit availableWidth() const { return availableLogicalWidth(); } - LayoutUnit availableHeight() const { return availableLogicalHeight(IncludeMarginBorderPadding); } - - virtual LayoutRect localCaretRect(InlineBox*, int caretOffset, LayoutUnit* extraWidthToEndOfLine = 0) override; - - virtual LayoutRect overflowClipRect(const LayoutPoint& location); - LayoutRect clipRect(const LayoutPoint& location); - bool pushContentsClip(PaintInfo&, const LayoutPoint& accumulatedOffset, ContentsClipBehavior); - void popContentsClip(PaintInfo&, const LayoutPoint& accumulatedOffset); - - virtual void paintBoxDecorationBackground(PaintInfo&, const LayoutPoint&); - - // Called when a positioned object moves but doesn't necessarily change size. A simplified layout is attempted - // that just updates the object's position. If the size does change, the object remains dirty. - bool tryLayoutDoingPositionedMovementOnly() - { - LayoutUnit oldWidth = width(); - updateLogicalWidth(); - // If we shrink to fit our width may have changed, so we still need full layout. - if (oldWidth != width()) - return false; - updateLogicalHeight(); - return true; - } - - virtual PositionWithAffinity positionForPoint(const LayoutPoint&) override; - - void removeFloatingOrPositionedChildFromBlockLists(); - - RenderLayer* enclosingFloatPaintingLayer() const; - - virtual int firstLineBoxBaseline(FontBaselineOrAuto baselineType) const { return -1; } - virtual int inlineBlockBaseline(LineDirectionMode) const { return -1; } // Returns -1 if we should skip this box when computing the baseline of an inline-block. - - bool isFlexItem() const { return !isInline() && !isFloatingOrOutOfFlowPositioned() && parent() && parent()->isFlexibleBox(); } - - virtual LayoutUnit lineHeight(bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const override; - virtual int baselinePosition(FontBaseline, bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const override; - - virtual LayoutUnit offsetLeft() const override; - virtual LayoutUnit offsetTop() const override; - - LayoutRect layoutOverflowRectForPropagation() const; - - bool hasRenderOverflow() const { return m_overflow; } - bool hasVisualOverflow() const { return m_overflow && !borderBoxRect().contains(m_overflow->visualOverflowRect()); } - - virtual bool needsPreferredWidthsRecalculation() const; - virtual void computeIntrinsicRatioInformation(FloatSize& /* intrinsicSize */, double& /* intrinsicRatio */) const { } - - virtual bool hasRelativeLogicalHeight() const; - - bool hasSameDirectionAs(const RenderBox* object) const { return style()->direction() == object->style()->direction(); } - -protected: - virtual void willBeDestroyed() override; - - virtual void styleWillChange(StyleDifference, const RenderStyle& newStyle) override; - virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle) override; - - void paintBackground(const PaintInfo&, const LayoutRect&, const Color& backgroundColor, BackgroundBleedAvoidance = BackgroundBleedNone); - void paintFillLayer(const PaintInfo&, const Color&, const FillLayer&, const LayoutRect&, BackgroundBleedAvoidance, RenderObject* backgroundObject, bool skipBaseColor = false); - void paintFillLayers(const PaintInfo&, const Color&, const FillLayer&, const LayoutRect&, BackgroundBleedAvoidance = BackgroundBleedNone, RenderObject* backgroundObject = 0); - void paintBoxDecorationBackgroundWithRect(PaintInfo&, const LayoutPoint&, const LayoutRect&); - - // Information extracted from RenderStyle for box painting. - // These are always needed during box painting and recomputing them takes time. - struct BoxDecorationData { - BoxDecorationData(const RenderStyle&); - - Color backgroundColor; - bool hasBackground; - bool hasBorder; - }; - - BackgroundBleedAvoidance determineBackgroundBleedAvoidance(GraphicsContext*, const BoxDecorationData&) const; - bool backgroundHasOpaqueTopLayer() const; - - void computePositionedLogicalWidth(LogicalExtentComputedValues&) const; - - LayoutUnit computeIntrinsicLogicalWidthUsing(const Length& logicalWidthLength, LayoutUnit availableLogicalWidth, LayoutUnit borderAndPadding) const; - LayoutUnit computeIntrinsicLogicalContentHeightUsing(const Length& logicalHeightLength, LayoutUnit intrinsicContentHeight, LayoutUnit borderAndPadding) const; - - virtual bool shouldComputeSizeAsReplaced() const { return isReplaced() && !isInlineBlock(); } - - virtual void mapLocalToContainer(const RenderBox* paintInvalidationContainer, TransformState&, MapCoordinatesFlags = ApplyContainerFlip) const override; - - void updateIntrinsicContentLogicalHeight(LayoutUnit intrinsicContentLogicalHeight) const { m_intrinsicContentLogicalHeight = intrinsicContentLogicalHeight; } - -private: - void updateTransformationMatrix(); - void updateTransform(const RenderStyle* oldStyle); - void updateFromStyle(); - - PassRefPtr createLocalTransformState( - RenderLayer* rootLayer, RenderLayer* containerLayer, - const LayoutRect& hitTestRect, const HitTestLocation& hitTestLocation, - const HitTestingTransformState* containerTransformState) const; - bool hitTestNonLayerDescendants(const HitTestRequest& request, HitTestResult& result, - const LayoutRect& layerBounds, const HitTestLocation& hitTestLocation); - - void paintLayerContents(GraphicsContext*, const LayerPaintingInfo&); - - void shrinkToFitWidth(const LayoutUnit availableSpace, const LayoutUnit logicalLeftValue, const LayoutUnit bordersPlusPadding, LogicalExtentComputedValues&) const; - - bool skipContainingBlockForPercentHeightCalculation(const RenderBox* containingBlock) const; - - LayoutUnit containingBlockLogicalWidthForPositioned(const RenderBoxModelObject* containingBlock) const; - LayoutUnit containingBlockLogicalHeightForPositioned(const RenderBoxModelObject* containingBlock) const; - - void computePositionedLogicalHeight(LogicalExtentComputedValues&) const; - void computePositionedLogicalWidthUsing(Length logicalWidth, const RenderBoxModelObject* containerBlock, TextDirection containerDirection, - LayoutUnit containerLogicalWidth, LayoutUnit bordersPlusPadding, - const Length& logicalLeft, const Length& logicalRight, const Length& marginLogicalLeft, - const Length& marginLogicalRight, LogicalExtentComputedValues&) const; - void computePositionedLogicalHeightUsing(Length logicalHeightLength, const RenderBoxModelObject* containerBlock, - LayoutUnit containerLogicalHeight, LayoutUnit bordersPlusPadding, LayoutUnit logicalHeight, - const Length& logicalTop, const Length& logicalBottom, const Length& marginLogicalTop, - const Length& marginLogicalBottom, LogicalExtentComputedValues&) const; - - void computePositionedLogicalHeightReplaced(LogicalExtentComputedValues&) const; - void computePositionedLogicalWidthReplaced(LogicalExtentComputedValues&) const; - - LayoutUnit fillAvailableMeasure(LayoutUnit availableLogicalWidth) const; - - virtual void computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const; - - // This function calculates the minimum and maximum preferred widths for an object. - // These values are used in shrink-to-fit layout systems. - // These include tables, positioned objects, floats and flexible boxes. - virtual void computePreferredLogicalWidths() { clearPreferredLogicalWidthsDirty(); } - - RenderBoxRareData& ensureRareData() - { - if (!m_rareData) - m_rareData = adoptPtr(new RenderBoxRareData()); - return *m_rareData.get(); - } - - bool logicalHeightComputesAsNone(SizeType) const; - - bool isBox() const = delete; // This will catch anyone doing an unnecessary check. - - // The width/height of the contents + borders + padding. The x/y location is relative to our container (which is not always our parent). - LayoutRect m_frameRect; - - // Our intrinsic height, used for min-height: min-content etc. Maintained by - // updateLogicalHeight. This is logicalHeight() before it is clamped to - // min/max. - mutable LayoutUnit m_intrinsicContentLogicalHeight; - -protected: - LayoutBoxExtent m_marginBox; - - // The preferred logical width of the element if it were to break its lines at every possible opportunity. - LayoutUnit m_minPreferredLogicalWidth; - - // The preferred logical width of the element if it never breaks any lines at all. - LayoutUnit m_maxPreferredLogicalWidth; - - // Our overflow information. - OwnPtr m_overflow; - - // TODO(ojan): Move these two into RenderBoxRareData. - OwnPtr m_transform; - -private: - OwnPtr m_layer; - OwnPtr m_rareData; + public: + explicit RenderBox(); + + // hasAutoZIndex only returns true if the element is positioned or a flex-item + // since position:static elements that are not flex-items get their z-index + // coerced to auto. + virtual LayerType layerTypeRequired() const { + if (isPositioned() || createsGroup() || hasClipPath() || hasTransform() || + !style()->hasAutoZIndex()) + return NormalLayer; + if (hasOverflowClip()) + return OverflowClipLayer; + return NoLayer; + } + + void destroyLayer(); + void createLayer(LayerType); + bool hasSelfPaintingLayer() const; + RenderLayer* layer() const { return m_layer.get(); } + + // Use this with caution! No type checking is done! + RenderBox* firstChildBox() const; + RenderBox* lastChildBox() const; + + LayoutUnit x() const { return m_frameRect.x(); } + LayoutUnit y() const { return m_frameRect.y(); } + LayoutUnit width() const { return m_frameRect.width(); } + LayoutUnit height() const { return m_frameRect.height(); } + + int pixelSnappedWidth() const { return m_frameRect.pixelSnappedWidth(); } + int pixelSnappedHeight() const { return m_frameRect.pixelSnappedHeight(); } + + // These represent your location relative to your container as a physical + // offset. In layout related methods you almost always want the logical + // location (e.g. x() and y()). + LayoutUnit top() const { return location().y(); } + LayoutUnit left() const { return location().x(); } + + void setX(LayoutUnit x) { m_frameRect.setX(x); } + void setY(LayoutUnit y) { m_frameRect.setY(y); } + void setWidth(LayoutUnit width) { m_frameRect.setWidth(width); } + void setHeight(LayoutUnit height) { m_frameRect.setHeight(height); } + + LayoutUnit logicalLeft() const { return x(); } + LayoutUnit logicalRight() const { return logicalLeft() + logicalWidth(); } + LayoutUnit logicalTop() const { return y(); } + LayoutUnit logicalBottom() const { return logicalTop() + logicalHeight(); } + LayoutUnit logicalWidth() const { return width(); } + LayoutUnit logicalHeight() const { return height(); } + + LayoutUnit constrainLogicalWidthByMinMax(LayoutUnit, + LayoutUnit, + RenderBlock*) const; + LayoutUnit constrainLogicalHeightByMinMax( + LayoutUnit logicalHeight, + LayoutUnit intrinsicContentHeight) const; + LayoutUnit constrainContentBoxLogicalHeightByMinMax( + LayoutUnit logicalHeight, + LayoutUnit intrinsicContentHeight) const; + + int pixelSnappedLogicalHeight() const { return pixelSnappedHeight(); } + int pixelSnappedLogicalWidth() const { return pixelSnappedWidth(); } + + void setLogicalLeft(LayoutUnit left) { setX(left); } + void setLogicalTop(LayoutUnit top) { setY(top); } + void setLogicalLocation(const LayoutPoint& location) { + setLocation(location); + } + void setLogicalWidth(LayoutUnit size) { setWidth(size); } + void setLogicalHeight(LayoutUnit size) { setHeight(size); } + void setLogicalSize(const LayoutSize& size) { setSize(size); } + + LayoutPoint location() const { return m_frameRect.location(); } + LayoutSize locationOffset() const { return LayoutSize(x(), y()); } + LayoutSize size() const { return m_frameRect.size(); } + IntSize pixelSnappedSize() const { return m_frameRect.pixelSnappedSize(); } + + void setLocation(const LayoutPoint& location) { + m_frameRect.setLocation(location); + } + + void setSize(const LayoutSize& size) { m_frameRect.setSize(size); } + void move(LayoutUnit dx, LayoutUnit dy) { m_frameRect.move(dx, dy); } + + LayoutRect frameRect() const { return m_frameRect; } + IntRect pixelSnappedFrameRect() const { + return pixelSnappedIntRect(m_frameRect); + } + void setFrameRect(const LayoutRect& rect) { m_frameRect = rect; } + + LayoutRect borderBoxRect() const { return LayoutRect(LayoutPoint(), size()); } + LayoutRect paddingBoxRect() const { + return LayoutRect(borderLeft(), borderTop(), clientWidth(), clientHeight()); + } + IntRect pixelSnappedBorderBoxRect() const { + return IntRect(IntPoint(), m_frameRect.pixelSnappedSize()); + } + virtual IntRect borderBoundingBox() const override final { + return pixelSnappedBorderBoxRect(); + } + + // The content area of the box (excludes padding - and intrinsic padding for + // table cells, etc... - and border). + LayoutRect contentBoxRect() const { + return LayoutRect(borderLeft() + paddingLeft(), borderTop() + paddingTop(), + contentWidth(), contentHeight()); + } + // The content box in absolute coords. Ignores transforms. + IntRect absoluteContentBox() const; + // The content box converted to absolute coords (taking transforms into + // account). + FloatQuad absoluteContentQuad() const; + + FloatPoint perspectiveOrigin() const; + + // This returns the content area of the box (excluding padding and border). + // The only difference with contentBoxRect is that computedCSSContentBoxRect + // does include the intrinsic padding in the content box as this is what some + // callers expect (like getComputedStyle). + LayoutRect computedCSSContentBoxRect() const { + return LayoutRect( + borderLeft() + computedCSSPaddingLeft(), + borderTop() + computedCSSPaddingTop(), + clientWidth() - computedCSSPaddingLeft() - computedCSSPaddingRight(), + clientHeight() - computedCSSPaddingTop() - computedCSSPaddingBottom()); + } + + virtual void addFocusRingRects( + Vector&, + const LayoutPoint& additionalOffset, + const RenderBox* paintContainer = 0) const override; + + // Use this with caution! No type checking is done! + RenderBox* previousSiblingBox() const; + RenderBox* nextSiblingBox() const; + RenderBox* parentBox() const; + + // Visual and layout overflow are in the coordinate space of the box. This + // means that they aren't purely physical directions. For horizontal-tb and + // vertical-lr they will match physical directions, but for horizontal-bt and + // vertical-rl, the top/bottom and left/right respectively are flipped when + // compared to their physical counterparts. For example minX is on the left + // in vertical-lr, but it is on the right in vertical-rl. + LayoutRect layoutOverflowRect() const { + return m_overflow ? m_overflow->layoutOverflowRect() : paddingBoxRect(); + } + IntRect pixelSnappedLayoutOverflowRect() const { + return pixelSnappedIntRect(layoutOverflowRect()); + } + LayoutSize maxLayoutOverflow() const { + return LayoutSize(layoutOverflowRect().maxX(), layoutOverflowRect().maxY()); + } + LayoutUnit logicalLefxlayoutOverflow() const { + return layoutOverflowRect().x(); + } + LayoutUnit logicalRightLayoutOverflow() const { + return layoutOverflowRect().maxX(); + } + + virtual LayoutRect visualOverflowRect() const { + return m_overflow ? m_overflow->visualOverflowRect() : borderBoxRect(); + } + LayoutUnit logicalLeftVisualOverflow() const { + return visualOverflowRect().x(); + } + LayoutUnit logicalRightVisualOverflow() const { + return visualOverflowRect().maxX(); + } + + LayoutRect contentsVisualOverflowRect() const { + return m_overflow ? m_overflow->contentsVisualOverflowRect() : LayoutRect(); + } + + void addLayoutOverflow(const LayoutRect&); + void addVisualOverflow(const LayoutRect&); + + // Clipped by the contents clip, if one exists. + void addContentsVisualOverflow(const LayoutRect&); + + void addVisualEffectOverflow(); + LayoutBoxExtent computeVisualEffectOverflowExtent() const; + void addOverflowFromChild(RenderBox* child) { + addOverflowFromChild(child, child->locationOffset()); + } + void addOverflowFromChild(RenderBox* child, const LayoutSize& delta); + void clearLayoutOverflow(); + void clearAllOverflows() { m_overflow.clear(); } + + void updateLayerTransformAfterLayout(); + + // This transform has the transform-origin baked in. + TransformationMatrix* transform() const { return m_transform.get(); } + bool has3DTransform() const { + return m_transform && !m_transform->isAffine(); + } + + LayoutUnit contentWidth() const { + return clientWidth() - paddingLeft() - paddingRight(); + } + LayoutUnit contentHeight() const { + return clientHeight() - paddingTop() - paddingBottom(); + } + LayoutUnit contentLogicalWidth() const { return contentWidth(); } + LayoutUnit contentLogicalHeight() const { return contentHeight(); } + + // IE extensions. Used to calculate offsetWidth/Height. Overridden by inlines + // (RenderFlow) to return the remaining width on a given line (and the height + // of a single line). + virtual LayoutUnit offsetWidth() const override { return width(); } + virtual LayoutUnit offsetHeight() const override { return height(); } + + virtual int pixelSnappedOffsetWidth() const override final; + virtual int pixelSnappedOffsetHeight() const override final; + + // More IE extensions. clientWidth and clientHeight represent the interior of + // an object excluding border and scrollbar. clientLeft/Top are just the + // borderLeftWidth and borderTopWidth. + LayoutUnit clientLeft() const { return borderLeft(); } + LayoutUnit clientTop() const { return borderTop(); } + LayoutUnit clientWidth() const; + LayoutUnit clientHeight() const; + LayoutUnit clientLogicalWidth() const { return clientWidth(); } + LayoutUnit clientLogicalHeight() const { return clientHeight(); } + LayoutUnit clientLogicalBottom() const { + return borderBefore() + clientLogicalHeight(); + } + LayoutRect clientBoxRect() const { + return LayoutRect(clientLeft(), clientTop(), clientWidth(), clientHeight()); + } + + int pixelSnappedClientWidth() const; + int pixelSnappedClientHeight() const; + + virtual LayoutUnit marginTop() const override { return m_marginBox.top(); } + virtual LayoutUnit marginBottom() const override { + return m_marginBox.bottom(); + } + virtual LayoutUnit marginLeft() const override { return m_marginBox.left(); } + virtual LayoutUnit marginRight() const override { + return m_marginBox.right(); + } + void setMarginTop(LayoutUnit margin) { m_marginBox.setTop(margin); } + void setMarginBottom(LayoutUnit margin) { m_marginBox.setBottom(margin); } + void setMarginLeft(LayoutUnit margin) { m_marginBox.setLeft(margin); } + void setMarginRight(LayoutUnit margin) { m_marginBox.setRight(margin); } + + LayoutUnit marginLogicalLeft() const { return m_marginBox.logicalLeft(); } + LayoutUnit marginLogicalRight() const { return m_marginBox.logicalRight(); } + + virtual LayoutUnit marginBefore( + const RenderStyle* overrideStyle = 0) const override final { + return m_marginBox.before(); + } + virtual LayoutUnit marginAfter( + const RenderStyle* overrideStyle = 0) const override final { + return m_marginBox.after(); + } + virtual LayoutUnit marginStart( + const RenderStyle* overrideStyle = 0) const override final { + const RenderStyle* styleToUse = overrideStyle ? overrideStyle : style(); + return m_marginBox.start(styleToUse->direction()); + } + virtual LayoutUnit marginEnd( + const RenderStyle* overrideStyle = 0) const override final { + const RenderStyle* styleToUse = overrideStyle ? overrideStyle : style(); + return m_marginBox.end(styleToUse->direction()); + } + void setMarginBefore(LayoutUnit value, const RenderStyle* overrideStyle = 0) { + m_marginBox.setBefore(value); + } + void setMarginAfter(LayoutUnit value, const RenderStyle* overrideStyle = 0) { + m_marginBox.setAfter(value); + } + void setMarginStart(LayoutUnit value, const RenderStyle* overrideStyle = 0) { + const RenderStyle* styleToUse = overrideStyle ? overrideStyle : style(); + m_marginBox.setStart(styleToUse->direction(), value); + } + void setMarginEnd(LayoutUnit value, const RenderStyle* overrideStyle = 0) { + const RenderStyle* styleToUse = overrideStyle ? overrideStyle : style(); + m_marginBox.setEnd(styleToUse->direction(), value); + } + + virtual void absoluteQuads(Vector&) const override; + + bool hitTestLayer(RenderLayer* rootLayer, + RenderLayer* containerLayer, + const HitTestRequest& request, + HitTestResult& result, + const LayoutRect& hitTestRect, + const HitTestLocation& hitTestLocation, + const HitTestingTransformState* transformState = 0, + double* zOffset = 0); + + void paintLayer(GraphicsContext*, const LayerPaintingInfo&); + + virtual void layout() override; + virtual void paint(PaintInfo&, + const LayoutPoint&, + Vector& layers) override; + virtual bool nodeAtPoint(const HitTestRequest&, + HitTestResult&, + const HitTestLocation& locationInContainer, + const LayoutPoint& accumulatedOffset) override; + + virtual LayoutUnit minPreferredLogicalWidth() const override; + virtual LayoutUnit maxPreferredLogicalWidth() const override; + + void setMinPreferredLogicalWidth(LayoutUnit); + void setMaxPreferredLogicalWidth(LayoutUnit); + + // FIXME: We should rename these back to overrideLogicalHeight/Width and have + // them store the border-box height/width like the regular height/width + // accessors on RenderBox. Right now, these are different than + // contentHeight/contentWidth because they still include the scrollbar + // height/width. + LayoutUnit overrideLogicalContentWidth() const; + LayoutUnit overrideLogicalContentHeight() const; + bool hasOverrideHeight() const; + bool hasOverrideWidth() const; + void setOverrideLogicalContentHeight(LayoutUnit); + void setOverrideLogicalContentWidth(LayoutUnit); + void clearOverrideSize(); + void clearOverrideLogicalContentHeight(); + void clearOverrideLogicalContentWidth(); + + virtual LayoutSize offsetFromContainer( + const RenderObject*, + const LayoutPoint&, + bool* offsetDependsOnPoint = 0) const override; + + LayoutUnit adjustBorderBoxLogicalWidthForBoxSizing(LayoutUnit width) const; + LayoutUnit adjustBorderBoxLogicalHeightForBoxSizing(LayoutUnit height) const; + LayoutUnit adjustContentBoxLogicalWidthForBoxSizing(LayoutUnit width) const; + LayoutUnit adjustContentBoxLogicalHeightForBoxSizing(LayoutUnit height) const; + + struct ComputedMarginValues { + ComputedMarginValues() {} + + LayoutUnit m_before; + LayoutUnit m_after; + LayoutUnit m_start; + LayoutUnit m_end; + }; + struct LogicalExtentComputedValues { + LogicalExtentComputedValues() {} + + LayoutUnit m_extent; + LayoutUnit m_position; + ComputedMarginValues m_margins; + }; + // Resolve auto margins in the chosen direction of the containing block so + // that objects can be pushed to the start, middle or end of the containing + // block. + void computeMarginsForDirection(MarginDirection forDirection, + const RenderBlock* containingBlock, + LayoutUnit containerWidth, + LayoutUnit childWidth, + LayoutUnit& marginStart, + LayoutUnit& marginEnd, + Length marginStartLength, + Length marginStartEnd) const; + + // Used to resolve margins in the containing block's block-flow direction. + void computeAndSetBlockDirectionMargins(const RenderBlock* containingBlock); + + void positionLineBox(InlineBox*); + + virtual InlineBox* createInlineBox(); + void dirtyLineBoxes(bool fullLayout); + + // For inline replaced elements, this function returns the inline box that + // owns us. Enables the replaced RenderObject to quickly determine what line + // it is contained on and to easily iterate over structures on the line. + InlineBox* inlineBoxWrapper() const { + return m_rareData ? m_rareData->m_inlineBoxWrapper : 0; + } + void setInlineBoxWrapper(InlineBox*); + void deleteLineBoxWrapper(); + + LayoutUnit containingBlockLogicalHeightForContent( + AvailableLogicalHeightType) const; + + virtual void updateLogicalWidth(); + virtual void updateLogicalHeight(); + virtual void computeLogicalHeight(LayoutUnit logicalHeight, + LayoutUnit logicalTop, + LogicalExtentComputedValues&) const; + + void computeLogicalWidth(LogicalExtentComputedValues&) const; + + virtual LayoutSize intrinsicSize() const { return LayoutSize(); } + LayoutUnit intrinsicLogicalWidth() const { return intrinsicSize().width(); } + LayoutUnit intrinsicLogicalHeight() const { return intrinsicSize().height(); } + virtual LayoutUnit intrinsicContentLogicalHeight() const { + return m_intrinsicContentLogicalHeight; + } + + // Whether or not the element shrinks to its intrinsic width (rather than + // filling the width of a containing block). HTML4 buttons, s, legends, and floating/compact elements do this. + bool sizesLogicalWidthToFitContent(const Length& logicalWidth) const; + + LayoutUnit computeLogicalWidthUsing(SizeType, + const Length& logicalWidth, + LayoutUnit availableLogicalWidth, + const RenderBlock* containingBlock) const; + LayoutUnit computeLogicalHeightUsing(const Length& height, + LayoutUnit intrinsicContentHeight) const; + LayoutUnit computeContentLogicalHeight( + const Length& height, + LayoutUnit intrinsicContentHeight) const; + LayoutUnit computeContentLogicalHeightUsing( + const Length& height, + LayoutUnit intrinsicContentHeight) const; + LayoutUnit computeReplacedLogicalWidthUsing(const Length& width) const; + LayoutUnit computeReplacedLogicalWidthRespectingMinMaxWidth( + LayoutUnit logicalWidth, + ShouldComputePreferred = ComputeActual) const; + LayoutUnit computeReplacedLogicalHeightUsing(const Length& height) const; + LayoutUnit computeReplacedLogicalHeightRespectingMinMaxHeight( + LayoutUnit logicalHeight) const; + + virtual LayoutUnit computeReplacedLogicalWidth( + ShouldComputePreferred = ComputeActual) const; + virtual LayoutUnit computeReplacedLogicalHeight() const; + + // Block flows subclass availableWidth/Height to handle multi column layout + // (shrinking the width/height available to children when laying out.) + virtual LayoutUnit availableLogicalWidth() const { + return contentLogicalWidth(); + } + virtual LayoutUnit availableLogicalHeight(AvailableLogicalHeightType) const; + LayoutUnit availableLogicalHeightUsing(const Length&, + AvailableLogicalHeightType) const; + + // There are a few cases where we need to refer specifically to the available + // physical width and available physical height. Relative positioning is one + // of those cases, since left/top offsets are physical. + LayoutUnit availableWidth() const { return availableLogicalWidth(); } + LayoutUnit availableHeight() const { + return availableLogicalHeight(IncludeMarginBorderPadding); + } + + virtual LayoutRect localCaretRect( + InlineBox*, + int caretOffset, + LayoutUnit* extraWidthToEndOfLine = 0) override; + + virtual LayoutRect overflowClipRect(const LayoutPoint& location); + LayoutRect clipRect(const LayoutPoint& location); + bool pushContentsClip(PaintInfo&, + const LayoutPoint& accumulatedOffset, + ContentsClipBehavior); + void popContentsClip(PaintInfo&, const LayoutPoint& accumulatedOffset); + + virtual void paintBoxDecorationBackground(PaintInfo&, const LayoutPoint&); + + // Called when a positioned object moves but doesn't necessarily change size. + // A simplified layout is attempted that just updates the object's position. + // If the size does change, the object remains dirty. + bool tryLayoutDoingPositionedMovementOnly() { + LayoutUnit oldWidth = width(); + updateLogicalWidth(); + // If we shrink to fit our width may have changed, so we still need full + // layout. + if (oldWidth != width()) + return false; + updateLogicalHeight(); + return true; + } + + virtual PositionWithAffinity positionForPoint(const LayoutPoint&) override; + + void removeFloatingOrPositionedChildFromBlockLists(); + + RenderLayer* enclosingFloatPaintingLayer() const; + + virtual int firstLineBoxBaseline(FontBaselineOrAuto baselineType) const { + return -1; + } + virtual int inlineBlockBaseline(LineDirectionMode) const { + return -1; + } // Returns -1 if we should skip this box when computing the baseline of an + // inline-block. + + bool isFlexItem() const { + return !isInline() && !isFloatingOrOutOfFlowPositioned() && parent() && + parent()->isFlexibleBox(); + } + + virtual LayoutUnit lineHeight( + bool firstLine, + LineDirectionMode, + LinePositionMode = PositionOnContainingLine) const override; + virtual int baselinePosition( + FontBaseline, + bool firstLine, + LineDirectionMode, + LinePositionMode = PositionOnContainingLine) const override; + + virtual LayoutUnit offsetLeft() const override; + virtual LayoutUnit offsetTop() const override; + + LayoutRect layoutOverflowRectForPropagation() const; + + bool hasRenderOverflow() const { return m_overflow; } + bool hasVisualOverflow() const { + return m_overflow && + !borderBoxRect().contains(m_overflow->visualOverflowRect()); + } + + virtual bool needsPreferredWidthsRecalculation() const; + virtual void computeIntrinsicRatioInformation( + FloatSize& /* intrinsicSize */, + double& /* intrinsicRatio */) const {} + + virtual bool hasRelativeLogicalHeight() const; + + bool hasSameDirectionAs(const RenderBox* object) const { + return style()->direction() == object->style()->direction(); + } + + protected: + virtual void willBeDestroyed() override; + + virtual void styleWillChange(StyleDifference, + const RenderStyle& newStyle) override; + virtual void styleDidChange(StyleDifference, + const RenderStyle* oldStyle) override; + + void paintBackground(const PaintInfo&, + const LayoutRect&, + const Color& backgroundColor, + BackgroundBleedAvoidance = BackgroundBleedNone); + void paintFillLayer(const PaintInfo&, + const Color&, + const FillLayer&, + const LayoutRect&, + BackgroundBleedAvoidance, + RenderObject* backgroundObject, + bool skipBaseColor = false); + void paintFillLayers(const PaintInfo&, + const Color&, + const FillLayer&, + const LayoutRect&, + BackgroundBleedAvoidance = BackgroundBleedNone, + RenderObject* backgroundObject = 0); + void paintBoxDecorationBackgroundWithRect(PaintInfo&, + const LayoutPoint&, + const LayoutRect&); + + // Information extracted from RenderStyle for box painting. + // These are always needed during box painting and recomputing them takes + // time. + struct BoxDecorationData { + BoxDecorationData(const RenderStyle&); + + Color backgroundColor; + bool hasBackground; + bool hasBorder; + }; + + BackgroundBleedAvoidance determineBackgroundBleedAvoidance( + GraphicsContext*, + const BoxDecorationData&) const; + bool backgroundHasOpaqueTopLayer() const; + + void computePositionedLogicalWidth(LogicalExtentComputedValues&) const; + + LayoutUnit computeIntrinsicLogicalWidthUsing( + const Length& logicalWidthLength, + LayoutUnit availableLogicalWidth, + LayoutUnit borderAndPadding) const; + LayoutUnit computeIntrinsicLogicalContentHeightUsing( + const Length& logicalHeightLength, + LayoutUnit intrinsicContentHeight, + LayoutUnit borderAndPadding) const; + + virtual bool shouldComputeSizeAsReplaced() const { + return isReplaced() && !isInlineBlock(); + } + + virtual void mapLocalToContainer( + const RenderBox* paintInvalidationContainer, + TransformState&, + MapCoordinatesFlags = ApplyContainerFlip) const override; + + void updateIntrinsicContentLogicalHeight( + LayoutUnit intrinsicContentLogicalHeight) const { + m_intrinsicContentLogicalHeight = intrinsicContentLogicalHeight; + } + + private: + void updateTransformationMatrix(); + void updateTransform(const RenderStyle* oldStyle); + void updateFromStyle(); + + PassRefPtr createLocalTransformState( + RenderLayer* rootLayer, + RenderLayer* containerLayer, + const LayoutRect& hitTestRect, + const HitTestLocation& hitTestLocation, + const HitTestingTransformState* containerTransformState) const; + bool hitTestNonLayerDescendants(const HitTestRequest& request, + HitTestResult& result, + const LayoutRect& layerBounds, + const HitTestLocation& hitTestLocation); + + void paintLayerContents(GraphicsContext*, const LayerPaintingInfo&); + + void shrinkToFitWidth(const LayoutUnit availableSpace, + const LayoutUnit logicalLeftValue, + const LayoutUnit bordersPlusPadding, + LogicalExtentComputedValues&) const; + + bool skipContainingBlockForPercentHeightCalculation( + const RenderBox* containingBlock) const; + + LayoutUnit containingBlockLogicalWidthForPositioned( + const RenderBoxModelObject* containingBlock) const; + LayoutUnit containingBlockLogicalHeightForPositioned( + const RenderBoxModelObject* containingBlock) const; + + void computePositionedLogicalHeight(LogicalExtentComputedValues&) const; + void computePositionedLogicalWidthUsing( + Length logicalWidth, + const RenderBoxModelObject* containerBlock, + TextDirection containerDirection, + LayoutUnit containerLogicalWidth, + LayoutUnit bordersPlusPadding, + const Length& logicalLeft, + const Length& logicalRight, + const Length& marginLogicalLeft, + const Length& marginLogicalRight, + LogicalExtentComputedValues&) const; + void computePositionedLogicalHeightUsing( + Length logicalHeightLength, + const RenderBoxModelObject* containerBlock, + LayoutUnit containerLogicalHeight, + LayoutUnit bordersPlusPadding, + LayoutUnit logicalHeight, + const Length& logicalTop, + const Length& logicalBottom, + const Length& marginLogicalTop, + const Length& marginLogicalBottom, + LogicalExtentComputedValues&) const; + + void computePositionedLogicalHeightReplaced( + LogicalExtentComputedValues&) const; + void computePositionedLogicalWidthReplaced( + LogicalExtentComputedValues&) const; + + LayoutUnit fillAvailableMeasure(LayoutUnit availableLogicalWidth) const; + + virtual void computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, + LayoutUnit& maxLogicalWidth) const; + + // This function calculates the minimum and maximum preferred widths for an + // object. These values are used in shrink-to-fit layout systems. These + // include tables, positioned objects, floats and flexible boxes. + virtual void computePreferredLogicalWidths() { + clearPreferredLogicalWidthsDirty(); + } + + RenderBoxRareData& ensureRareData() { + if (!m_rareData) + m_rareData = adoptPtr(new RenderBoxRareData()); + return *m_rareData.get(); + } + + bool logicalHeightComputesAsNone(SizeType) const; + + bool isBox() const = + delete; // This will catch anyone doing an unnecessary check. + + // The width/height of the contents + borders + padding. The x/y location is + // relative to our container (which is not always our parent). + LayoutRect m_frameRect; + + // Our intrinsic height, used for min-height: min-content etc. Maintained by + // updateLogicalHeight. This is logicalHeight() before it is clamped to + // min/max. + mutable LayoutUnit m_intrinsicContentLogicalHeight; + + protected: + LayoutBoxExtent m_marginBox; + + // The preferred logical width of the element if it were to break its lines at + // every possible opportunity. + LayoutUnit m_minPreferredLogicalWidth; + + // The preferred logical width of the element if it never breaks any lines at + // all. + LayoutUnit m_maxPreferredLogicalWidth; + + // Our overflow information. + OwnPtr m_overflow; + + // TODO(ojan): Move these two into RenderBoxRareData. + OwnPtr m_transform; + + private: + OwnPtr m_layer; + OwnPtr m_rareData; }; DEFINE_RENDER_OBJECT_TYPE_CASTS(RenderBox, isBox()); -inline RenderBox* RenderBox::previousSiblingBox() const -{ - return toRenderBox(previousSibling()); +inline RenderBox* RenderBox::previousSiblingBox() const { + return toRenderBox(previousSibling()); } -inline RenderBox* RenderBox::nextSiblingBox() const -{ - return toRenderBox(nextSibling()); +inline RenderBox* RenderBox::nextSiblingBox() const { + return toRenderBox(nextSibling()); } -inline RenderBox* RenderBox::parentBox() const -{ - return toRenderBox(parent()); +inline RenderBox* RenderBox::parentBox() const { + return toRenderBox(parent()); } -inline RenderBox* RenderBox::firstChildBox() const -{ - return toRenderBox(slowFirstChild()); +inline RenderBox* RenderBox::firstChildBox() const { + return toRenderBox(slowFirstChild()); } -inline RenderBox* RenderBox::lastChildBox() const -{ - return toRenderBox(slowLastChild()); +inline RenderBox* RenderBox::lastChildBox() const { + return toRenderBox(slowLastChild()); } -inline void RenderBox::setInlineBoxWrapper(InlineBox* boxWrapper) -{ - if (boxWrapper) { - ASSERT(!inlineBoxWrapper()); - // m_inlineBoxWrapper should already be 0. Deleting it is a safeguard against security issues. - // Otherwise, there will two line box wrappers keeping the reference to this renderer, and - // only one will be notified when the renderer is getting destroyed. The second line box wrapper - // will keep a stale reference. - if (UNLIKELY(inlineBoxWrapper() != 0)) - deleteLineBoxWrapper(); - } - - ensureRareData().m_inlineBoxWrapper = boxWrapper; +inline void RenderBox::setInlineBoxWrapper(InlineBox* boxWrapper) { + if (boxWrapper) { + ASSERT(!inlineBoxWrapper()); + // m_inlineBoxWrapper should already be 0. Deleting it is a safeguard + // against security issues. Otherwise, there will two line box wrappers + // keeping the reference to this renderer, and only one will be notified + // when the renderer is getting destroyed. The second line box wrapper will + // keep a stale reference. + if (UNLIKELY(inlineBoxWrapper() != 0)) + deleteLineBoxWrapper(); + } + + ensureRareData().m_inlineBoxWrapper = boxWrapper; } -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_RENDERBOX_H_ diff --git a/sky/engine/core/rendering/RenderBoxModelObject.cpp b/sky/engine/core/rendering/RenderBoxModelObject.cpp index d6d7df00f361f..99d93b2990538 100644 --- a/sky/engine/core/rendering/RenderBoxModelObject.cpp +++ b/sky/engine/core/rendering/RenderBoxModelObject.cpp @@ -40,1990 +40,2348 @@ namespace blink { -void RenderBoxModelObject::setSelectionState(SelectionState state) -{ - if (state == SelectionInside && selectionState() != SelectionNone) - return; - - if ((state == SelectionStart && selectionState() == SelectionEnd) - || (state == SelectionEnd && selectionState() == SelectionStart)) - RenderObject::setSelectionState(SelectionBoth); - else - RenderObject::setSelectionState(state); - - // FIXME: We should consider whether it is OK propagating to ancestor RenderInlines. - // This is a workaround for http://webkit.org/b/32123 - // The containing block can be null in case of an orphaned tree. - RenderBlock* containingBlock = this->containingBlock(); - if (containingBlock && !containingBlock->isRenderView()) - containingBlock->setSelectionState(state); -} - -RenderBoxModelObject::RenderBoxModelObject() -{ -} - -RenderBoxModelObject::~RenderBoxModelObject() -{ -} - -bool RenderBoxModelObject::hasAutoHeightOrContainingBlockWithAutoHeight() const -{ - Length logicalHeightLength = style()->logicalHeight(); - if (logicalHeightLength.isAuto()) - return true; - - // For percentage heights: The percentage is calculated with respect to the height of the generated box's - // containing block. If the height of the containing block is not specified explicitly (i.e., it depends - // on content height), and this element is not absolutely positioned, the value computes to 'auto'. - // FIXME(sky): We might want to make height: 100% be sensible. - if (!logicalHeightLength.isPercent() || isOutOfFlowPositioned()) - return false; - - RenderBlock* cb = containingBlock(); - - // Match RenderBox::availableLogicalHeightUsing by special casing - // the render view. The available height is taken from the frame. - if (cb->isRenderView()) - return false; +void RenderBoxModelObject::setSelectionState(SelectionState state) { + if (state == SelectionInside && selectionState() != SelectionNone) + return; - if (cb->isOutOfFlowPositioned() && !cb->style()->logicalTop().isAuto() && !cb->style()->logicalBottom().isAuto()) - return false; + if ((state == SelectionStart && selectionState() == SelectionEnd) || + (state == SelectionEnd && selectionState() == SelectionStart)) + RenderObject::setSelectionState(SelectionBoth); + else + RenderObject::setSelectionState(state); - // If the height of the containing block computes to 'auto', then it hasn't been 'specified explicitly'. - return cb->hasAutoHeightOrContainingBlockWithAutoHeight(); + // FIXME: We should consider whether it is OK propagating to ancestor + // RenderInlines. This is a workaround for http://webkit.org/b/32123 The + // containing block can be null in case of an orphaned tree. + RenderBlock* containingBlock = this->containingBlock(); + if (containingBlock && !containingBlock->isRenderView()) + containingBlock->setSelectionState(state); } -LayoutSize RenderBoxModelObject::relativePositionOffset() const -{ - LayoutSize offset; +RenderBoxModelObject::RenderBoxModelObject() {} - RenderBlock* containingBlock = this->containingBlock(); +RenderBoxModelObject::~RenderBoxModelObject() {} - // Objects that shrink to avoid floats normally use available line width when computing containing block width. However - // in the case of relative positioning using percentages, we can't do this. The offset should always be resolved using the - // available width of the containing block. Therefore we don't use containingBlockLogicalWidthForContent() here, but instead explicitly - // call availableWidth on our containing block. - if (!style()->left().isAuto()) { - if (!style()->right().isAuto() && !containingBlock->style()->isLeftToRightDirection()) - offset.setWidth(-valueForLength(style()->right(), containingBlock->availableWidth())); - else - offset.expand(valueForLength(style()->left(), containingBlock->availableWidth()), 0); - } else if (!style()->right().isAuto()) { - offset.expand(-valueForLength(style()->right(), containingBlock->availableWidth()), 0); - } - - // If the containing block of a relatively positioned element does not - // specify a height, a percentage top or bottom offset should be resolved as - // auto. An exception to this is if the containing block has the WinIE quirk - // where and assume the size of the viewport. In this case, - // calculate the percent offset based on this height. - // See . - if (!style()->top().isAuto() - && (!containingBlock->hasAutoHeightOrContainingBlockWithAutoHeight() - || !style()->top().isPercent())) - offset.expand(0, valueForLength(style()->top(), containingBlock->availableHeight())); - - else if (!style()->bottom().isAuto() - && (!containingBlock->hasAutoHeightOrContainingBlockWithAutoHeight() - || !style()->bottom().isPercent())) - offset.expand(0, -valueForLength(style()->bottom(), containingBlock->availableHeight())); - - return offset; -} - -LayoutPoint RenderBoxModelObject::adjustedPositionRelativeToOffsetParent(const LayoutPoint& startPoint) const -{ - if (!parent()) - return LayoutPoint(); +bool RenderBoxModelObject::hasAutoHeightOrContainingBlockWithAutoHeight() + const { + Length logicalHeightLength = style()->logicalHeight(); + if (logicalHeightLength.isAuto()) + return true; - return startPoint; -} + // For percentage heights: The percentage is calculated with respect to the + // height of the generated box's containing block. If the height of the + // containing block is not specified explicitly (i.e., it depends on content + // height), and this element is not absolutely positioned, the value computes + // to 'auto'. + // FIXME(sky): We might want to make height: 100% be sensible. + if (!logicalHeightLength.isPercent() || isOutOfFlowPositioned()) + return false; -LayoutUnit RenderBoxModelObject::offsetLeft() const -{ - // Note that RenderInline and RenderBox override this to pass a different - // startPoint to adjustedPositionRelativeToOffsetParent. - return adjustedPositionRelativeToOffsetParent(LayoutPoint()).x(); -} + RenderBlock* cb = containingBlock(); -LayoutUnit RenderBoxModelObject::offsetTop() const -{ - // Note that RenderInline and RenderBox override this to pass a different - // startPoint to adjustedPositionRelativeToOffsetParent. - return adjustedPositionRelativeToOffsetParent(LayoutPoint()).y(); -} + // Match RenderBox::availableLogicalHeightUsing by special casing + // the render view. The available height is taken from the frame. + if (cb->isRenderView()) + return false; -int RenderBoxModelObject::pixelSnappedOffsetWidth() const -{ - return snapSizeToPixel(offsetWidth(), offsetLeft()); -} + if (cb->isOutOfFlowPositioned() && !cb->style()->logicalTop().isAuto() && + !cb->style()->logicalBottom().isAuto()) + return false; -int RenderBoxModelObject::pixelSnappedOffsetHeight() const -{ - return snapSizeToPixel(offsetHeight(), offsetTop()); + // If the height of the containing block computes to 'auto', then it hasn't + // been 'specified explicitly'. + return cb->hasAutoHeightOrContainingBlockWithAutoHeight(); } -LayoutUnit RenderBoxModelObject::computedCSSPadding(const Length& padding) const -{ - LayoutUnit w = 0; - if (padding.isPercent()) - w = containingBlockLogicalWidthForContent(); - return minimumValueForLength(padding, w); -} +LayoutSize RenderBoxModelObject::relativePositionOffset() const { + LayoutSize offset; -RoundedRect RenderBoxModelObject::getBackgroundRoundedRect(const LayoutRect& borderRect, InlineFlowBox* box, LayoutUnit inlineBoxWidth, LayoutUnit inlineBoxHeight, - bool includeLogicalLeftEdge, bool includeLogicalRightEdge) const -{ - RoundedRect border = style()->getRoundedBorderFor(borderRect, includeLogicalLeftEdge, includeLogicalRightEdge); - if (box && (box->nextLineBox() || box->prevLineBox())) { - RoundedRect segmentBorder = style()->getRoundedBorderFor(LayoutRect(0, 0, inlineBoxWidth, inlineBoxHeight), includeLogicalLeftEdge, includeLogicalRightEdge); - border.setRadii(segmentBorder.radii()); - } + RenderBlock* containingBlock = this->containingBlock(); - return border; -} - -void RenderBoxModelObject::clipRoundedInnerRect(GraphicsContext * context, const LayoutRect& rect, const RoundedRect& clipRect) -{ - if (clipRect.isRenderable()) - context->clipRoundedRect(clipRect); - else { - // We create a rounded rect for each of the corners and clip it, while making sure we clip opposing corners together. - if (!clipRect.radii().topLeft().isEmpty() || !clipRect.radii().bottomRight().isEmpty()) { - IntRect topCorner(clipRect.rect().x(), clipRect.rect().y(), rect.maxX() - clipRect.rect().x(), rect.maxY() - clipRect.rect().y()); - RoundedRect::Radii topCornerRadii; - topCornerRadii.setTopLeft(clipRect.radii().topLeft()); - context->clipRoundedRect(RoundedRect(topCorner, topCornerRadii)); - - IntRect bottomCorner(rect.x(), rect.y(), clipRect.rect().maxX() - rect.x(), clipRect.rect().maxY() - rect.y()); - RoundedRect::Radii bottomCornerRadii; - bottomCornerRadii.setBottomRight(clipRect.radii().bottomRight()); - context->clipRoundedRect(RoundedRect(bottomCorner, bottomCornerRadii)); - } - - if (!clipRect.radii().topRight().isEmpty() || !clipRect.radii().bottomLeft().isEmpty()) { - IntRect topCorner(rect.x(), clipRect.rect().y(), clipRect.rect().maxX() - rect.x(), rect.maxY() - clipRect.rect().y()); - RoundedRect::Radii topCornerRadii; - topCornerRadii.setTopRight(clipRect.radii().topRight()); - context->clipRoundedRect(RoundedRect(topCorner, topCornerRadii)); - - IntRect bottomCorner(clipRect.rect().x(), rect.y(), rect.maxX() - clipRect.rect().x(), clipRect.rect().maxY() - rect.y()); - RoundedRect::Radii bottomCornerRadii; - bottomCornerRadii.setBottomLeft(clipRect.radii().bottomLeft()); - context->clipRoundedRect(RoundedRect(bottomCorner, bottomCornerRadii)); - } - } -} - -// FIXME: See crbug.com/382491. The use of getCTM in this context is incorrect because the matrix returned does not -// include scales applied at raster time, such as the device zoom. -static LayoutRect shrinkRectByOnePixel(GraphicsContext* context, const LayoutRect& rect) -{ - LayoutRect shrunkRect = rect; - AffineTransform transform = context->getCTM(); - shrunkRect.inflateX(-static_cast(ceil(1 / transform.xScale()))); - shrunkRect.inflateY(-static_cast(ceil(1 / transform.yScale()))); - return shrunkRect; -} - -LayoutRect RenderBoxModelObject::borderInnerRectAdjustedForBleedAvoidance(GraphicsContext* context, const LayoutRect& rect, BackgroundBleedAvoidance bleedAvoidance) const -{ - // We shrink the rectangle by one pixel on each side to make it fully overlap the anti-aliased background border - return (bleedAvoidance == BackgroundBleedBackgroundOverBorder) ? shrinkRectByOnePixel(context, rect) : rect; -} - -RoundedRect RenderBoxModelObject::backgroundRoundedRectAdjustedForBleedAvoidance(GraphicsContext* context, const LayoutRect& borderRect, BackgroundBleedAvoidance bleedAvoidance, InlineFlowBox* box, const LayoutSize& boxSize, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) const -{ - if (bleedAvoidance == BackgroundBleedShrinkBackground) { - // We shrink the rectangle by one pixel on each side because the bleed is one pixel maximum. - return getBackgroundRoundedRect(shrinkRectByOnePixel(context, borderRect), box, boxSize.width(), boxSize.height(), includeLogicalLeftEdge, includeLogicalRightEdge); - } - if (bleedAvoidance == BackgroundBleedBackgroundOverBorder) - return style()->getRoundedInnerBorderFor(borderRect, includeLogicalLeftEdge, includeLogicalRightEdge); - - return getBackgroundRoundedRect(borderRect, box, boxSize.width(), boxSize.height(), includeLogicalLeftEdge, includeLogicalRightEdge); -} - -static void applyBoxShadowForBackground(GraphicsContext* context, const RenderObject* renderer) -{ - const ShadowList* shadowList = renderer->style()->boxShadow(); - ASSERT(shadowList); - for (size_t i = shadowList->shadows().size(); i--; ) { - const ShadowData& boxShadow = shadowList->shadows()[i]; - if (boxShadow.style() != Normal) - continue; - FloatSize shadowOffset(boxShadow.x(), boxShadow.y()); - context->setShadow(shadowOffset, boxShadow.blur(), boxShadow.color(), - DrawLooperBuilder::ShadowRespectsTransforms, DrawLooperBuilder::ShadowIgnoresAlpha); - return; + // Objects that shrink to avoid floats normally use available line width when + // computing containing block width. However in the case of relative + // positioning using percentages, we can't do this. The offset should always + // be resolved using the available width of the containing block. Therefore + // we don't use containingBlockLogicalWidthForContent() here, but instead + // explicitly call availableWidth on our containing block. + if (!style()->left().isAuto()) { + if (!style()->right().isAuto() && + !containingBlock->style()->isLeftToRightDirection()) + offset.setWidth( + -valueForLength(style()->right(), containingBlock->availableWidth())); + else + offset.expand( + valueForLength(style()->left(), containingBlock->availableWidth()), + 0); + } else if (!style()->right().isAuto()) { + offset.expand( + -valueForLength(style()->right(), containingBlock->availableWidth()), + 0); + } + + // If the containing block of a relatively positioned element does not + // specify a height, a percentage top or bottom offset should be resolved as + // auto. An exception to this is if the containing block has the WinIE quirk + // where and assume the size of the viewport. In this case, + // calculate the percent offset based on this height. + // See . + if (!style()->top().isAuto() && + (!containingBlock->hasAutoHeightOrContainingBlockWithAutoHeight() || + !style()->top().isPercent())) + offset.expand( + 0, valueForLength(style()->top(), containingBlock->availableHeight())); + + else if (!style()->bottom().isAuto() && + (!containingBlock->hasAutoHeightOrContainingBlockWithAutoHeight() || + !style()->bottom().isPercent())) + offset.expand(0, -valueForLength(style()->bottom(), + containingBlock->availableHeight())); + + return offset; +} + +LayoutPoint RenderBoxModelObject::adjustedPositionRelativeToOffsetParent( + const LayoutPoint& startPoint) const { + if (!parent()) + return LayoutPoint(); + + return startPoint; +} + +LayoutUnit RenderBoxModelObject::offsetLeft() const { + // Note that RenderInline and RenderBox override this to pass a different + // startPoint to adjustedPositionRelativeToOffsetParent. + return adjustedPositionRelativeToOffsetParent(LayoutPoint()).x(); +} + +LayoutUnit RenderBoxModelObject::offsetTop() const { + // Note that RenderInline and RenderBox override this to pass a different + // startPoint to adjustedPositionRelativeToOffsetParent. + return adjustedPositionRelativeToOffsetParent(LayoutPoint()).y(); +} + +int RenderBoxModelObject::pixelSnappedOffsetWidth() const { + return snapSizeToPixel(offsetWidth(), offsetLeft()); +} + +int RenderBoxModelObject::pixelSnappedOffsetHeight() const { + return snapSizeToPixel(offsetHeight(), offsetTop()); +} + +LayoutUnit RenderBoxModelObject::computedCSSPadding( + const Length& padding) const { + LayoutUnit w = 0; + if (padding.isPercent()) + w = containingBlockLogicalWidthForContent(); + return minimumValueForLength(padding, w); +} + +RoundedRect RenderBoxModelObject::getBackgroundRoundedRect( + const LayoutRect& borderRect, + InlineFlowBox* box, + LayoutUnit inlineBoxWidth, + LayoutUnit inlineBoxHeight, + bool includeLogicalLeftEdge, + bool includeLogicalRightEdge) const { + RoundedRect border = style()->getRoundedBorderFor( + borderRect, includeLogicalLeftEdge, includeLogicalRightEdge); + if (box && (box->nextLineBox() || box->prevLineBox())) { + RoundedRect segmentBorder = style()->getRoundedBorderFor( + LayoutRect(0, 0, inlineBoxWidth, inlineBoxHeight), + includeLogicalLeftEdge, includeLogicalRightEdge); + border.setRadii(segmentBorder.radii()); + } + + return border; +} + +void RenderBoxModelObject::clipRoundedInnerRect(GraphicsContext* context, + const LayoutRect& rect, + const RoundedRect& clipRect) { + if (clipRect.isRenderable()) + context->clipRoundedRect(clipRect); + else { + // We create a rounded rect for each of the corners and clip it, while + // making sure we clip opposing corners together. + if (!clipRect.radii().topLeft().isEmpty() || + !clipRect.radii().bottomRight().isEmpty()) { + IntRect topCorner(clipRect.rect().x(), clipRect.rect().y(), + rect.maxX() - clipRect.rect().x(), + rect.maxY() - clipRect.rect().y()); + RoundedRect::Radii topCornerRadii; + topCornerRadii.setTopLeft(clipRect.radii().topLeft()); + context->clipRoundedRect(RoundedRect(topCorner, topCornerRadii)); + + IntRect bottomCorner(rect.x(), rect.y(), + clipRect.rect().maxX() - rect.x(), + clipRect.rect().maxY() - rect.y()); + RoundedRect::Radii bottomCornerRadii; + bottomCornerRadii.setBottomRight(clipRect.radii().bottomRight()); + context->clipRoundedRect(RoundedRect(bottomCorner, bottomCornerRadii)); } -} -void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, const Color& color, const FillLayer& bgLayer, const LayoutRect& rect, - BackgroundBleedAvoidance bleedAvoidance, InlineFlowBox* box, const LayoutSize& boxSize, RenderObject* backgroundObject, bool skipBaseColor) -{ - GraphicsContext* context = paintInfo.context; - if (rect.isEmpty()) - return; - - bool includeLeftEdge = box ? box->includeLogicalLeftEdge() : true; - bool includeRightEdge = box ? box->includeLogicalRightEdge() : true; - - bool hasRoundedBorder = style()->hasBorderRadius() && (includeLeftEdge || includeRightEdge); - bool clippedWithLocalScrolling = hasOverflowClip() && bgLayer.attachment() == LocalBackgroundAttachment; - bool isBorderFill = bgLayer.clip() == BorderFillBox; - bool isBottomLayer = !bgLayer.next(); - - Color bgColor = color; - StyleImage* bgImage = bgLayer.image(); - bool shouldPaintBackgroundImage = bgImage && bgImage->canRender(*this); - - bool colorVisible = bgColor.alpha(); - - // Fast path for drawing simple color backgrounds. - if (!clippedWithLocalScrolling && !shouldPaintBackgroundImage && isBorderFill && isBottomLayer) { - if (!colorVisible) - return; - - bool boxShadowShouldBeAppliedToBackground = this->boxShadowShouldBeAppliedToBackground(bleedAvoidance, box); - GraphicsContextStateSaver shadowStateSaver(*context, boxShadowShouldBeAppliedToBackground); - if (boxShadowShouldBeAppliedToBackground) - applyBoxShadowForBackground(context, this); - - if (hasRoundedBorder && bleedAvoidance != BackgroundBleedClipBackground) { - RoundedRect border = backgroundRoundedRectAdjustedForBleedAvoidance(context, rect, bleedAvoidance, box, boxSize, includeLeftEdge, includeRightEdge); - if (border.isRenderable()) - context->fillRoundedRect(border, bgColor); - else { - context->save(); - clipRoundedInnerRect(context, rect, border); - context->fillRect(border.rect(), bgColor); - context->restore(); - } - } else { - context->fillRect(pixelSnappedIntRect(rect), bgColor); - } - - return; + if (!clipRect.radii().topRight().isEmpty() || + !clipRect.radii().bottomLeft().isEmpty()) { + IntRect topCorner(rect.x(), clipRect.rect().y(), + clipRect.rect().maxX() - rect.x(), + rect.maxY() - clipRect.rect().y()); + RoundedRect::Radii topCornerRadii; + topCornerRadii.setTopRight(clipRect.radii().topRight()); + context->clipRoundedRect(RoundedRect(topCorner, topCornerRadii)); + + IntRect bottomCorner(clipRect.rect().x(), rect.y(), + rect.maxX() - clipRect.rect().x(), + clipRect.rect().maxY() - rect.y()); + RoundedRect::Radii bottomCornerRadii; + bottomCornerRadii.setBottomLeft(clipRect.radii().bottomLeft()); + context->clipRoundedRect(RoundedRect(bottomCorner, bottomCornerRadii)); } - - // BorderFillBox radius clipping is taken care of by BackgroundBleedClipBackground - bool clipToBorderRadius = hasRoundedBorder && !(isBorderFill && bleedAvoidance == BackgroundBleedClipBackground); - GraphicsContextStateSaver clipToBorderStateSaver(*context, clipToBorderRadius); - if (clipToBorderRadius) { - RoundedRect border = isBorderFill ? backgroundRoundedRectAdjustedForBleedAvoidance(context, rect, bleedAvoidance, box, boxSize, includeLeftEdge, includeRightEdge) : getBackgroundRoundedRect(rect, box, boxSize.width(), boxSize.height(), includeLeftEdge, includeRightEdge); - - // Clip to the padding or content boxes as necessary. - if (bgLayer.clip() == ContentFillBox) { - border = style()->getRoundedInnerBorderFor(border.rect(), - paddingTop() + borderTop(), paddingBottom() + borderBottom(), paddingLeft() + borderLeft(), paddingRight() + borderRight(), includeLeftEdge, includeRightEdge); - } else if (bgLayer.clip() == PaddingFillBox) - border = style()->getRoundedInnerBorderFor(border.rect(), includeLeftEdge, includeRightEdge); - + } +} + +// FIXME: See crbug.com/382491. The use of getCTM in this context is incorrect +// because the matrix returned does not include scales applied at raster time, +// such as the device zoom. +static LayoutRect shrinkRectByOnePixel(GraphicsContext* context, + const LayoutRect& rect) { + LayoutRect shrunkRect = rect; + AffineTransform transform = context->getCTM(); + shrunkRect.inflateX(-static_cast(ceil(1 / transform.xScale()))); + shrunkRect.inflateY(-static_cast(ceil(1 / transform.yScale()))); + return shrunkRect; +} + +LayoutRect RenderBoxModelObject::borderInnerRectAdjustedForBleedAvoidance( + GraphicsContext* context, + const LayoutRect& rect, + BackgroundBleedAvoidance bleedAvoidance) const { + // We shrink the rectangle by one pixel on each side to make it fully overlap + // the anti-aliased background border + return (bleedAvoidance == BackgroundBleedBackgroundOverBorder) + ? shrinkRectByOnePixel(context, rect) + : rect; +} + +RoundedRect +RenderBoxModelObject::backgroundRoundedRectAdjustedForBleedAvoidance( + GraphicsContext* context, + const LayoutRect& borderRect, + BackgroundBleedAvoidance bleedAvoidance, + InlineFlowBox* box, + const LayoutSize& boxSize, + bool includeLogicalLeftEdge, + bool includeLogicalRightEdge) const { + if (bleedAvoidance == BackgroundBleedShrinkBackground) { + // We shrink the rectangle by one pixel on each side because the bleed is + // one pixel maximum. + return getBackgroundRoundedRect( + shrinkRectByOnePixel(context, borderRect), box, boxSize.width(), + boxSize.height(), includeLogicalLeftEdge, includeLogicalRightEdge); + } + if (bleedAvoidance == BackgroundBleedBackgroundOverBorder) + return style()->getRoundedInnerBorderFor(borderRect, includeLogicalLeftEdge, + includeLogicalRightEdge); + + return getBackgroundRoundedRect(borderRect, box, boxSize.width(), + boxSize.height(), includeLogicalLeftEdge, + includeLogicalRightEdge); +} + +static void applyBoxShadowForBackground(GraphicsContext* context, + const RenderObject* renderer) { + const ShadowList* shadowList = renderer->style()->boxShadow(); + ASSERT(shadowList); + for (size_t i = shadowList->shadows().size(); i--;) { + const ShadowData& boxShadow = shadowList->shadows()[i]; + if (boxShadow.style() != Normal) + continue; + FloatSize shadowOffset(boxShadow.x(), boxShadow.y()); + context->setShadow(shadowOffset, boxShadow.blur(), boxShadow.color(), + DrawLooperBuilder::ShadowRespectsTransforms, + DrawLooperBuilder::ShadowIgnoresAlpha); + return; + } +} + +void RenderBoxModelObject::paintFillLayerExtended( + const PaintInfo& paintInfo, + const Color& color, + const FillLayer& bgLayer, + const LayoutRect& rect, + BackgroundBleedAvoidance bleedAvoidance, + InlineFlowBox* box, + const LayoutSize& boxSize, + RenderObject* backgroundObject, + bool skipBaseColor) { + GraphicsContext* context = paintInfo.context; + if (rect.isEmpty()) + return; + + bool includeLeftEdge = box ? box->includeLogicalLeftEdge() : true; + bool includeRightEdge = box ? box->includeLogicalRightEdge() : true; + + bool hasRoundedBorder = + style()->hasBorderRadius() && (includeLeftEdge || includeRightEdge); + bool clippedWithLocalScrolling = + hasOverflowClip() && bgLayer.attachment() == LocalBackgroundAttachment; + bool isBorderFill = bgLayer.clip() == BorderFillBox; + bool isBottomLayer = !bgLayer.next(); + + Color bgColor = color; + StyleImage* bgImage = bgLayer.image(); + bool shouldPaintBackgroundImage = bgImage && bgImage->canRender(*this); + + bool colorVisible = bgColor.alpha(); + + // Fast path for drawing simple color backgrounds. + if (!clippedWithLocalScrolling && !shouldPaintBackgroundImage && + isBorderFill && isBottomLayer) { + if (!colorVisible) + return; + + bool boxShadowShouldBeAppliedToBackground = + this->boxShadowShouldBeAppliedToBackground(bleedAvoidance, box); + GraphicsContextStateSaver shadowStateSaver( + *context, boxShadowShouldBeAppliedToBackground); + if (boxShadowShouldBeAppliedToBackground) + applyBoxShadowForBackground(context, this); + + if (hasRoundedBorder && bleedAvoidance != BackgroundBleedClipBackground) { + RoundedRect border = backgroundRoundedRectAdjustedForBleedAvoidance( + context, rect, bleedAvoidance, box, boxSize, includeLeftEdge, + includeRightEdge); + if (border.isRenderable()) + context->fillRoundedRect(border, bgColor); + else { + context->save(); clipRoundedInnerRect(context, rect, border); + context->fillRect(border.rect(), bgColor); + context->restore(); + } + } else { + context->fillRect(pixelSnappedIntRect(rect), bgColor); } - int bLeft = includeLeftEdge ? borderLeft() : 0; - int bRight = includeRightEdge ? borderRight() : 0; - LayoutUnit pLeft = includeLeftEdge ? paddingLeft() : LayoutUnit(); - LayoutUnit pRight = includeRightEdge ? paddingRight() : LayoutUnit(); - - GraphicsContextStateSaver clipWithScrollingStateSaver(*context, clippedWithLocalScrolling); - LayoutRect scrolledPaintRect = rect; - if (clippedWithLocalScrolling) { - // Clip to the overflow area. - RenderBox* thisBox = toRenderBox(this); - context->clip(thisBox->overflowClipRect(rect.location())); - - // Adjust the paint rect to reflect a scrolled content box with borders at the ends. - scrolledPaintRect.setWidth(bLeft + thisBox->clientWidth() + bRight); - scrolledPaintRect.setHeight(borderTop() + thisBox->clientHeight() + borderBottom()); - } - - GraphicsContextStateSaver backgroundClipStateSaver(*context, false); - - switch (bgLayer.clip()) { + return; + } + + // BorderFillBox radius clipping is taken care of by + // BackgroundBleedClipBackground + bool clipToBorderRadius = + hasRoundedBorder && + !(isBorderFill && bleedAvoidance == BackgroundBleedClipBackground); + GraphicsContextStateSaver clipToBorderStateSaver(*context, + clipToBorderRadius); + if (clipToBorderRadius) { + RoundedRect border = isBorderFill + ? backgroundRoundedRectAdjustedForBleedAvoidance( + context, rect, bleedAvoidance, box, boxSize, + includeLeftEdge, includeRightEdge) + : getBackgroundRoundedRect( + rect, box, boxSize.width(), boxSize.height(), + includeLeftEdge, includeRightEdge); + + // Clip to the padding or content boxes as necessary. + if (bgLayer.clip() == ContentFillBox) { + border = style()->getRoundedInnerBorderFor( + border.rect(), paddingTop() + borderTop(), + paddingBottom() + borderBottom(), paddingLeft() + borderLeft(), + paddingRight() + borderRight(), includeLeftEdge, includeRightEdge); + } else if (bgLayer.clip() == PaddingFillBox) + border = style()->getRoundedInnerBorderFor(border.rect(), includeLeftEdge, + includeRightEdge); + + clipRoundedInnerRect(context, rect, border); + } + + int bLeft = includeLeftEdge ? borderLeft() : 0; + int bRight = includeRightEdge ? borderRight() : 0; + LayoutUnit pLeft = includeLeftEdge ? paddingLeft() : LayoutUnit(); + LayoutUnit pRight = includeRightEdge ? paddingRight() : LayoutUnit(); + + GraphicsContextStateSaver clipWithScrollingStateSaver( + *context, clippedWithLocalScrolling); + LayoutRect scrolledPaintRect = rect; + if (clippedWithLocalScrolling) { + // Clip to the overflow area. + RenderBox* thisBox = toRenderBox(this); + context->clip(thisBox->overflowClipRect(rect.location())); + + // Adjust the paint rect to reflect a scrolled content box with borders at + // the ends. + scrolledPaintRect.setWidth(bLeft + thisBox->clientWidth() + bRight); + scrolledPaintRect.setHeight(borderTop() + thisBox->clientHeight() + + borderBottom()); + } + + GraphicsContextStateSaver backgroundClipStateSaver(*context, false); + + switch (bgLayer.clip()) { case PaddingFillBox: case ContentFillBox: { - if (clipToBorderRadius) - break; - - // Clip to the padding or content boxes as necessary. - bool includePadding = bgLayer.clip() == ContentFillBox; - LayoutRect clipRect = LayoutRect(scrolledPaintRect.x() + bLeft + (includePadding ? pLeft : LayoutUnit()), - scrolledPaintRect.y() + borderTop() + (includePadding ? paddingTop() : LayoutUnit()), - scrolledPaintRect.width() - bLeft - bRight - (includePadding ? pLeft + pRight : LayoutUnit()), - scrolledPaintRect.height() - borderTop() - borderBottom() - (includePadding ? paddingTop() + paddingBottom() : LayoutUnit())); - backgroundClipStateSaver.save(); - context->clip(clipRect); - + if (clipToBorderRadius) break; + + // Clip to the padding or content boxes as necessary. + bool includePadding = bgLayer.clip() == ContentFillBox; + LayoutRect clipRect = LayoutRect( + scrolledPaintRect.x() + bLeft + + (includePadding ? pLeft : LayoutUnit()), + scrolledPaintRect.y() + borderTop() + + (includePadding ? paddingTop() : LayoutUnit()), + scrolledPaintRect.width() - bLeft - bRight - + (includePadding ? pLeft + pRight : LayoutUnit()), + scrolledPaintRect.height() - borderTop() - borderBottom() - + (includePadding ? paddingTop() + paddingBottom() : LayoutUnit())); + backgroundClipStateSaver.save(); + context->clip(clipRect); + + break; } case BorderFillBox: - break; + break; default: - ASSERT_NOT_REACHED(); - break; - } - - // Paint the color first underneath all images, culled if background image occludes it. - // FIXME: In the bgLayer->hasFiniteBounds() case, we could improve the culling test - // by verifying whether the background image covers the entire layout rect. - if (isBottomLayer) { - IntRect backgroundRect(pixelSnappedIntRect(scrolledPaintRect)); - bool boxShadowShouldBeAppliedToBackground = this->boxShadowShouldBeAppliedToBackground(bleedAvoidance, box); - if (boxShadowShouldBeAppliedToBackground || !shouldPaintBackgroundImage || !bgLayer.hasOpaqueImage(this) || !bgLayer.hasRepeatXY()) { - if (!boxShadowShouldBeAppliedToBackground) - backgroundRect.intersect(paintInfo.rect); - - GraphicsContextStateSaver shadowStateSaver(*context, boxShadowShouldBeAppliedToBackground); - if (boxShadowShouldBeAppliedToBackground) - applyBoxShadowForBackground(context, this); - - if (bgColor.alpha()) - context->fillRect(backgroundRect, bgColor, context->compositeOperation()); - } + ASSERT_NOT_REACHED(); + break; + } + + // Paint the color first underneath all images, culled if background image + // occludes it. + // FIXME: In the bgLayer->hasFiniteBounds() case, we could improve the culling + // test by verifying whether the background image covers the entire layout + // rect. + if (isBottomLayer) { + IntRect backgroundRect(pixelSnappedIntRect(scrolledPaintRect)); + bool boxShadowShouldBeAppliedToBackground = + this->boxShadowShouldBeAppliedToBackground(bleedAvoidance, box); + if (boxShadowShouldBeAppliedToBackground || !shouldPaintBackgroundImage || + !bgLayer.hasOpaqueImage(this) || !bgLayer.hasRepeatXY()) { + if (!boxShadowShouldBeAppliedToBackground) + backgroundRect.intersect(paintInfo.rect); + + GraphicsContextStateSaver shadowStateSaver( + *context, boxShadowShouldBeAppliedToBackground); + if (boxShadowShouldBeAppliedToBackground) + applyBoxShadowForBackground(context, this); + + if (bgColor.alpha()) + context->fillRect(backgroundRect, bgColor, + context->compositeOperation()); } + } } -static inline int resolveWidthForRatio(int height, const FloatSize& intrinsicRatio) -{ - return ceilf(height * intrinsicRatio.width() / intrinsicRatio.height()); +static inline int resolveWidthForRatio(int height, + const FloatSize& intrinsicRatio) { + return ceilf(height * intrinsicRatio.width() / intrinsicRatio.height()); } -static inline int resolveHeightForRatio(int width, const FloatSize& intrinsicRatio) -{ - return ceilf(width * intrinsicRatio.height() / intrinsicRatio.width()); +static inline int resolveHeightForRatio(int width, + const FloatSize& intrinsicRatio) { + return ceilf(width * intrinsicRatio.height() / intrinsicRatio.width()); } -static inline IntSize resolveAgainstIntrinsicWidthOrHeightAndRatio(const IntSize& size, const FloatSize& intrinsicRatio, int useWidth, int useHeight) -{ - if (intrinsicRatio.isEmpty()) { - if (useWidth) - return IntSize(useWidth, size.height()); - return IntSize(size.width(), useHeight); - } - +static inline IntSize resolveAgainstIntrinsicWidthOrHeightAndRatio( + const IntSize& size, + const FloatSize& intrinsicRatio, + int useWidth, + int useHeight) { + if (intrinsicRatio.isEmpty()) { if (useWidth) - return IntSize(useWidth, resolveHeightForRatio(useWidth, intrinsicRatio)); - return IntSize(resolveWidthForRatio(useHeight, intrinsicRatio), useHeight); -} - -static inline IntSize resolveAgainstIntrinsicRatio(const IntSize& size, const FloatSize& intrinsicRatio) -{ - // Two possible solutions: (size.width(), solutionHeight) or (solutionWidth, size.height()) - // "... must be assumed to be the largest dimensions..." = easiest answer: the rect with the largest surface area. - - int solutionWidth = resolveWidthForRatio(size.height(), intrinsicRatio); - int solutionHeight = resolveHeightForRatio(size.width(), intrinsicRatio); - if (solutionWidth <= size.width()) { - if (solutionHeight <= size.height()) { - // If both solutions fit, choose the one covering the larger area. - int areaOne = solutionWidth * size.height(); - int areaTwo = size.width() * solutionHeight; - if (areaOne < areaTwo) - return IntSize(size.width(), solutionHeight); - return IntSize(solutionWidth, size.height()); - } - - // Only the first solution fits. - return IntSize(solutionWidth, size.height()); + return IntSize(useWidth, size.height()); + return IntSize(size.width(), useHeight); + } + + if (useWidth) + return IntSize(useWidth, resolveHeightForRatio(useWidth, intrinsicRatio)); + return IntSize(resolveWidthForRatio(useHeight, intrinsicRatio), useHeight); +} + +static inline IntSize resolveAgainstIntrinsicRatio( + const IntSize& size, + const FloatSize& intrinsicRatio) { + // Two possible solutions: (size.width(), solutionHeight) or (solutionWidth, + // size.height()) + // "... must be assumed to be the largest dimensions..." = easiest answer: the + // rect with the largest surface area. + + int solutionWidth = resolveWidthForRatio(size.height(), intrinsicRatio); + int solutionHeight = resolveHeightForRatio(size.width(), intrinsicRatio); + if (solutionWidth <= size.width()) { + if (solutionHeight <= size.height()) { + // If both solutions fit, choose the one covering the larger area. + int areaOne = solutionWidth * size.height(); + int areaTwo = size.width() * solutionHeight; + if (areaOne < areaTwo) + return IntSize(size.width(), solutionHeight); + return IntSize(solutionWidth, size.height()); } - // Only the second solution fits, assert that. - ASSERT(solutionHeight <= size.height()); - return IntSize(size.width(), solutionHeight); -} - -IntSize RenderBoxModelObject::calculateImageIntrinsicDimensions(StyleImage* image, const IntSize& positioningAreaSize) const -{ - // A generated image without a fixed size, will always return the container size as intrinsic size. - if (image->isGeneratedImage() && image->usesImageContainerSize()) - return IntSize(positioningAreaSize.width(), positioningAreaSize.height()); - - Length intrinsicWidth; - Length intrinsicHeight; - FloatSize intrinsicRatio; - image->computeIntrinsicDimensions(this, intrinsicWidth, intrinsicHeight, intrinsicRatio); - - ASSERT(!intrinsicWidth.isPercent()); - ASSERT(!intrinsicHeight.isPercent()); - - IntSize resolvedSize(intrinsicWidth.value(), intrinsicHeight.value()); - IntSize minimumSize(resolvedSize.width() > 0 ? 1 : 0, resolvedSize.height() > 0 ? 1 : 0); - resolvedSize.clampToMinimumSize(minimumSize); - - if (!resolvedSize.isEmpty()) - return resolvedSize; - - // If the image has one of either an intrinsic width or an intrinsic height: - // * and an intrinsic aspect ratio, then the missing dimension is calculated from the given dimension and the ratio. - // * and no intrinsic aspect ratio, then the missing dimension is assumed to be the size of the rectangle that - // establishes the coordinate system for the 'background-position' property. - if (resolvedSize.width() > 0 || resolvedSize.height() > 0) - return resolveAgainstIntrinsicWidthOrHeightAndRatio(positioningAreaSize, intrinsicRatio, resolvedSize.width(), resolvedSize.height()); - - // If the image has no intrinsic dimensions and has an intrinsic ratio the dimensions must be assumed to be the - // largest dimensions at that ratio such that neither dimension exceeds the dimensions of the rectangle that - // establishes the coordinate system for the 'background-position' property. - if (!intrinsicRatio.isEmpty()) - return resolveAgainstIntrinsicRatio(positioningAreaSize, intrinsicRatio); - - // If the image has no intrinsic ratio either, then the dimensions must be assumed to be the rectangle that - // establishes the coordinate system for the 'background-position' property. - return positioningAreaSize; -} - -static inline void applySubPixelHeuristicForTileSize(LayoutSize& tileSize, const IntSize& positioningAreaSize) -{ - tileSize.setWidth(positioningAreaSize.width() - tileSize.width() <= 1 ? tileSize.width().ceil() : tileSize.width().floor()); - tileSize.setHeight(positioningAreaSize.height() - tileSize.height() <= 1 ? tileSize.height().ceil() : tileSize.height().floor()); -} - -IntSize RenderBoxModelObject::calculateFillTileSize(const FillLayer& fillLayer, const IntSize& positioningAreaSize) const -{ - StyleImage* image = fillLayer.image(); - EFillSizeType type = fillLayer.size().type; - - IntSize imageIntrinsicSize = calculateImageIntrinsicDimensions(image, positioningAreaSize); - imageIntrinsicSize.scale(1 / image->imageScaleFactor(), 1 / image->imageScaleFactor()); - switch (type) { - case SizeLength: { - LayoutSize tileSize = positioningAreaSize; - - Length layerWidth = fillLayer.size().size.width(); - Length layerHeight = fillLayer.size().size.height(); - - if (layerWidth.isFixed()) - tileSize.setWidth(layerWidth.value()); - else if (layerWidth.isPercent()) - tileSize.setWidth(valueForLength(layerWidth, positioningAreaSize.width())); - - if (layerHeight.isFixed()) - tileSize.setHeight(layerHeight.value()); - else if (layerHeight.isPercent()) - tileSize.setHeight(valueForLength(layerHeight, positioningAreaSize.height())); - - applySubPixelHeuristicForTileSize(tileSize, positioningAreaSize); - - // If one of the values is auto we have to use the appropriate - // scale to maintain our aspect ratio. - if (layerWidth.isAuto() && !layerHeight.isAuto()) { - if (imageIntrinsicSize.height()) - tileSize.setWidth(imageIntrinsicSize.width() * tileSize.height() / imageIntrinsicSize.height()); - } else if (!layerWidth.isAuto() && layerHeight.isAuto()) { - if (imageIntrinsicSize.width()) - tileSize.setHeight(imageIntrinsicSize.height() * tileSize.width() / imageIntrinsicSize.width()); - } else if (layerWidth.isAuto() && layerHeight.isAuto()) { - // If both width and height are auto, use the image's intrinsic size. - tileSize = imageIntrinsicSize; - } - - tileSize.clampNegativeToZero(); - return flooredIntSize(tileSize); - } - case SizeNone: { - // If both values are ‘auto’ then the intrinsic width and/or height of the image should be used, if any. - if (!imageIntrinsicSize.isEmpty()) - return imageIntrinsicSize; - - // If the image has neither an intrinsic width nor an intrinsic height, its size is determined as for ‘contain’. - type = Contain; - } - case Contain: - case Cover: { - float horizontalScaleFactor = imageIntrinsicSize.width() - ? static_cast(positioningAreaSize.width()) / imageIntrinsicSize.width() : 1; - float verticalScaleFactor = imageIntrinsicSize.height() - ? static_cast(positioningAreaSize.height()) / imageIntrinsicSize.height() : 1; - float scaleFactor = type == Contain ? std::min(horizontalScaleFactor, verticalScaleFactor) : std::max(horizontalScaleFactor, verticalScaleFactor); - return IntSize(std::max(1l, lround(imageIntrinsicSize.width() * scaleFactor)), std::max(1l, lround(imageIntrinsicSize.height() * scaleFactor))); - } + // Only the first solution fits. + return IntSize(solutionWidth, size.height()); + } + + // Only the second solution fits, assert that. + ASSERT(solutionHeight <= size.height()); + return IntSize(size.width(), solutionHeight); +} + +IntSize RenderBoxModelObject::calculateImageIntrinsicDimensions( + StyleImage* image, + const IntSize& positioningAreaSize) const { + // A generated image without a fixed size, will always return the container + // size as intrinsic size. + if (image->isGeneratedImage() && image->usesImageContainerSize()) + return IntSize(positioningAreaSize.width(), positioningAreaSize.height()); + + Length intrinsicWidth; + Length intrinsicHeight; + FloatSize intrinsicRatio; + image->computeIntrinsicDimensions(this, intrinsicWidth, intrinsicHeight, + intrinsicRatio); + + ASSERT(!intrinsicWidth.isPercent()); + ASSERT(!intrinsicHeight.isPercent()); + + IntSize resolvedSize(intrinsicWidth.value(), intrinsicHeight.value()); + IntSize minimumSize(resolvedSize.width() > 0 ? 1 : 0, + resolvedSize.height() > 0 ? 1 : 0); + resolvedSize.clampToMinimumSize(minimumSize); + + if (!resolvedSize.isEmpty()) + return resolvedSize; + + // If the image has one of either an intrinsic width or an intrinsic height: + // * and an intrinsic aspect ratio, then the missing dimension is calculated + // from the given dimension and the ratio. + // * and no intrinsic aspect ratio, then the missing dimension is assumed to + // be the size of the rectangle that + // establishes the coordinate system for the 'background-position' property. + if (resolvedSize.width() > 0 || resolvedSize.height() > 0) + return resolveAgainstIntrinsicWidthOrHeightAndRatio( + positioningAreaSize, intrinsicRatio, resolvedSize.width(), + resolvedSize.height()); + + // If the image has no intrinsic dimensions and has an intrinsic ratio the + // dimensions must be assumed to be the largest dimensions at that ratio such + // that neither dimension exceeds the dimensions of the rectangle that + // establishes the coordinate system for the 'background-position' property. + if (!intrinsicRatio.isEmpty()) + return resolveAgainstIntrinsicRatio(positioningAreaSize, intrinsicRatio); + + // If the image has no intrinsic ratio either, then the dimensions must be + // assumed to be the rectangle that establishes the coordinate system for the + // 'background-position' property. + return positioningAreaSize; +} + +static inline void applySubPixelHeuristicForTileSize( + LayoutSize& tileSize, + const IntSize& positioningAreaSize) { + tileSize.setWidth(positioningAreaSize.width() - tileSize.width() <= 1 + ? tileSize.width().ceil() + : tileSize.width().floor()); + tileSize.setHeight(positioningAreaSize.height() - tileSize.height() <= 1 + ? tileSize.height().ceil() + : tileSize.height().floor()); +} + +IntSize RenderBoxModelObject::calculateFillTileSize( + const FillLayer& fillLayer, + const IntSize& positioningAreaSize) const { + StyleImage* image = fillLayer.image(); + EFillSizeType type = fillLayer.size().type; + + IntSize imageIntrinsicSize = + calculateImageIntrinsicDimensions(image, positioningAreaSize); + imageIntrinsicSize.scale(1 / image->imageScaleFactor(), + 1 / image->imageScaleFactor()); + switch (type) { + case SizeLength: { + LayoutSize tileSize = positioningAreaSize; + + Length layerWidth = fillLayer.size().size.width(); + Length layerHeight = fillLayer.size().size.height(); + + if (layerWidth.isFixed()) + tileSize.setWidth(layerWidth.value()); + else if (layerWidth.isPercent()) + tileSize.setWidth( + valueForLength(layerWidth, positioningAreaSize.width())); + + if (layerHeight.isFixed()) + tileSize.setHeight(layerHeight.value()); + else if (layerHeight.isPercent()) + tileSize.setHeight( + valueForLength(layerHeight, positioningAreaSize.height())); + + applySubPixelHeuristicForTileSize(tileSize, positioningAreaSize); + + // If one of the values is auto we have to use the appropriate + // scale to maintain our aspect ratio. + if (layerWidth.isAuto() && !layerHeight.isAuto()) { + if (imageIntrinsicSize.height()) + tileSize.setWidth(imageIntrinsicSize.width() * tileSize.height() / + imageIntrinsicSize.height()); + } else if (!layerWidth.isAuto() && layerHeight.isAuto()) { + if (imageIntrinsicSize.width()) + tileSize.setHeight(imageIntrinsicSize.height() * tileSize.width() / + imageIntrinsicSize.width()); + } else if (layerWidth.isAuto() && layerHeight.isAuto()) { + // If both width and height are auto, use the image's intrinsic size. + tileSize = imageIntrinsicSize; + } + + tileSize.clampNegativeToZero(); + return flooredIntSize(tileSize); } + case SizeNone: { + // If both values are ‘auto’ then the intrinsic width and/or height of the + // image should be used, if any. + if (!imageIntrinsicSize.isEmpty()) + return imageIntrinsicSize; + + // If the image has neither an intrinsic width nor an intrinsic height, + // its size is determined as for ‘contain’. + type = Contain; + } + case Contain: + case Cover: { + float horizontalScaleFactor = + imageIntrinsicSize.width() + ? static_cast(positioningAreaSize.width()) / + imageIntrinsicSize.width() + : 1; + float verticalScaleFactor = + imageIntrinsicSize.height() + ? static_cast(positioningAreaSize.height()) / + imageIntrinsicSize.height() + : 1; + float scaleFactor = + type == Contain + ? std::min(horizontalScaleFactor, verticalScaleFactor) + : std::max(horizontalScaleFactor, verticalScaleFactor); + return IntSize( + std::max(1l, lround(imageIntrinsicSize.width() * scaleFactor)), + std::max(1l, lround(imageIntrinsicSize.height() * scaleFactor))); + } + } - ASSERT_NOT_REACHED(); - return IntSize(); + ASSERT_NOT_REACHED(); + return IntSize(); } -void RenderBoxModelObject::BackgroundImageGeometry::setNoRepeatX(int xOffset) -{ - m_destRect.move(std::max(xOffset, 0), 0); - m_phase.setX(-std::min(xOffset, 0)); - m_destRect.setWidth(m_tileSize.width() + std::min(xOffset, 0)); +void RenderBoxModelObject::BackgroundImageGeometry::setNoRepeatX(int xOffset) { + m_destRect.move(std::max(xOffset, 0), 0); + m_phase.setX(-std::min(xOffset, 0)); + m_destRect.setWidth(m_tileSize.width() + std::min(xOffset, 0)); } -void RenderBoxModelObject::BackgroundImageGeometry::setNoRepeatY(int yOffset) -{ - m_destRect.move(0, std::max(yOffset, 0)); - m_phase.setY(-std::min(yOffset, 0)); - m_destRect.setHeight(m_tileSize.height() + std::min(yOffset, 0)); +void RenderBoxModelObject::BackgroundImageGeometry::setNoRepeatY(int yOffset) { + m_destRect.move(0, std::max(yOffset, 0)); + m_phase.setY(-std::min(yOffset, 0)); + m_destRect.setHeight(m_tileSize.height() + std::min(yOffset, 0)); } -void RenderBoxModelObject::BackgroundImageGeometry::useFixedAttachment(const IntPoint& attachmentPoint) -{ - IntPoint alignedPoint = attachmentPoint; - m_phase.move(std::max(alignedPoint.x() - m_destRect.x(), 0), std::max(alignedPoint.y() - m_destRect.y(), 0)); +void RenderBoxModelObject::BackgroundImageGeometry::useFixedAttachment( + const IntPoint& attachmentPoint) { + IntPoint alignedPoint = attachmentPoint; + m_phase.move(std::max(alignedPoint.x() - m_destRect.x(), 0), + std::max(alignedPoint.y() - m_destRect.y(), 0)); } -void RenderBoxModelObject::BackgroundImageGeometry::clip(const IntRect& clipRect) -{ - m_destRect.intersect(clipRect); +void RenderBoxModelObject::BackgroundImageGeometry::clip( + const IntRect& clipRect) { + m_destRect.intersect(clipRect); } -IntPoint RenderBoxModelObject::BackgroundImageGeometry::relativePhase() const -{ - IntPoint phase = m_phase; - phase += m_destRect.location() - m_destOrigin; - return phase; +IntPoint RenderBoxModelObject::BackgroundImageGeometry::relativePhase() const { + IntPoint phase = m_phase; + phase += m_destRect.location() - m_destOrigin; + return phase; } class BorderEdge { -public: - BorderEdge(int edgeWidth, const Color& edgeColor, EBorderStyle edgeStyle, bool edgeIsTransparent, bool edgeIsPresent = true) - : width(edgeWidth) - , color(edgeColor) - , style(edgeStyle) - , isTransparent(edgeIsTransparent) - , isPresent(edgeIsPresent) - { - if (style == DOUBLE && edgeWidth < 3) - style = SOLID; - } - - BorderEdge() - : width(0) - , style(BHIDDEN) - , isTransparent(false) - , isPresent(false) - { - } + public: + BorderEdge(int edgeWidth, + const Color& edgeColor, + EBorderStyle edgeStyle, + bool edgeIsTransparent, + bool edgeIsPresent = true) + : width(edgeWidth), + color(edgeColor), + style(edgeStyle), + isTransparent(edgeIsTransparent), + isPresent(edgeIsPresent) { + if (style == DOUBLE && edgeWidth < 3) + style = SOLID; + } + + BorderEdge() + : width(0), style(BHIDDEN), isTransparent(false), isPresent(false) {} + + bool hasVisibleColorAndStyle() const { + return style > BHIDDEN && !isTransparent; + } + bool shouldRender() const { + return isPresent && width && hasVisibleColorAndStyle(); + } + bool presentButInvisible() const { + return usedWidth() && !hasVisibleColorAndStyle(); + } + bool obscuresBackgroundEdge(float scale) const { + if (!isPresent || isTransparent || (width * scale) < 2 || + color.hasAlpha() || style == BHIDDEN) + return false; + + if (style == DOTTED || style == DASHED) + return false; + + if (style == DOUBLE) + return width >= 5 * scale; // The outer band needs to be >= 2px wide at + // unit scale. - bool hasVisibleColorAndStyle() const { return style > BHIDDEN && !isTransparent; } - bool shouldRender() const { return isPresent && width && hasVisibleColorAndStyle(); } - bool presentButInvisible() const { return usedWidth() && !hasVisibleColorAndStyle(); } - bool obscuresBackgroundEdge(float scale) const - { - if (!isPresent || isTransparent || (width * scale) < 2 || color.hasAlpha() || style == BHIDDEN) - return false; - - if (style == DOTTED || style == DASHED) - return false; - - if (style == DOUBLE) - return width >= 5 * scale; // The outer band needs to be >= 2px wide at unit scale. - - return true; - } - bool obscuresBackground() const - { - if (!isPresent || isTransparent || color.hasAlpha() || style == BHIDDEN) - return false; + return true; + } + bool obscuresBackground() const { + if (!isPresent || isTransparent || color.hasAlpha() || style == BHIDDEN) + return false; - if (style == DOTTED || style == DASHED || style == DOUBLE) - return false; + if (style == DOTTED || style == DASHED || style == DOUBLE) + return false; - return true; - } + return true; + } - int usedWidth() const { return isPresent ? width : 0; } + int usedWidth() const { return isPresent ? width : 0; } - void getDoubleBorderStripeWidths(int& outerWidth, int& innerWidth) const - { - int fullWidth = usedWidth(); - outerWidth = fullWidth / 3; - innerWidth = fullWidth * 2 / 3; + void getDoubleBorderStripeWidths(int& outerWidth, int& innerWidth) const { + int fullWidth = usedWidth(); + outerWidth = fullWidth / 3; + innerWidth = fullWidth * 2 / 3; - // We need certain integer rounding results - if (fullWidth % 3 == 2) - outerWidth += 1; + // We need certain integer rounding results + if (fullWidth % 3 == 2) + outerWidth += 1; - if (fullWidth % 3 == 1) - innerWidth += 1; - } + if (fullWidth % 3 == 1) + innerWidth += 1; + } - int width; - Color color; - EBorderStyle style; - bool isTransparent; - bool isPresent; + int width; + Color color; + EBorderStyle style; + bool isTransparent; + bool isPresent; }; -static bool allCornersClippedOut(const RoundedRect& border, const LayoutRect& clipRect) -{ - LayoutRect boundingRect = border.rect(); - if (clipRect.contains(boundingRect)) - return false; +static bool allCornersClippedOut(const RoundedRect& border, + const LayoutRect& clipRect) { + LayoutRect boundingRect = border.rect(); + if (clipRect.contains(boundingRect)) + return false; - RoundedRect::Radii radii = border.radii(); + RoundedRect::Radii radii = border.radii(); - LayoutRect topLeftRect(boundingRect.location(), radii.topLeft()); - if (clipRect.intersects(topLeftRect)) - return false; + LayoutRect topLeftRect(boundingRect.location(), radii.topLeft()); + if (clipRect.intersects(topLeftRect)) + return false; - LayoutRect topRightRect(boundingRect.location(), radii.topRight()); - topRightRect.setX(boundingRect.maxX() - topRightRect.width()); - if (clipRect.intersects(topRightRect)) - return false; + LayoutRect topRightRect(boundingRect.location(), radii.topRight()); + topRightRect.setX(boundingRect.maxX() - topRightRect.width()); + if (clipRect.intersects(topRightRect)) + return false; - LayoutRect bottomLeftRect(boundingRect.location(), radii.bottomLeft()); - bottomLeftRect.setY(boundingRect.maxY() - bottomLeftRect.height()); - if (clipRect.intersects(bottomLeftRect)) - return false; + LayoutRect bottomLeftRect(boundingRect.location(), radii.bottomLeft()); + bottomLeftRect.setY(boundingRect.maxY() - bottomLeftRect.height()); + if (clipRect.intersects(bottomLeftRect)) + return false; - LayoutRect bottomRightRect(boundingRect.location(), radii.bottomRight()); - bottomRightRect.setX(boundingRect.maxX() - bottomRightRect.width()); - bottomRightRect.setY(boundingRect.maxY() - bottomRightRect.height()); - if (clipRect.intersects(bottomRightRect)) - return false; + LayoutRect bottomRightRect(boundingRect.location(), radii.bottomRight()); + bottomRightRect.setX(boundingRect.maxX() - bottomRightRect.width()); + bottomRightRect.setY(boundingRect.maxY() - bottomRightRect.height()); + if (clipRect.intersects(bottomRightRect)) + return false; - return true; + return true; } -static bool borderWillArcInnerEdge(const LayoutSize& firstRadius, const FloatSize& secondRadius) -{ - return !firstRadius.isZero() || !secondRadius.isZero(); +static bool borderWillArcInnerEdge(const LayoutSize& firstRadius, + const FloatSize& secondRadius) { + return !firstRadius.isZero() || !secondRadius.isZero(); } enum BorderEdgeFlag { - TopBorderEdge = 1 << BSTop, - RightBorderEdge = 1 << BSRight, - BottomBorderEdge = 1 << BSBottom, - LeftBorderEdge = 1 << BSLeft, - AllBorderEdges = TopBorderEdge | BottomBorderEdge | LeftBorderEdge | RightBorderEdge + TopBorderEdge = 1 << BSTop, + RightBorderEdge = 1 << BSRight, + BottomBorderEdge = 1 << BSBottom, + LeftBorderEdge = 1 << BSLeft, + AllBorderEdges = + TopBorderEdge | BottomBorderEdge | LeftBorderEdge | RightBorderEdge }; -static inline BorderEdgeFlag edgeFlagForSide(BoxSide side) -{ - return static_cast(1 << side); +static inline BorderEdgeFlag edgeFlagForSide(BoxSide side) { + return static_cast(1 << side); } -static inline bool includesEdge(BorderEdgeFlags flags, BoxSide side) -{ - return flags & edgeFlagForSide(side); +static inline bool includesEdge(BorderEdgeFlags flags, BoxSide side) { + return flags & edgeFlagForSide(side); } -static inline bool includesAdjacentEdges(BorderEdgeFlags flags) -{ - return (flags & (TopBorderEdge | RightBorderEdge)) == (TopBorderEdge | RightBorderEdge) - || (flags & (RightBorderEdge | BottomBorderEdge)) == (RightBorderEdge | BottomBorderEdge) - || (flags & (BottomBorderEdge | LeftBorderEdge)) == (BottomBorderEdge | LeftBorderEdge) - || (flags & (LeftBorderEdge | TopBorderEdge)) == (LeftBorderEdge | TopBorderEdge); +static inline bool includesAdjacentEdges(BorderEdgeFlags flags) { + return (flags & (TopBorderEdge | RightBorderEdge)) == + (TopBorderEdge | RightBorderEdge) || + (flags & (RightBorderEdge | BottomBorderEdge)) == + (RightBorderEdge | BottomBorderEdge) || + (flags & (BottomBorderEdge | LeftBorderEdge)) == + (BottomBorderEdge | LeftBorderEdge) || + (flags & (LeftBorderEdge | TopBorderEdge)) == + (LeftBorderEdge | TopBorderEdge); } -inline bool edgesShareColor(const BorderEdge& firstEdge, const BorderEdge& secondEdge) -{ - return firstEdge.color == secondEdge.color; +inline bool edgesShareColor(const BorderEdge& firstEdge, + const BorderEdge& secondEdge) { + return firstEdge.color == secondEdge.color; } -inline bool styleRequiresClipPolygon(EBorderStyle style) -{ - return style == DOTTED || style == DASHED; // These are drawn with a stroke, so we have to clip to get corner miters. +inline bool styleRequiresClipPolygon(EBorderStyle style) { + return style == DOTTED || style == DASHED; // These are drawn with a stroke, + // so we have to clip to get + // corner miters. } -static bool borderStyleFillsBorderArea(EBorderStyle style) -{ - return !(style == DOTTED || style == DASHED || style == DOUBLE); +static bool borderStyleFillsBorderArea(EBorderStyle style) { + return !(style == DOTTED || style == DASHED || style == DOUBLE); } -static bool borderStyleHasInnerDetail(EBorderStyle style) -{ - return style == GROOVE || style == RIDGE || style == DOUBLE; +static bool borderStyleHasInnerDetail(EBorderStyle style) { + return style == GROOVE || style == RIDGE || style == DOUBLE; } -static bool borderStyleIsDottedOrDashed(EBorderStyle style) -{ - return style == DOTTED || style == DASHED; +static bool borderStyleIsDottedOrDashed(EBorderStyle style) { + return style == DOTTED || style == DASHED; } // OUTSET darkens the bottom and right (and maybe lightens the top and left) // INSET darkens the top and left (and maybe lightens the bottom and right) -static inline bool borderStyleHasUnmatchedColorsAtCorner(EBorderStyle style, BoxSide side, BoxSide adjacentSide) -{ - // These styles match at the top/left and bottom/right. - if (style == INSET || style == GROOVE || style == RIDGE || style == OUTSET) { - const BorderEdgeFlags topRightFlags = edgeFlagForSide(BSTop) | edgeFlagForSide(BSRight); - const BorderEdgeFlags bottomLeftFlags = edgeFlagForSide(BSBottom) | edgeFlagForSide(BSLeft); - - BorderEdgeFlags flags = edgeFlagForSide(side) | edgeFlagForSide(adjacentSide); - return flags == topRightFlags || flags == bottomLeftFlags; - } +static inline bool borderStyleHasUnmatchedColorsAtCorner(EBorderStyle style, + BoxSide side, + BoxSide adjacentSide) { + // These styles match at the top/left and bottom/right. + if (style == INSET || style == GROOVE || style == RIDGE || style == OUTSET) { + const BorderEdgeFlags topRightFlags = + edgeFlagForSide(BSTop) | edgeFlagForSide(BSRight); + const BorderEdgeFlags bottomLeftFlags = + edgeFlagForSide(BSBottom) | edgeFlagForSide(BSLeft); + + BorderEdgeFlags flags = + edgeFlagForSide(side) | edgeFlagForSide(adjacentSide); + return flags == topRightFlags || flags == bottomLeftFlags; + } + return false; +} + +static inline bool colorsMatchAtCorner(BoxSide side, + BoxSide adjacentSide, + const BorderEdge edges[]) { + if (edges[side].shouldRender() != edges[adjacentSide].shouldRender()) return false; -} -static inline bool colorsMatchAtCorner(BoxSide side, BoxSide adjacentSide, const BorderEdge edges[]) -{ - if (edges[side].shouldRender() != edges[adjacentSide].shouldRender()) - return false; - - if (!edgesShareColor(edges[side], edges[adjacentSide])) - return false; + if (!edgesShareColor(edges[side], edges[adjacentSide])) + return false; - return !borderStyleHasUnmatchedColorsAtCorner(edges[side].style, side, adjacentSide); + return !borderStyleHasUnmatchedColorsAtCorner(edges[side].style, side, + adjacentSide); } +static inline bool colorNeedsAntiAliasAtCorner(BoxSide side, + BoxSide adjacentSide, + const BorderEdge edges[]) { + if (!edges[side].color.hasAlpha()) + return false; -static inline bool colorNeedsAntiAliasAtCorner(BoxSide side, BoxSide adjacentSide, const BorderEdge edges[]) -{ - if (!edges[side].color.hasAlpha()) - return false; - - if (edges[side].shouldRender() != edges[adjacentSide].shouldRender()) - return false; + if (edges[side].shouldRender() != edges[adjacentSide].shouldRender()) + return false; - if (!edgesShareColor(edges[side], edges[adjacentSide])) - return true; + if (!edgesShareColor(edges[side], edges[adjacentSide])) + return true; - return borderStyleHasUnmatchedColorsAtCorner(edges[side].style, side, adjacentSide); + return borderStyleHasUnmatchedColorsAtCorner(edges[side].style, side, + adjacentSide); } // This assumes that we draw in order: top, bottom, left, right. -static inline bool willBeOverdrawn(BoxSide side, BoxSide adjacentSide, const BorderEdge edges[]) -{ - switch (side) { +static inline bool willBeOverdrawn(BoxSide side, + BoxSide adjacentSide, + const BorderEdge edges[]) { + switch (side) { case BSTop: case BSBottom: - if (edges[adjacentSide].presentButInvisible()) - return false; + if (edges[adjacentSide].presentButInvisible()) + return false; - if (!edgesShareColor(edges[side], edges[adjacentSide]) && edges[adjacentSide].color.hasAlpha()) - return false; + if (!edgesShareColor(edges[side], edges[adjacentSide]) && + edges[adjacentSide].color.hasAlpha()) + return false; - if (!borderStyleFillsBorderArea(edges[adjacentSide].style)) - return false; + if (!borderStyleFillsBorderArea(edges[adjacentSide].style)) + return false; - return true; + return true; case BSLeft: case BSRight: - // These draw last, so are never overdrawn. - return false; - } - return false; -} - -static inline bool borderStylesRequireMitre(BoxSide side, BoxSide adjacentSide, EBorderStyle style, EBorderStyle adjacentStyle) -{ - if (style == DOUBLE || adjacentStyle == DOUBLE || adjacentStyle == GROOVE || adjacentStyle == RIDGE) - return true; + // These draw last, so are never overdrawn. + return false; + } + return false; +} + +static inline bool borderStylesRequireMitre(BoxSide side, + BoxSide adjacentSide, + EBorderStyle style, + EBorderStyle adjacentStyle) { + if (style == DOUBLE || adjacentStyle == DOUBLE || adjacentStyle == GROOVE || + adjacentStyle == RIDGE) + return true; - if (borderStyleIsDottedOrDashed(style) != borderStyleIsDottedOrDashed(adjacentStyle)) - return true; + if (borderStyleIsDottedOrDashed(style) != + borderStyleIsDottedOrDashed(adjacentStyle)) + return true; - if (style != adjacentStyle) - return true; + if (style != adjacentStyle) + return true; - return borderStyleHasUnmatchedColorsAtCorner(style, side, adjacentSide); + return borderStyleHasUnmatchedColorsAtCorner(style, side, adjacentSide); } -static bool joinRequiresMitre(BoxSide side, BoxSide adjacentSide, const BorderEdge edges[], bool allowOverdraw) -{ - if ((edges[side].isTransparent && edges[adjacentSide].isTransparent) || !edges[adjacentSide].isPresent) - return false; - - if (allowOverdraw && willBeOverdrawn(side, adjacentSide, edges)) - return false; - - if (!edgesShareColor(edges[side], edges[adjacentSide])) - return true; - - if (borderStylesRequireMitre(side, adjacentSide, edges[side].style, edges[adjacentSide].style)) - return true; - +static bool joinRequiresMitre(BoxSide side, + BoxSide adjacentSide, + const BorderEdge edges[], + bool allowOverdraw) { + if ((edges[side].isTransparent && edges[adjacentSide].isTransparent) || + !edges[adjacentSide].isPresent) return false; -} - -void RenderBoxModelObject::paintOneBorderSide(GraphicsContext* graphicsContext, const RenderStyle* style, const RoundedRect& outerBorder, const RoundedRect& innerBorder, - const IntRect& sideRect, BoxSide side, BoxSide adjacentSide1, BoxSide adjacentSide2, const BorderEdge edges[], const Path* path, - BackgroundBleedAvoidance bleedAvoidance, bool includeLogicalLeftEdge, bool includeLogicalRightEdge, bool antialias, const Color* overrideColor) -{ - const BorderEdge& edgeToRender = edges[side]; - ASSERT(edgeToRender.width); - const BorderEdge& adjacentEdge1 = edges[adjacentSide1]; - const BorderEdge& adjacentEdge2 = edges[adjacentSide2]; - bool mitreAdjacentSide1 = joinRequiresMitre(side, adjacentSide1, edges, !antialias); - bool mitreAdjacentSide2 = joinRequiresMitre(side, adjacentSide2, edges, !antialias); - - bool adjacentSide1StylesMatch = colorsMatchAtCorner(side, adjacentSide1, edges); - bool adjacentSide2StylesMatch = colorsMatchAtCorner(side, adjacentSide2, edges); + if (allowOverdraw && willBeOverdrawn(side, adjacentSide, edges)) + return false; - const Color& colorToPaint = overrideColor ? *overrideColor : edgeToRender.color; + if (!edgesShareColor(edges[side], edges[adjacentSide])) + return true; - if (path) { - GraphicsContextStateSaver stateSaver(*graphicsContext); - if (innerBorder.isRenderable()) - clipBorderSidePolygon(graphicsContext, outerBorder, innerBorder, side, adjacentSide1StylesMatch, adjacentSide2StylesMatch); - else - clipBorderSideForComplexInnerPath(graphicsContext, outerBorder, innerBorder, side, edges); - float thickness = std::max(std::max(edgeToRender.width, adjacentEdge1.width), adjacentEdge2.width); - drawBoxSideFromPath(graphicsContext, outerBorder.rect(), *path, edges, edgeToRender.width, thickness, side, style, - colorToPaint, edgeToRender.style, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge); - } else { - bool clipForStyle = styleRequiresClipPolygon(edgeToRender.style) && (mitreAdjacentSide1 || mitreAdjacentSide2); - bool clipAdjacentSide1 = colorNeedsAntiAliasAtCorner(side, adjacentSide1, edges) && mitreAdjacentSide1; - bool clipAdjacentSide2 = colorNeedsAntiAliasAtCorner(side, adjacentSide2, edges) && mitreAdjacentSide2; - bool shouldClip = clipForStyle || clipAdjacentSide1 || clipAdjacentSide2; - - GraphicsContextStateSaver clipStateSaver(*graphicsContext, shouldClip); - if (shouldClip) { - bool aliasAdjacentSide1 = clipAdjacentSide1 || (clipForStyle && mitreAdjacentSide1); - bool aliasAdjacentSide2 = clipAdjacentSide2 || (clipForStyle && mitreAdjacentSide2); - clipBorderSidePolygon(graphicsContext, outerBorder, innerBorder, side, !aliasAdjacentSide1, !aliasAdjacentSide2); - // Since we clipped, no need to draw with a mitre. - mitreAdjacentSide1 = false; - mitreAdjacentSide2 = false; - } + if (borderStylesRequireMitre(side, adjacentSide, edges[side].style, + edges[adjacentSide].style)) + return true; - drawLineForBoxSide(graphicsContext, sideRect.x(), sideRect.y(), sideRect.maxX(), sideRect.maxY(), side, colorToPaint, edgeToRender.style, - mitreAdjacentSide1 ? adjacentEdge1.width : 0, mitreAdjacentSide2 ? adjacentEdge2.width : 0, antialias); + return false; +} + +void RenderBoxModelObject::paintOneBorderSide( + GraphicsContext* graphicsContext, + const RenderStyle* style, + const RoundedRect& outerBorder, + const RoundedRect& innerBorder, + const IntRect& sideRect, + BoxSide side, + BoxSide adjacentSide1, + BoxSide adjacentSide2, + const BorderEdge edges[], + const Path* path, + BackgroundBleedAvoidance bleedAvoidance, + bool includeLogicalLeftEdge, + bool includeLogicalRightEdge, + bool antialias, + const Color* overrideColor) { + const BorderEdge& edgeToRender = edges[side]; + ASSERT(edgeToRender.width); + const BorderEdge& adjacentEdge1 = edges[adjacentSide1]; + const BorderEdge& adjacentEdge2 = edges[adjacentSide2]; + + bool mitreAdjacentSide1 = + joinRequiresMitre(side, adjacentSide1, edges, !antialias); + bool mitreAdjacentSide2 = + joinRequiresMitre(side, adjacentSide2, edges, !antialias); + + bool adjacentSide1StylesMatch = + colorsMatchAtCorner(side, adjacentSide1, edges); + bool adjacentSide2StylesMatch = + colorsMatchAtCorner(side, adjacentSide2, edges); + + const Color& colorToPaint = + overrideColor ? *overrideColor : edgeToRender.color; + + if (path) { + GraphicsContextStateSaver stateSaver(*graphicsContext); + if (innerBorder.isRenderable()) + clipBorderSidePolygon(graphicsContext, outerBorder, innerBorder, side, + adjacentSide1StylesMatch, adjacentSide2StylesMatch); + else + clipBorderSideForComplexInnerPath(graphicsContext, outerBorder, + innerBorder, side, edges); + float thickness = std::max( + std::max(edgeToRender.width, adjacentEdge1.width), adjacentEdge2.width); + drawBoxSideFromPath(graphicsContext, outerBorder.rect(), *path, edges, + edgeToRender.width, thickness, side, style, + colorToPaint, edgeToRender.style, bleedAvoidance, + includeLogicalLeftEdge, includeLogicalRightEdge); + } else { + bool clipForStyle = styleRequiresClipPolygon(edgeToRender.style) && + (mitreAdjacentSide1 || mitreAdjacentSide2); + bool clipAdjacentSide1 = + colorNeedsAntiAliasAtCorner(side, adjacentSide1, edges) && + mitreAdjacentSide1; + bool clipAdjacentSide2 = + colorNeedsAntiAliasAtCorner(side, adjacentSide2, edges) && + mitreAdjacentSide2; + bool shouldClip = clipForStyle || clipAdjacentSide1 || clipAdjacentSide2; + + GraphicsContextStateSaver clipStateSaver(*graphicsContext, shouldClip); + if (shouldClip) { + bool aliasAdjacentSide1 = + clipAdjacentSide1 || (clipForStyle && mitreAdjacentSide1); + bool aliasAdjacentSide2 = + clipAdjacentSide2 || (clipForStyle && mitreAdjacentSide2); + clipBorderSidePolygon(graphicsContext, outerBorder, innerBorder, side, + !aliasAdjacentSide1, !aliasAdjacentSide2); + // Since we clipped, no need to draw with a mitre. + mitreAdjacentSide1 = false; + mitreAdjacentSide2 = false; } -} -static IntRect calculateSideRect(const RoundedRect& outerBorder, const BorderEdge edges[], int side) -{ + drawLineForBoxSide(graphicsContext, sideRect.x(), sideRect.y(), + sideRect.maxX(), sideRect.maxY(), side, colorToPaint, + edgeToRender.style, + mitreAdjacentSide1 ? adjacentEdge1.width : 0, + mitreAdjacentSide2 ? adjacentEdge2.width : 0, antialias); + } +} + +static IntRect calculateSideRect(const RoundedRect& outerBorder, + const BorderEdge edges[], + int side) { + IntRect sideRect = outerBorder.rect(); + int width = edges[side].width; + + if (side == BSTop) + sideRect.setHeight(width); + else if (side == BSBottom) + sideRect.shiftYEdgeTo(sideRect.maxY() - width); + else if (side == BSLeft) + sideRect.setWidth(width); + else + sideRect.shiftXEdgeTo(sideRect.maxX() - width); + + return sideRect; +} + +void RenderBoxModelObject::paintBorderSides( + GraphicsContext* graphicsContext, + const RenderStyle* style, + const RoundedRect& outerBorder, + const RoundedRect& innerBorder, + const IntPoint& innerBorderAdjustment, + const BorderEdge edges[], + BorderEdgeFlags edgeSet, + BackgroundBleedAvoidance bleedAvoidance, + bool includeLogicalLeftEdge, + bool includeLogicalRightEdge, + bool antialias, + const Color* overrideColor) { + bool renderRadii = outerBorder.isRounded(); + + Path roundedPath; + if (renderRadii) + roundedPath.addRoundedRect(outerBorder); + + // The inner border adjustment for bleed avoidance mode + // BackgroundBleedBackgroundOverBorder is only applied to sideRect, which is + // okay since BackgroundBleedBackgroundOverBorder is only to be used for solid + // borders and the shape of the border painted by drawBoxSideFromPath only + // depends on sideRect when painting solid borders. + + if (edges[BSTop].shouldRender() && includesEdge(edgeSet, BSTop)) { IntRect sideRect = outerBorder.rect(); - int width = edges[side].width; - - if (side == BSTop) - sideRect.setHeight(width); - else if (side == BSBottom) - sideRect.shiftYEdgeTo(sideRect.maxY() - width); - else if (side == BSLeft) - sideRect.setWidth(width); - else - sideRect.shiftXEdgeTo(sideRect.maxX() - width); - - return sideRect; -} - -void RenderBoxModelObject::paintBorderSides(GraphicsContext* graphicsContext, const RenderStyle* style, const RoundedRect& outerBorder, const RoundedRect& innerBorder, - const IntPoint& innerBorderAdjustment, const BorderEdge edges[], BorderEdgeFlags edgeSet, BackgroundBleedAvoidance bleedAvoidance, - bool includeLogicalLeftEdge, bool includeLogicalRightEdge, bool antialias, const Color* overrideColor) -{ - bool renderRadii = outerBorder.isRounded(); - - Path roundedPath; - if (renderRadii) - roundedPath.addRoundedRect(outerBorder); - - // The inner border adjustment for bleed avoidance mode BackgroundBleedBackgroundOverBorder - // is only applied to sideRect, which is okay since BackgroundBleedBackgroundOverBorder - // is only to be used for solid borders and the shape of the border painted by drawBoxSideFromPath - // only depends on sideRect when painting solid borders. - - if (edges[BSTop].shouldRender() && includesEdge(edgeSet, BSTop)) { - IntRect sideRect = outerBorder.rect(); - sideRect.setHeight(edges[BSTop].width + innerBorderAdjustment.y()); - - bool usePath = renderRadii && (borderStyleHasInnerDetail(edges[BSTop].style) || borderWillArcInnerEdge(innerBorder.radii().topLeft(), innerBorder.radii().topRight())); - paintOneBorderSide(graphicsContext, style, outerBorder, innerBorder, sideRect, BSTop, BSLeft, BSRight, edges, usePath ? &roundedPath : 0, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor); + sideRect.setHeight(edges[BSTop].width + innerBorderAdjustment.y()); + + bool usePath = + renderRadii && (borderStyleHasInnerDetail(edges[BSTop].style) || + borderWillArcInnerEdge(innerBorder.radii().topLeft(), + innerBorder.radii().topRight())); + paintOneBorderSide(graphicsContext, style, outerBorder, innerBorder, + sideRect, BSTop, BSLeft, BSRight, edges, + usePath ? &roundedPath : 0, bleedAvoidance, + includeLogicalLeftEdge, includeLogicalRightEdge, + antialias, overrideColor); + } + + if (edges[BSBottom].shouldRender() && includesEdge(edgeSet, BSBottom)) { + IntRect sideRect = outerBorder.rect(); + sideRect.shiftYEdgeTo(sideRect.maxY() - edges[BSBottom].width - + innerBorderAdjustment.y()); + + bool usePath = renderRadii && + (borderStyleHasInnerDetail(edges[BSBottom].style) || + borderWillArcInnerEdge(innerBorder.radii().bottomLeft(), + innerBorder.radii().bottomRight())); + paintOneBorderSide(graphicsContext, style, outerBorder, innerBorder, + sideRect, BSBottom, BSLeft, BSRight, edges, + usePath ? &roundedPath : 0, bleedAvoidance, + includeLogicalLeftEdge, includeLogicalRightEdge, + antialias, overrideColor); + } + + if (edges[BSLeft].shouldRender() && includesEdge(edgeSet, BSLeft)) { + IntRect sideRect = outerBorder.rect(); + sideRect.setWidth(edges[BSLeft].width + innerBorderAdjustment.x()); + + bool usePath = + renderRadii && (borderStyleHasInnerDetail(edges[BSLeft].style) || + borderWillArcInnerEdge(innerBorder.radii().bottomLeft(), + innerBorder.radii().topLeft())); + paintOneBorderSide(graphicsContext, style, outerBorder, innerBorder, + sideRect, BSLeft, BSTop, BSBottom, edges, + usePath ? &roundedPath : 0, bleedAvoidance, + includeLogicalLeftEdge, includeLogicalRightEdge, + antialias, overrideColor); + } + + if (edges[BSRight].shouldRender() && includesEdge(edgeSet, BSRight)) { + IntRect sideRect = outerBorder.rect(); + sideRect.shiftXEdgeTo(sideRect.maxX() - edges[BSRight].width - + innerBorderAdjustment.x()); + + bool usePath = renderRadii && + (borderStyleHasInnerDetail(edges[BSRight].style) || + borderWillArcInnerEdge(innerBorder.radii().bottomRight(), + innerBorder.radii().topRight())); + paintOneBorderSide(graphicsContext, style, outerBorder, innerBorder, + sideRect, BSRight, BSTop, BSBottom, edges, + usePath ? &roundedPath : 0, bleedAvoidance, + includeLogicalLeftEdge, includeLogicalRightEdge, + antialias, overrideColor); + } +} + +void RenderBoxModelObject::paintTranslucentBorderSides( + GraphicsContext* graphicsContext, + const RenderStyle* style, + const RoundedRect& outerBorder, + const RoundedRect& innerBorder, + const IntPoint& innerBorderAdjustment, + const BorderEdge edges[], + BorderEdgeFlags edgesToDraw, + BackgroundBleedAvoidance bleedAvoidance, + bool includeLogicalLeftEdge, + bool includeLogicalRightEdge, + bool antialias) { + // willBeOverdrawn assumes that we draw in order: top, bottom, left, right. + // This is different from BoxSide enum order. + static const BoxSide paintOrder[] = {BSTop, BSBottom, BSLeft, BSRight}; + + while (edgesToDraw) { + // Find undrawn edges sharing a color. + Color commonColor; + + BorderEdgeFlags commonColorEdgeSet = 0; + for (size_t i = 0; i < sizeof(paintOrder) / sizeof(paintOrder[0]); ++i) { + BoxSide currSide = paintOrder[i]; + if (!includesEdge(edgesToDraw, currSide)) + continue; + + bool includeEdge; + if (!commonColorEdgeSet) { + commonColor = edges[currSide].color; + includeEdge = true; + } else + includeEdge = edges[currSide].color == commonColor; + + if (includeEdge) + commonColorEdgeSet |= edgeFlagForSide(currSide); } - if (edges[BSBottom].shouldRender() && includesEdge(edgeSet, BSBottom)) { - IntRect sideRect = outerBorder.rect(); - sideRect.shiftYEdgeTo(sideRect.maxY() - edges[BSBottom].width - innerBorderAdjustment.y()); - - bool usePath = renderRadii && (borderStyleHasInnerDetail(edges[BSBottom].style) || borderWillArcInnerEdge(innerBorder.radii().bottomLeft(), innerBorder.radii().bottomRight())); - paintOneBorderSide(graphicsContext, style, outerBorder, innerBorder, sideRect, BSBottom, BSLeft, BSRight, edges, usePath ? &roundedPath : 0, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor); + bool useTransparencyLayer = + includesAdjacentEdges(commonColorEdgeSet) && commonColor.hasAlpha(); + if (useTransparencyLayer) { + graphicsContext->beginTransparencyLayer( + static_cast(commonColor.alpha()) / 255); + commonColor = + Color(commonColor.red(), commonColor.green(), commonColor.blue()); } - if (edges[BSLeft].shouldRender() && includesEdge(edgeSet, BSLeft)) { - IntRect sideRect = outerBorder.rect(); - sideRect.setWidth(edges[BSLeft].width + innerBorderAdjustment.x()); - - bool usePath = renderRadii && (borderStyleHasInnerDetail(edges[BSLeft].style) || borderWillArcInnerEdge(innerBorder.radii().bottomLeft(), innerBorder.radii().topLeft())); - paintOneBorderSide(graphicsContext, style, outerBorder, innerBorder, sideRect, BSLeft, BSTop, BSBottom, edges, usePath ? &roundedPath : 0, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor); + paintBorderSides(graphicsContext, style, outerBorder, innerBorder, + innerBorderAdjustment, edges, commonColorEdgeSet, + bleedAvoidance, includeLogicalLeftEdge, + includeLogicalRightEdge, antialias, &commonColor); + + if (useTransparencyLayer) + graphicsContext->endLayer(); + + edgesToDraw &= ~commonColorEdgeSet; + } +} + +void RenderBoxModelObject::paintBorder(const PaintInfo& info, + const LayoutRect& rect, + const RenderStyle* style, + BackgroundBleedAvoidance bleedAvoidance, + bool includeLogicalLeftEdge, + bool includeLogicalRightEdge) { + GraphicsContext* graphicsContext = info.context; + + BorderEdge edges[4]; + getBorderEdgeInfo(edges, style, includeLogicalLeftEdge, + includeLogicalRightEdge); + RoundedRect outerBorder = style->getRoundedBorderFor( + rect, includeLogicalLeftEdge, includeLogicalRightEdge); + RoundedRect innerBorder = style->getRoundedInnerBorderFor( + borderInnerRectAdjustedForBleedAvoidance(graphicsContext, rect, + bleedAvoidance), + includeLogicalLeftEdge, includeLogicalRightEdge); + + if (outerBorder.rect().isEmpty()) + return; + + bool haveAlphaColor = false; + bool haveAllSolidEdges = true; + bool haveAllDoubleEdges = true; + int numEdgesVisible = 4; + bool allEdgesShareColor = true; + bool allEdgesShareWidth = true; + int firstVisibleEdge = -1; + BorderEdgeFlags edgesToDraw = 0; + + for (int i = BSTop; i <= BSLeft; ++i) { + const BorderEdge& currEdge = edges[i]; + + if (edges[i].shouldRender()) + edgesToDraw |= edgeFlagForSide(static_cast(i)); + + if (currEdge.presentButInvisible()) { + --numEdgesVisible; + allEdgesShareColor = false; + allEdgesShareWidth = false; + continue; } - if (edges[BSRight].shouldRender() && includesEdge(edgeSet, BSRight)) { - IntRect sideRect = outerBorder.rect(); - sideRect.shiftXEdgeTo(sideRect.maxX() - edges[BSRight].width - innerBorderAdjustment.x()); - - bool usePath = renderRadii && (borderStyleHasInnerDetail(edges[BSRight].style) || borderWillArcInnerEdge(innerBorder.radii().bottomRight(), innerBorder.radii().topRight())); - paintOneBorderSide(graphicsContext, style, outerBorder, innerBorder, sideRect, BSRight, BSTop, BSBottom, edges, usePath ? &roundedPath : 0, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor); + if (!currEdge.shouldRender()) { + --numEdgesVisible; + continue; } -} - -void RenderBoxModelObject::paintTranslucentBorderSides(GraphicsContext* graphicsContext, const RenderStyle* style, const RoundedRect& outerBorder, const RoundedRect& innerBorder, const IntPoint& innerBorderAdjustment, - const BorderEdge edges[], BorderEdgeFlags edgesToDraw, BackgroundBleedAvoidance bleedAvoidance, bool includeLogicalLeftEdge, bool includeLogicalRightEdge, bool antialias) -{ - // willBeOverdrawn assumes that we draw in order: top, bottom, left, right. - // This is different from BoxSide enum order. - static const BoxSide paintOrder[] = { BSTop, BSBottom, BSLeft, BSRight }; - - while (edgesToDraw) { - // Find undrawn edges sharing a color. - Color commonColor; - - BorderEdgeFlags commonColorEdgeSet = 0; - for (size_t i = 0; i < sizeof(paintOrder) / sizeof(paintOrder[0]); ++i) { - BoxSide currSide = paintOrder[i]; - if (!includesEdge(edgesToDraw, currSide)) - continue; - - bool includeEdge; - if (!commonColorEdgeSet) { - commonColor = edges[currSide].color; - includeEdge = true; - } else - includeEdge = edges[currSide].color == commonColor; - - if (includeEdge) - commonColorEdgeSet |= edgeFlagForSide(currSide); - } - - bool useTransparencyLayer = includesAdjacentEdges(commonColorEdgeSet) && commonColor.hasAlpha(); - if (useTransparencyLayer) { - graphicsContext->beginTransparencyLayer(static_cast(commonColor.alpha()) / 255); - commonColor = Color(commonColor.red(), commonColor.green(), commonColor.blue()); - } - paintBorderSides(graphicsContext, style, outerBorder, innerBorder, innerBorderAdjustment, edges, commonColorEdgeSet, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, &commonColor); - - if (useTransparencyLayer) - graphicsContext->endLayer(); - - edgesToDraw &= ~commonColorEdgeSet; + if (firstVisibleEdge == -1) { + firstVisibleEdge = i; + } else { + if (currEdge.color != edges[firstVisibleEdge].color) + allEdgesShareColor = false; + if (currEdge.width != edges[firstVisibleEdge].width) + allEdgesShareWidth = false; } -} - -void RenderBoxModelObject::paintBorder(const PaintInfo& info, const LayoutRect& rect, const RenderStyle* style, - BackgroundBleedAvoidance bleedAvoidance, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) -{ - GraphicsContext* graphicsContext = info.context; - BorderEdge edges[4]; - getBorderEdgeInfo(edges, style, includeLogicalLeftEdge, includeLogicalRightEdge); - RoundedRect outerBorder = style->getRoundedBorderFor(rect, includeLogicalLeftEdge, includeLogicalRightEdge); - RoundedRect innerBorder = style->getRoundedInnerBorderFor(borderInnerRectAdjustedForBleedAvoidance(graphicsContext, rect, bleedAvoidance), includeLogicalLeftEdge, includeLogicalRightEdge); + if (currEdge.color.hasAlpha()) + haveAlphaColor = true; - if (outerBorder.rect().isEmpty()) - return; + if (currEdge.style != SOLID) + haveAllSolidEdges = false; - bool haveAlphaColor = false; - bool haveAllSolidEdges = true; - bool haveAllDoubleEdges = true; - int numEdgesVisible = 4; - bool allEdgesShareColor = true; - bool allEdgesShareWidth = true; - int firstVisibleEdge = -1; - BorderEdgeFlags edgesToDraw = 0; + if (currEdge.style != DOUBLE) + haveAllDoubleEdges = false; + } - for (int i = BSTop; i <= BSLeft; ++i) { - const BorderEdge& currEdge = edges[i]; + // If no corner intersects the clip region, we can pretend outerBorder is + // rectangular to improve performance. + if (haveAllSolidEdges && outerBorder.isRounded() && + allCornersClippedOut(outerBorder, info.rect)) + outerBorder.setRadii(RoundedRect::Radii()); - if (edges[i].shouldRender()) - edgesToDraw |= edgeFlagForSide(static_cast(i)); + // isRenderable() check avoids issue described in + // https://bugs.webkit.org/show_bug.cgi?id=38787 + if ((haveAllSolidEdges || haveAllDoubleEdges) && allEdgesShareColor && + innerBorder.isRenderable()) { + // Fast path for drawing all solid edges and all unrounded double edges - if (currEdge.presentButInvisible()) { - --numEdgesVisible; - allEdgesShareColor = false; - allEdgesShareWidth = false; - continue; - } + if (numEdgesVisible == 4 && (outerBorder.isRounded() || haveAlphaColor) && + (haveAllSolidEdges || + (!outerBorder.isRounded() && !innerBorder.isRounded()))) { + Path path; - if (!currEdge.shouldRender()) { - --numEdgesVisible; - continue; - } + if (outerBorder.isRounded() && allEdgesShareWidth) { + // Very fast path for single stroked round rect with circular corners - if (firstVisibleEdge == -1) { - firstVisibleEdge = i; - } else { - if (currEdge.color != edges[firstVisibleEdge].color) - allEdgesShareColor = false; - if (currEdge.width != edges[firstVisibleEdge].width) - allEdgesShareWidth = false; + graphicsContext->fillBetweenRoundedRects(outerBorder, innerBorder, + edges[firstVisibleEdge].color); + return; + } + if (outerBorder.isRounded() && + bleedAvoidance != BackgroundBleedClipBackground) + path.addRoundedRect(outerBorder); + else + path.addRect(outerBorder.rect()); + + if (haveAllDoubleEdges) { + IntRect innerThirdRect = outerBorder.rect(); + IntRect outerThirdRect = outerBorder.rect(); + for (int side = BSTop; side <= BSLeft; ++side) { + int outerWidth; + int innerWidth; + edges[side].getDoubleBorderStripeWidths(outerWidth, innerWidth); + + if (side == BSTop) { + innerThirdRect.shiftYEdgeTo(innerThirdRect.y() + innerWidth); + outerThirdRect.shiftYEdgeTo(outerThirdRect.y() + outerWidth); + } else if (side == BSBottom) { + innerThirdRect.setHeight(innerThirdRect.height() - innerWidth); + outerThirdRect.setHeight(outerThirdRect.height() - outerWidth); + } else if (side == BSLeft) { + innerThirdRect.shiftXEdgeTo(innerThirdRect.x() + innerWidth); + outerThirdRect.shiftXEdgeTo(outerThirdRect.x() + outerWidth); + } else { + innerThirdRect.setWidth(innerThirdRect.width() - innerWidth); + outerThirdRect.setWidth(outerThirdRect.width() - outerWidth); + } } - if (currEdge.color.hasAlpha()) - haveAlphaColor = true; + RoundedRect outerThird = outerBorder; + RoundedRect innerThird = innerBorder; + innerThird.setRect(innerThirdRect); + outerThird.setRect(outerThirdRect); - if (currEdge.style != SOLID) - haveAllSolidEdges = false; + if (outerThird.isRounded() && + bleedAvoidance != BackgroundBleedClipBackground) + path.addRoundedRect(outerThird); + else + path.addRect(outerThird.rect()); - if (currEdge.style != DOUBLE) - haveAllDoubleEdges = false; + if (innerThird.isRounded() && + bleedAvoidance != BackgroundBleedClipBackground) + path.addRoundedRect(innerThird); + else + path.addRect(innerThird.rect()); + } + + if (innerBorder.isRounded()) + path.addRoundedRect(innerBorder); + else + path.addRect(innerBorder.rect()); + + graphicsContext->setFillRule(RULE_EVENODD); + graphicsContext->setFillColor(edges[firstVisibleEdge].color); + graphicsContext->fillPath(path); + return; } + // Avoid creating transparent layers + if (haveAllSolidEdges && numEdgesVisible != 4 && !outerBorder.isRounded() && + haveAlphaColor) { + Path path; - // If no corner intersects the clip region, we can pretend outerBorder is - // rectangular to improve performance. - if (haveAllSolidEdges && outerBorder.isRounded() && allCornersClippedOut(outerBorder, info.rect)) - outerBorder.setRadii(RoundedRect::Radii()); - - // isRenderable() check avoids issue described in https://bugs.webkit.org/show_bug.cgi?id=38787 - if ((haveAllSolidEdges || haveAllDoubleEdges) && allEdgesShareColor && innerBorder.isRenderable()) { - // Fast path for drawing all solid edges and all unrounded double edges - - if (numEdgesVisible == 4 && (outerBorder.isRounded() || haveAlphaColor) - && (haveAllSolidEdges || (!outerBorder.isRounded() && !innerBorder.isRounded()))) { - Path path; - - if (outerBorder.isRounded() && allEdgesShareWidth) { - - // Very fast path for single stroked round rect with circular corners - - graphicsContext->fillBetweenRoundedRects(outerBorder, innerBorder, edges[firstVisibleEdge].color); - return; - } - if (outerBorder.isRounded() && bleedAvoidance != BackgroundBleedClipBackground) - path.addRoundedRect(outerBorder); - else - path.addRect(outerBorder.rect()); - - if (haveAllDoubleEdges) { - IntRect innerThirdRect = outerBorder.rect(); - IntRect outerThirdRect = outerBorder.rect(); - for (int side = BSTop; side <= BSLeft; ++side) { - int outerWidth; - int innerWidth; - edges[side].getDoubleBorderStripeWidths(outerWidth, innerWidth); - - if (side == BSTop) { - innerThirdRect.shiftYEdgeTo(innerThirdRect.y() + innerWidth); - outerThirdRect.shiftYEdgeTo(outerThirdRect.y() + outerWidth); - } else if (side == BSBottom) { - innerThirdRect.setHeight(innerThirdRect.height() - innerWidth); - outerThirdRect.setHeight(outerThirdRect.height() - outerWidth); - } else if (side == BSLeft) { - innerThirdRect.shiftXEdgeTo(innerThirdRect.x() + innerWidth); - outerThirdRect.shiftXEdgeTo(outerThirdRect.x() + outerWidth); - } else { - innerThirdRect.setWidth(innerThirdRect.width() - innerWidth); - outerThirdRect.setWidth(outerThirdRect.width() - outerWidth); - } - } - - RoundedRect outerThird = outerBorder; - RoundedRect innerThird = innerBorder; - innerThird.setRect(innerThirdRect); - outerThird.setRect(outerThirdRect); - - if (outerThird.isRounded() && bleedAvoidance != BackgroundBleedClipBackground) - path.addRoundedRect(outerThird); - else - path.addRect(outerThird.rect()); - - if (innerThird.isRounded() && bleedAvoidance != BackgroundBleedClipBackground) - path.addRoundedRect(innerThird); - else - path.addRect(innerThird.rect()); - } - - if (innerBorder.isRounded()) - path.addRoundedRect(innerBorder); - else - path.addRect(innerBorder.rect()); - - graphicsContext->setFillRule(RULE_EVENODD); - graphicsContext->setFillColor(edges[firstVisibleEdge].color); - graphicsContext->fillPath(path); - return; - } - // Avoid creating transparent layers - if (haveAllSolidEdges && numEdgesVisible != 4 && !outerBorder.isRounded() && haveAlphaColor) { - Path path; - - for (int i = BSTop; i <= BSLeft; ++i) { - const BorderEdge& currEdge = edges[i]; - if (currEdge.shouldRender()) { - IntRect sideRect = calculateSideRect(outerBorder, edges, i); - path.addRect(sideRect); - } - } - - graphicsContext->setFillRule(RULE_NONZERO); - graphicsContext->setFillColor(edges[firstVisibleEdge].color); - graphicsContext->fillPath(path); - return; + for (int i = BSTop; i <= BSLeft; ++i) { + const BorderEdge& currEdge = edges[i]; + if (currEdge.shouldRender()) { + IntRect sideRect = calculateSideRect(outerBorder, edges, i); + path.addRect(sideRect); } - } + } - bool clipToOuterBorder = outerBorder.isRounded(); - GraphicsContextStateSaver stateSaver(*graphicsContext, clipToOuterBorder); - if (clipToOuterBorder) { - // Clip to the inner and outer radii rects. - if (bleedAvoidance != BackgroundBleedClipBackground) - graphicsContext->clipRoundedRect(outerBorder); - // isRenderable() check avoids issue described in https://bugs.webkit.org/show_bug.cgi?id=38787 - // The inside will be clipped out later (in clipBorderSideForComplexInnerPath) - if (innerBorder.isRenderable() && !innerBorder.isEmpty()) - graphicsContext->clipOutRoundedRect(innerBorder); + graphicsContext->setFillRule(RULE_NONZERO); + graphicsContext->setFillColor(edges[firstVisibleEdge].color); + graphicsContext->fillPath(path); + return; } - - // If only one edge visible antialiasing doesn't create seams - bool antialias = shouldAntialiasLines(graphicsContext) || numEdgesVisible == 1; - RoundedRect unadjustedInnerBorder = (bleedAvoidance == BackgroundBleedBackgroundOverBorder) ? style->getRoundedInnerBorderFor(rect, includeLogicalLeftEdge, includeLogicalRightEdge) : innerBorder; - IntPoint innerBorderAdjustment(innerBorder.rect().x() - unadjustedInnerBorder.rect().x(), innerBorder.rect().y() - unadjustedInnerBorder.rect().y()); - if (haveAlphaColor) - paintTranslucentBorderSides(graphicsContext, style, outerBorder, unadjustedInnerBorder, innerBorderAdjustment, edges, edgesToDraw, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias); - else - paintBorderSides(graphicsContext, style, outerBorder, unadjustedInnerBorder, innerBorderAdjustment, edges, edgesToDraw, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias); -} - -void RenderBoxModelObject::drawBoxSideFromPath(GraphicsContext* graphicsContext, const LayoutRect& borderRect, const Path& borderPath, const BorderEdge edges[], - float thickness, float drawThickness, BoxSide side, const RenderStyle* style, Color color, EBorderStyle borderStyle, BackgroundBleedAvoidance bleedAvoidance, - bool includeLogicalLeftEdge, bool includeLogicalRightEdge) -{ - if (thickness <= 0) - return; - - if (borderStyle == DOUBLE && thickness < 3) - borderStyle = SOLID; - - switch (borderStyle) { + } + + bool clipToOuterBorder = outerBorder.isRounded(); + GraphicsContextStateSaver stateSaver(*graphicsContext, clipToOuterBorder); + if (clipToOuterBorder) { + // Clip to the inner and outer radii rects. + if (bleedAvoidance != BackgroundBleedClipBackground) + graphicsContext->clipRoundedRect(outerBorder); + // isRenderable() check avoids issue described in + // https://bugs.webkit.org/show_bug.cgi?id=38787 The inside will be clipped + // out later (in clipBorderSideForComplexInnerPath) + if (innerBorder.isRenderable() && !innerBorder.isEmpty()) + graphicsContext->clipOutRoundedRect(innerBorder); + } + + // If only one edge visible antialiasing doesn't create seams + bool antialias = + shouldAntialiasLines(graphicsContext) || numEdgesVisible == 1; + RoundedRect unadjustedInnerBorder = + (bleedAvoidance == BackgroundBleedBackgroundOverBorder) + ? style->getRoundedInnerBorderFor(rect, includeLogicalLeftEdge, + includeLogicalRightEdge) + : innerBorder; + IntPoint innerBorderAdjustment( + innerBorder.rect().x() - unadjustedInnerBorder.rect().x(), + innerBorder.rect().y() - unadjustedInnerBorder.rect().y()); + if (haveAlphaColor) + paintTranslucentBorderSides( + graphicsContext, style, outerBorder, unadjustedInnerBorder, + innerBorderAdjustment, edges, edgesToDraw, bleedAvoidance, + includeLogicalLeftEdge, includeLogicalRightEdge, antialias); + else + paintBorderSides(graphicsContext, style, outerBorder, unadjustedInnerBorder, + innerBorderAdjustment, edges, edgesToDraw, bleedAvoidance, + includeLogicalLeftEdge, includeLogicalRightEdge, + antialias); +} + +void RenderBoxModelObject::drawBoxSideFromPath( + GraphicsContext* graphicsContext, + const LayoutRect& borderRect, + const Path& borderPath, + const BorderEdge edges[], + float thickness, + float drawThickness, + BoxSide side, + const RenderStyle* style, + Color color, + EBorderStyle borderStyle, + BackgroundBleedAvoidance bleedAvoidance, + bool includeLogicalLeftEdge, + bool includeLogicalRightEdge) { + if (thickness <= 0) + return; + + if (borderStyle == DOUBLE && thickness < 3) + borderStyle = SOLID; + + switch (borderStyle) { case BNONE: case BHIDDEN: - return; + return; case DOTTED: case DASHED: { - graphicsContext->setStrokeColor(color); - - // The stroke is doubled here because the provided path is the - // outside edge of the border so half the stroke is clipped off. - // The extra multiplier is so that the clipping mask can antialias - // the edges to prevent jaggies. - graphicsContext->setStrokeThickness(drawThickness * 2 * 1.1f); - graphicsContext->setStrokeStyle(borderStyle == DASHED ? DashedStroke : DottedStroke); - - // If the number of dashes that fit in the path is odd and non-integral then we - // will have an awkwardly-sized dash at the end of the path. To try to avoid that - // here, we simply make the whitespace dashes ever so slightly bigger. - // FIXME: This could be even better if we tried to manipulate the dash offset - // and possibly the gapLength to get the corners dash-symmetrical. - float dashLength = thickness * ((borderStyle == DASHED) ? 3.0f : 1.0f); - float gapLength = dashLength; - float numberOfDashes = borderPath.length() / dashLength; - // Don't try to show dashes if we have less than 2 dashes + 2 gaps. - // FIXME: should do this test per side. - if (numberOfDashes >= 4) { - bool evenNumberOfFullDashes = !((int)numberOfDashes % 2); - bool integralNumberOfDashes = !(numberOfDashes - (int)numberOfDashes); - if (!evenNumberOfFullDashes && !integralNumberOfDashes) { - float numberOfGaps = numberOfDashes / 2; - gapLength += (dashLength / numberOfGaps); - } - - DashArray lineDash; - lineDash.append(dashLength); - lineDash.append(gapLength); - graphicsContext->setLineDash(lineDash, dashLength); + graphicsContext->setStrokeColor(color); + + // The stroke is doubled here because the provided path is the + // outside edge of the border so half the stroke is clipped off. + // The extra multiplier is so that the clipping mask can antialias + // the edges to prevent jaggies. + graphicsContext->setStrokeThickness(drawThickness * 2 * 1.1f); + graphicsContext->setStrokeStyle(borderStyle == DASHED ? DashedStroke + : DottedStroke); + + // If the number of dashes that fit in the path is odd and non-integral + // then we will have an awkwardly-sized dash at the end of the path. To + // try to avoid that here, we simply make the whitespace dashes ever so + // slightly bigger. + // FIXME: This could be even better if we tried to manipulate the dash + // offset and possibly the gapLength to get the corners dash-symmetrical. + float dashLength = thickness * ((borderStyle == DASHED) ? 3.0f : 1.0f); + float gapLength = dashLength; + float numberOfDashes = borderPath.length() / dashLength; + // Don't try to show dashes if we have less than 2 dashes + 2 gaps. + // FIXME: should do this test per side. + if (numberOfDashes >= 4) { + bool evenNumberOfFullDashes = !((int)numberOfDashes % 2); + bool integralNumberOfDashes = !(numberOfDashes - (int)numberOfDashes); + if (!evenNumberOfFullDashes && !integralNumberOfDashes) { + float numberOfGaps = numberOfDashes / 2; + gapLength += (dashLength / numberOfGaps); } - // FIXME: stroking the border path causes issues with tight corners: - // https://bugs.webkit.org/show_bug.cgi?id=58711 - // Also, to get the best appearance we should stroke a path between the two borders. - graphicsContext->strokePath(borderPath); - return; + DashArray lineDash; + lineDash.append(dashLength); + lineDash.append(gapLength); + graphicsContext->setLineDash(lineDash, dashLength); + } + + // FIXME: stroking the border path causes issues with tight corners: + // https://bugs.webkit.org/show_bug.cgi?id=58711 + // Also, to get the best appearance we should stroke a path between the + // two borders. + graphicsContext->strokePath(borderPath); + return; } case DOUBLE: { - // Get the inner border rects for both the outer border line and the inner border line - int outerBorderTopWidth; - int innerBorderTopWidth; - edges[BSTop].getDoubleBorderStripeWidths(outerBorderTopWidth, innerBorderTopWidth); - - int outerBorderRightWidth; - int innerBorderRightWidth; - edges[BSRight].getDoubleBorderStripeWidths(outerBorderRightWidth, innerBorderRightWidth); - - int outerBorderBottomWidth; - int innerBorderBottomWidth; - edges[BSBottom].getDoubleBorderStripeWidths(outerBorderBottomWidth, innerBorderBottomWidth); - - int outerBorderLeftWidth; - int innerBorderLeftWidth; - edges[BSLeft].getDoubleBorderStripeWidths(outerBorderLeftWidth, innerBorderLeftWidth); - - // Draw inner border line - { - GraphicsContextStateSaver stateSaver(*graphicsContext); - RoundedRect innerClip = style->getRoundedInnerBorderFor(borderRect, - innerBorderTopWidth, innerBorderBottomWidth, innerBorderLeftWidth, innerBorderRightWidth, - includeLogicalLeftEdge, includeLogicalRightEdge); - - graphicsContext->clipRoundedRect(innerClip); - drawBoxSideFromPath(graphicsContext, borderRect, borderPath, edges, thickness, drawThickness, side, style, color, SOLID, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge); + // Get the inner border rects for both the outer border line and the inner + // border line + int outerBorderTopWidth; + int innerBorderTopWidth; + edges[BSTop].getDoubleBorderStripeWidths(outerBorderTopWidth, + innerBorderTopWidth); + + int outerBorderRightWidth; + int innerBorderRightWidth; + edges[BSRight].getDoubleBorderStripeWidths(outerBorderRightWidth, + innerBorderRightWidth); + + int outerBorderBottomWidth; + int innerBorderBottomWidth; + edges[BSBottom].getDoubleBorderStripeWidths(outerBorderBottomWidth, + innerBorderBottomWidth); + + int outerBorderLeftWidth; + int innerBorderLeftWidth; + edges[BSLeft].getDoubleBorderStripeWidths(outerBorderLeftWidth, + innerBorderLeftWidth); + + // Draw inner border line + { + GraphicsContextStateSaver stateSaver(*graphicsContext); + RoundedRect innerClip = style->getRoundedInnerBorderFor( + borderRect, innerBorderTopWidth, innerBorderBottomWidth, + innerBorderLeftWidth, innerBorderRightWidth, includeLogicalLeftEdge, + includeLogicalRightEdge); + + graphicsContext->clipRoundedRect(innerClip); + drawBoxSideFromPath(graphicsContext, borderRect, borderPath, edges, + thickness, drawThickness, side, style, color, SOLID, + bleedAvoidance, includeLogicalLeftEdge, + includeLogicalRightEdge); + } + + // Draw outer border line + { + GraphicsContextStateSaver stateSaver(*graphicsContext); + LayoutRect outerRect = borderRect; + if (bleedAvoidance == BackgroundBleedClipBackground) { + outerRect.inflate(1); + ++outerBorderTopWidth; + ++outerBorderBottomWidth; + ++outerBorderLeftWidth; + ++outerBorderRightWidth; } - // Draw outer border line - { - GraphicsContextStateSaver stateSaver(*graphicsContext); - LayoutRect outerRect = borderRect; - if (bleedAvoidance == BackgroundBleedClipBackground) { - outerRect.inflate(1); - ++outerBorderTopWidth; - ++outerBorderBottomWidth; - ++outerBorderLeftWidth; - ++outerBorderRightWidth; - } - - RoundedRect outerClip = style->getRoundedInnerBorderFor(outerRect, - outerBorderTopWidth, outerBorderBottomWidth, outerBorderLeftWidth, outerBorderRightWidth, - includeLogicalLeftEdge, includeLogicalRightEdge); - graphicsContext->clipOutRoundedRect(outerClip); - drawBoxSideFromPath(graphicsContext, borderRect, borderPath, edges, thickness, drawThickness, side, style, color, SOLID, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge); - } - return; + RoundedRect outerClip = style->getRoundedInnerBorderFor( + outerRect, outerBorderTopWidth, outerBorderBottomWidth, + outerBorderLeftWidth, outerBorderRightWidth, includeLogicalLeftEdge, + includeLogicalRightEdge); + graphicsContext->clipOutRoundedRect(outerClip); + drawBoxSideFromPath(graphicsContext, borderRect, borderPath, edges, + thickness, drawThickness, side, style, color, SOLID, + bleedAvoidance, includeLogicalLeftEdge, + includeLogicalRightEdge); + } + return; } case RIDGE: - case GROOVE: - { - EBorderStyle s1; - EBorderStyle s2; - if (borderStyle == GROOVE) { - s1 = INSET; - s2 = OUTSET; - } else { - s1 = OUTSET; - s2 = INSET; - } - - // Paint full border - drawBoxSideFromPath(graphicsContext, borderRect, borderPath, edges, thickness, drawThickness, side, style, color, s1, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge); - - // Paint inner only - GraphicsContextStateSaver stateSaver(*graphicsContext); - LayoutUnit topWidth = edges[BSTop].usedWidth() / 2; - LayoutUnit bottomWidth = edges[BSBottom].usedWidth() / 2; - LayoutUnit leftWidth = edges[BSLeft].usedWidth() / 2; - LayoutUnit rightWidth = edges[BSRight].usedWidth() / 2; - - RoundedRect clipRect = style->getRoundedInnerBorderFor(borderRect, - topWidth, bottomWidth, leftWidth, rightWidth, - includeLogicalLeftEdge, includeLogicalRightEdge); - - graphicsContext->clipRoundedRect(clipRect); - drawBoxSideFromPath(graphicsContext, borderRect, borderPath, edges, thickness, drawThickness, side, style, color, s2, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge); - return; + case GROOVE: { + EBorderStyle s1; + EBorderStyle s2; + if (borderStyle == GROOVE) { + s1 = INSET; + s2 = OUTSET; + } else { + s1 = OUTSET; + s2 = INSET; + } + + // Paint full border + drawBoxSideFromPath(graphicsContext, borderRect, borderPath, edges, + thickness, drawThickness, side, style, color, s1, + bleedAvoidance, includeLogicalLeftEdge, + includeLogicalRightEdge); + + // Paint inner only + GraphicsContextStateSaver stateSaver(*graphicsContext); + LayoutUnit topWidth = edges[BSTop].usedWidth() / 2; + LayoutUnit bottomWidth = edges[BSBottom].usedWidth() / 2; + LayoutUnit leftWidth = edges[BSLeft].usedWidth() / 2; + LayoutUnit rightWidth = edges[BSRight].usedWidth() / 2; + + RoundedRect clipRect = style->getRoundedInnerBorderFor( + borderRect, topWidth, bottomWidth, leftWidth, rightWidth, + includeLogicalLeftEdge, includeLogicalRightEdge); + + graphicsContext->clipRoundedRect(clipRect); + drawBoxSideFromPath(graphicsContext, borderRect, borderPath, edges, + thickness, drawThickness, side, style, color, s2, + bleedAvoidance, includeLogicalLeftEdge, + includeLogicalRightEdge); + return; } case INSET: - if (side == BSTop || side == BSLeft) - color = color.dark(); - break; + if (side == BSTop || side == BSLeft) + color = color.dark(); + break; case OUTSET: - if (side == BSBottom || side == BSRight) - color = color.dark(); - break; + if (side == BSBottom || side == BSRight) + color = color.dark(); + break; default: - break; - } - - graphicsContext->setStrokeStyle(NoStroke); - graphicsContext->setFillColor(color); - graphicsContext->drawRect(pixelSnappedIntRect(borderRect)); -} - -void RenderBoxModelObject::clipBorderSidePolygon(GraphicsContext* graphicsContext, const RoundedRect& outerBorder, const RoundedRect& innerBorder, - BoxSide side, bool firstEdgeMatches, bool secondEdgeMatches) -{ - FloatPoint quad[4]; - - const LayoutRect& outerRect = outerBorder.rect(); - const LayoutRect& innerRect = innerBorder.rect(); - - FloatPoint centerPoint(innerRect.location().x().toFloat() + innerRect.width().toFloat() / 2, innerRect.location().y().toFloat() + innerRect.height().toFloat() / 2); - - // For each side, create a quad that encompasses all parts of that side that may draw, - // including areas inside the innerBorder. - // - // 0----------------3 - // 0 \ / 0 - // |\ 1----------- 2 /| - // | 1 1 | - // | | | | - // | | | | - // | 2 2 | - // |/ 1------------2 \| - // 3 / \ 3 - // 0----------------3 - // - switch (side) { + break; + } + + graphicsContext->setStrokeStyle(NoStroke); + graphicsContext->setFillColor(color); + graphicsContext->drawRect(pixelSnappedIntRect(borderRect)); +} + +void RenderBoxModelObject::clipBorderSidePolygon( + GraphicsContext* graphicsContext, + const RoundedRect& outerBorder, + const RoundedRect& innerBorder, + BoxSide side, + bool firstEdgeMatches, + bool secondEdgeMatches) { + FloatPoint quad[4]; + + const LayoutRect& outerRect = outerBorder.rect(); + const LayoutRect& innerRect = innerBorder.rect(); + + FloatPoint centerPoint( + innerRect.location().x().toFloat() + innerRect.width().toFloat() / 2, + innerRect.location().y().toFloat() + innerRect.height().toFloat() / 2); + + // For each side, create a quad that encompasses all parts of that side that + // may draw, including areas inside the innerBorder. + // + // 0----------------3 + // 0 \ / 0 + // |\ 1----------- 2 /| + // | 1 1 | + // | | | | + // | | | | + // | 2 2 | + // |/ 1------------2 \| + // 3 / \ 3 + // 0----------------3 + // + switch (side) { case BSTop: - quad[0] = outerRect.minXMinYCorner(); - quad[1] = innerRect.minXMinYCorner(); - quad[2] = innerRect.maxXMinYCorner(); - quad[3] = outerRect.maxXMinYCorner(); - - if (!innerBorder.radii().topLeft().isZero()) { - findIntersection(quad[0], quad[1], - FloatPoint( - quad[1].x() + innerBorder.radii().topLeft().width(), - quad[1].y()), - FloatPoint( - quad[1].x(), - quad[1].y() + innerBorder.radii().topLeft().height()), - quad[1]); - } - - if (!innerBorder.radii().topRight().isZero()) { - findIntersection(quad[3], quad[2], - FloatPoint( - quad[2].x() - innerBorder.radii().topRight().width(), - quad[2].y()), - FloatPoint( - quad[2].x(), - quad[2].y() + innerBorder.radii().topRight().height()), - quad[2]); - } - break; + quad[0] = outerRect.minXMinYCorner(); + quad[1] = innerRect.minXMinYCorner(); + quad[2] = innerRect.maxXMinYCorner(); + quad[3] = outerRect.maxXMinYCorner(); + + if (!innerBorder.radii().topLeft().isZero()) { + findIntersection( + quad[0], quad[1], + FloatPoint(quad[1].x() + innerBorder.radii().topLeft().width(), + quad[1].y()), + FloatPoint(quad[1].x(), + quad[1].y() + innerBorder.radii().topLeft().height()), + quad[1]); + } + + if (!innerBorder.radii().topRight().isZero()) { + findIntersection( + quad[3], quad[2], + FloatPoint(quad[2].x() - innerBorder.radii().topRight().width(), + quad[2].y()), + FloatPoint(quad[2].x(), + quad[2].y() + innerBorder.radii().topRight().height()), + quad[2]); + } + break; case BSLeft: - quad[0] = outerRect.minXMinYCorner(); - quad[1] = innerRect.minXMinYCorner(); - quad[2] = innerRect.minXMaxYCorner(); - quad[3] = outerRect.minXMaxYCorner(); - - if (!innerBorder.radii().topLeft().isZero()) { - findIntersection(quad[0], quad[1], - FloatPoint( - quad[1].x() + innerBorder.radii().topLeft().width(), - quad[1].y()), - FloatPoint( - quad[1].x(), - quad[1].y() + innerBorder.radii().topLeft().height()), - quad[1]); - } - - if (!innerBorder.radii().bottomLeft().isZero()) { - findIntersection(quad[3], quad[2], - FloatPoint( - quad[2].x() + innerBorder.radii().bottomLeft().width(), - quad[2].y()), - FloatPoint( - quad[2].x(), - quad[2].y() - innerBorder.radii().bottomLeft().height()), - quad[2]); - } - break; + quad[0] = outerRect.minXMinYCorner(); + quad[1] = innerRect.minXMinYCorner(); + quad[2] = innerRect.minXMaxYCorner(); + quad[3] = outerRect.minXMaxYCorner(); + + if (!innerBorder.radii().topLeft().isZero()) { + findIntersection( + quad[0], quad[1], + FloatPoint(quad[1].x() + innerBorder.radii().topLeft().width(), + quad[1].y()), + FloatPoint(quad[1].x(), + quad[1].y() + innerBorder.radii().topLeft().height()), + quad[1]); + } + + if (!innerBorder.radii().bottomLeft().isZero()) { + findIntersection( + quad[3], quad[2], + FloatPoint(quad[2].x() + innerBorder.radii().bottomLeft().width(), + quad[2].y()), + FloatPoint(quad[2].x(), + quad[2].y() - innerBorder.radii().bottomLeft().height()), + quad[2]); + } + break; case BSBottom: - quad[0] = outerRect.minXMaxYCorner(); - quad[1] = innerRect.minXMaxYCorner(); - quad[2] = innerRect.maxXMaxYCorner(); - quad[3] = outerRect.maxXMaxYCorner(); - - if (!innerBorder.radii().bottomLeft().isZero()) { - findIntersection(quad[0], quad[1], - FloatPoint( - quad[1].x() + innerBorder.radii().bottomLeft().width(), - quad[1].y()), - FloatPoint( - quad[1].x(), - quad[1].y() - innerBorder.radii().bottomLeft().height()), - quad[1]); - } - - if (!innerBorder.radii().bottomRight().isZero()) { - findIntersection(quad[3], quad[2], - FloatPoint( - quad[2].x() - innerBorder.radii().bottomRight().width(), - quad[2].y()), - FloatPoint( - quad[2].x(), - quad[2].y() - innerBorder.radii().bottomRight().height()), - quad[2]); - } - break; + quad[0] = outerRect.minXMaxYCorner(); + quad[1] = innerRect.minXMaxYCorner(); + quad[2] = innerRect.maxXMaxYCorner(); + quad[3] = outerRect.maxXMaxYCorner(); + + if (!innerBorder.radii().bottomLeft().isZero()) { + findIntersection( + quad[0], quad[1], + FloatPoint(quad[1].x() + innerBorder.radii().bottomLeft().width(), + quad[1].y()), + FloatPoint(quad[1].x(), + quad[1].y() - innerBorder.radii().bottomLeft().height()), + quad[1]); + } + + if (!innerBorder.radii().bottomRight().isZero()) { + findIntersection( + quad[3], quad[2], + FloatPoint(quad[2].x() - innerBorder.radii().bottomRight().width(), + quad[2].y()), + FloatPoint( + quad[2].x(), + quad[2].y() - innerBorder.radii().bottomRight().height()), + quad[2]); + } + break; case BSRight: - quad[0] = outerRect.maxXMinYCorner(); - quad[1] = innerRect.maxXMinYCorner(); - quad[2] = innerRect.maxXMaxYCorner(); - quad[3] = outerRect.maxXMaxYCorner(); - - if (!innerBorder.radii().topRight().isZero()) { - findIntersection(quad[0], quad[1], - FloatPoint( - quad[1].x() - innerBorder.radii().topRight().width(), - quad[1].y()), - FloatPoint( - quad[1].x(), - quad[1].y() + innerBorder.radii().topRight().height()), - quad[1]); - } - - if (!innerBorder.radii().bottomRight().isZero()) { - findIntersection(quad[3], quad[2], - FloatPoint( - quad[2].x() - innerBorder.radii().bottomRight().width(), - quad[2].y()), - FloatPoint( - quad[2].x(), - quad[2].y() - innerBorder.radii().bottomRight().height()), - quad[2]); - } - break; - } - - // If the border matches both of its adjacent sides, don't anti-alias the clip, and - // if neither side matches, anti-alias the clip. - if (firstEdgeMatches == secondEdgeMatches) { - graphicsContext->clipConvexPolygon(4, quad, !firstEdgeMatches); - return; - } - - // If antialiasing settings for the first edge and second edge is different, - // they have to be addressed separately. We do this by breaking the quad into - // two parallelograms, made by moving quad[1] and quad[2]. - float ax = quad[1].x() - quad[0].x(); - float ay = quad[1].y() - quad[0].y(); - float bx = quad[2].x() - quad[1].x(); - float by = quad[2].y() - quad[1].y(); - float cx = quad[3].x() - quad[2].x(); - float cy = quad[3].y() - quad[2].y(); - - const static float kEpsilon = 1e-2f; - float r1, r2; - if (fabsf(bx) < kEpsilon && fabsf(by) < kEpsilon) { - // The quad was actually a triangle. - r1 = r2 = 1.0f; - } else { - // Extend parallelogram a bit to hide calculation error - const static float kExtendFill = 1e-2f; - - r1 = (-ax * by + ay * bx) / (cx * by - cy * bx) + kExtendFill; - r2 = (-cx * by + cy * bx) / (ax * by - ay * bx) + kExtendFill; - } - - FloatPoint firstQuad[4]; - firstQuad[0] = quad[0]; - firstQuad[1] = quad[1]; - firstQuad[2] = FloatPoint(quad[3].x() + r2 * ax, quad[3].y() + r2 * ay); - firstQuad[3] = quad[3]; - graphicsContext->clipConvexPolygon(4, firstQuad, !firstEdgeMatches); - - FloatPoint secondQuad[4]; - secondQuad[0] = quad[0]; - secondQuad[1] = FloatPoint(quad[0].x() - r1 * cx, quad[0].y() - r1 * cy); - secondQuad[2] = quad[2]; - secondQuad[3] = quad[3]; - graphicsContext->clipConvexPolygon(4, secondQuad, !secondEdgeMatches); -} - -static IntRect calculateSideRectIncludingInner(const RoundedRect& outerBorder, const BorderEdge edges[], BoxSide side) -{ - IntRect sideRect = outerBorder.rect(); - int width; - - switch (side) { + quad[0] = outerRect.maxXMinYCorner(); + quad[1] = innerRect.maxXMinYCorner(); + quad[2] = innerRect.maxXMaxYCorner(); + quad[3] = outerRect.maxXMaxYCorner(); + + if (!innerBorder.radii().topRight().isZero()) { + findIntersection( + quad[0], quad[1], + FloatPoint(quad[1].x() - innerBorder.radii().topRight().width(), + quad[1].y()), + FloatPoint(quad[1].x(), + quad[1].y() + innerBorder.radii().topRight().height()), + quad[1]); + } + + if (!innerBorder.radii().bottomRight().isZero()) { + findIntersection( + quad[3], quad[2], + FloatPoint(quad[2].x() - innerBorder.radii().bottomRight().width(), + quad[2].y()), + FloatPoint( + quad[2].x(), + quad[2].y() - innerBorder.radii().bottomRight().height()), + quad[2]); + } + break; + } + + // If the border matches both of its adjacent sides, don't anti-alias the + // clip, and if neither side matches, anti-alias the clip. + if (firstEdgeMatches == secondEdgeMatches) { + graphicsContext->clipConvexPolygon(4, quad, !firstEdgeMatches); + return; + } + + // If antialiasing settings for the first edge and second edge is different, + // they have to be addressed separately. We do this by breaking the quad into + // two parallelograms, made by moving quad[1] and quad[2]. + float ax = quad[1].x() - quad[0].x(); + float ay = quad[1].y() - quad[0].y(); + float bx = quad[2].x() - quad[1].x(); + float by = quad[2].y() - quad[1].y(); + float cx = quad[3].x() - quad[2].x(); + float cy = quad[3].y() - quad[2].y(); + + const static float kEpsilon = 1e-2f; + float r1, r2; + if (fabsf(bx) < kEpsilon && fabsf(by) < kEpsilon) { + // The quad was actually a triangle. + r1 = r2 = 1.0f; + } else { + // Extend parallelogram a bit to hide calculation error + const static float kExtendFill = 1e-2f; + + r1 = (-ax * by + ay * bx) / (cx * by - cy * bx) + kExtendFill; + r2 = (-cx * by + cy * bx) / (ax * by - ay * bx) + kExtendFill; + } + + FloatPoint firstQuad[4]; + firstQuad[0] = quad[0]; + firstQuad[1] = quad[1]; + firstQuad[2] = FloatPoint(quad[3].x() + r2 * ax, quad[3].y() + r2 * ay); + firstQuad[3] = quad[3]; + graphicsContext->clipConvexPolygon(4, firstQuad, !firstEdgeMatches); + + FloatPoint secondQuad[4]; + secondQuad[0] = quad[0]; + secondQuad[1] = FloatPoint(quad[0].x() - r1 * cx, quad[0].y() - r1 * cy); + secondQuad[2] = quad[2]; + secondQuad[3] = quad[3]; + graphicsContext->clipConvexPolygon(4, secondQuad, !secondEdgeMatches); +} + +static IntRect calculateSideRectIncludingInner(const RoundedRect& outerBorder, + const BorderEdge edges[], + BoxSide side) { + IntRect sideRect = outerBorder.rect(); + int width; + + switch (side) { case BSTop: - width = sideRect.height() - edges[BSBottom].width; - sideRect.setHeight(width); - break; + width = sideRect.height() - edges[BSBottom].width; + sideRect.setHeight(width); + break; case BSBottom: - width = sideRect.height() - edges[BSTop].width; - sideRect.shiftYEdgeTo(sideRect.maxY() - width); - break; + width = sideRect.height() - edges[BSTop].width; + sideRect.shiftYEdgeTo(sideRect.maxY() - width); + break; case BSLeft: - width = sideRect.width() - edges[BSRight].width; - sideRect.setWidth(width); - break; + width = sideRect.width() - edges[BSRight].width; + sideRect.setWidth(width); + break; case BSRight: - width = sideRect.width() - edges[BSLeft].width; - sideRect.shiftXEdgeTo(sideRect.maxX() - width); - break; - } + width = sideRect.width() - edges[BSLeft].width; + sideRect.shiftXEdgeTo(sideRect.maxX() - width); + break; + } - return sideRect; + return sideRect; } -static RoundedRect calculateAdjustedInnerBorder(const RoundedRect&innerBorder, BoxSide side) -{ - // Expand the inner border as necessary to make it a rounded rect (i.e. radii contained within each edge). - // This function relies on the fact we only get radii not contained within each edge if one of the radii - // for an edge is zero, so we can shift the arc towards the zero radius corner. - RoundedRect::Radii newRadii = innerBorder.radii(); - IntRect newRect = innerBorder.rect(); +static RoundedRect calculateAdjustedInnerBorder(const RoundedRect& innerBorder, + BoxSide side) { + // Expand the inner border as necessary to make it a rounded rect (i.e. radii + // contained within each edge). This function relies on the fact we only get + // radii not contained within each edge if one of the radii for an edge is + // zero, so we can shift the arc towards the zero radius corner. + RoundedRect::Radii newRadii = innerBorder.radii(); + IntRect newRect = innerBorder.rect(); - float overshoot; - float maxRadii; + float overshoot; + float maxRadii; - switch (side) { + switch (side) { case BSTop: - overshoot = newRadii.topLeft().width() + newRadii.topRight().width() - newRect.width(); - if (overshoot > 0) { - ASSERT(!(newRadii.topLeft().width() && newRadii.topRight().width())); - newRect.setWidth(newRect.width() + overshoot); - if (!newRadii.topLeft().width()) - newRect.move(-overshoot, 0); - } - newRadii.setBottomLeft(IntSize(0, 0)); - newRadii.setBottomRight(IntSize(0, 0)); - maxRadii = std::max(newRadii.topLeft().height(), newRadii.topRight().height()); - if (maxRadii > newRect.height()) - newRect.setHeight(maxRadii); - break; + overshoot = newRadii.topLeft().width() + newRadii.topRight().width() - + newRect.width(); + if (overshoot > 0) { + ASSERT(!(newRadii.topLeft().width() && newRadii.topRight().width())); + newRect.setWidth(newRect.width() + overshoot); + if (!newRadii.topLeft().width()) + newRect.move(-overshoot, 0); + } + newRadii.setBottomLeft(IntSize(0, 0)); + newRadii.setBottomRight(IntSize(0, 0)); + maxRadii = + std::max(newRadii.topLeft().height(), newRadii.topRight().height()); + if (maxRadii > newRect.height()) + newRect.setHeight(maxRadii); + break; case BSBottom: - overshoot = newRadii.bottomLeft().width() + newRadii.bottomRight().width() - newRect.width(); - if (overshoot > 0) { - ASSERT(!(newRadii.bottomLeft().width() && newRadii.bottomRight().width())); - newRect.setWidth(newRect.width() + overshoot); - if (!newRadii.bottomLeft().width()) - newRect.move(-overshoot, 0); - } - newRadii.setTopLeft(IntSize(0, 0)); - newRadii.setTopRight(IntSize(0, 0)); - maxRadii = std::max(newRadii.bottomLeft().height(), newRadii.bottomRight().height()); - if (maxRadii > newRect.height()) { - newRect.move(0, newRect.height() - maxRadii); - newRect.setHeight(maxRadii); - } - break; + overshoot = newRadii.bottomLeft().width() + + newRadii.bottomRight().width() - newRect.width(); + if (overshoot > 0) { + ASSERT( + !(newRadii.bottomLeft().width() && newRadii.bottomRight().width())); + newRect.setWidth(newRect.width() + overshoot); + if (!newRadii.bottomLeft().width()) + newRect.move(-overshoot, 0); + } + newRadii.setTopLeft(IntSize(0, 0)); + newRadii.setTopRight(IntSize(0, 0)); + maxRadii = std::max(newRadii.bottomLeft().height(), + newRadii.bottomRight().height()); + if (maxRadii > newRect.height()) { + newRect.move(0, newRect.height() - maxRadii); + newRect.setHeight(maxRadii); + } + break; case BSLeft: - overshoot = newRadii.topLeft().height() + newRadii.bottomLeft().height() - newRect.height(); - if (overshoot > 0) { - ASSERT(!(newRadii.topLeft().height() && newRadii.bottomLeft().height())); - newRect.setHeight(newRect.height() + overshoot); - if (!newRadii.topLeft().height()) - newRect.move(0, -overshoot); - } - newRadii.setTopRight(IntSize(0, 0)); - newRadii.setBottomRight(IntSize(0, 0)); - maxRadii = std::max(newRadii.topLeft().width(), newRadii.bottomLeft().width()); - if (maxRadii > newRect.width()) - newRect.setWidth(maxRadii); - break; + overshoot = newRadii.topLeft().height() + newRadii.bottomLeft().height() - + newRect.height(); + if (overshoot > 0) { + ASSERT( + !(newRadii.topLeft().height() && newRadii.bottomLeft().height())); + newRect.setHeight(newRect.height() + overshoot); + if (!newRadii.topLeft().height()) + newRect.move(0, -overshoot); + } + newRadii.setTopRight(IntSize(0, 0)); + newRadii.setBottomRight(IntSize(0, 0)); + maxRadii = + std::max(newRadii.topLeft().width(), newRadii.bottomLeft().width()); + if (maxRadii > newRect.width()) + newRect.setWidth(maxRadii); + break; case BSRight: - overshoot = newRadii.topRight().height() + newRadii.bottomRight().height() - newRect.height(); - if (overshoot > 0) { - ASSERT(!(newRadii.topRight().height() && newRadii.bottomRight().height())); - newRect.setHeight(newRect.height() + overshoot); - if (!newRadii.topRight().height()) - newRect.move(0, -overshoot); - } - newRadii.setTopLeft(IntSize(0, 0)); - newRadii.setBottomLeft(IntSize(0, 0)); - maxRadii = std::max(newRadii.topRight().width(), newRadii.bottomRight().width()); - if (maxRadii > newRect.width()) { - newRect.move(newRect.width() - maxRadii, 0); - newRect.setWidth(maxRadii); - } - break; - } - - return RoundedRect(newRect, newRadii); -} - -void RenderBoxModelObject::clipBorderSideForComplexInnerPath(GraphicsContext* graphicsContext, const RoundedRect& outerBorder, const RoundedRect& innerBorder, - BoxSide side, const class BorderEdge edges[]) -{ - graphicsContext->clip(calculateSideRectIncludingInner(outerBorder, edges, side)); - RoundedRect adjustedInnerRect = calculateAdjustedInnerBorder(innerBorder, side); - if (!adjustedInnerRect.isEmpty()) - graphicsContext->clipOutRoundedRect(adjustedInnerRect); -} - -void RenderBoxModelObject::getBorderEdgeInfo(BorderEdge edges[], const RenderStyle* style, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) const -{ - edges[BSTop] = BorderEdge(style->borderTopWidth(), - style->resolveColor(style->borderTopColor()), - style->borderTopStyle(), - style->borderTopIsTransparent(), - true); - - edges[BSRight] = BorderEdge(style->borderRightWidth(), - style->resolveColor(style->borderRightColor()), - style->borderRightStyle(), - style->borderRightIsTransparent(), - includeLogicalRightEdge); - - edges[BSBottom] = BorderEdge(style->borderBottomWidth(), - style->resolveColor(style->borderBottomColor()), - style->borderBottomStyle(), - style->borderBottomIsTransparent(), - true); - - edges[BSLeft] = BorderEdge(style->borderLeftWidth(), - style->resolveColor(style->borderLeftColor()), - style->borderLeftStyle(), - style->borderLeftIsTransparent(), - includeLogicalLeftEdge); -} - -bool RenderBoxModelObject::borderObscuresBackgroundEdge(const FloatSize& contextScale) const -{ - BorderEdge edges[4]; - getBorderEdgeInfo(edges, style()); - - for (int i = BSTop; i <= BSLeft; ++i) { - const BorderEdge& currEdge = edges[i]; - // FIXME: for vertical text - float axisScale = (i == BSTop || i == BSBottom) ? contextScale.height() : contextScale.width(); - if (!currEdge.obscuresBackgroundEdge(axisScale)) - return false; - } - - return true; -} - -bool RenderBoxModelObject::borderObscuresBackground() const -{ - if (!style()->hasBorder()) - return false; + overshoot = newRadii.topRight().height() + + newRadii.bottomRight().height() - newRect.height(); + if (overshoot > 0) { + ASSERT( + !(newRadii.topRight().height() && newRadii.bottomRight().height())); + newRect.setHeight(newRect.height() + overshoot); + if (!newRadii.topRight().height()) + newRect.move(0, -overshoot); + } + newRadii.setTopLeft(IntSize(0, 0)); + newRadii.setBottomLeft(IntSize(0, 0)); + maxRadii = + std::max(newRadii.topRight().width(), newRadii.bottomRight().width()); + if (maxRadii > newRect.width()) { + newRect.move(newRect.width() - maxRadii, 0); + newRect.setWidth(maxRadii); + } + break; + } + + return RoundedRect(newRect, newRadii); +} + +void RenderBoxModelObject::clipBorderSideForComplexInnerPath( + GraphicsContext* graphicsContext, + const RoundedRect& outerBorder, + const RoundedRect& innerBorder, + BoxSide side, + const class BorderEdge edges[]) { + graphicsContext->clip( + calculateSideRectIncludingInner(outerBorder, edges, side)); + RoundedRect adjustedInnerRect = + calculateAdjustedInnerBorder(innerBorder, side); + if (!adjustedInnerRect.isEmpty()) + graphicsContext->clipOutRoundedRect(adjustedInnerRect); +} + +void RenderBoxModelObject::getBorderEdgeInfo( + BorderEdge edges[], + const RenderStyle* style, + bool includeLogicalLeftEdge, + bool includeLogicalRightEdge) const { + edges[BSTop] = BorderEdge( + style->borderTopWidth(), style->resolveColor(style->borderTopColor()), + style->borderTopStyle(), style->borderTopIsTransparent(), true); + + edges[BSRight] = BorderEdge( + style->borderRightWidth(), style->resolveColor(style->borderRightColor()), + style->borderRightStyle(), style->borderRightIsTransparent(), + includeLogicalRightEdge); + + edges[BSBottom] = BorderEdge(style->borderBottomWidth(), + style->resolveColor(style->borderBottomColor()), + style->borderBottomStyle(), + style->borderBottomIsTransparent(), true); + + edges[BSLeft] = BorderEdge( + style->borderLeftWidth(), style->resolveColor(style->borderLeftColor()), + style->borderLeftStyle(), style->borderLeftIsTransparent(), + includeLogicalLeftEdge); +} + +bool RenderBoxModelObject::borderObscuresBackgroundEdge( + const FloatSize& contextScale) const { + BorderEdge edges[4]; + getBorderEdgeInfo(edges, style()); + + for (int i = BSTop; i <= BSLeft; ++i) { + const BorderEdge& currEdge = edges[i]; + // FIXME: for vertical text + float axisScale = (i == BSTop || i == BSBottom) ? contextScale.height() + : contextScale.width(); + if (!currEdge.obscuresBackgroundEdge(axisScale)) + return false; + } + + return true; +} + +bool RenderBoxModelObject::borderObscuresBackground() const { + if (!style()->hasBorder()) + return false; - BorderEdge edges[4]; - getBorderEdgeInfo(edges, style()); + BorderEdge edges[4]; + getBorderEdgeInfo(edges, style()); - for (int i = BSTop; i <= BSLeft; ++i) { - const BorderEdge& currEdge = edges[i]; - if (!currEdge.obscuresBackground()) - return false; - } + for (int i = BSTop; i <= BSLeft; ++i) { + const BorderEdge& currEdge = edges[i]; + if (!currEdge.obscuresBackground()) + return false; + } - return true; + return true; } -bool RenderBoxModelObject::boxShadowShouldBeAppliedToBackground(BackgroundBleedAvoidance bleedAvoidance, InlineFlowBox* inlineFlowBox) const -{ - if (bleedAvoidance != BackgroundBleedNone) - return false; - - const ShadowList* shadowList = style()->boxShadow(); - if (!shadowList) - return false; - - bool hasOneNormalBoxShadow = false; - size_t shadowCount = shadowList->shadows().size(); - for (size_t i = 0; i < shadowCount; ++i) { - const ShadowData& currentShadow = shadowList->shadows()[i]; - if (currentShadow.style() != Normal) - continue; +bool RenderBoxModelObject::boxShadowShouldBeAppliedToBackground( + BackgroundBleedAvoidance bleedAvoidance, + InlineFlowBox* inlineFlowBox) const { + if (bleedAvoidance != BackgroundBleedNone) + return false; - if (hasOneNormalBoxShadow) - return false; - hasOneNormalBoxShadow = true; + const ShadowList* shadowList = style()->boxShadow(); + if (!shadowList) + return false; - if (currentShadow.spread()) - return false; - } + bool hasOneNormalBoxShadow = false; + size_t shadowCount = shadowList->shadows().size(); + for (size_t i = 0; i < shadowCount; ++i) { + const ShadowData& currentShadow = shadowList->shadows()[i]; + if (currentShadow.style() != Normal) + continue; - if (!hasOneNormalBoxShadow) - return false; + if (hasOneNormalBoxShadow) + return false; + hasOneNormalBoxShadow = true; - Color backgroundColor = style()->resolveColor(style()->backgroundColor()); - if (backgroundColor.hasAlpha()) - return false; + if (currentShadow.spread()) + return false; + } - const FillLayer* lastBackgroundLayer = &style()->backgroundLayers(); - for (const FillLayer* next = lastBackgroundLayer->next(); next; next = lastBackgroundLayer->next()) - lastBackgroundLayer = next; + if (!hasOneNormalBoxShadow) + return false; - if (lastBackgroundLayer->clip() != BorderFillBox) - return false; + Color backgroundColor = style()->resolveColor(style()->backgroundColor()); + if (backgroundColor.hasAlpha()) + return false; - if (lastBackgroundLayer->image() && style()->hasBorderRadius()) - return false; + const FillLayer* lastBackgroundLayer = &style()->backgroundLayers(); + for (const FillLayer* next = lastBackgroundLayer->next(); next; + next = lastBackgroundLayer->next()) + lastBackgroundLayer = next; - if (inlineFlowBox && !inlineFlowBox->boxShadowCanBeAppliedToBackground(*lastBackgroundLayer)) - return false; + if (lastBackgroundLayer->clip() != BorderFillBox) + return false; - if (hasOverflowClip() && lastBackgroundLayer->attachment() == LocalBackgroundAttachment) - return false; + if (lastBackgroundLayer->image() && style()->hasBorderRadius()) + return false; - return true; -} + if (inlineFlowBox && + !inlineFlowBox->boxShadowCanBeAppliedToBackground(*lastBackgroundLayer)) + return false; -void RenderBoxModelObject::paintBoxShadow(const PaintInfo& info, const LayoutRect& paintRect, const RenderStyle* s, ShadowStyle shadowStyle, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) -{ - // FIXME: Deal with border-image. Would be great to use border-image as a mask. - GraphicsContext* context = info.context; - if (!s->boxShadow()) - return; + if (hasOverflowClip() && + lastBackgroundLayer->attachment() == LocalBackgroundAttachment) + return false; - RoundedRect border = (shadowStyle == Inset) ? s->getRoundedInnerBorderFor(paintRect, includeLogicalLeftEdge, includeLogicalRightEdge) - : s->getRoundedBorderFor(paintRect, includeLogicalLeftEdge, includeLogicalRightEdge); - - bool hasBorderRadius = s->hasBorderRadius(); - bool hasOpaqueBackground = s->resolveColor(s->backgroundColor()).alpha() == 255; - - GraphicsContextStateSaver stateSaver(*context, false); - - const ShadowList* shadowList = s->boxShadow(); - for (size_t i = shadowList->shadows().size(); i--; ) { - const ShadowData& shadow = shadowList->shadows()[i]; - if (shadow.style() != shadowStyle) - continue; - - FloatSize shadowOffset(shadow.x(), shadow.y()); - float shadowBlur = shadow.blur(); - float shadowSpread = shadow.spread(); - - if (shadowOffset.isZero() && !shadowBlur && !shadowSpread) - continue; - - const Color& shadowColor = shadow.color(); - - if (shadow.style() == Normal) { - FloatRect fillRect = border.rect(); - fillRect.inflate(shadowSpread); - if (fillRect.isEmpty()) - continue; - - FloatRect shadowRect(border.rect()); - shadowRect.inflate(shadowBlur + shadowSpread); - shadowRect.move(shadowOffset); - - // Save the state and clip, if not already done. - // The clip does not depend on any shadow-specific properties. - if (!stateSaver.saved()) { - stateSaver.save(); - if (hasBorderRadius) { - RoundedRect rectToClipOut = border; - - // If the box is opaque, it is unnecessary to clip it out. However, doing so saves time - // when painting the shadow. On the other hand, it introduces subpixel gaps along the - // corners. Those are avoided by insetting the clipping path by one pixel. - if (hasOpaqueBackground) - rectToClipOut.inflateWithRadii(-1); - - if (!rectToClipOut.isEmpty()) { - context->clipOutRoundedRect(rectToClipOut); - } - } else { - // This IntRect is correct even with fractional shadows, because it is used for the rectangle - // of the box itself, which is always pixel-aligned. - IntRect rectToClipOut = border.rect(); - - // If the box is opaque, it is unnecessary to clip it out. However, doing so saves time - // when painting the shadow. On the other hand, it introduces subpixel gaps along the - // edges if they are not pixel-aligned. Those are avoided by insetting the clipping path - // by one pixel. - if (hasOpaqueBackground) { - // FIXME: The function to decide on the policy based on the transform should be a named function. - // FIXME: It's not clear if this check is right. What about integral scale factors? - AffineTransform transform = context->getCTM(); - if (transform.a() != 1 || (transform.d() != 1 && transform.d() != -1) || transform.b() || transform.c()) - rectToClipOut.inflate(-1); - } - - if (!rectToClipOut.isEmpty()) { - context->clipOut(rectToClipOut); - } - } - } - - // Draw only the shadow. - OwnPtr drawLooperBuilder = DrawLooperBuilder::create(); - drawLooperBuilder->addShadow(shadowOffset, shadowBlur, shadowColor, - DrawLooperBuilder::ShadowRespectsTransforms, DrawLooperBuilder::ShadowIgnoresAlpha); - context->setDrawLooper(drawLooperBuilder.release()); - - if (hasBorderRadius) { - RoundedRect influenceRect(pixelSnappedIntRect(LayoutRect(shadowRect)), border.radii()); - influenceRect.expandRadii(2 * shadowBlur + shadowSpread); - if (allCornersClippedOut(influenceRect, info.rect)) - context->fillRect(fillRect, Color::black); - else { - // TODO: support non-integer shadows - crbug.com/334829 - RoundedRect roundedFillRect = border; - roundedFillRect.inflate(shadowSpread); - - roundedFillRect.expandRadii(shadowSpread); - if (!roundedFillRect.isRenderable()) - roundedFillRect.adjustRadii(); - context->fillRoundedRect(roundedFillRect, Color::black); - } - } else { - context->fillRect(fillRect, Color::black); - } + return true; +} + +void RenderBoxModelObject::paintBoxShadow(const PaintInfo& info, + const LayoutRect& paintRect, + const RenderStyle* s, + ShadowStyle shadowStyle, + bool includeLogicalLeftEdge, + bool includeLogicalRightEdge) { + // FIXME: Deal with border-image. Would be great to use border-image as a + // mask. + GraphicsContext* context = info.context; + if (!s->boxShadow()) + return; + + RoundedRect border = + (shadowStyle == Inset) + ? s->getRoundedInnerBorderFor(paintRect, includeLogicalLeftEdge, + includeLogicalRightEdge) + : s->getRoundedBorderFor(paintRect, includeLogicalLeftEdge, + includeLogicalRightEdge); + + bool hasBorderRadius = s->hasBorderRadius(); + bool hasOpaqueBackground = + s->resolveColor(s->backgroundColor()).alpha() == 255; + + GraphicsContextStateSaver stateSaver(*context, false); + + const ShadowList* shadowList = s->boxShadow(); + for (size_t i = shadowList->shadows().size(); i--;) { + const ShadowData& shadow = shadowList->shadows()[i]; + if (shadow.style() != shadowStyle) + continue; + + FloatSize shadowOffset(shadow.x(), shadow.y()); + float shadowBlur = shadow.blur(); + float shadowSpread = shadow.spread(); + + if (shadowOffset.isZero() && !shadowBlur && !shadowSpread) + continue; + + const Color& shadowColor = shadow.color(); + + if (shadow.style() == Normal) { + FloatRect fillRect = border.rect(); + fillRect.inflate(shadowSpread); + if (fillRect.isEmpty()) + continue; + + FloatRect shadowRect(border.rect()); + shadowRect.inflate(shadowBlur + shadowSpread); + shadowRect.move(shadowOffset); + + // Save the state and clip, if not already done. + // The clip does not depend on any shadow-specific properties. + if (!stateSaver.saved()) { + stateSaver.save(); + if (hasBorderRadius) { + RoundedRect rectToClipOut = border; + + // If the box is opaque, it is unnecessary to clip it out. However, + // doing so saves time when painting the shadow. On the other hand, it + // introduces subpixel gaps along the corners. Those are avoided by + // insetting the clipping path by one pixel. + if (hasOpaqueBackground) + rectToClipOut.inflateWithRadii(-1); + + if (!rectToClipOut.isEmpty()) { + context->clipOutRoundedRect(rectToClipOut); + } } else { - // The inset shadow case. - GraphicsContext::Edges clippedEdges = GraphicsContext::NoEdge; - if (!includeLogicalLeftEdge) { - clippedEdges |= GraphicsContext::LeftEdge; - } - if (!includeLogicalRightEdge) { - clippedEdges |= GraphicsContext::RightEdge; - } - // TODO: support non-integer shadows - crbug.com/334828 - context->drawInnerShadow(border, shadowColor, flooredIntSize(shadowOffset), shadowBlur, shadowSpread, clippedEdges); + // This IntRect is correct even with fractional shadows, because it is + // used for the rectangle of the box itself, which is always + // pixel-aligned. + IntRect rectToClipOut = border.rect(); + + // If the box is opaque, it is unnecessary to clip it out. However, + // doing so saves time when painting the shadow. On the other hand, it + // introduces subpixel gaps along the edges if they are not + // pixel-aligned. Those are avoided by insetting the clipping path by + // one pixel. + if (hasOpaqueBackground) { + // FIXME: The function to decide on the policy based on the + // transform should be a named function. + // FIXME: It's not clear if this check is right. What about integral + // scale factors? + AffineTransform transform = context->getCTM(); + if (transform.a() != 1 || + (transform.d() != 1 && transform.d() != -1) || transform.b() || + transform.c()) + rectToClipOut.inflate(-1); + } + + if (!rectToClipOut.isEmpty()) { + context->clipOut(rectToClipOut); + } + } + } + + // Draw only the shadow. + OwnPtr drawLooperBuilder = DrawLooperBuilder::create(); + drawLooperBuilder->addShadow(shadowOffset, shadowBlur, shadowColor, + DrawLooperBuilder::ShadowRespectsTransforms, + DrawLooperBuilder::ShadowIgnoresAlpha); + context->setDrawLooper(drawLooperBuilder.release()); + + if (hasBorderRadius) { + RoundedRect influenceRect(pixelSnappedIntRect(LayoutRect(shadowRect)), + border.radii()); + influenceRect.expandRadii(2 * shadowBlur + shadowSpread); + if (allCornersClippedOut(influenceRect, info.rect)) + context->fillRect(fillRect, Color::black); + else { + // TODO: support non-integer shadows - crbug.com/334829 + RoundedRect roundedFillRect = border; + roundedFillRect.inflate(shadowSpread); + + roundedFillRect.expandRadii(shadowSpread); + if (!roundedFillRect.isRenderable()) + roundedFillRect.adjustRadii(); + context->fillRoundedRect(roundedFillRect, Color::black); } + } else { + context->fillRect(fillRect, Color::black); + } + } else { + // The inset shadow case. + GraphicsContext::Edges clippedEdges = GraphicsContext::NoEdge; + if (!includeLogicalLeftEdge) { + clippedEdges |= GraphicsContext::LeftEdge; + } + if (!includeLogicalRightEdge) { + clippedEdges |= GraphicsContext::RightEdge; + } + // TODO: support non-integer shadows - crbug.com/334828 + context->drawInnerShadow(border, shadowColor, + flooredIntSize(shadowOffset), shadowBlur, + shadowSpread, clippedEdges); } + } } -LayoutUnit RenderBoxModelObject::containingBlockLogicalWidthForContent() const -{ - return containingBlock()->availableLogicalWidth(); +LayoutUnit RenderBoxModelObject::containingBlockLogicalWidthForContent() const { + return containingBlock()->availableLogicalWidth(); } -LayoutRect RenderBoxModelObject::localCaretRectForEmptyElement(LayoutUnit width, LayoutUnit textIndentOffset) -{ - ASSERT(!slowFirstChild()); +LayoutRect RenderBoxModelObject::localCaretRectForEmptyElement( + LayoutUnit width, + LayoutUnit textIndentOffset) { + ASSERT(!slowFirstChild()); - // FIXME: This does not take into account either :first-line or :first-letter - // However, as soon as some content is entered, the line boxes will be - // constructed and this kludge is not called any more. So only the caret size - // of an empty :first-line'd block is wrong. I think we can live with that. - RenderStyle* currentStyle = firstLineStyle(); + // FIXME: This does not take into account either :first-line or :first-letter + // However, as soon as some content is entered, the line boxes will be + // constructed and this kludge is not called any more. So only the caret size + // of an empty :first-line'd block is wrong. I think we can live with that. + RenderStyle* currentStyle = firstLineStyle(); - enum CaretAlignment { alignLeft, alignRight, alignCenter }; + enum CaretAlignment { alignLeft, alignRight, alignCenter }; - CaretAlignment alignment = alignLeft; + CaretAlignment alignment = alignLeft; - switch (currentStyle->textAlign()) { + switch (currentStyle->textAlign()) { case LEFT: - break; + break; case CENTER: - alignment = alignCenter; - break; + alignment = alignCenter; + break; case RIGHT: - alignment = alignRight; - break; + alignment = alignRight; + break; case JUSTIFY: case TASTART: - if (!currentStyle->isLeftToRightDirection()) - alignment = alignRight; - break; + if (!currentStyle->isLeftToRightDirection()) + alignment = alignRight; + break; case TAEND: - if (currentStyle->isLeftToRightDirection()) - alignment = alignRight; - break; - } + if (currentStyle->isLeftToRightDirection()) + alignment = alignRight; + break; + } - LayoutUnit x = borderLeft() + paddingLeft(); - LayoutUnit maxX = width - borderRight() - paddingRight(); + LayoutUnit x = borderLeft() + paddingLeft(); + LayoutUnit maxX = width - borderRight() - paddingRight(); - switch (alignment) { + switch (alignment) { case alignLeft: - if (currentStyle->isLeftToRightDirection()) - x += textIndentOffset; - break; + if (currentStyle->isLeftToRightDirection()) + x += textIndentOffset; + break; case alignCenter: - x = (x + maxX) / 2; - if (currentStyle->isLeftToRightDirection()) - x += textIndentOffset / 2; - else - x -= textIndentOffset / 2; - break; + x = (x + maxX) / 2; + if (currentStyle->isLeftToRightDirection()) + x += textIndentOffset / 2; + else + x -= textIndentOffset / 2; + break; case alignRight: - x = maxX - caretWidth; - if (!currentStyle->isLeftToRightDirection()) - x -= textIndentOffset; - break; - } - x = std::min(x, std::max(maxX - caretWidth, 0)); - - LayoutUnit height = style()->fontMetrics().height(); - LayoutUnit verticalSpace = lineHeight(true, HorizontalLine, PositionOfInteriorLineBoxes) - height; - LayoutUnit y = paddingTop() + borderTop() + (verticalSpace / 2); - return LayoutRect(x, y, caretWidth, height); -} - -bool RenderBoxModelObject::shouldAntialiasLines(GraphicsContext* context) -{ - // FIXME: We may want to not antialias when scaled by an integral value, - // and we may want to antialias when translated by a non-integral value. - // FIXME: See crbug.com/382491. getCTM does not include scale factors applied at raster time, such - // as device zoom. - return !context->getCTM().isIdentityOrTranslationOrFlipped(); -} - -void RenderBoxModelObject::mapAbsoluteToLocalPoint(MapCoordinatesFlags mode, TransformState& transformState) const -{ - RenderObject* o = container(); - if (!o) - return; - - o->mapAbsoluteToLocalPoint(mode, transformState); - - LayoutSize containerOffset = offsetFromContainer(o, LayoutPoint()); - - bool preserve3D = mode & UseTransforms && (o->style()->preserves3D() || style()->preserves3D()); - if (mode & UseTransforms && shouldUseTransformFromContainer(o)) { - TransformationMatrix t; - getTransformFromContainer(o, containerOffset, t); - transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); - } else - transformState.move(containerOffset.width(), containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); -} - -const RenderObject* RenderBoxModelObject::pushMappingToContainer(const RenderBox* ancestorToStopAt, RenderGeometryMap& geometryMap) const -{ - ASSERT(ancestorToStopAt != this); - - bool ancestorSkipped; - RenderObject* container = this->container(ancestorToStopAt, &ancestorSkipped); - if (!container) - return 0; - - bool isInline = isRenderInline(); - bool hasTransform = !isInline && isBox() && toRenderBox(this)->transform(); - - LayoutSize adjustmentForSkippedAncestor; - if (ancestorSkipped) { - // There can't be a transform between paintInvalidationContainer and o, because transforms create containers, so it should be safe - // to just subtract the delta between the ancestor and o. - adjustmentForSkippedAncestor = -ancestorToStopAt->offsetFromAncestorContainer(container); + x = maxX - caretWidth; + if (!currentStyle->isLeftToRightDirection()) + x -= textIndentOffset; + break; + } + x = std::min(x, std::max(maxX - caretWidth, 0)); + + LayoutUnit height = style()->fontMetrics().height(); + LayoutUnit verticalSpace = + lineHeight(true, HorizontalLine, PositionOfInteriorLineBoxes) - height; + LayoutUnit y = paddingTop() + borderTop() + (verticalSpace / 2); + return LayoutRect(x, y, caretWidth, height); +} + +bool RenderBoxModelObject::shouldAntialiasLines(GraphicsContext* context) { + // FIXME: We may want to not antialias when scaled by an integral value, + // and we may want to antialias when translated by a non-integral value. + // FIXME: See crbug.com/382491. getCTM does not include scale factors applied + // at raster time, such as device zoom. + return !context->getCTM().isIdentityOrTranslationOrFlipped(); +} + +void RenderBoxModelObject::mapAbsoluteToLocalPoint( + MapCoordinatesFlags mode, + TransformState& transformState) const { + RenderObject* o = container(); + if (!o) + return; + + o->mapAbsoluteToLocalPoint(mode, transformState); + + LayoutSize containerOffset = offsetFromContainer(o, LayoutPoint()); + + bool preserve3D = mode & UseTransforms && + (o->style()->preserves3D() || style()->preserves3D()); + if (mode & UseTransforms && shouldUseTransformFromContainer(o)) { + TransformationMatrix t; + getTransformFromContainer(o, containerOffset, t); + transformState.applyTransform(t, preserve3D + ? TransformState::AccumulateTransform + : TransformState::FlattenTransform); + } else + transformState.move(containerOffset.width(), containerOffset.height(), + preserve3D ? TransformState::AccumulateTransform + : TransformState::FlattenTransform); +} + +const RenderObject* RenderBoxModelObject::pushMappingToContainer( + const RenderBox* ancestorToStopAt, + RenderGeometryMap& geometryMap) const { + ASSERT(ancestorToStopAt != this); + + bool ancestorSkipped; + RenderObject* container = this->container(ancestorToStopAt, &ancestorSkipped); + if (!container) + return 0; + + bool isInline = isRenderInline(); + bool hasTransform = !isInline && isBox() && toRenderBox(this)->transform(); + + LayoutSize adjustmentForSkippedAncestor; + if (ancestorSkipped) { + // There can't be a transform between paintInvalidationContainer and o, + // because transforms create containers, so it should be safe to just + // subtract the delta between the ancestor and o. + adjustmentForSkippedAncestor = + -ancestorToStopAt->offsetFromAncestorContainer(container); + } + + bool offsetDependsOnPoint = false; + LayoutSize containerOffset = + offsetFromContainer(container, LayoutPoint(), &offsetDependsOnPoint); + + bool preserve3D = container->style()->preserves3D() || style()->preserves3D(); + if (shouldUseTransformFromContainer(container)) { + TransformationMatrix t; + getTransformFromContainer(container, containerOffset, t); + t.translateRight(adjustmentForSkippedAncestor.width().toFloat(), + adjustmentForSkippedAncestor.height().toFloat()); + geometryMap.push(this, t, preserve3D, offsetDependsOnPoint, hasTransform); + } else { + containerOffset += adjustmentForSkippedAncestor; + geometryMap.push(this, containerOffset, preserve3D, offsetDependsOnPoint, + hasTransform); + } + + return ancestorSkipped ? ancestorToStopAt : container; +} + +void RenderBoxModelObject::collectSelfPaintingLayers( + Vector& layers) { + for (RenderObject* child = slowFirstChild(); child; + child = child->nextSibling()) { + if (child->isBox()) { + RenderBox* childBox = toRenderBox(child); + if (childBox->hasSelfPaintingLayer()) + layers.append(childBox); + else + childBox->collectSelfPaintingLayers(layers); + } else if (child->isBoxModelObject()) { + toRenderBoxModelObject(child)->collectSelfPaintingLayers(layers); } - - bool offsetDependsOnPoint = false; - LayoutSize containerOffset = offsetFromContainer(container, LayoutPoint(), &offsetDependsOnPoint); - - bool preserve3D = container->style()->preserves3D() || style()->preserves3D(); - if (shouldUseTransformFromContainer(container)) { - TransformationMatrix t; - getTransformFromContainer(container, containerOffset, t); - t.translateRight(adjustmentForSkippedAncestor.width().toFloat(), adjustmentForSkippedAncestor.height().toFloat()); - geometryMap.push(this, t, preserve3D, offsetDependsOnPoint, hasTransform); - } else { - containerOffset += adjustmentForSkippedAncestor; - geometryMap.push(this, containerOffset, preserve3D, offsetDependsOnPoint, hasTransform); - } - - return ancestorSkipped ? ancestorToStopAt : container; -} - -void RenderBoxModelObject::collectSelfPaintingLayers(Vector& layers) -{ - for (RenderObject* child = slowFirstChild(); child; child = child->nextSibling()) { - if (child->isBox()) { - RenderBox* childBox = toRenderBox(child); - if (childBox->hasSelfPaintingLayer()) - layers.append(childBox); - else - childBox->collectSelfPaintingLayers(layers); - } else if (child->isBoxModelObject()) { - toRenderBoxModelObject(child)->collectSelfPaintingLayers(layers); - } - } -} - -void RenderBoxModelObject::moveChildTo(RenderBoxModelObject* toBoxModelObject, RenderObject* child, RenderObject* beforeChild, bool fullRemoveInsert) -{ - // We assume that callers have cleared their positioned objects list for child moves (!fullRemoveInsert) so the - // positioned renderer maps don't become stale. It would be too slow to do the map lookup on each call. - ASSERT(!fullRemoveInsert || !isRenderBlock() || !toRenderBlock(this)->hasPositionedObjects()); - - ASSERT(this == child->parent()); - ASSERT(!beforeChild || toBoxModelObject == beforeChild->parent()); - if (fullRemoveInsert && (toBoxModelObject->isRenderBlock() || toBoxModelObject->isRenderInline())) { - // Takes care of adding the new child correctly if toBlock and fromBlock - // have different kind of children (block vs inline). - toBoxModelObject->addChild(virtualChildren()->removeChildNode(this, child), beforeChild); - } else - toBoxModelObject->virtualChildren()->insertChildNode(toBoxModelObject, virtualChildren()->removeChildNode(this, child, fullRemoveInsert), beforeChild, fullRemoveInsert); -} - -void RenderBoxModelObject::moveAllChildrenTo(RenderBoxModelObject* toBoxModelObject, RenderObject* beforeChild, bool fullRemoveInsert) -{ - // This condition is rarely hit since this function is usually called on - // anonymous blocks which can no longer carry positioned objects (see r120761) - // or when fullRemoveInsert is false. - if (fullRemoveInsert && isRenderBlock()) { - RenderBlock* block = toRenderBlock(this); - block->removePositionedObjects(0); - } - - ASSERT(!beforeChild || toBoxModelObject == beforeChild->parent()); - for (RenderObject* child = slowFirstChild(); child; ) { - // Save our next sibling as moveChildTo will clear it. - RenderObject* nextSibling = child->nextSibling(); - moveChildTo(toBoxModelObject, child, beforeChild, fullRemoveInsert); - child = nextSibling; - } -} - -} // namespace blink + } +} + +void RenderBoxModelObject::moveChildTo(RenderBoxModelObject* toBoxModelObject, + RenderObject* child, + RenderObject* beforeChild, + bool fullRemoveInsert) { + // We assume that callers have cleared their positioned objects list for child + // moves (!fullRemoveInsert) so the positioned renderer maps don't become + // stale. It would be too slow to do the map lookup on each call. + ASSERT(!fullRemoveInsert || !isRenderBlock() || + !toRenderBlock(this)->hasPositionedObjects()); + + ASSERT(this == child->parent()); + ASSERT(!beforeChild || toBoxModelObject == beforeChild->parent()); + if (fullRemoveInsert && (toBoxModelObject->isRenderBlock() || + toBoxModelObject->isRenderInline())) { + // Takes care of adding the new child correctly if toBlock and fromBlock + // have different kind of children (block vs inline). + toBoxModelObject->addChild(virtualChildren()->removeChildNode(this, child), + beforeChild); + } else + toBoxModelObject->virtualChildren()->insertChildNode( + toBoxModelObject, + virtualChildren()->removeChildNode(this, child, fullRemoveInsert), + beforeChild, fullRemoveInsert); +} + +void RenderBoxModelObject::moveAllChildrenTo( + RenderBoxModelObject* toBoxModelObject, + RenderObject* beforeChild, + bool fullRemoveInsert) { + // This condition is rarely hit since this function is usually called on + // anonymous blocks which can no longer carry positioned objects (see r120761) + // or when fullRemoveInsert is false. + if (fullRemoveInsert && isRenderBlock()) { + RenderBlock* block = toRenderBlock(this); + block->removePositionedObjects(0); + } + + ASSERT(!beforeChild || toBoxModelObject == beforeChild->parent()); + for (RenderObject* child = slowFirstChild(); child;) { + // Save our next sibling as moveChildTo will clear it. + RenderObject* nextSibling = child->nextSibling(); + moveChildTo(toBoxModelObject, child, beforeChild, fullRemoveInsert); + child = nextSibling; + } +} + +} // namespace blink diff --git a/sky/engine/core/rendering/RenderBoxModelObject.h b/sky/engine/core/rendering/RenderBoxModelObject.h index b963a4e30e7f1..cb907d8b4c35b 100644 --- a/sky/engine/core/rendering/RenderBoxModelObject.h +++ b/sky/engine/core/rendering/RenderBoxModelObject.h @@ -37,248 +37,395 @@ enum LineDirectionMode { HorizontalLine }; typedef unsigned BorderEdgeFlags; enum BackgroundBleedAvoidance { - BackgroundBleedNone, - BackgroundBleedShrinkBackground, - BackgroundBleedClipBackground, - BackgroundBleedBackgroundOverBorder + BackgroundBleedNone, + BackgroundBleedShrinkBackground, + BackgroundBleedClipBackground, + BackgroundBleedBackgroundOverBorder }; -enum ContentChangeType { - CanvasChanged, - CanvasContextChanged -}; +enum ContentChangeType { CanvasChanged, CanvasContextChanged }; -// This class is the base for all objects that adhere to the CSS box model as described -// at http://www.w3.org/TR/CSS21/box.html +// This class is the base for all objects that adhere to the CSS box model as +// described at http://www.w3.org/TR/CSS21/box.html class RenderBoxModelObject : public RenderObject { -public: - RenderBoxModelObject(); - virtual ~RenderBoxModelObject(); - - LayoutSize relativePositionOffset() const; - LayoutSize relativePositionLogicalOffset() const { return relativePositionOffset(); } - - // IE extensions. Used to calculate offsetWidth/Height. Overridden by inlines (RenderFlow) - // to return the remaining width on a given line (and the height of a single line). - virtual LayoutUnit offsetLeft() const; - virtual LayoutUnit offsetTop() const; - virtual LayoutUnit offsetWidth() const = 0; - virtual LayoutUnit offsetHeight() const = 0; - - int pixelSnappedOffsetLeft() const { return roundToInt(offsetLeft()); } - int pixelSnappedOffsetTop() const { return roundToInt(offsetTop()); } - virtual int pixelSnappedOffsetWidth() const; - virtual int pixelSnappedOffsetHeight() const; - - // This will work on inlines to return the bounding box of all of the lines' border boxes. - virtual IntRect borderBoundingBox() const = 0; - - // These return the CSS computed padding values. - LayoutUnit computedCSSPaddingTop() const { return computedCSSPadding(style()->paddingTop()); } - LayoutUnit computedCSSPaddingBottom() const { return computedCSSPadding(style()->paddingBottom()); } - LayoutUnit computedCSSPaddingLeft() const { return computedCSSPadding(style()->paddingLeft()); } - LayoutUnit computedCSSPaddingRight() const { return computedCSSPadding(style()->paddingRight()); } - LayoutUnit computedCSSPaddingBefore() const { return computedCSSPadding(style()->paddingBefore()); } - LayoutUnit computedCSSPaddingAfter() const { return computedCSSPadding(style()->paddingAfter()); } - LayoutUnit computedCSSPaddingStart() const { return computedCSSPadding(style()->paddingStart()); } - LayoutUnit computedCSSPaddingEnd() const { return computedCSSPadding(style()->paddingEnd()); } - - // These functions are used during layout. Table cells - // override them to include some extra intrinsic padding. - virtual LayoutUnit paddingTop() const { return computedCSSPaddingTop(); } - virtual LayoutUnit paddingBottom() const { return computedCSSPaddingBottom(); } - virtual LayoutUnit paddingLeft() const { return computedCSSPaddingLeft(); } - virtual LayoutUnit paddingRight() const { return computedCSSPaddingRight(); } - virtual LayoutUnit paddingBefore() const { return computedCSSPaddingBefore(); } - virtual LayoutUnit paddingAfter() const { return computedCSSPaddingAfter(); } - virtual LayoutUnit paddingStart() const { return computedCSSPaddingStart(); } - virtual LayoutUnit paddingEnd() const { return computedCSSPaddingEnd(); } - - virtual int borderTop() const { return style()->borderTopWidth(); } - virtual int borderBottom() const { return style()->borderBottomWidth(); } - virtual int borderLeft() const { return style()->borderLeftWidth(); } - virtual int borderRight() const { return style()->borderRightWidth(); } - virtual int borderBefore() const { return style()->borderBeforeWidth(); } - virtual int borderAfter() const { return style()->borderAfterWidth(); } - virtual int borderStart() const { return style()->borderStartWidth(); } - virtual int borderEnd() const { return style()->borderEndWidth(); } - - int borderWidth() const { return borderLeft() + borderRight(); } - int borderHeight() const { return borderTop() + borderBottom(); } - - LayoutUnit borderAndPaddingStart() const { return borderStart() + paddingStart(); } - LayoutUnit borderAndPaddingBefore() const { return borderBefore() + paddingBefore(); } - LayoutUnit borderAndPaddingAfter() const { return borderAfter() + paddingAfter(); } - - LayoutUnit borderAndPaddingHeight() const { return borderTop() + borderBottom() + paddingTop() + paddingBottom(); } - LayoutUnit borderAndPaddingWidth() const { return borderLeft() + borderRight() + paddingLeft() + paddingRight(); } - LayoutUnit borderAndPaddingLogicalHeight() const { return borderAndPaddingBefore() + borderAndPaddingAfter(); } - LayoutUnit borderAndPaddingLogicalWidth() const { return borderStart() + borderEnd() + paddingStart() + paddingEnd(); } - LayoutUnit borderAndPaddingLogicalLeft() const { return borderLeft() + paddingLeft(); } - - - LayoutUnit borderLogicalLeft() const { return borderLeft(); } - LayoutUnit borderLogicalRight() const { return borderRight(); } - LayoutUnit borderLogicalWidth() const { return borderStart() + borderEnd(); } - LayoutUnit borderLogicalHeight() const { return borderBefore() + borderAfter(); } - - LayoutUnit paddingLogicalLeft() const { return paddingLeft(); } - LayoutUnit paddingLogicalRight() const { return paddingRight(); } - LayoutUnit paddingLogicalWidth() const { return paddingStart() + paddingEnd(); } - LayoutUnit paddingLogicalHeight() const { return paddingBefore() + paddingAfter(); } - - virtual LayoutUnit marginTop() const = 0; - virtual LayoutUnit marginBottom() const = 0; - virtual LayoutUnit marginLeft() const = 0; - virtual LayoutUnit marginRight() const = 0; - virtual LayoutUnit marginBefore(const RenderStyle* otherStyle = 0) const = 0; - virtual LayoutUnit marginAfter(const RenderStyle* otherStyle = 0) const = 0; - virtual LayoutUnit marginStart(const RenderStyle* otherStyle = 0) const = 0; - virtual LayoutUnit marginEnd(const RenderStyle* otherStyle = 0) const = 0; - LayoutUnit marginHeight() const { return marginTop() + marginBottom(); } - LayoutUnit marginWidth() const { return marginLeft() + marginRight(); } - LayoutUnit marginLogicalHeight() const { return marginBefore() + marginAfter(); } - LayoutUnit marginLogicalWidth() const { return marginStart() + marginEnd(); } - - bool hasInlineDirectionBordersPaddingOrMargin() const { return hasInlineDirectionBordersOrPadding() || marginStart()|| marginEnd(); } - bool hasInlineDirectionBordersOrPadding() const { return borderStart() || borderEnd() || paddingStart()|| paddingEnd(); } - - LayoutUnit containingBlockLogicalWidthForContent() const; - - void paintBorder(const PaintInfo&, const LayoutRect&, const RenderStyle*, BackgroundBleedAvoidance = BackgroundBleedNone, bool includeLogicalLeftEdge = true, bool includeLogicalRightEdge = true); - void paintBoxShadow(const PaintInfo&, const LayoutRect&, const RenderStyle*, ShadowStyle, bool includeLogicalLeftEdge = true, bool includeLogicalRightEdge = true); - void paintFillLayerExtended(const PaintInfo&, const Color&, const FillLayer&, const LayoutRect&, BackgroundBleedAvoidance, InlineFlowBox* = 0, const LayoutSize& = LayoutSize(), RenderObject* backgroundObject = 0, bool skipBaseColor = false); - - bool boxShadowShouldBeAppliedToBackground(BackgroundBleedAvoidance, InlineFlowBox* = 0) const; - - // Overridden by subclasses to determine line height and baseline position. - virtual LayoutUnit lineHeight(bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const = 0; - virtual int baselinePosition(FontBaseline, bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const = 0; - - virtual void mapAbsoluteToLocalPoint(MapCoordinatesFlags, TransformState&) const override; - virtual const RenderObject* pushMappingToContainer(const RenderBox* ancestorToStopAt, RenderGeometryMap&) const override; - - void collectSelfPaintingLayers(Vector& layers); - - virtual void setSelectionState(SelectionState) override; - -protected: - class BackgroundImageGeometry { - public: - BackgroundImageGeometry() - : m_hasNonLocalGeometry(false) - { } - - IntPoint destOrigin() const { return m_destOrigin; } - void setDestOrigin(const IntPoint& destOrigin) - { - m_destOrigin = destOrigin; - } - - IntRect destRect() const { return m_destRect; } - void setDestRect(const IntRect& destRect) - { - m_destRect = destRect; - } - - // Returns the phase relative to the destination rectangle. - IntPoint relativePhase() const; - - IntPoint phase() const { return m_phase; } - void setPhase(const IntPoint& phase) - { - m_phase = phase; - } - - IntSize tileSize() const { return m_tileSize; } - void setTileSize(const IntSize& tileSize) - { - m_tileSize = tileSize; - } - - // Space-size represents extra width and height that may be added to - // the image if used as a pattern with repeat: space - IntSize spaceSize() const { return m_repeatSpacing; } - void setSpaceSize(const IntSize& repeatSpacing) - { - m_repeatSpacing = repeatSpacing; - } - - void setPhaseX(int x) { m_phase.setX(x); } - void setPhaseY(int y) { m_phase.setY(y); } - - void setNoRepeatX(int xOffset); - void setNoRepeatY(int yOffset); - - void useFixedAttachment(const IntPoint& attachmentPoint); - - void clip(const IntRect&); - - void setHasNonLocalGeometry(bool hasNonLocalGeometry = true) { m_hasNonLocalGeometry = hasNonLocalGeometry; } - bool hasNonLocalGeometry() const { return m_hasNonLocalGeometry; } - - private: - IntRect m_destRect; - IntPoint m_destOrigin; - IntPoint m_phase; - IntSize m_tileSize; - IntSize m_repeatSpacing; - bool m_hasNonLocalGeometry; // Has background-attachment: fixed. Implies that we can't always cheaply compute destRect. - }; - - LayoutPoint adjustedPositionRelativeToOffsetParent(const LayoutPoint&) const; - - void getBorderEdgeInfo(class BorderEdge[], const RenderStyle*, bool includeLogicalLeftEdge = true, bool includeLogicalRightEdge = true) const; - bool borderObscuresBackgroundEdge(const FloatSize& contextScale) const; - bool borderObscuresBackground() const; - RoundedRect backgroundRoundedRectAdjustedForBleedAvoidance(GraphicsContext*, const LayoutRect&, BackgroundBleedAvoidance, InlineFlowBox*, const LayoutSize&, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) const; - LayoutRect borderInnerRectAdjustedForBleedAvoidance(GraphicsContext*, const LayoutRect&, BackgroundBleedAvoidance) const; - - LayoutRect localCaretRectForEmptyElement(LayoutUnit width, LayoutUnit textIndentOffset); - - static void clipRoundedInnerRect(GraphicsContext*, const LayoutRect&, const RoundedRect& clipRect); - - bool hasAutoHeightOrContainingBlockWithAutoHeight() const; - - void paintRootBackgroundColor(const PaintInfo&, const LayoutRect&, const Color&); - -public: - static bool shouldAntialiasLines(GraphicsContext*); - - // These functions are only used internally to manipulate the render tree structure via remove/insert/appendChildNode. - void moveChildTo(RenderBoxModelObject* toBoxModelObject, RenderObject* child, RenderObject* beforeChild, bool fullRemoveInsert); - void moveAllChildrenTo(RenderBoxModelObject* toBoxModelObject, RenderObject* beforeChild, bool fullRemoveInsert); - - IntSize calculateImageIntrinsicDimensions(StyleImage*, const IntSize& scaledPositioningAreaSize) const; - -private: - LayoutUnit computedCSSPadding(const Length&) const; - virtual bool isBoxModelObject() const override final { return true; } - - IntSize calculateFillTileSize(const FillLayer&, const IntSize& scaledPositioningAreaSize) const; - - RoundedRect getBackgroundRoundedRect(const LayoutRect&, InlineFlowBox*, LayoutUnit inlineBoxWidth, LayoutUnit inlineBoxHeight, - bool includeLogicalLeftEdge, bool includeLogicalRightEdge) const; - - void clipBorderSidePolygon(GraphicsContext*, const RoundedRect& outerBorder, const RoundedRect& innerBorder, - BoxSide, bool firstEdgeMatches, bool secondEdgeMatches); - void clipBorderSideForComplexInnerPath(GraphicsContext*, const RoundedRect&, const RoundedRect&, BoxSide, const class BorderEdge[]); - void paintOneBorderSide(GraphicsContext*, const RenderStyle*, const RoundedRect& outerBorder, const RoundedRect& innerBorder, - const IntRect& sideRect, BoxSide, BoxSide adjacentSide1, BoxSide adjacentSide2, const class BorderEdge[], - const Path*, BackgroundBleedAvoidance, bool includeLogicalLeftEdge, bool includeLogicalRightEdge, bool antialias, const Color* overrideColor = 0); - void paintTranslucentBorderSides(GraphicsContext*, const RenderStyle*, const RoundedRect& outerBorder, const RoundedRect& innerBorder, const IntPoint& innerBorderAdjustment, - const class BorderEdge[], BorderEdgeFlags, BackgroundBleedAvoidance, bool includeLogicalLeftEdge, bool includeLogicalRightEdge, bool antialias = false); - void paintBorderSides(GraphicsContext*, const RenderStyle*, const RoundedRect& outerBorder, const RoundedRect& innerBorder, - const IntPoint& innerBorderAdjustment, const class BorderEdge[], BorderEdgeFlags, BackgroundBleedAvoidance, - bool includeLogicalLeftEdge, bool includeLogicalRightEdge, bool antialias = false, const Color* overrideColor = 0); - void drawBoxSideFromPath(GraphicsContext*, const LayoutRect&, const Path&, const class BorderEdge[], - float thickness, float drawThickness, BoxSide, const RenderStyle*, - Color, EBorderStyle, BackgroundBleedAvoidance, bool includeLogicalLeftEdge, bool includeLogicalRightEdge); + public: + RenderBoxModelObject(); + virtual ~RenderBoxModelObject(); + + LayoutSize relativePositionOffset() const; + LayoutSize relativePositionLogicalOffset() const { + return relativePositionOffset(); + } + + // IE extensions. Used to calculate offsetWidth/Height. Overridden by inlines + // (RenderFlow) to return the remaining width on a given line (and the height + // of a single line). + virtual LayoutUnit offsetLeft() const; + virtual LayoutUnit offsetTop() const; + virtual LayoutUnit offsetWidth() const = 0; + virtual LayoutUnit offsetHeight() const = 0; + + int pixelSnappedOffsetLeft() const { return roundToInt(offsetLeft()); } + int pixelSnappedOffsetTop() const { return roundToInt(offsetTop()); } + virtual int pixelSnappedOffsetWidth() const; + virtual int pixelSnappedOffsetHeight() const; + + // This will work on inlines to return the bounding box of all of the lines' + // border boxes. + virtual IntRect borderBoundingBox() const = 0; + + // These return the CSS computed padding values. + LayoutUnit computedCSSPaddingTop() const { + return computedCSSPadding(style()->paddingTop()); + } + LayoutUnit computedCSSPaddingBottom() const { + return computedCSSPadding(style()->paddingBottom()); + } + LayoutUnit computedCSSPaddingLeft() const { + return computedCSSPadding(style()->paddingLeft()); + } + LayoutUnit computedCSSPaddingRight() const { + return computedCSSPadding(style()->paddingRight()); + } + LayoutUnit computedCSSPaddingBefore() const { + return computedCSSPadding(style()->paddingBefore()); + } + LayoutUnit computedCSSPaddingAfter() const { + return computedCSSPadding(style()->paddingAfter()); + } + LayoutUnit computedCSSPaddingStart() const { + return computedCSSPadding(style()->paddingStart()); + } + LayoutUnit computedCSSPaddingEnd() const { + return computedCSSPadding(style()->paddingEnd()); + } + + // These functions are used during layout. Table cells + // override them to include some extra intrinsic padding. + virtual LayoutUnit paddingTop() const { return computedCSSPaddingTop(); } + virtual LayoutUnit paddingBottom() const { + return computedCSSPaddingBottom(); + } + virtual LayoutUnit paddingLeft() const { return computedCSSPaddingLeft(); } + virtual LayoutUnit paddingRight() const { return computedCSSPaddingRight(); } + virtual LayoutUnit paddingBefore() const { + return computedCSSPaddingBefore(); + } + virtual LayoutUnit paddingAfter() const { return computedCSSPaddingAfter(); } + virtual LayoutUnit paddingStart() const { return computedCSSPaddingStart(); } + virtual LayoutUnit paddingEnd() const { return computedCSSPaddingEnd(); } + + virtual int borderTop() const { return style()->borderTopWidth(); } + virtual int borderBottom() const { return style()->borderBottomWidth(); } + virtual int borderLeft() const { return style()->borderLeftWidth(); } + virtual int borderRight() const { return style()->borderRightWidth(); } + virtual int borderBefore() const { return style()->borderBeforeWidth(); } + virtual int borderAfter() const { return style()->borderAfterWidth(); } + virtual int borderStart() const { return style()->borderStartWidth(); } + virtual int borderEnd() const { return style()->borderEndWidth(); } + + int borderWidth() const { return borderLeft() + borderRight(); } + int borderHeight() const { return borderTop() + borderBottom(); } + + LayoutUnit borderAndPaddingStart() const { + return borderStart() + paddingStart(); + } + LayoutUnit borderAndPaddingBefore() const { + return borderBefore() + paddingBefore(); + } + LayoutUnit borderAndPaddingAfter() const { + return borderAfter() + paddingAfter(); + } + + LayoutUnit borderAndPaddingHeight() const { + return borderTop() + borderBottom() + paddingTop() + paddingBottom(); + } + LayoutUnit borderAndPaddingWidth() const { + return borderLeft() + borderRight() + paddingLeft() + paddingRight(); + } + LayoutUnit borderAndPaddingLogicalHeight() const { + return borderAndPaddingBefore() + borderAndPaddingAfter(); + } + LayoutUnit borderAndPaddingLogicalWidth() const { + return borderStart() + borderEnd() + paddingStart() + paddingEnd(); + } + LayoutUnit borderAndPaddingLogicalLeft() const { + return borderLeft() + paddingLeft(); + } + + LayoutUnit borderLogicalLeft() const { return borderLeft(); } + LayoutUnit borderLogicalRight() const { return borderRight(); } + LayoutUnit borderLogicalWidth() const { return borderStart() + borderEnd(); } + LayoutUnit borderLogicalHeight() const { + return borderBefore() + borderAfter(); + } + + LayoutUnit paddingLogicalLeft() const { return paddingLeft(); } + LayoutUnit paddingLogicalRight() const { return paddingRight(); } + LayoutUnit paddingLogicalWidth() const { + return paddingStart() + paddingEnd(); + } + LayoutUnit paddingLogicalHeight() const { + return paddingBefore() + paddingAfter(); + } + + virtual LayoutUnit marginTop() const = 0; + virtual LayoutUnit marginBottom() const = 0; + virtual LayoutUnit marginLeft() const = 0; + virtual LayoutUnit marginRight() const = 0; + virtual LayoutUnit marginBefore(const RenderStyle* otherStyle = 0) const = 0; + virtual LayoutUnit marginAfter(const RenderStyle* otherStyle = 0) const = 0; + virtual LayoutUnit marginStart(const RenderStyle* otherStyle = 0) const = 0; + virtual LayoutUnit marginEnd(const RenderStyle* otherStyle = 0) const = 0; + LayoutUnit marginHeight() const { return marginTop() + marginBottom(); } + LayoutUnit marginWidth() const { return marginLeft() + marginRight(); } + LayoutUnit marginLogicalHeight() const { + return marginBefore() + marginAfter(); + } + LayoutUnit marginLogicalWidth() const { return marginStart() + marginEnd(); } + + bool hasInlineDirectionBordersPaddingOrMargin() const { + return hasInlineDirectionBordersOrPadding() || marginStart() || marginEnd(); + } + bool hasInlineDirectionBordersOrPadding() const { + return borderStart() || borderEnd() || paddingStart() || paddingEnd(); + } + + LayoutUnit containingBlockLogicalWidthForContent() const; + + void paintBorder(const PaintInfo&, + const LayoutRect&, + const RenderStyle*, + BackgroundBleedAvoidance = BackgroundBleedNone, + bool includeLogicalLeftEdge = true, + bool includeLogicalRightEdge = true); + void paintBoxShadow(const PaintInfo&, + const LayoutRect&, + const RenderStyle*, + ShadowStyle, + bool includeLogicalLeftEdge = true, + bool includeLogicalRightEdge = true); + void paintFillLayerExtended(const PaintInfo&, + const Color&, + const FillLayer&, + const LayoutRect&, + BackgroundBleedAvoidance, + InlineFlowBox* = 0, + const LayoutSize& = LayoutSize(), + RenderObject* backgroundObject = 0, + bool skipBaseColor = false); + + bool boxShadowShouldBeAppliedToBackground(BackgroundBleedAvoidance, + InlineFlowBox* = 0) const; + + // Overridden by subclasses to determine line height and baseline position. + virtual LayoutUnit lineHeight( + bool firstLine, + LineDirectionMode, + LinePositionMode = PositionOnContainingLine) const = 0; + virtual int baselinePosition( + FontBaseline, + bool firstLine, + LineDirectionMode, + LinePositionMode = PositionOnContainingLine) const = 0; + + virtual void mapAbsoluteToLocalPoint(MapCoordinatesFlags, + TransformState&) const override; + virtual const RenderObject* pushMappingToContainer( + const RenderBox* ancestorToStopAt, + RenderGeometryMap&) const override; + + void collectSelfPaintingLayers(Vector& layers); + + virtual void setSelectionState(SelectionState) override; + + protected: + class BackgroundImageGeometry { + public: + BackgroundImageGeometry() : m_hasNonLocalGeometry(false) {} + + IntPoint destOrigin() const { return m_destOrigin; } + void setDestOrigin(const IntPoint& destOrigin) { + m_destOrigin = destOrigin; + } + + IntRect destRect() const { return m_destRect; } + void setDestRect(const IntRect& destRect) { m_destRect = destRect; } + + // Returns the phase relative to the destination rectangle. + IntPoint relativePhase() const; + + IntPoint phase() const { return m_phase; } + void setPhase(const IntPoint& phase) { m_phase = phase; } + + IntSize tileSize() const { return m_tileSize; } + void setTileSize(const IntSize& tileSize) { m_tileSize = tileSize; } + + // Space-size represents extra width and height that may be added to + // the image if used as a pattern with repeat: space + IntSize spaceSize() const { return m_repeatSpacing; } + void setSpaceSize(const IntSize& repeatSpacing) { + m_repeatSpacing = repeatSpacing; + } + + void setPhaseX(int x) { m_phase.setX(x); } + void setPhaseY(int y) { m_phase.setY(y); } + + void setNoRepeatX(int xOffset); + void setNoRepeatY(int yOffset); + + void useFixedAttachment(const IntPoint& attachmentPoint); + + void clip(const IntRect&); + + void setHasNonLocalGeometry(bool hasNonLocalGeometry = true) { + m_hasNonLocalGeometry = hasNonLocalGeometry; + } + bool hasNonLocalGeometry() const { return m_hasNonLocalGeometry; } + + private: + IntRect m_destRect; + IntPoint m_destOrigin; + IntPoint m_phase; + IntSize m_tileSize; + IntSize m_repeatSpacing; + bool m_hasNonLocalGeometry; // Has background-attachment: fixed. Implies + // that we can't always cheaply compute + // destRect. + }; + + LayoutPoint adjustedPositionRelativeToOffsetParent(const LayoutPoint&) const; + + void getBorderEdgeInfo(class BorderEdge[], + const RenderStyle*, + bool includeLogicalLeftEdge = true, + bool includeLogicalRightEdge = true) const; + bool borderObscuresBackgroundEdge(const FloatSize& contextScale) const; + bool borderObscuresBackground() const; + RoundedRect backgroundRoundedRectAdjustedForBleedAvoidance( + GraphicsContext*, + const LayoutRect&, + BackgroundBleedAvoidance, + InlineFlowBox*, + const LayoutSize&, + bool includeLogicalLeftEdge, + bool includeLogicalRightEdge) const; + LayoutRect borderInnerRectAdjustedForBleedAvoidance( + GraphicsContext*, + const LayoutRect&, + BackgroundBleedAvoidance) const; + + LayoutRect localCaretRectForEmptyElement(LayoutUnit width, + LayoutUnit textIndentOffset); + + static void clipRoundedInnerRect(GraphicsContext*, + const LayoutRect&, + const RoundedRect& clipRect); + + bool hasAutoHeightOrContainingBlockWithAutoHeight() const; + + void paintRootBackgroundColor(const PaintInfo&, + const LayoutRect&, + const Color&); + + public: + static bool shouldAntialiasLines(GraphicsContext*); + + // These functions are only used internally to manipulate the render tree + // structure via remove/insert/appendChildNode. + void moveChildTo(RenderBoxModelObject* toBoxModelObject, + RenderObject* child, + RenderObject* beforeChild, + bool fullRemoveInsert); + void moveAllChildrenTo(RenderBoxModelObject* toBoxModelObject, + RenderObject* beforeChild, + bool fullRemoveInsert); + + IntSize calculateImageIntrinsicDimensions( + StyleImage*, + const IntSize& scaledPositioningAreaSize) const; + + private: + LayoutUnit computedCSSPadding(const Length&) const; + virtual bool isBoxModelObject() const override final { return true; } + + IntSize calculateFillTileSize(const FillLayer&, + const IntSize& scaledPositioningAreaSize) const; + + RoundedRect getBackgroundRoundedRect(const LayoutRect&, + InlineFlowBox*, + LayoutUnit inlineBoxWidth, + LayoutUnit inlineBoxHeight, + bool includeLogicalLeftEdge, + bool includeLogicalRightEdge) const; + + void clipBorderSidePolygon(GraphicsContext*, + const RoundedRect& outerBorder, + const RoundedRect& innerBorder, + BoxSide, + bool firstEdgeMatches, + bool secondEdgeMatches); + void clipBorderSideForComplexInnerPath(GraphicsContext*, + const RoundedRect&, + const RoundedRect&, + BoxSide, + const class BorderEdge[]); + void paintOneBorderSide(GraphicsContext*, + const RenderStyle*, + const RoundedRect& outerBorder, + const RoundedRect& innerBorder, + const IntRect& sideRect, + BoxSide, + BoxSide adjacentSide1, + BoxSide adjacentSide2, + const class BorderEdge[], + const Path*, + BackgroundBleedAvoidance, + bool includeLogicalLeftEdge, + bool includeLogicalRightEdge, + bool antialias, + const Color* overrideColor = 0); + void paintTranslucentBorderSides(GraphicsContext*, + const RenderStyle*, + const RoundedRect& outerBorder, + const RoundedRect& innerBorder, + const IntPoint& innerBorderAdjustment, + const class BorderEdge[], + BorderEdgeFlags, + BackgroundBleedAvoidance, + bool includeLogicalLeftEdge, + bool includeLogicalRightEdge, + bool antialias = false); + void paintBorderSides(GraphicsContext*, + const RenderStyle*, + const RoundedRect& outerBorder, + const RoundedRect& innerBorder, + const IntPoint& innerBorderAdjustment, + const class BorderEdge[], + BorderEdgeFlags, + BackgroundBleedAvoidance, + bool includeLogicalLeftEdge, + bool includeLogicalRightEdge, + bool antialias = false, + const Color* overrideColor = 0); + void drawBoxSideFromPath(GraphicsContext*, + const LayoutRect&, + const Path&, + const class BorderEdge[], + float thickness, + float drawThickness, + BoxSide, + const RenderStyle*, + Color, + EBorderStyle, + BackgroundBleedAvoidance, + bool includeLogicalLeftEdge, + bool includeLogicalRightEdge); }; DEFINE_RENDER_OBJECT_TYPE_CASTS(RenderBoxModelObject, isBoxModelObject()); -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_RENDERBOXMODELOBJECT_H_ diff --git a/sky/engine/core/rendering/RenderFlexibleBox.cpp b/sky/engine/core/rendering/RenderFlexibleBox.cpp index 9b94964bc8ef8..0460166083916 100644 --- a/sky/engine/core/rendering/RenderFlexibleBox.cpp +++ b/sky/engine/core/rendering/RenderFlexibleBox.cpp @@ -39,1216 +39,1399 @@ namespace blink { struct RenderFlexibleBox::LineContext { - LineContext(LayoutUnit crossAxisOffset, LayoutUnit crossAxisExtent, size_t numberOfChildren, LayoutUnit maxAscent) - : crossAxisOffset(crossAxisOffset) - , crossAxisExtent(crossAxisExtent) - , numberOfChildren(numberOfChildren) - , maxAscent(maxAscent) - { - } - - LayoutUnit crossAxisOffset; - LayoutUnit crossAxisExtent; - size_t numberOfChildren; - LayoutUnit maxAscent; + LineContext(LayoutUnit crossAxisOffset, + LayoutUnit crossAxisExtent, + size_t numberOfChildren, + LayoutUnit maxAscent) + : crossAxisOffset(crossAxisOffset), + crossAxisExtent(crossAxisExtent), + numberOfChildren(numberOfChildren), + maxAscent(maxAscent) {} + + LayoutUnit crossAxisOffset; + LayoutUnit crossAxisExtent; + size_t numberOfChildren; + LayoutUnit maxAscent; }; struct RenderFlexibleBox::Violation { - Violation(RenderBox* child, LayoutUnit childSize) - : child(child) - , childSize(childSize) - { - } + Violation(RenderBox* child, LayoutUnit childSize) + : child(child), childSize(childSize) {} - RenderBox* child; - LayoutUnit childSize; + RenderBox* child; + LayoutUnit childSize; }; - RenderFlexibleBox::RenderFlexibleBox() - : m_orderIterator(this) - , m_numberOfInFlowChildrenOnFirstLine(-1) -{ -} - -RenderFlexibleBox::~RenderFlexibleBox() -{ -} - -const char* RenderFlexibleBox::renderName() const -{ - return "RenderFlexibleBox"; -} - -void RenderFlexibleBox::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const -{ - // FIXME: We're ignoring flex-basis here and we shouldn't. We can't start honoring it though until - // the flex shorthand stops setting it to 0. - // See https://bugs.webkit.org/show_bug.cgi?id=116117 and http://crbug.com/240765. - for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { - if (child->isOutOfFlowPositioned()) - continue; - - LayoutUnit margin = marginIntrinsicLogicalWidthForChild(child); - LayoutUnit minPreferredLogicalWidth = child->minPreferredLogicalWidth(); - LayoutUnit maxPreferredLogicalWidth = child->maxPreferredLogicalWidth(); - minPreferredLogicalWidth += margin; - maxPreferredLogicalWidth += margin; - if (!isColumnFlow()) { - maxLogicalWidth += maxPreferredLogicalWidth; - if (isMultiline()) { - // For multiline, the min preferred width is if you put a break between each item. - minLogicalWidth = std::max(minLogicalWidth, minPreferredLogicalWidth); - } else - minLogicalWidth += minPreferredLogicalWidth; - } else { - minLogicalWidth = std::max(minPreferredLogicalWidth, minLogicalWidth); - maxLogicalWidth = std::max(maxPreferredLogicalWidth, maxLogicalWidth); - } + : m_orderIterator(this), m_numberOfInFlowChildrenOnFirstLine(-1) {} + +RenderFlexibleBox::~RenderFlexibleBox() {} + +const char* RenderFlexibleBox::renderName() const { + return "RenderFlexibleBox"; +} + +void RenderFlexibleBox::computeIntrinsicLogicalWidths( + LayoutUnit& minLogicalWidth, + LayoutUnit& maxLogicalWidth) const { + // FIXME: We're ignoring flex-basis here and we shouldn't. We can't start + // honoring it though until the flex shorthand stops setting it to 0. See + // https://bugs.webkit.org/show_bug.cgi?id=116117 and http://crbug.com/240765. + for (RenderBox* child = firstChildBox(); child; + child = child->nextSiblingBox()) { + if (child->isOutOfFlowPositioned()) + continue; + + LayoutUnit margin = marginIntrinsicLogicalWidthForChild(child); + LayoutUnit minPreferredLogicalWidth = child->minPreferredLogicalWidth(); + LayoutUnit maxPreferredLogicalWidth = child->maxPreferredLogicalWidth(); + minPreferredLogicalWidth += margin; + maxPreferredLogicalWidth += margin; + if (!isColumnFlow()) { + maxLogicalWidth += maxPreferredLogicalWidth; + if (isMultiline()) { + // For multiline, the min preferred width is if you put a break between + // each item. + minLogicalWidth = std::max(minLogicalWidth, minPreferredLogicalWidth); + } else + minLogicalWidth += minPreferredLogicalWidth; + } else { + minLogicalWidth = std::max(minPreferredLogicalWidth, minLogicalWidth); + maxLogicalWidth = std::max(maxPreferredLogicalWidth, maxLogicalWidth); } - - maxLogicalWidth = std::max(minLogicalWidth, maxLogicalWidth); -} - -static int synthesizedBaselineFromContentBox(const RenderBox* box, LineDirectionMode direction) -{ - return direction == HorizontalLine ? box->borderTop() + box->paddingTop() + box->contentHeight() : box->borderRight() + box->paddingRight() + box->contentWidth(); -} - -int RenderFlexibleBox::baselinePosition(FontBaseline, bool, LineDirectionMode direction, LinePositionMode mode) const -{ - ASSERT(mode == PositionOnContainingLine); - int baseline = firstLineBoxBaseline(FontBaselineOrAuto()); - if (baseline == -1) - baseline = synthesizedBaselineFromContentBox(this, direction); - - return beforeMarginInLineDirection(direction) + baseline; -} - -int RenderFlexibleBox::firstLineBoxBaseline(FontBaselineOrAuto baselineType) const -{ - if (m_numberOfInFlowChildrenOnFirstLine <= 0) - return -1; - RenderBox* baselineChild = 0; - int childNumber = 0; - for (RenderBox* child = m_orderIterator.first(); child; child = m_orderIterator.next()) { - if (child->isOutOfFlowPositioned()) - continue; - if (alignmentForChild(child) == ItemPositionBaseline && !hasAutoMarginsInCrossAxis(child)) { - baselineChild = child; - break; - } - if (!baselineChild) - baselineChild = child; - - ++childNumber; - if (childNumber == m_numberOfInFlowChildrenOnFirstLine) - break; + } + + maxLogicalWidth = std::max(minLogicalWidth, maxLogicalWidth); +} + +static int synthesizedBaselineFromContentBox(const RenderBox* box, + LineDirectionMode direction) { + return direction == HorizontalLine + ? box->borderTop() + box->paddingTop() + box->contentHeight() + : box->borderRight() + box->paddingRight() + box->contentWidth(); +} + +int RenderFlexibleBox::baselinePosition(FontBaseline, + bool, + LineDirectionMode direction, + LinePositionMode mode) const { + ASSERT(mode == PositionOnContainingLine); + int baseline = firstLineBoxBaseline(FontBaselineOrAuto()); + if (baseline == -1) + baseline = synthesizedBaselineFromContentBox(this, direction); + + return beforeMarginInLineDirection(direction) + baseline; +} + +int RenderFlexibleBox::firstLineBoxBaseline( + FontBaselineOrAuto baselineType) const { + if (m_numberOfInFlowChildrenOnFirstLine <= 0) + return -1; + RenderBox* baselineChild = 0; + int childNumber = 0; + for (RenderBox* child = m_orderIterator.first(); child; + child = m_orderIterator.next()) { + if (child->isOutOfFlowPositioned()) + continue; + if (alignmentForChild(child) == ItemPositionBaseline && + !hasAutoMarginsInCrossAxis(child)) { + baselineChild = child; + break; } - if (!baselineChild) - return -1; - - if (!isColumnFlow() && hasOrthogonalFlow(baselineChild)) - return crossAxisExtentForChild(baselineChild) + baselineChild->logicalTop(); - if (isColumnFlow() && !hasOrthogonalFlow(baselineChild)) - return mainAxisExtentForChild(baselineChild) + baselineChild->logicalTop(); - - int baseline = baselineChild->firstLineBoxBaseline(baselineType); - if (baseline == -1) { - // FIXME: We should pass |direction| into firstLineBoxBaseline and stop bailing out if we're a writing mode root. - // This would also fix some cases where the flexbox is orthogonal to its container. - LineDirectionMode direction = HorizontalLine; - return synthesizedBaselineFromContentBox(baselineChild, direction) + baselineChild->logicalTop(); + baselineChild = child; + + ++childNumber; + if (childNumber == m_numberOfInFlowChildrenOnFirstLine) + break; + } + + if (!baselineChild) + return -1; + + if (!isColumnFlow() && hasOrthogonalFlow(baselineChild)) + return crossAxisExtentForChild(baselineChild) + baselineChild->logicalTop(); + if (isColumnFlow() && !hasOrthogonalFlow(baselineChild)) + return mainAxisExtentForChild(baselineChild) + baselineChild->logicalTop(); + + int baseline = baselineChild->firstLineBoxBaseline(baselineType); + if (baseline == -1) { + // FIXME: We should pass |direction| into firstLineBoxBaseline and stop + // bailing out if we're a writing mode root. This would also fix some cases + // where the flexbox is orthogonal to its container. + LineDirectionMode direction = HorizontalLine; + return synthesizedBaselineFromContentBox(baselineChild, direction) + + baselineChild->logicalTop(); + } + + return baseline + baselineChild->logicalTop(); +} + +int RenderFlexibleBox::inlineBlockBaseline(LineDirectionMode direction) const { + int baseline = firstLineBoxBaseline(FontBaselineOrAuto()); + if (baseline != -1) + return baseline; + + int marginAscent = direction == HorizontalLine ? marginTop() : marginRight(); + return synthesizedBaselineFromContentBox(this, direction) + marginAscent; +} + +static ItemPosition resolveAlignment(const RenderStyle* parentStyle, + const RenderStyle* childStyle) { + ItemPosition align = childStyle->alignSelf(); + if (align == ItemPositionAuto) + align = (parentStyle->alignItems() == ItemPositionAuto) + ? ItemPositionStretch + : parentStyle->alignItems(); + return align; +} + +void RenderFlexibleBox::removeChild(RenderObject* child) { + RenderBlock::removeChild(child); + m_intrinsicSizeAlongMainAxis.remove(child); +} + +void RenderFlexibleBox::styleDidChange(StyleDifference diff, + const RenderStyle* oldStyle) { + RenderBlock::styleDidChange(diff, oldStyle); + + if (oldStyle && oldStyle->alignItems() == ItemPositionStretch && + diff.needsFullLayout()) { + // Flex items that were previously stretching need to be relayed out so we + // can compute new available cross axis space. This is only necessary for + // stretching since other alignment values don't change the size of the box. + for (RenderBox* child = firstChildBox(); child; + child = child->nextSiblingBox()) { + ItemPosition previousAlignment = + resolveAlignment(oldStyle, child->style()); + if (previousAlignment == ItemPositionStretch && + previousAlignment != resolveAlignment(style(), child->style())) + child->setChildNeedsLayout(MarkOnlyThis); } - - return baseline + baselineChild->logicalTop(); + } } -int RenderFlexibleBox::inlineBlockBaseline(LineDirectionMode direction) const -{ - int baseline = firstLineBoxBaseline(FontBaselineOrAuto()); - if (baseline != -1) - return baseline; +void RenderFlexibleBox::layout() { + ASSERT(needsLayout()); - int marginAscent = direction == HorizontalLine ? marginTop() : marginRight(); - return synthesizedBaselineFromContentBox(this, direction) + marginAscent; -} + if (simplifiedLayout()) + return; -static ItemPosition resolveAlignment(const RenderStyle* parentStyle, const RenderStyle* childStyle) -{ - ItemPosition align = childStyle->alignSelf(); - if (align == ItemPositionAuto) - align = (parentStyle->alignItems() == ItemPositionAuto) ? ItemPositionStretch : parentStyle->alignItems(); - return align; -} + bool relayoutChildren = updateLogicalWidthAndColumnWidth(); + LayoutUnit previousHeight = logicalHeight(); + setLogicalHeight(borderAndPaddingLogicalHeight()); -void RenderFlexibleBox::removeChild(RenderObject* child) -{ - RenderBlock::removeChild(child); - m_intrinsicSizeAlongMainAxis.remove(child); -} + m_numberOfInFlowChildrenOnFirstLine = -1; -void RenderFlexibleBox::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) -{ - RenderBlock::styleDidChange(diff, oldStyle); + prepareOrderIteratorAndMargins(); - if (oldStyle && oldStyle->alignItems() == ItemPositionStretch && diff.needsFullLayout()) { - // Flex items that were previously stretching need to be relayed out so we can compute new available cross axis space. - // This is only necessary for stretching since other alignment values don't change the size of the box. - for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { - ItemPosition previousAlignment = resolveAlignment(oldStyle, child->style()); - if (previousAlignment == ItemPositionStretch && previousAlignment != resolveAlignment(style(), child->style())) - child->setChildNeedsLayout(MarkOnlyThis); - } - } -} - -void RenderFlexibleBox::layout() -{ - ASSERT(needsLayout()); - - if (simplifiedLayout()) - return; - - bool relayoutChildren = updateLogicalWidthAndColumnWidth(); - LayoutUnit previousHeight = logicalHeight(); - setLogicalHeight(borderAndPaddingLogicalHeight()); - - m_numberOfInFlowChildrenOnFirstLine = -1; - - prepareOrderIteratorAndMargins(); + ChildFrameRects oldChildRects; + appendChildFrameRects(oldChildRects); - ChildFrameRects oldChildRects; - appendChildFrameRects(oldChildRects); + layoutFlexItems(relayoutChildren); - layoutFlexItems(relayoutChildren); + if (logicalHeight() != previousHeight) + relayoutChildren = true; - if (logicalHeight() != previousHeight) - relayoutChildren = true; + layoutPositionedObjects(relayoutChildren); - layoutPositionedObjects(relayoutChildren); + // FIXME: css3/flexbox/repaint-rtl-column.html seems to issue paint + // invalidations for more overflow than it needs to. + computeOverflow(clientLogicalBottomAfterRepositioning()); - // FIXME: css3/flexbox/repaint-rtl-column.html seems to issue paint invalidations for more overflow than it needs to. - computeOverflow(clientLogicalBottomAfterRepositioning()); + updateLayerTransformAfterLayout(); - updateLayerTransformAfterLayout(); - - clearNeedsLayout(); + clearNeedsLayout(); } -void RenderFlexibleBox::appendChildFrameRects(ChildFrameRects& childFrameRects) -{ - for (RenderBox* child = m_orderIterator.first(); child; child = m_orderIterator.next()) { - if (!child->isOutOfFlowPositioned()) - childFrameRects.append(child->frameRect()); - } +void RenderFlexibleBox::appendChildFrameRects( + ChildFrameRects& childFrameRects) { + for (RenderBox* child = m_orderIterator.first(); child; + child = m_orderIterator.next()) { + if (!child->isOutOfFlowPositioned()) + childFrameRects.append(child->frameRect()); + } } -void RenderFlexibleBox::paintChildren(PaintInfo& paintInfo, const LayoutPoint& paintOffset, Vector& layers) -{ - for (RenderBox* child = m_orderIterator.first(); child; child = m_orderIterator.next()) { - if (child->hasSelfPaintingLayer()) - layers.append(child); - else - child->paint(paintInfo, paintOffset, layers); - } +void RenderFlexibleBox::paintChildren(PaintInfo& paintInfo, + const LayoutPoint& paintOffset, + Vector& layers) { + for (RenderBox* child = m_orderIterator.first(); child; + child = m_orderIterator.next()) { + if (child->hasSelfPaintingLayer()) + layers.append(child); + else + child->paint(paintInfo, paintOffset, layers); + } } -void RenderFlexibleBox::repositionLogicalHeightDependentFlexItems(Vector& lineContexts) -{ - LayoutUnit crossAxisStartEdge = lineContexts.isEmpty() ? LayoutUnit() : lineContexts[0].crossAxisOffset; - alignFlexLines(lineContexts); +void RenderFlexibleBox::repositionLogicalHeightDependentFlexItems( + Vector& lineContexts) { + LayoutUnit crossAxisStartEdge = + lineContexts.isEmpty() ? LayoutUnit() : lineContexts[0].crossAxisOffset; + alignFlexLines(lineContexts); - alignChildren(lineContexts); + alignChildren(lineContexts); - if (style()->flexWrap() == FlexWrapReverse) - flipForWrapReverse(lineContexts, crossAxisStartEdge); + if (style()->flexWrap() == FlexWrapReverse) + flipForWrapReverse(lineContexts, crossAxisStartEdge); - // direction:rtl + flex-direction:column means the cross-axis direction is flipped. - flipForRightToLeftColumn(); + // direction:rtl + flex-direction:column means the cross-axis direction is + // flipped. + flipForRightToLeftColumn(); } -LayoutUnit RenderFlexibleBox::clientLogicalBottomAfterRepositioning() -{ - LayoutUnit maxChildLogicalBottom = 0; - for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { - if (child->isOutOfFlowPositioned()) - continue; - LayoutUnit childLogicalBottom = logicalTopForChild(child) + logicalHeightForChild(child) + marginAfterForChild(child); - maxChildLogicalBottom = std::max(maxChildLogicalBottom, childLogicalBottom); - } - return std::max(clientLogicalBottom(), maxChildLogicalBottom + paddingAfter()); +LayoutUnit RenderFlexibleBox::clientLogicalBottomAfterRepositioning() { + LayoutUnit maxChildLogicalBottom = 0; + for (RenderBox* child = firstChildBox(); child; + child = child->nextSiblingBox()) { + if (child->isOutOfFlowPositioned()) + continue; + LayoutUnit childLogicalBottom = logicalTopForChild(child) + + logicalHeightForChild(child) + + marginAfterForChild(child); + maxChildLogicalBottom = std::max(maxChildLogicalBottom, childLogicalBottom); + } + return std::max(clientLogicalBottom(), + maxChildLogicalBottom + paddingAfter()); } -bool RenderFlexibleBox::hasOrthogonalFlow(RenderBox* child) const -{ - // FIXME: If the child is a flexbox, then we need to check isHorizontalFlow. - return !isHorizontalFlow(); +bool RenderFlexibleBox::hasOrthogonalFlow(RenderBox* child) const { + // FIXME: If the child is a flexbox, then we need to check isHorizontalFlow. + return !isHorizontalFlow(); } -bool RenderFlexibleBox::isColumnFlow() const -{ - return style()->isColumnFlexDirection(); +bool RenderFlexibleBox::isColumnFlow() const { + return style()->isColumnFlexDirection(); } -bool RenderFlexibleBox::isHorizontalFlow() const -{ - return !isColumnFlow(); +bool RenderFlexibleBox::isHorizontalFlow() const { + return !isColumnFlow(); } -bool RenderFlexibleBox::isLeftToRightFlow() const -{ - if (isColumnFlow()) - return true; - return style()->isLeftToRightDirection() ^ (style()->flexDirection() == FlowRowReverse); +bool RenderFlexibleBox::isLeftToRightFlow() const { + if (isColumnFlow()) + return true; + return style()->isLeftToRightDirection() ^ + (style()->flexDirection() == FlowRowReverse); } -bool RenderFlexibleBox::isMultiline() const -{ - return style()->flexWrap() != FlexNoWrap; +bool RenderFlexibleBox::isMultiline() const { + return style()->flexWrap() != FlexNoWrap; } -Length RenderFlexibleBox::flexBasisForChild(RenderBox* child) const -{ - Length flexLength = child->style()->flexBasis(); - if (flexLength.isAuto()) - flexLength = isHorizontalFlow() ? child->style()->width() : child->style()->height(); - return flexLength; +Length RenderFlexibleBox::flexBasisForChild(RenderBox* child) const { + Length flexLength = child->style()->flexBasis(); + if (flexLength.isAuto()) + flexLength = + isHorizontalFlow() ? child->style()->width() : child->style()->height(); + return flexLength; } -LayoutUnit RenderFlexibleBox::crossAxisExtentForChild(RenderBox* child) const -{ - return isHorizontalFlow() ? child->height() : child->width(); +LayoutUnit RenderFlexibleBox::crossAxisExtentForChild(RenderBox* child) const { + return isHorizontalFlow() ? child->height() : child->width(); } -static inline LayoutUnit constrainedChildIntrinsicContentLogicalHeight(RenderBox* child) -{ - LayoutUnit childIntrinsicContentLogicalHeight = child->intrinsicContentLogicalHeight(); - return child->constrainLogicalHeightByMinMax(childIntrinsicContentLogicalHeight + child->borderAndPaddingLogicalHeight(), childIntrinsicContentLogicalHeight); +static inline LayoutUnit constrainedChildIntrinsicContentLogicalHeight( + RenderBox* child) { + LayoutUnit childIntrinsicContentLogicalHeight = + child->intrinsicContentLogicalHeight(); + return child->constrainLogicalHeightByMinMax( + childIntrinsicContentLogicalHeight + + child->borderAndPaddingLogicalHeight(), + childIntrinsicContentLogicalHeight); } -LayoutUnit RenderFlexibleBox::childIntrinsicHeight(RenderBox* child) const -{ - if (needToStretchChildLogicalHeight(child)) - return constrainedChildIntrinsicContentLogicalHeight(child); - return child->height(); +LayoutUnit RenderFlexibleBox::childIntrinsicHeight(RenderBox* child) const { + if (needToStretchChildLogicalHeight(child)) + return constrainedChildIntrinsicContentLogicalHeight(child); + return child->height(); } -LayoutUnit RenderFlexibleBox::childIntrinsicWidth(RenderBox* child) const -{ - // FIXME(sky): Remove - return child->width(); +LayoutUnit RenderFlexibleBox::childIntrinsicWidth(RenderBox* child) const { + // FIXME(sky): Remove + return child->width(); } -LayoutUnit RenderFlexibleBox::crossAxisIntrinsicExtentForChild(RenderBox* child) const -{ - return isHorizontalFlow() ? childIntrinsicHeight(child) : childIntrinsicWidth(child); +LayoutUnit RenderFlexibleBox::crossAxisIntrinsicExtentForChild( + RenderBox* child) const { + return isHorizontalFlow() ? childIntrinsicHeight(child) + : childIntrinsicWidth(child); } -LayoutUnit RenderFlexibleBox::mainAxisExtentForChild(RenderBox* child) const -{ - return isHorizontalFlow() ? child->width() : child->height(); +LayoutUnit RenderFlexibleBox::mainAxisExtentForChild(RenderBox* child) const { + return isHorizontalFlow() ? child->width() : child->height(); } -LayoutUnit RenderFlexibleBox::crossAxisExtent() const -{ - return isHorizontalFlow() ? height() : width(); +LayoutUnit RenderFlexibleBox::crossAxisExtent() const { + return isHorizontalFlow() ? height() : width(); } -LayoutUnit RenderFlexibleBox::mainAxisExtent() const -{ - return isHorizontalFlow() ? width() : height(); +LayoutUnit RenderFlexibleBox::mainAxisExtent() const { + return isHorizontalFlow() ? width() : height(); } -LayoutUnit RenderFlexibleBox::crossAxisContentExtent() const -{ - return isHorizontalFlow() ? contentHeight() : contentWidth(); +LayoutUnit RenderFlexibleBox::crossAxisContentExtent() const { + return isHorizontalFlow() ? contentHeight() : contentWidth(); } -LayoutUnit RenderFlexibleBox::mainAxisContentExtent(LayoutUnit contentLogicalHeight) -{ - if (isColumnFlow()) { - LogicalExtentComputedValues computedValues; - LayoutUnit borderPaddingAndScrollbar = borderAndPaddingLogicalHeight(); - LayoutUnit borderBoxLogicalHeight = contentLogicalHeight + borderPaddingAndScrollbar; - computeLogicalHeight(borderBoxLogicalHeight, logicalTop(), computedValues); - if (computedValues.m_extent == LayoutUnit::max()) - return computedValues.m_extent; - return std::max(LayoutUnit(0), computedValues.m_extent - borderPaddingAndScrollbar); - } - return contentLogicalWidth(); -} - -LayoutUnit RenderFlexibleBox::computeMainAxisExtentForChild(RenderBox* child, SizeType sizeType, const Length& size) -{ - // FIXME: This is wrong for orthogonal flows. It should use the flexbox's writing-mode, not the child's in order - // to figure out the logical height/width. - if (isColumnFlow()) { - // We don't have to check for "auto" here - computeContentLogicalHeight will just return -1 for that case anyway. - if (size.isIntrinsic()) - child->layoutIfNeeded(); - return child->computeContentLogicalHeight(size, child->logicalHeight() - child->borderAndPaddingLogicalHeight()); - } - return child->computeLogicalWidthUsing(sizeType, size, contentLogicalWidth(), this) - child->borderAndPaddingLogicalWidth(); +LayoutUnit RenderFlexibleBox::mainAxisContentExtent( + LayoutUnit contentLogicalHeight) { + if (isColumnFlow()) { + LogicalExtentComputedValues computedValues; + LayoutUnit borderPaddingAndScrollbar = borderAndPaddingLogicalHeight(); + LayoutUnit borderBoxLogicalHeight = + contentLogicalHeight + borderPaddingAndScrollbar; + computeLogicalHeight(borderBoxLogicalHeight, logicalTop(), computedValues); + if (computedValues.m_extent == LayoutUnit::max()) + return computedValues.m_extent; + return std::max(LayoutUnit(0), + computedValues.m_extent - borderPaddingAndScrollbar); + } + return contentLogicalWidth(); } -LayoutUnit RenderFlexibleBox::flowAwareBorderStart() const -{ - if (isHorizontalFlow()) - return isLeftToRightFlow() ? borderLeft() : borderRight(); - return isLeftToRightFlow() ? borderTop() : borderBottom(); +LayoutUnit RenderFlexibleBox::computeMainAxisExtentForChild( + RenderBox* child, + SizeType sizeType, + const Length& size) { + // FIXME: This is wrong for orthogonal flows. It should use the flexbox's + // writing-mode, not the child's in order to figure out the logical + // height/width. + if (isColumnFlow()) { + // We don't have to check for "auto" here - computeContentLogicalHeight will + // just return -1 for that case anyway. + if (size.isIntrinsic()) + child->layoutIfNeeded(); + return child->computeContentLogicalHeight( + size, child->logicalHeight() - child->borderAndPaddingLogicalHeight()); + } + return child->computeLogicalWidthUsing(sizeType, size, contentLogicalWidth(), + this) - + child->borderAndPaddingLogicalWidth(); } -LayoutUnit RenderFlexibleBox::flowAwareBorderEnd() const -{ - if (isHorizontalFlow()) - return isLeftToRightFlow() ? borderRight() : borderLeft(); - return isLeftToRightFlow() ? borderBottom() : borderTop(); +LayoutUnit RenderFlexibleBox::flowAwareBorderStart() const { + if (isHorizontalFlow()) + return isLeftToRightFlow() ? borderLeft() : borderRight(); + return isLeftToRightFlow() ? borderTop() : borderBottom(); } -LayoutUnit RenderFlexibleBox::flowAwareBorderBefore() const -{ - return isHorizontalFlow() ? borderTop() : borderLeft(); +LayoutUnit RenderFlexibleBox::flowAwareBorderEnd() const { + if (isHorizontalFlow()) + return isLeftToRightFlow() ? borderRight() : borderLeft(); + return isLeftToRightFlow() ? borderBottom() : borderTop(); } -LayoutUnit RenderFlexibleBox::flowAwareBorderAfter() const -{ - return isHorizontalFlow() ? borderBottom() : borderRight(); +LayoutUnit RenderFlexibleBox::flowAwareBorderBefore() const { + return isHorizontalFlow() ? borderTop() : borderLeft(); } -LayoutUnit RenderFlexibleBox::flowAwarePaddingStart() const -{ - if (isHorizontalFlow()) - return isLeftToRightFlow() ? paddingLeft() : paddingRight(); - return isLeftToRightFlow() ? paddingTop() : paddingBottom(); +LayoutUnit RenderFlexibleBox::flowAwareBorderAfter() const { + return isHorizontalFlow() ? borderBottom() : borderRight(); } -LayoutUnit RenderFlexibleBox::flowAwarePaddingEnd() const -{ - if (isHorizontalFlow()) - return isLeftToRightFlow() ? paddingRight() : paddingLeft(); - return isLeftToRightFlow() ? paddingBottom() : paddingTop(); +LayoutUnit RenderFlexibleBox::flowAwarePaddingStart() const { + if (isHorizontalFlow()) + return isLeftToRightFlow() ? paddingLeft() : paddingRight(); + return isLeftToRightFlow() ? paddingTop() : paddingBottom(); } -LayoutUnit RenderFlexibleBox::flowAwarePaddingBefore() const -{ - return isHorizontalFlow() ? paddingTop() : paddingLeft(); +LayoutUnit RenderFlexibleBox::flowAwarePaddingEnd() const { + if (isHorizontalFlow()) + return isLeftToRightFlow() ? paddingRight() : paddingLeft(); + return isLeftToRightFlow() ? paddingBottom() : paddingTop(); } -LayoutUnit RenderFlexibleBox::flowAwarePaddingAfter() const -{ - return isHorizontalFlow() ? paddingBottom() : paddingRight(); +LayoutUnit RenderFlexibleBox::flowAwarePaddingBefore() const { + return isHorizontalFlow() ? paddingTop() : paddingLeft(); } -LayoutUnit RenderFlexibleBox::flowAwareMarginStartForChild(RenderBox* child) const -{ - if (isHorizontalFlow()) - return isLeftToRightFlow() ? child->marginLeft() : child->marginRight(); - return isLeftToRightFlow() ? child->marginTop() : child->marginBottom(); +LayoutUnit RenderFlexibleBox::flowAwarePaddingAfter() const { + return isHorizontalFlow() ? paddingBottom() : paddingRight(); } -LayoutUnit RenderFlexibleBox::flowAwareMarginEndForChild(RenderBox* child) const -{ - if (isHorizontalFlow()) - return isLeftToRightFlow() ? child->marginRight() : child->marginLeft(); - return isLeftToRightFlow() ? child->marginBottom() : child->marginTop(); +LayoutUnit RenderFlexibleBox::flowAwareMarginStartForChild( + RenderBox* child) const { + if (isHorizontalFlow()) + return isLeftToRightFlow() ? child->marginLeft() : child->marginRight(); + return isLeftToRightFlow() ? child->marginTop() : child->marginBottom(); } -LayoutUnit RenderFlexibleBox::flowAwareMarginBeforeForChild(RenderBox* child) const -{ - return isHorizontalFlow() ? child->marginTop() : child->marginLeft(); +LayoutUnit RenderFlexibleBox::flowAwareMarginEndForChild( + RenderBox* child) const { + if (isHorizontalFlow()) + return isLeftToRightFlow() ? child->marginRight() : child->marginLeft(); + return isLeftToRightFlow() ? child->marginBottom() : child->marginTop(); } -LayoutUnit RenderFlexibleBox::crossAxisMarginExtentForChild(RenderBox* child) const -{ - return isHorizontalFlow() ? child->marginHeight() : child->marginWidth(); +LayoutUnit RenderFlexibleBox::flowAwareMarginBeforeForChild( + RenderBox* child) const { + return isHorizontalFlow() ? child->marginTop() : child->marginLeft(); } -LayoutPoint RenderFlexibleBox::flowAwareLocationForChild(RenderBox* child) const -{ - return isHorizontalFlow() ? child->location() : child->location().transposedPoint(); +LayoutUnit RenderFlexibleBox::crossAxisMarginExtentForChild( + RenderBox* child) const { + return isHorizontalFlow() ? child->marginHeight() : child->marginWidth(); } -void RenderFlexibleBox::setFlowAwareLocationForChild(RenderBox* child, const LayoutPoint& location) -{ - if (isHorizontalFlow()) - child->setLocation(location); - else - child->setLocation(location.transposedPoint()); +LayoutPoint RenderFlexibleBox::flowAwareLocationForChild( + RenderBox* child) const { + return isHorizontalFlow() ? child->location() + : child->location().transposedPoint(); } -LayoutUnit RenderFlexibleBox::mainAxisBorderAndPaddingExtentForChild(RenderBox* child) const -{ - return isHorizontalFlow() ? child->borderAndPaddingWidth() : child->borderAndPaddingHeight(); +void RenderFlexibleBox::setFlowAwareLocationForChild( + RenderBox* child, + const LayoutPoint& location) { + if (isHorizontalFlow()) + child->setLocation(location); + else + child->setLocation(location.transposedPoint()); } -static inline bool preferredMainAxisExtentDependsOnLayout(const Length& flexBasis, bool hasInfiniteLineLength) -{ - return flexBasis.isAuto() || (flexBasis.isPercent() && hasInfiniteLineLength); +LayoutUnit RenderFlexibleBox::mainAxisBorderAndPaddingExtentForChild( + RenderBox* child) const { + return isHorizontalFlow() ? child->borderAndPaddingWidth() + : child->borderAndPaddingHeight(); } -bool RenderFlexibleBox::childPreferredMainAxisContentExtentRequiresLayout(RenderBox* child, bool hasInfiniteLineLength) const -{ - return preferredMainAxisExtentDependsOnLayout(flexBasisForChild(child), hasInfiniteLineLength) && hasOrthogonalFlow(child); +static inline bool preferredMainAxisExtentDependsOnLayout( + const Length& flexBasis, + bool hasInfiniteLineLength) { + return flexBasis.isAuto() || (flexBasis.isPercent() && hasInfiniteLineLength); } -LayoutUnit RenderFlexibleBox::preferredMainAxisContentExtentForChild(RenderBox* child, bool hasInfiniteLineLength, bool relayoutChildren) -{ - child->clearOverrideSize(); - - Length flexBasis = flexBasisForChild(child); - if (preferredMainAxisExtentDependsOnLayout(flexBasis, hasInfiniteLineLength)) { - LayoutUnit mainAxisExtent; - if (hasOrthogonalFlow(child)) { - if (child->needsLayout() || relayoutChildren) { - m_intrinsicSizeAlongMainAxis.remove(child); - child->forceChildLayout(); - m_intrinsicSizeAlongMainAxis.set(child, child->logicalHeight()); - } - ASSERT(m_intrinsicSizeAlongMainAxis.contains(child)); - mainAxisExtent = m_intrinsicSizeAlongMainAxis.get(child); - } else { - mainAxisExtent = child->maxPreferredLogicalWidth(); - } - ASSERT(mainAxisExtent - mainAxisBorderAndPaddingExtentForChild(child) >= 0); - return mainAxisExtent - mainAxisBorderAndPaddingExtentForChild(child); - } - return std::max(LayoutUnit(0), computeMainAxisExtentForChild(child, MainOrPreferredSize, flexBasis)); -} - -void RenderFlexibleBox::layoutFlexItems(bool relayoutChildren) -{ - Vector lineContexts; - OrderedFlexItemList orderedChildren; - LayoutUnit sumFlexBaseSize; - double totalFlexGrow; - double totalWeightedFlexShrink; - LayoutUnit sumHypotheticalMainSize; - - Vector childSizes; - - m_orderIterator.first(); - LayoutUnit crossAxisOffset = flowAwareBorderBefore() + flowAwarePaddingBefore(); - bool hasInfiniteLineLength = false; - while (computeNextFlexLine(orderedChildren, sumFlexBaseSize, totalFlexGrow, totalWeightedFlexShrink, sumHypotheticalMainSize, hasInfiniteLineLength, relayoutChildren)) { - LayoutUnit containerMainInnerSize = mainAxisContentExtent(sumHypotheticalMainSize); - LayoutUnit availableFreeSpace = containerMainInnerSize - sumFlexBaseSize; - FlexSign flexSign = (sumHypotheticalMainSize < containerMainInnerSize) ? PositiveFlexibility : NegativeFlexibility; - InflexibleFlexItemSize inflexibleItems; - childSizes.reserveCapacity(orderedChildren.size()); - while (!resolveFlexibleLengths(flexSign, orderedChildren, availableFreeSpace, totalFlexGrow, totalWeightedFlexShrink, inflexibleItems, childSizes, hasInfiniteLineLength)) { - ASSERT(totalFlexGrow >= 0 && totalWeightedFlexShrink >= 0); - ASSERT(inflexibleItems.size() > 0); - } - - layoutAndPlaceChildren(crossAxisOffset, orderedChildren, childSizes, availableFreeSpace, relayoutChildren, lineContexts, hasInfiniteLineLength); - } - if (hasLineIfEmpty()) { - // Even if computeNextFlexLine returns true, the flexbox might not have - // a line because all our children might be out of flow positioned. - // Instead of just checking if we have a line, make sure the flexbox - // has at least a line's worth of height to cover this case. - LayoutUnit minHeight = borderAndPaddingLogicalHeight() - + lineHeight(true, HorizontalLine, PositionOfInteriorLineBoxes); - if (height() < minHeight) - setLogicalHeight(minHeight); - } - - updateLogicalHeight(); - repositionLogicalHeightDependentFlexItems(lineContexts); -} - -LayoutUnit RenderFlexibleBox::autoMarginOffsetInMainAxis(const OrderedFlexItemList& children, LayoutUnit& availableFreeSpace) -{ - if (availableFreeSpace <= 0) - return 0; - - int numberOfAutoMargins = 0; - bool isHorizontal = isHorizontalFlow(); - for (size_t i = 0; i < children.size(); ++i) { - RenderBox* child = children[i]; - if (child->isOutOfFlowPositioned()) - continue; - if (isHorizontal) { - if (child->style()->marginLeft().isAuto()) - ++numberOfAutoMargins; - if (child->style()->marginRight().isAuto()) - ++numberOfAutoMargins; - } else { - if (child->style()->marginTop().isAuto()) - ++numberOfAutoMargins; - if (child->style()->marginBottom().isAuto()) - ++numberOfAutoMargins; - } - } - if (!numberOfAutoMargins) - return 0; - - LayoutUnit sizeOfAutoMargin = availableFreeSpace / numberOfAutoMargins; - availableFreeSpace = 0; - return sizeOfAutoMargin; +bool RenderFlexibleBox::childPreferredMainAxisContentExtentRequiresLayout( + RenderBox* child, + bool hasInfiniteLineLength) const { + return preferredMainAxisExtentDependsOnLayout(flexBasisForChild(child), + hasInfiniteLineLength) && + hasOrthogonalFlow(child); } -void RenderFlexibleBox::updateAutoMarginsInMainAxis(RenderBox* child, LayoutUnit autoMarginOffset) -{ - ASSERT(autoMarginOffset >= 0); +LayoutUnit RenderFlexibleBox::preferredMainAxisContentExtentForChild( + RenderBox* child, + bool hasInfiniteLineLength, + bool relayoutChildren) { + child->clearOverrideSize(); - if (isHorizontalFlow()) { - if (child->style()->marginLeft().isAuto()) - child->setMarginLeft(autoMarginOffset); - if (child->style()->marginRight().isAuto()) - child->setMarginRight(autoMarginOffset); + Length flexBasis = flexBasisForChild(child); + if (preferredMainAxisExtentDependsOnLayout(flexBasis, + hasInfiniteLineLength)) { + LayoutUnit mainAxisExtent; + if (hasOrthogonalFlow(child)) { + if (child->needsLayout() || relayoutChildren) { + m_intrinsicSizeAlongMainAxis.remove(child); + child->forceChildLayout(); + m_intrinsicSizeAlongMainAxis.set(child, child->logicalHeight()); + } + ASSERT(m_intrinsicSizeAlongMainAxis.contains(child)); + mainAxisExtent = m_intrinsicSizeAlongMainAxis.get(child); } else { - if (child->style()->marginTop().isAuto()) - child->setMarginTop(autoMarginOffset); - if (child->style()->marginBottom().isAuto()) - child->setMarginBottom(autoMarginOffset); - } -} - -bool RenderFlexibleBox::hasAutoMarginsInCrossAxis(RenderBox* child) const -{ - if (isHorizontalFlow()) - return child->style()->marginTop().isAuto() || child->style()->marginBottom().isAuto(); - return child->style()->marginLeft().isAuto() || child->style()->marginRight().isAuto(); -} - -LayoutUnit RenderFlexibleBox::availableAlignmentSpaceForChild(LayoutUnit lineCrossAxisExtent, RenderBox* child) -{ - ASSERT(!child->isOutOfFlowPositioned()); - LayoutUnit childCrossExtent = crossAxisMarginExtentForChild(child) + crossAxisExtentForChild(child); - return lineCrossAxisExtent - childCrossExtent; -} - -LayoutUnit RenderFlexibleBox::availableAlignmentSpaceForChildBeforeStretching(LayoutUnit lineCrossAxisExtent, RenderBox* child) -{ - ASSERT(!child->isOutOfFlowPositioned()); - LayoutUnit childCrossExtent = crossAxisMarginExtentForChild(child) + crossAxisIntrinsicExtentForChild(child); - return lineCrossAxisExtent - childCrossExtent; -} - -bool RenderFlexibleBox::updateAutoMarginsInCrossAxis(RenderBox* child, LayoutUnit availableAlignmentSpace) -{ - ASSERT(!child->isOutOfFlowPositioned()); - ASSERT(availableAlignmentSpace >= 0); - - bool isHorizontal = isHorizontalFlow(); - Length topOrLeft = isHorizontal ? child->style()->marginTop() : child->style()->marginLeft(); - Length bottomOrRight = isHorizontal ? child->style()->marginBottom() : child->style()->marginRight(); - if (topOrLeft.isAuto() && bottomOrRight.isAuto()) { - adjustAlignmentForChild(child, availableAlignmentSpace / 2); - if (isHorizontal) { - child->setMarginTop(availableAlignmentSpace / 2); - child->setMarginBottom(availableAlignmentSpace / 2); - } else { - child->setMarginLeft(availableAlignmentSpace / 2); - child->setMarginRight(availableAlignmentSpace / 2); - } - return true; + mainAxisExtent = child->maxPreferredLogicalWidth(); } - bool shouldAdjustTopOrLeft = true; - if (isColumnFlow() && !child->style()->isLeftToRightDirection()) { - // For column flows, only make this adjustment if topOrLeft corresponds to the "before" margin, - // so that flipForRightToLeftColumn will do the right thing. - shouldAdjustTopOrLeft = false; + ASSERT(mainAxisExtent - mainAxisBorderAndPaddingExtentForChild(child) >= 0); + return mainAxisExtent - mainAxisBorderAndPaddingExtentForChild(child); + } + return std::max(LayoutUnit(0), computeMainAxisExtentForChild( + child, MainOrPreferredSize, flexBasis)); +} + +void RenderFlexibleBox::layoutFlexItems(bool relayoutChildren) { + Vector lineContexts; + OrderedFlexItemList orderedChildren; + LayoutUnit sumFlexBaseSize; + double totalFlexGrow; + double totalWeightedFlexShrink; + LayoutUnit sumHypotheticalMainSize; + + Vector childSizes; + + m_orderIterator.first(); + LayoutUnit crossAxisOffset = + flowAwareBorderBefore() + flowAwarePaddingBefore(); + bool hasInfiniteLineLength = false; + while (computeNextFlexLine(orderedChildren, sumFlexBaseSize, totalFlexGrow, + totalWeightedFlexShrink, sumHypotheticalMainSize, + hasInfiniteLineLength, relayoutChildren)) { + LayoutUnit containerMainInnerSize = + mainAxisContentExtent(sumHypotheticalMainSize); + LayoutUnit availableFreeSpace = containerMainInnerSize - sumFlexBaseSize; + FlexSign flexSign = (sumHypotheticalMainSize < containerMainInnerSize) + ? PositiveFlexibility + : NegativeFlexibility; + InflexibleFlexItemSize inflexibleItems; + childSizes.reserveCapacity(orderedChildren.size()); + while (!resolveFlexibleLengths(flexSign, orderedChildren, + availableFreeSpace, totalFlexGrow, + totalWeightedFlexShrink, inflexibleItems, + childSizes, hasInfiniteLineLength)) { + ASSERT(totalFlexGrow >= 0 && totalWeightedFlexShrink >= 0); + ASSERT(inflexibleItems.size() > 0); } - if (topOrLeft.isAuto()) { - if (shouldAdjustTopOrLeft) - adjustAlignmentForChild(child, availableAlignmentSpace); - - if (isHorizontal) - child->setMarginTop(availableAlignmentSpace); - else - child->setMarginLeft(availableAlignmentSpace); - return true; - } - if (bottomOrRight.isAuto()) { - if (!shouldAdjustTopOrLeft) - adjustAlignmentForChild(child, availableAlignmentSpace); - - if (isHorizontal) - child->setMarginBottom(availableAlignmentSpace); - else - child->setMarginRight(availableAlignmentSpace); - return true; - } - return false; -} - -LayoutUnit RenderFlexibleBox::marginBoxAscentForChild(RenderBox* child) -{ - LayoutUnit ascent = child->firstLineBoxBaseline(FontBaselineOrAuto()); - if (ascent == -1) - ascent = crossAxisExtentForChild(child); - return ascent + flowAwareMarginBeforeForChild(child); -} - -LayoutUnit RenderFlexibleBox::computeChildMarginValue(Length margin) -{ - // When resolving the margins, we use the content size for resolving percent and calc (for percents in calc expressions) margins. - // Fortunately, percent margins are always computed with respect to the block's width, even for margin-top and margin-bottom. - LayoutUnit availableSize = contentLogicalWidth(); - return minimumValueForLength(margin, availableSize); -} - -void RenderFlexibleBox::prepareOrderIteratorAndMargins() -{ - OrderIteratorPopulator populator(m_orderIterator); - - for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { - populator.collectChild(child); - - if (child->isOutOfFlowPositioned()) - continue; - - // Before running the flex algorithm, 'auto' has a margin of 0. - // Also, if we're not auto sizing, we don't do a layout that computes the start/end margins. - if (isHorizontalFlow()) { - child->setMarginLeft(computeChildMarginValue(child->style()->marginLeft())); - child->setMarginRight(computeChildMarginValue(child->style()->marginRight())); - } else { - child->setMarginTop(computeChildMarginValue(child->style()->marginTop())); - child->setMarginBottom(computeChildMarginValue(child->style()->marginBottom())); - } - } -} + layoutAndPlaceChildren(crossAxisOffset, orderedChildren, childSizes, + availableFreeSpace, relayoutChildren, lineContexts, + hasInfiniteLineLength); + } + if (hasLineIfEmpty()) { + // Even if computeNextFlexLine returns true, the flexbox might not have + // a line because all our children might be out of flow positioned. + // Instead of just checking if we have a line, make sure the flexbox + // has at least a line's worth of height to cover this case. + LayoutUnit minHeight = + borderAndPaddingLogicalHeight() + + lineHeight(true, HorizontalLine, PositionOfInteriorLineBoxes); + if (height() < minHeight) + setLogicalHeight(minHeight); + } + + updateLogicalHeight(); + repositionLogicalHeightDependentFlexItems(lineContexts); +} + +LayoutUnit RenderFlexibleBox::autoMarginOffsetInMainAxis( + const OrderedFlexItemList& children, + LayoutUnit& availableFreeSpace) { + if (availableFreeSpace <= 0) + return 0; -LayoutUnit RenderFlexibleBox::adjustChildSizeForMinAndMax(RenderBox* child, LayoutUnit childSize) -{ - Length max = isHorizontalFlow() ? child->style()->maxWidth() : child->style()->maxHeight(); - if (max.isSpecifiedOrIntrinsic()) { - LayoutUnit maxExtent = computeMainAxisExtentForChild(child, MaxSize, max); - if (maxExtent != -1 && childSize > maxExtent) - childSize = maxExtent; + int numberOfAutoMargins = 0; + bool isHorizontal = isHorizontalFlow(); + for (size_t i = 0; i < children.size(); ++i) { + RenderBox* child = children[i]; + if (child->isOutOfFlowPositioned()) + continue; + if (isHorizontal) { + if (child->style()->marginLeft().isAuto()) + ++numberOfAutoMargins; + if (child->style()->marginRight().isAuto()) + ++numberOfAutoMargins; + } else { + if (child->style()->marginTop().isAuto()) + ++numberOfAutoMargins; + if (child->style()->marginBottom().isAuto()) + ++numberOfAutoMargins; } + } + if (!numberOfAutoMargins) + return 0; - Length min = isHorizontalFlow() ? child->style()->minWidth() : child->style()->minHeight(); - LayoutUnit minExtent = 0; - if (min.isSpecifiedOrIntrinsic()) - minExtent = computeMainAxisExtentForChild(child, MinSize, min); - return std::max(childSize, minExtent); -} - -bool RenderFlexibleBox::computeNextFlexLine(OrderedFlexItemList& orderedChildren, LayoutUnit& sumFlexBaseSize, double& totalFlexGrow, double& totalWeightedFlexShrink, LayoutUnit& sumHypotheticalMainSize, bool& hasInfiniteLineLength, bool relayoutChildren) -{ - orderedChildren.clear(); - sumFlexBaseSize = 0; - totalFlexGrow = totalWeightedFlexShrink = 0; - sumHypotheticalMainSize = 0; - - if (!m_orderIterator.currentChild()) - return false; - - LayoutUnit lineBreakLength = mainAxisContentExtent(LayoutUnit::max()); - hasInfiniteLineLength = lineBreakLength == LayoutUnit::max(); - - bool lineHasInFlowItem = false; - - for (RenderBox* child = m_orderIterator.currentChild(); child; child = m_orderIterator.next()) { - if (child->isOutOfFlowPositioned()) { - orderedChildren.append(child); - continue; - } - - LayoutUnit childMainAxisExtent = preferredMainAxisContentExtentForChild(child, hasInfiniteLineLength, relayoutChildren); - LayoutUnit childMainAxisMarginBorderPadding = mainAxisBorderAndPaddingExtentForChild(child) - + (isHorizontalFlow() ? child->marginWidth() : child->marginHeight()); - LayoutUnit childFlexBaseSize = childMainAxisExtent + childMainAxisMarginBorderPadding; - - LayoutUnit childMinMaxAppliedMainAxisExtent = adjustChildSizeForMinAndMax(child, childMainAxisExtent); - LayoutUnit childHypotheticalMainSize = childMinMaxAppliedMainAxisExtent + childMainAxisMarginBorderPadding; - - if (isMultiline() && sumHypotheticalMainSize + childHypotheticalMainSize > lineBreakLength && lineHasInFlowItem) - break; - orderedChildren.append(child); - lineHasInFlowItem = true; - sumFlexBaseSize += childFlexBaseSize; - totalFlexGrow += child->style()->flexGrow(); - totalWeightedFlexShrink += child->style()->flexShrink() * childMainAxisExtent; - sumHypotheticalMainSize += childHypotheticalMainSize; + LayoutUnit sizeOfAutoMargin = availableFreeSpace / numberOfAutoMargins; + availableFreeSpace = 0; + return sizeOfAutoMargin; +} + +void RenderFlexibleBox::updateAutoMarginsInMainAxis( + RenderBox* child, + LayoutUnit autoMarginOffset) { + ASSERT(autoMarginOffset >= 0); + + if (isHorizontalFlow()) { + if (child->style()->marginLeft().isAuto()) + child->setMarginLeft(autoMarginOffset); + if (child->style()->marginRight().isAuto()) + child->setMarginRight(autoMarginOffset); + } else { + if (child->style()->marginTop().isAuto()) + child->setMarginTop(autoMarginOffset); + if (child->style()->marginBottom().isAuto()) + child->setMarginBottom(autoMarginOffset); + } +} + +bool RenderFlexibleBox::hasAutoMarginsInCrossAxis(RenderBox* child) const { + if (isHorizontalFlow()) + return child->style()->marginTop().isAuto() || + child->style()->marginBottom().isAuto(); + return child->style()->marginLeft().isAuto() || + child->style()->marginRight().isAuto(); +} + +LayoutUnit RenderFlexibleBox::availableAlignmentSpaceForChild( + LayoutUnit lineCrossAxisExtent, + RenderBox* child) { + ASSERT(!child->isOutOfFlowPositioned()); + LayoutUnit childCrossExtent = + crossAxisMarginExtentForChild(child) + crossAxisExtentForChild(child); + return lineCrossAxisExtent - childCrossExtent; +} + +LayoutUnit RenderFlexibleBox::availableAlignmentSpaceForChildBeforeStretching( + LayoutUnit lineCrossAxisExtent, + RenderBox* child) { + ASSERT(!child->isOutOfFlowPositioned()); + LayoutUnit childCrossExtent = crossAxisMarginExtentForChild(child) + + crossAxisIntrinsicExtentForChild(child); + return lineCrossAxisExtent - childCrossExtent; +} + +bool RenderFlexibleBox::updateAutoMarginsInCrossAxis( + RenderBox* child, + LayoutUnit availableAlignmentSpace) { + ASSERT(!child->isOutOfFlowPositioned()); + ASSERT(availableAlignmentSpace >= 0); + + bool isHorizontal = isHorizontalFlow(); + Length topOrLeft = + isHorizontal ? child->style()->marginTop() : child->style()->marginLeft(); + Length bottomOrRight = isHorizontal ? child->style()->marginBottom() + : child->style()->marginRight(); + if (topOrLeft.isAuto() && bottomOrRight.isAuto()) { + adjustAlignmentForChild(child, availableAlignmentSpace / 2); + if (isHorizontal) { + child->setMarginTop(availableAlignmentSpace / 2); + child->setMarginBottom(availableAlignmentSpace / 2); + } else { + child->setMarginLeft(availableAlignmentSpace / 2); + child->setMarginRight(availableAlignmentSpace / 2); } return true; -} - -void RenderFlexibleBox::freezeViolations(const Vector& violations, LayoutUnit& availableFreeSpace, double& totalFlexGrow, double& totalWeightedFlexShrink, InflexibleFlexItemSize& inflexibleItems, bool hasInfiniteLineLength) -{ - for (size_t i = 0; i < violations.size(); ++i) { - RenderBox* child = violations[i].child; - LayoutUnit childSize = violations[i].childSize; - LayoutUnit preferredChildSize = preferredMainAxisContentExtentForChild(child, hasInfiniteLineLength); - availableFreeSpace -= childSize - preferredChildSize; - totalFlexGrow -= child->style()->flexGrow(); - totalWeightedFlexShrink -= child->style()->flexShrink() * preferredChildSize; - inflexibleItems.set(child, childSize); - } -} - -// Returns true if we successfully ran the algorithm and sized the flex items. -bool RenderFlexibleBox::resolveFlexibleLengths(FlexSign flexSign, const OrderedFlexItemList& children, LayoutUnit& availableFreeSpace, double& totalFlexGrow, double& totalWeightedFlexShrink, InflexibleFlexItemSize& inflexibleItems, Vector& childSizes, bool hasInfiniteLineLength) -{ - childSizes.resize(0); - LayoutUnit totalViolation = 0; - LayoutUnit usedFreeSpace = 0; - Vector minViolations; - Vector maxViolations; - for (size_t i = 0; i < children.size(); ++i) { - RenderBox* child = children[i]; - if (child->isOutOfFlowPositioned()) { - childSizes.append(0); - continue; - } - - if (inflexibleItems.contains(child)) - childSizes.append(inflexibleItems.get(child)); - else { - LayoutUnit preferredChildSize = preferredMainAxisContentExtentForChild(child, hasInfiniteLineLength); - LayoutUnit childSize = preferredChildSize; - double extraSpace = 0; - if (availableFreeSpace > 0 && totalFlexGrow > 0 && flexSign == PositiveFlexibility && std::isfinite(totalFlexGrow)) - extraSpace = availableFreeSpace * child->style()->flexGrow() / totalFlexGrow; - else if (availableFreeSpace < 0 && totalWeightedFlexShrink > 0 && flexSign == NegativeFlexibility && std::isfinite(totalWeightedFlexShrink)) - extraSpace = availableFreeSpace * child->style()->flexShrink() * preferredChildSize / totalWeightedFlexShrink; - if (std::isfinite(extraSpace)) - childSize += LayoutUnit::fromFloatRound(extraSpace); - - LayoutUnit adjustedChildSize = adjustChildSizeForMinAndMax(child, childSize); - childSizes.append(adjustedChildSize); - usedFreeSpace += adjustedChildSize - preferredChildSize; - - LayoutUnit violation = adjustedChildSize - childSize; - if (violation > 0) - minViolations.append(Violation(child, adjustedChildSize)); - else if (violation < 0) - maxViolations.append(Violation(child, adjustedChildSize)); - totalViolation += violation; - } - } - - if (totalViolation) - freezeViolations(totalViolation < 0 ? maxViolations : minViolations, availableFreeSpace, totalFlexGrow, totalWeightedFlexShrink, inflexibleItems, hasInfiniteLineLength); + } + bool shouldAdjustTopOrLeft = true; + if (isColumnFlow() && !child->style()->isLeftToRightDirection()) { + // For column flows, only make this adjustment if topOrLeft corresponds to + // the "before" margin, so that flipForRightToLeftColumn will do the right + // thing. + shouldAdjustTopOrLeft = false; + } + + if (topOrLeft.isAuto()) { + if (shouldAdjustTopOrLeft) + adjustAlignmentForChild(child, availableAlignmentSpace); + + if (isHorizontal) + child->setMarginTop(availableAlignmentSpace); else - availableFreeSpace -= usedFreeSpace; - - return !totalViolation; -} - -static LayoutUnit initialJustifyContentOffset(LayoutUnit availableFreeSpace, EJustifyContent justifyContent, unsigned numberOfChildren) -{ - if (justifyContent == JustifyFlexEnd) - return availableFreeSpace; - if (justifyContent == JustifyCenter) - return availableFreeSpace / 2; - if (justifyContent == JustifySpaceAround) { - if (availableFreeSpace > 0 && numberOfChildren) - return availableFreeSpace / (2 * numberOfChildren); - else - return availableFreeSpace / 2; - } - return 0; -} - -static LayoutUnit justifyContentSpaceBetweenChildren(LayoutUnit availableFreeSpace, EJustifyContent justifyContent, unsigned numberOfChildren) -{ - if (availableFreeSpace > 0 && numberOfChildren > 1) { - if (justifyContent == JustifySpaceBetween) - return availableFreeSpace / (numberOfChildren - 1); - if (justifyContent == JustifySpaceAround) - return availableFreeSpace / numberOfChildren; - } - return 0; -} + child->setMarginLeft(availableAlignmentSpace); + return true; + } + if (bottomOrRight.isAuto()) { + if (!shouldAdjustTopOrLeft) + adjustAlignmentForChild(child, availableAlignmentSpace); -void RenderFlexibleBox::setLogicalOverrideSize(RenderBox* child, LayoutUnit childPreferredSize) -{ - if (hasOrthogonalFlow(child)) - child->setOverrideLogicalContentHeight(childPreferredSize - child->borderAndPaddingLogicalHeight()); + if (isHorizontal) + child->setMarginBottom(availableAlignmentSpace); else - child->setOverrideLogicalContentWidth(childPreferredSize - child->borderAndPaddingLogicalWidth()); -} - -ItemPosition RenderFlexibleBox::alignmentForChild(RenderBox* child) const -{ - ItemPosition align = resolveAlignment(style(), child->style()); - - if (align == ItemPositionBaseline && hasOrthogonalFlow(child)) - align = ItemPositionFlexStart; - - if (style()->flexWrap() == FlexWrapReverse) { - if (align == ItemPositionFlexStart) - align = ItemPositionFlexEnd; - else if (align == ItemPositionFlexEnd) - align = ItemPositionFlexStart; - } - - return align; + child->setMarginRight(availableAlignmentSpace); + return true; + } + return false; } -size_t RenderFlexibleBox::numberOfInFlowPositionedChildren(const OrderedFlexItemList& children) const -{ - size_t count = 0; - for (size_t i = 0; i < children.size(); ++i) { - RenderBox* child = children[i]; - if (!child->isOutOfFlowPositioned()) - ++count; - } - return count; -} - -void RenderFlexibleBox::resetAutoMarginsAndLogicalTopInCrossAxis(RenderBox* child) -{ - if (hasAutoMarginsInCrossAxis(child)) { - child->updateLogicalHeight(); - if (isHorizontalFlow()) { - if (child->style()->marginTop().isAuto()) - child->setMarginTop(0); - if (child->style()->marginBottom().isAuto()) - child->setMarginBottom(0); - } else { - if (child->style()->marginLeft().isAuto()) - child->setMarginLeft(0); - if (child->style()->marginRight().isAuto()) - child->setMarginRight(0); - } - } +LayoutUnit RenderFlexibleBox::marginBoxAscentForChild(RenderBox* child) { + LayoutUnit ascent = child->firstLineBoxBaseline(FontBaselineOrAuto()); + if (ascent == -1) + ascent = crossAxisExtentForChild(child); + return ascent + flowAwareMarginBeforeForChild(child); } -bool RenderFlexibleBox::needToStretchChildLogicalHeight(RenderBox* child) const -{ - if (alignmentForChild(child) != ItemPositionStretch) - return false; - - return isHorizontalFlow() && child->style()->height().isAuto(); +LayoutUnit RenderFlexibleBox::computeChildMarginValue(Length margin) { + // When resolving the margins, we use the content size for resolving percent + // and calc (for percents in calc expressions) margins. Fortunately, percent + // margins are always computed with respect to the block's width, even for + // margin-top and margin-bottom. + LayoutUnit availableSize = contentLogicalWidth(); + return minimumValueForLength(margin, availableSize); } -void RenderFlexibleBox::layoutAndPlaceChildren(LayoutUnit& crossAxisOffset, const OrderedFlexItemList& children, const Vector& childSizes, LayoutUnit availableFreeSpace, bool relayoutChildren, Vector& lineContexts, bool hasInfiniteLineLength) -{ - ASSERT(childSizes.size() == children.size()); - - size_t numberOfChildrenForJustifyContent = numberOfInFlowPositionedChildren(children); - LayoutUnit autoMarginOffset = autoMarginOffsetInMainAxis(children, availableFreeSpace); - LayoutUnit mainAxisOffset = flowAwareBorderStart() + flowAwarePaddingStart(); - mainAxisOffset += initialJustifyContentOffset(availableFreeSpace, style()->justifyContent(), numberOfChildrenForJustifyContent); - - LayoutUnit totalMainExtent = mainAxisExtent(); - LayoutUnit maxAscent = 0, maxDescent = 0; // Used when align-items: baseline. - LayoutUnit maxChildCrossAxisExtent = 0; - size_t seenInFlowPositionedChildren = 0; - bool shouldFlipMainAxis = !isColumnFlow() && !isLeftToRightFlow(); - for (size_t i = 0; i < children.size(); ++i) { - RenderBox* child = children[i]; - - if (child->isOutOfFlowPositioned()) { - child->containingBlock()->insertPositionedObject(child); - continue; - } - - LayoutUnit childPreferredSize = childSizes[i] + mainAxisBorderAndPaddingExtentForChild(child); - setLogicalOverrideSize(child, childPreferredSize); - if (childPreferredSize != mainAxisExtentForChild(child)) { - child->setChildNeedsLayout(MarkOnlyThis); - } else { - // To avoid double applying margin changes in updateAutoMarginsInCrossAxis, we reset the margins here. - resetAutoMarginsAndLogicalTopInCrossAxis(child); - } - // We may have already forced relayout for orthogonal flowing children in preferredMainAxisContentExtentForChild. - bool forceChildRelayout = relayoutChildren && !childPreferredMainAxisContentExtentRequiresLayout(child, hasInfiniteLineLength); - updateBlockChildDirtyBitsBeforeLayout(forceChildRelayout, child); - child->layoutIfNeeded(); +void RenderFlexibleBox::prepareOrderIteratorAndMargins() { + OrderIteratorPopulator populator(m_orderIterator); - updateAutoMarginsInMainAxis(child, autoMarginOffset); + for (RenderBox* child = firstChildBox(); child; + child = child->nextSiblingBox()) { + populator.collectChild(child); - LayoutUnit childCrossAxisMarginBoxExtent; - if (alignmentForChild(child) == ItemPositionBaseline && !hasAutoMarginsInCrossAxis(child)) { - LayoutUnit ascent = marginBoxAscentForChild(child); - LayoutUnit descent = (crossAxisMarginExtentForChild(child) + crossAxisExtentForChild(child)) - ascent; - - maxAscent = std::max(maxAscent, ascent); - maxDescent = std::max(maxDescent, descent); - - childCrossAxisMarginBoxExtent = maxAscent + maxDescent; - } else { - childCrossAxisMarginBoxExtent = crossAxisIntrinsicExtentForChild(child) + crossAxisMarginExtentForChild(child); - } - if (!isColumnFlow()) - setLogicalHeight(std::max(logicalHeight(), crossAxisOffset + flowAwareBorderAfter() + flowAwarePaddingAfter() + childCrossAxisMarginBoxExtent)); - maxChildCrossAxisExtent = std::max(maxChildCrossAxisExtent, childCrossAxisMarginBoxExtent); + if (child->isOutOfFlowPositioned()) + continue; - mainAxisOffset += flowAwareMarginStartForChild(child); - - LayoutUnit childMainExtent = mainAxisExtentForChild(child); - // In an RTL column situation, this will apply the margin-right/margin-end on the left. - // This will be fixed later in flipForRightToLeftColumn. - LayoutPoint childLocation(shouldFlipMainAxis ? totalMainExtent - mainAxisOffset - childMainExtent : mainAxisOffset, - crossAxisOffset + flowAwareMarginBeforeForChild(child)); - - // FIXME: Supporting layout deltas. - setFlowAwareLocationForChild(child, childLocation); - mainAxisOffset += childMainExtent + flowAwareMarginEndForChild(child); - - ++seenInFlowPositionedChildren; - if (seenInFlowPositionedChildren < numberOfChildrenForJustifyContent) - mainAxisOffset += justifyContentSpaceBetweenChildren(availableFreeSpace, style()->justifyContent(), numberOfChildrenForJustifyContent); + // Before running the flex algorithm, 'auto' has a margin of 0. + // Also, if we're not auto sizing, we don't do a layout that computes the + // start/end margins. + if (isHorizontalFlow()) { + child->setMarginLeft( + computeChildMarginValue(child->style()->marginLeft())); + child->setMarginRight( + computeChildMarginValue(child->style()->marginRight())); + } else { + child->setMarginTop(computeChildMarginValue(child->style()->marginTop())); + child->setMarginBottom( + computeChildMarginValue(child->style()->marginBottom())); } + } +} + +LayoutUnit RenderFlexibleBox::adjustChildSizeForMinAndMax( + RenderBox* child, + LayoutUnit childSize) { + Length max = isHorizontalFlow() ? child->style()->maxWidth() + : child->style()->maxHeight(); + if (max.isSpecifiedOrIntrinsic()) { + LayoutUnit maxExtent = computeMainAxisExtentForChild(child, MaxSize, max); + if (maxExtent != -1 && childSize > maxExtent) + childSize = maxExtent; + } + + Length min = isHorizontalFlow() ? child->style()->minWidth() + : child->style()->minHeight(); + LayoutUnit minExtent = 0; + if (min.isSpecifiedOrIntrinsic()) + minExtent = computeMainAxisExtentForChild(child, MinSize, min); + return std::max(childSize, minExtent); +} + +bool RenderFlexibleBox::computeNextFlexLine( + OrderedFlexItemList& orderedChildren, + LayoutUnit& sumFlexBaseSize, + double& totalFlexGrow, + double& totalWeightedFlexShrink, + LayoutUnit& sumHypotheticalMainSize, + bool& hasInfiniteLineLength, + bool relayoutChildren) { + orderedChildren.clear(); + sumFlexBaseSize = 0; + totalFlexGrow = totalWeightedFlexShrink = 0; + sumHypotheticalMainSize = 0; + + if (!m_orderIterator.currentChild()) + return false; + + LayoutUnit lineBreakLength = mainAxisContentExtent(LayoutUnit::max()); + hasInfiniteLineLength = lineBreakLength == LayoutUnit::max(); - if (isColumnFlow()) - setLogicalHeight(mainAxisOffset + flowAwareBorderEnd() + flowAwarePaddingEnd()); + bool lineHasInFlowItem = false; - if (style()->flexDirection() == FlowColumnReverse) { - // We have to do an extra pass for column-reverse to reposition the flex items since the start depends - // on the height of the flexbox, which we only know after we've positioned all the flex items. - updateLogicalHeight(); - layoutColumnReverse(children, crossAxisOffset, availableFreeSpace); + for (RenderBox* child = m_orderIterator.currentChild(); child; + child = m_orderIterator.next()) { + if (child->isOutOfFlowPositioned()) { + orderedChildren.append(child); + continue; } - if (m_numberOfInFlowChildrenOnFirstLine == -1) - m_numberOfInFlowChildrenOnFirstLine = seenInFlowPositionedChildren; - lineContexts.append(LineContext(crossAxisOffset, maxChildCrossAxisExtent, children.size(), maxAscent)); - crossAxisOffset += maxChildCrossAxisExtent; + LayoutUnit childMainAxisExtent = preferredMainAxisContentExtentForChild( + child, hasInfiniteLineLength, relayoutChildren); + LayoutUnit childMainAxisMarginBorderPadding = + mainAxisBorderAndPaddingExtentForChild(child) + + (isHorizontalFlow() ? child->marginWidth() : child->marginHeight()); + LayoutUnit childFlexBaseSize = + childMainAxisExtent + childMainAxisMarginBorderPadding; + + LayoutUnit childMinMaxAppliedMainAxisExtent = + adjustChildSizeForMinAndMax(child, childMainAxisExtent); + LayoutUnit childHypotheticalMainSize = + childMinMaxAppliedMainAxisExtent + childMainAxisMarginBorderPadding; + + if (isMultiline() && + sumHypotheticalMainSize + childHypotheticalMainSize > lineBreakLength && + lineHasInFlowItem) + break; + orderedChildren.append(child); + lineHasInFlowItem = true; + sumFlexBaseSize += childFlexBaseSize; + totalFlexGrow += child->style()->flexGrow(); + totalWeightedFlexShrink += + child->style()->flexShrink() * childMainAxisExtent; + sumHypotheticalMainSize += childHypotheticalMainSize; + } + return true; +} + +void RenderFlexibleBox::freezeViolations( + const Vector& violations, + LayoutUnit& availableFreeSpace, + double& totalFlexGrow, + double& totalWeightedFlexShrink, + InflexibleFlexItemSize& inflexibleItems, + bool hasInfiniteLineLength) { + for (size_t i = 0; i < violations.size(); ++i) { + RenderBox* child = violations[i].child; + LayoutUnit childSize = violations[i].childSize; + LayoutUnit preferredChildSize = + preferredMainAxisContentExtentForChild(child, hasInfiniteLineLength); + availableFreeSpace -= childSize - preferredChildSize; + totalFlexGrow -= child->style()->flexGrow(); + totalWeightedFlexShrink -= + child->style()->flexShrink() * preferredChildSize; + inflexibleItems.set(child, childSize); + } } -void RenderFlexibleBox::layoutColumnReverse(const OrderedFlexItemList& children, LayoutUnit crossAxisOffset, LayoutUnit availableFreeSpace) -{ - // This is similar to the logic in layoutAndPlaceChildren, except we place the children - // starting from the end of the flexbox. We also don't need to layout anything since we're - // just moving the children to a new position. - size_t numberOfChildrenForJustifyContent = numberOfInFlowPositionedChildren(children); - LayoutUnit mainAxisOffset = logicalHeight() - flowAwareBorderEnd() - flowAwarePaddingEnd(); - mainAxisOffset -= initialJustifyContentOffset(availableFreeSpace, style()->justifyContent(), numberOfChildrenForJustifyContent); - - size_t seenInFlowPositionedChildren = 0; - for (size_t i = 0; i < children.size(); ++i) { - RenderBox* child = children[i]; - - if (child->isOutOfFlowPositioned()) - continue; - - mainAxisOffset -= mainAxisExtentForChild(child) + flowAwareMarginEndForChild(child); - - setFlowAwareLocationForChild(child, LayoutPoint(mainAxisOffset, crossAxisOffset + flowAwareMarginBeforeForChild(child))); - - mainAxisOffset -= flowAwareMarginStartForChild(child); - - ++seenInFlowPositionedChildren; - if (seenInFlowPositionedChildren < numberOfChildrenForJustifyContent) - mainAxisOffset -= justifyContentSpaceBetweenChildren(availableFreeSpace, style()->justifyContent(), numberOfChildrenForJustifyContent); +// Returns true if we successfully ran the algorithm and sized the flex items. +bool RenderFlexibleBox::resolveFlexibleLengths( + FlexSign flexSign, + const OrderedFlexItemList& children, + LayoutUnit& availableFreeSpace, + double& totalFlexGrow, + double& totalWeightedFlexShrink, + InflexibleFlexItemSize& inflexibleItems, + Vector& childSizes, + bool hasInfiniteLineLength) { + childSizes.resize(0); + LayoutUnit totalViolation = 0; + LayoutUnit usedFreeSpace = 0; + Vector minViolations; + Vector maxViolations; + for (size_t i = 0; i < children.size(); ++i) { + RenderBox* child = children[i]; + if (child->isOutOfFlowPositioned()) { + childSizes.append(0); + continue; } -} -static LayoutUnit initialAlignContentOffset(LayoutUnit availableFreeSpace, EAlignContent alignContent, unsigned numberOfLines) -{ - if (numberOfLines <= 1) - return 0; - if (alignContent == AlignContentFlexEnd) - return availableFreeSpace; - if (alignContent == AlignContentCenter) - return availableFreeSpace / 2; - if (alignContent == AlignContentSpaceAround) { - if (availableFreeSpace > 0 && numberOfLines) - return availableFreeSpace / (2 * numberOfLines); - if (availableFreeSpace < 0) - return availableFreeSpace / 2; + if (inflexibleItems.contains(child)) + childSizes.append(inflexibleItems.get(child)); + else { + LayoutUnit preferredChildSize = + preferredMainAxisContentExtentForChild(child, hasInfiniteLineLength); + LayoutUnit childSize = preferredChildSize; + double extraSpace = 0; + if (availableFreeSpace > 0 && totalFlexGrow > 0 && + flexSign == PositiveFlexibility && std::isfinite(totalFlexGrow)) + extraSpace = + availableFreeSpace * child->style()->flexGrow() / totalFlexGrow; + else if (availableFreeSpace < 0 && totalWeightedFlexShrink > 0 && + flexSign == NegativeFlexibility && + std::isfinite(totalWeightedFlexShrink)) + extraSpace = availableFreeSpace * child->style()->flexShrink() * + preferredChildSize / totalWeightedFlexShrink; + if (std::isfinite(extraSpace)) + childSize += LayoutUnit::fromFloatRound(extraSpace); + + LayoutUnit adjustedChildSize = + adjustChildSizeForMinAndMax(child, childSize); + childSizes.append(adjustedChildSize); + usedFreeSpace += adjustedChildSize - preferredChildSize; + + LayoutUnit violation = adjustedChildSize - childSize; + if (violation > 0) + minViolations.append(Violation(child, adjustedChildSize)); + else if (violation < 0) + maxViolations.append(Violation(child, adjustedChildSize)); + totalViolation += violation; } - return 0; -} - -static LayoutUnit alignContentSpaceBetweenChildren(LayoutUnit availableFreeSpace, EAlignContent alignContent, unsigned numberOfLines) -{ - if (availableFreeSpace > 0 && numberOfLines > 1) { - if (alignContent == AlignContentSpaceBetween) - return availableFreeSpace / (numberOfLines - 1); - if (alignContent == AlignContentSpaceAround || alignContent == AlignContentStretch) - return availableFreeSpace / numberOfLines; + } + + if (totalViolation) + freezeViolations(totalViolation < 0 ? maxViolations : minViolations, + availableFreeSpace, totalFlexGrow, totalWeightedFlexShrink, + inflexibleItems, hasInfiniteLineLength); + else + availableFreeSpace -= usedFreeSpace; + + return !totalViolation; +} + +static LayoutUnit initialJustifyContentOffset(LayoutUnit availableFreeSpace, + EJustifyContent justifyContent, + unsigned numberOfChildren) { + if (justifyContent == JustifyFlexEnd) + return availableFreeSpace; + if (justifyContent == JustifyCenter) + return availableFreeSpace / 2; + if (justifyContent == JustifySpaceAround) { + if (availableFreeSpace > 0 && numberOfChildren) + return availableFreeSpace / (2 * numberOfChildren); + else + return availableFreeSpace / 2; + } + return 0; +} + +static LayoutUnit justifyContentSpaceBetweenChildren( + LayoutUnit availableFreeSpace, + EJustifyContent justifyContent, + unsigned numberOfChildren) { + if (availableFreeSpace > 0 && numberOfChildren > 1) { + if (justifyContent == JustifySpaceBetween) + return availableFreeSpace / (numberOfChildren - 1); + if (justifyContent == JustifySpaceAround) + return availableFreeSpace / numberOfChildren; + } + return 0; +} + +void RenderFlexibleBox::setLogicalOverrideSize(RenderBox* child, + LayoutUnit childPreferredSize) { + if (hasOrthogonalFlow(child)) + child->setOverrideLogicalContentHeight( + childPreferredSize - child->borderAndPaddingLogicalHeight()); + else + child->setOverrideLogicalContentWidth( + childPreferredSize - child->borderAndPaddingLogicalWidth()); +} + +ItemPosition RenderFlexibleBox::alignmentForChild(RenderBox* child) const { + ItemPosition align = resolveAlignment(style(), child->style()); + + if (align == ItemPositionBaseline && hasOrthogonalFlow(child)) + align = ItemPositionFlexStart; + + if (style()->flexWrap() == FlexWrapReverse) { + if (align == ItemPositionFlexStart) + align = ItemPositionFlexEnd; + else if (align == ItemPositionFlexEnd) + align = ItemPositionFlexStart; + } + + return align; +} + +size_t RenderFlexibleBox::numberOfInFlowPositionedChildren( + const OrderedFlexItemList& children) const { + size_t count = 0; + for (size_t i = 0; i < children.size(); ++i) { + RenderBox* child = children[i]; + if (!child->isOutOfFlowPositioned()) + ++count; + } + return count; +} + +void RenderFlexibleBox::resetAutoMarginsAndLogicalTopInCrossAxis( + RenderBox* child) { + if (hasAutoMarginsInCrossAxis(child)) { + child->updateLogicalHeight(); + if (isHorizontalFlow()) { + if (child->style()->marginTop().isAuto()) + child->setMarginTop(0); + if (child->style()->marginBottom().isAuto()) + child->setMarginBottom(0); + } else { + if (child->style()->marginLeft().isAuto()) + child->setMarginLeft(0); + if (child->style()->marginRight().isAuto()) + child->setMarginRight(0); } - return 0; + } } -void RenderFlexibleBox::alignFlexLines(Vector& lineContexts) -{ - // If we have a single line flexbox or a multiline line flexbox with only one flex line, - // the line height is all the available space. - // For flex-direction: row, this means we need to use the height, so we do this after calling updateLogicalHeight. - if (lineContexts.size() == 1) { - lineContexts[0].crossAxisExtent = crossAxisContentExtent(); - return; - } - - if (style()->alignContent() == AlignContentFlexStart) - return; - - LayoutUnit availableCrossAxisSpace = crossAxisContentExtent(); - for (size_t i = 0; i < lineContexts.size(); ++i) - availableCrossAxisSpace -= lineContexts[i].crossAxisExtent; - - RenderBox* child = m_orderIterator.first(); - LayoutUnit lineOffset = initialAlignContentOffset(availableCrossAxisSpace, style()->alignContent(), lineContexts.size()); - for (unsigned lineNumber = 0; lineNumber < lineContexts.size(); ++lineNumber) { - lineContexts[lineNumber].crossAxisOffset += lineOffset; - for (size_t childNumber = 0; childNumber < lineContexts[lineNumber].numberOfChildren; ++childNumber, child = m_orderIterator.next()) - adjustAlignmentForChild(child, lineOffset); - - if (style()->alignContent() == AlignContentStretch && availableCrossAxisSpace > 0) - lineContexts[lineNumber].crossAxisExtent += availableCrossAxisSpace / static_cast(lineContexts.size()); +bool RenderFlexibleBox::needToStretchChildLogicalHeight( + RenderBox* child) const { + if (alignmentForChild(child) != ItemPositionStretch) + return false; - lineOffset += alignContentSpaceBetweenChildren(availableCrossAxisSpace, style()->alignContent(), lineContexts.size()); - } -} + return isHorizontalFlow() && child->style()->height().isAuto(); +} + +void RenderFlexibleBox::layoutAndPlaceChildren( + LayoutUnit& crossAxisOffset, + const OrderedFlexItemList& children, + const Vector& childSizes, + LayoutUnit availableFreeSpace, + bool relayoutChildren, + Vector& lineContexts, + bool hasInfiniteLineLength) { + ASSERT(childSizes.size() == children.size()); + + size_t numberOfChildrenForJustifyContent = + numberOfInFlowPositionedChildren(children); + LayoutUnit autoMarginOffset = + autoMarginOffsetInMainAxis(children, availableFreeSpace); + LayoutUnit mainAxisOffset = flowAwareBorderStart() + flowAwarePaddingStart(); + mainAxisOffset += + initialJustifyContentOffset(availableFreeSpace, style()->justifyContent(), + numberOfChildrenForJustifyContent); + + LayoutUnit totalMainExtent = mainAxisExtent(); + LayoutUnit maxAscent = 0, maxDescent = 0; // Used when align-items: baseline. + LayoutUnit maxChildCrossAxisExtent = 0; + size_t seenInFlowPositionedChildren = 0; + bool shouldFlipMainAxis = !isColumnFlow() && !isLeftToRightFlow(); + for (size_t i = 0; i < children.size(); ++i) { + RenderBox* child = children[i]; -void RenderFlexibleBox::adjustAlignmentForChild(RenderBox* child, LayoutUnit delta) -{ if (child->isOutOfFlowPositioned()) { - return; + child->containingBlock()->insertPositionedObject(child); + continue; } - setFlowAwareLocationForChild(child, flowAwareLocationForChild(child) + LayoutSize(0, delta)); -} - -void RenderFlexibleBox::alignChildren(const Vector& lineContexts) -{ - // Keep track of the space between the baseline edge and the after edge of the box for each line. - Vector minMarginAfterBaselines; - - RenderBox* child = m_orderIterator.first(); - for (size_t lineNumber = 0; lineNumber < lineContexts.size(); ++lineNumber) { - LayoutUnit minMarginAfterBaseline = LayoutUnit::max(); - LayoutUnit lineCrossAxisExtent = lineContexts[lineNumber].crossAxisExtent; - LayoutUnit maxAscent = lineContexts[lineNumber].maxAscent; - - for (size_t childNumber = 0; childNumber < lineContexts[lineNumber].numberOfChildren; ++childNumber, child = m_orderIterator.next()) { - ASSERT(child); - if (child->isOutOfFlowPositioned()) { - if (style()->flexWrap() == FlexWrapReverse) - adjustAlignmentForChild(child, lineCrossAxisExtent); - continue; - } - - if (updateAutoMarginsInCrossAxis(child, std::max(LayoutUnit(0), availableAlignmentSpaceForChild(lineCrossAxisExtent, child)))) - continue; - - switch (alignmentForChild(child)) { - case ItemPositionAuto: - ASSERT_NOT_REACHED(); - break; - case ItemPositionStretch: { - applyStretchAlignmentToChild(child, lineCrossAxisExtent); - // Since wrap-reverse flips cross start and cross end, strech children should be aligned with the cross end. - if (style()->flexWrap() == FlexWrapReverse) - adjustAlignmentForChild(child, availableAlignmentSpaceForChild(lineCrossAxisExtent, child)); - break; - } - case ItemPositionFlexStart: - break; - case ItemPositionFlexEnd: - adjustAlignmentForChild(child, availableAlignmentSpaceForChild(lineCrossAxisExtent, child)); - break; - case ItemPositionCenter: - adjustAlignmentForChild(child, availableAlignmentSpaceForChild(lineCrossAxisExtent, child) / 2); - break; - case ItemPositionBaseline: { - // FIXME: If we get here in columns, we want the use the descent, except we currently can't get the ascent/descent of orthogonal children. - // https://bugs.webkit.org/show_bug.cgi?id=98076 - LayoutUnit ascent = marginBoxAscentForChild(child); - LayoutUnit startOffset = maxAscent - ascent; - adjustAlignmentForChild(child, startOffset); - - if (style()->flexWrap() == FlexWrapReverse) - minMarginAfterBaseline = std::min(minMarginAfterBaseline, availableAlignmentSpaceForChild(lineCrossAxisExtent, child) - startOffset); - break; - } - case ItemPositionLastBaseline: - case ItemPositionSelfStart: - case ItemPositionSelfEnd: - case ItemPositionStart: - case ItemPositionEnd: - case ItemPositionLeft: - case ItemPositionRight: - // FIXME: File a bug about implementing that. The extended grammar - // is not enabled by default so we shouldn't hit this codepath. - ASSERT_NOT_REACHED(); - break; - } - } - minMarginAfterBaselines.append(minMarginAfterBaseline); + LayoutUnit childPreferredSize = + childSizes[i] + mainAxisBorderAndPaddingExtentForChild(child); + setLogicalOverrideSize(child, childPreferredSize); + if (childPreferredSize != mainAxisExtentForChild(child)) { + child->setChildNeedsLayout(MarkOnlyThis); + } else { + // To avoid double applying margin changes in + // updateAutoMarginsInCrossAxis, we reset the margins here. + resetAutoMarginsAndLogicalTopInCrossAxis(child); } - - if (style()->flexWrap() != FlexWrapReverse) - return; - - // wrap-reverse flips the cross axis start and end. For baseline alignment, this means we - // need to align the after edge of baseline elements with the after edge of the flex line. - child = m_orderIterator.first(); - for (size_t lineNumber = 0; lineNumber < lineContexts.size(); ++lineNumber) { - LayoutUnit minMarginAfterBaseline = minMarginAfterBaselines[lineNumber]; - for (size_t childNumber = 0; childNumber < lineContexts[lineNumber].numberOfChildren; ++childNumber, child = m_orderIterator.next()) { - ASSERT(child); - if (alignmentForChild(child) == ItemPositionBaseline && !hasAutoMarginsInCrossAxis(child) && minMarginAfterBaseline) - adjustAlignmentForChild(child, minMarginAfterBaseline); - } + // We may have already forced relayout for orthogonal flowing children in + // preferredMainAxisContentExtentForChild. + bool forceChildRelayout = + relayoutChildren && !childPreferredMainAxisContentExtentRequiresLayout( + child, hasInfiniteLineLength); + updateBlockChildDirtyBitsBeforeLayout(forceChildRelayout, child); + child->layoutIfNeeded(); + + updateAutoMarginsInMainAxis(child, autoMarginOffset); + + LayoutUnit childCrossAxisMarginBoxExtent; + if (alignmentForChild(child) == ItemPositionBaseline && + !hasAutoMarginsInCrossAxis(child)) { + LayoutUnit ascent = marginBoxAscentForChild(child); + LayoutUnit descent = (crossAxisMarginExtentForChild(child) + + crossAxisExtentForChild(child)) - + ascent; + + maxAscent = std::max(maxAscent, ascent); + maxDescent = std::max(maxDescent, descent); + + childCrossAxisMarginBoxExtent = maxAscent + maxDescent; + } else { + childCrossAxisMarginBoxExtent = crossAxisIntrinsicExtentForChild(child) + + crossAxisMarginExtentForChild(child); } -} - -void RenderFlexibleBox::applyStretchAlignmentToChild(RenderBox* child, LayoutUnit lineCrossAxisExtent) -{ - if (!isColumnFlow() && child->style()->logicalHeight().isAuto()) { - // FIXME: If the child has orthogonal flow, then it already has an override height set, so use it. - if (!hasOrthogonalFlow(child)) { - LayoutUnit heightBeforeStretching = needToStretchChildLogicalHeight(child) ? constrainedChildIntrinsicContentLogicalHeight(child) : child->logicalHeight(); - LayoutUnit stretchedLogicalHeight = heightBeforeStretching + availableAlignmentSpaceForChildBeforeStretching(lineCrossAxisExtent, child); - ASSERT(!child->needsLayout()); - LayoutUnit desiredLogicalHeight = child->constrainLogicalHeightByMinMax(stretchedLogicalHeight, heightBeforeStretching - child->borderAndPaddingLogicalHeight()); - - // FIXME: Can avoid laying out here in some cases. See https://webkit.org/b/87905. - if (desiredLogicalHeight != child->logicalHeight()) { - child->setOverrideLogicalContentHeight(desiredLogicalHeight - child->borderAndPaddingLogicalHeight()); - child->setLogicalHeight(0); - child->forceChildLayout(); - } + if (!isColumnFlow()) + setLogicalHeight( + std::max(logicalHeight(), crossAxisOffset + flowAwareBorderAfter() + + flowAwarePaddingAfter() + + childCrossAxisMarginBoxExtent)); + maxChildCrossAxisExtent = + std::max(maxChildCrossAxisExtent, childCrossAxisMarginBoxExtent); + + mainAxisOffset += flowAwareMarginStartForChild(child); + + LayoutUnit childMainExtent = mainAxisExtentForChild(child); + // In an RTL column situation, this will apply the margin-right/margin-end + // on the left. This will be fixed later in flipForRightToLeftColumn. + LayoutPoint childLocation( + shouldFlipMainAxis ? totalMainExtent - mainAxisOffset - childMainExtent + : mainAxisOffset, + crossAxisOffset + flowAwareMarginBeforeForChild(child)); + + // FIXME: Supporting layout deltas. + setFlowAwareLocationForChild(child, childLocation); + mainAxisOffset += childMainExtent + flowAwareMarginEndForChild(child); + + ++seenInFlowPositionedChildren; + if (seenInFlowPositionedChildren < numberOfChildrenForJustifyContent) + mainAxisOffset += justifyContentSpaceBetweenChildren( + availableFreeSpace, style()->justifyContent(), + numberOfChildrenForJustifyContent); + } + + if (isColumnFlow()) + setLogicalHeight(mainAxisOffset + flowAwareBorderEnd() + + flowAwarePaddingEnd()); + + if (style()->flexDirection() == FlowColumnReverse) { + // We have to do an extra pass for column-reverse to reposition the flex + // items since the start depends on the height of the flexbox, which we only + // know after we've positioned all the flex items. + updateLogicalHeight(); + layoutColumnReverse(children, crossAxisOffset, availableFreeSpace); + } + + if (m_numberOfInFlowChildrenOnFirstLine == -1) + m_numberOfInFlowChildrenOnFirstLine = seenInFlowPositionedChildren; + lineContexts.append(LineContext(crossAxisOffset, maxChildCrossAxisExtent, + children.size(), maxAscent)); + crossAxisOffset += maxChildCrossAxisExtent; +} + +void RenderFlexibleBox::layoutColumnReverse(const OrderedFlexItemList& children, + LayoutUnit crossAxisOffset, + LayoutUnit availableFreeSpace) { + // This is similar to the logic in layoutAndPlaceChildren, except we place the + // children starting from the end of the flexbox. We also don't need to layout + // anything since we're just moving the children to a new position. + size_t numberOfChildrenForJustifyContent = + numberOfInFlowPositionedChildren(children); + LayoutUnit mainAxisOffset = + logicalHeight() - flowAwareBorderEnd() - flowAwarePaddingEnd(); + mainAxisOffset -= + initialJustifyContentOffset(availableFreeSpace, style()->justifyContent(), + numberOfChildrenForJustifyContent); + + size_t seenInFlowPositionedChildren = 0; + for (size_t i = 0; i < children.size(); ++i) { + RenderBox* child = children[i]; + + if (child->isOutOfFlowPositioned()) + continue; + + mainAxisOffset -= + mainAxisExtentForChild(child) + flowAwareMarginEndForChild(child); + + setFlowAwareLocationForChild( + child, + LayoutPoint(mainAxisOffset, + crossAxisOffset + flowAwareMarginBeforeForChild(child))); + + mainAxisOffset -= flowAwareMarginStartForChild(child); + + ++seenInFlowPositionedChildren; + if (seenInFlowPositionedChildren < numberOfChildrenForJustifyContent) + mainAxisOffset -= justifyContentSpaceBetweenChildren( + availableFreeSpace, style()->justifyContent(), + numberOfChildrenForJustifyContent); + } +} + +static LayoutUnit initialAlignContentOffset(LayoutUnit availableFreeSpace, + EAlignContent alignContent, + unsigned numberOfLines) { + if (numberOfLines <= 1) + return 0; + if (alignContent == AlignContentFlexEnd) + return availableFreeSpace; + if (alignContent == AlignContentCenter) + return availableFreeSpace / 2; + if (alignContent == AlignContentSpaceAround) { + if (availableFreeSpace > 0 && numberOfLines) + return availableFreeSpace / (2 * numberOfLines); + if (availableFreeSpace < 0) + return availableFreeSpace / 2; + } + return 0; +} + +static LayoutUnit alignContentSpaceBetweenChildren( + LayoutUnit availableFreeSpace, + EAlignContent alignContent, + unsigned numberOfLines) { + if (availableFreeSpace > 0 && numberOfLines > 1) { + if (alignContent == AlignContentSpaceBetween) + return availableFreeSpace / (numberOfLines - 1); + if (alignContent == AlignContentSpaceAround || + alignContent == AlignContentStretch) + return availableFreeSpace / numberOfLines; + } + return 0; +} + +void RenderFlexibleBox::alignFlexLines(Vector& lineContexts) { + // If we have a single line flexbox or a multiline line flexbox with only one + // flex line, the line height is all the available space. For flex-direction: + // row, this means we need to use the height, so we do this after calling + // updateLogicalHeight. + if (lineContexts.size() == 1) { + lineContexts[0].crossAxisExtent = crossAxisContentExtent(); + return; + } + + if (style()->alignContent() == AlignContentFlexStart) + return; + + LayoutUnit availableCrossAxisSpace = crossAxisContentExtent(); + for (size_t i = 0; i < lineContexts.size(); ++i) + availableCrossAxisSpace -= lineContexts[i].crossAxisExtent; + + RenderBox* child = m_orderIterator.first(); + LayoutUnit lineOffset = initialAlignContentOffset( + availableCrossAxisSpace, style()->alignContent(), lineContexts.size()); + for (unsigned lineNumber = 0; lineNumber < lineContexts.size(); + ++lineNumber) { + lineContexts[lineNumber].crossAxisOffset += lineOffset; + for (size_t childNumber = 0; + childNumber < lineContexts[lineNumber].numberOfChildren; + ++childNumber, child = m_orderIterator.next()) + adjustAlignmentForChild(child, lineOffset); + + if (style()->alignContent() == AlignContentStretch && + availableCrossAxisSpace > 0) + lineContexts[lineNumber].crossAxisExtent += + availableCrossAxisSpace / static_cast(lineContexts.size()); + + lineOffset += alignContentSpaceBetweenChildren( + availableCrossAxisSpace, style()->alignContent(), lineContexts.size()); + } +} + +void RenderFlexibleBox::adjustAlignmentForChild(RenderBox* child, + LayoutUnit delta) { + if (child->isOutOfFlowPositioned()) { + return; + } + + setFlowAwareLocationForChild( + child, flowAwareLocationForChild(child) + LayoutSize(0, delta)); +} + +void RenderFlexibleBox::alignChildren(const Vector& lineContexts) { + // Keep track of the space between the baseline edge and the after edge of the + // box for each line. + Vector minMarginAfterBaselines; + + RenderBox* child = m_orderIterator.first(); + for (size_t lineNumber = 0; lineNumber < lineContexts.size(); ++lineNumber) { + LayoutUnit minMarginAfterBaseline = LayoutUnit::max(); + LayoutUnit lineCrossAxisExtent = lineContexts[lineNumber].crossAxisExtent; + LayoutUnit maxAscent = lineContexts[lineNumber].maxAscent; + + for (size_t childNumber = 0; + childNumber < lineContexts[lineNumber].numberOfChildren; + ++childNumber, child = m_orderIterator.next()) { + ASSERT(child); + if (child->isOutOfFlowPositioned()) { + if (style()->flexWrap() == FlexWrapReverse) + adjustAlignmentForChild(child, lineCrossAxisExtent); + continue; + } + + if (updateAutoMarginsInCrossAxis( + child, std::max(LayoutUnit(0), availableAlignmentSpaceForChild( + lineCrossAxisExtent, child)))) + continue; + + switch (alignmentForChild(child)) { + case ItemPositionAuto: + ASSERT_NOT_REACHED(); + break; + case ItemPositionStretch: { + applyStretchAlignmentToChild(child, lineCrossAxisExtent); + // Since wrap-reverse flips cross start and cross end, strech children + // should be aligned with the cross end. + if (style()->flexWrap() == FlexWrapReverse) + adjustAlignmentForChild(child, availableAlignmentSpaceForChild( + lineCrossAxisExtent, child)); + break; } - } else if (isColumnFlow() && child->style()->logicalWidth().isAuto()) { - // FIXME: If the child doesn't have orthogonal flow, then it already has an override width set, so use it. - if (hasOrthogonalFlow(child)) { - LayoutUnit childWidth = std::max(0, lineCrossAxisExtent - crossAxisMarginExtentForChild(child)); - childWidth = child->constrainLogicalWidthByMinMax(childWidth, childWidth, this); - - if (childWidth != child->logicalWidth()) { - child->setOverrideLogicalContentWidth(childWidth - child->borderAndPaddingLogicalWidth()); - child->forceChildLayout(); - } + case ItemPositionFlexStart: + break; + case ItemPositionFlexEnd: + adjustAlignmentForChild(child, availableAlignmentSpaceForChild( + lineCrossAxisExtent, child)); + break; + case ItemPositionCenter: + adjustAlignmentForChild( + child, + availableAlignmentSpaceForChild(lineCrossAxisExtent, child) / 2); + break; + case ItemPositionBaseline: { + // FIXME: If we get here in columns, we want the use the descent, + // except we currently can't get the ascent/descent of orthogonal + // children. https://bugs.webkit.org/show_bug.cgi?id=98076 + LayoutUnit ascent = marginBoxAscentForChild(child); + LayoutUnit startOffset = maxAscent - ascent; + adjustAlignmentForChild(child, startOffset); + + if (style()->flexWrap() == FlexWrapReverse) + minMarginAfterBaseline = std::min( + minMarginAfterBaseline, + availableAlignmentSpaceForChild(lineCrossAxisExtent, child) - + startOffset); + break; } + case ItemPositionLastBaseline: + case ItemPositionSelfStart: + case ItemPositionSelfEnd: + case ItemPositionStart: + case ItemPositionEnd: + case ItemPositionLeft: + case ItemPositionRight: + // FIXME: File a bug about implementing that. The extended grammar + // is not enabled by default so we shouldn't hit this codepath. + ASSERT_NOT_REACHED(); + break; + } } -} - -void RenderFlexibleBox::flipForRightToLeftColumn() -{ - if (style()->isLeftToRightDirection() || !isColumnFlow()) - return; - - LayoutUnit crossExtent = crossAxisExtent(); - for (RenderBox* child = m_orderIterator.first(); child; child = m_orderIterator.next()) { - if (child->isOutOfFlowPositioned()) - continue; - LayoutPoint location = flowAwareLocationForChild(child); - // For vertical flows, setFlowAwareLocationForChild will transpose x and y, - // so using the y axis for a column cross axis extent is correct. - location.setY(crossExtent - crossAxisExtentForChild(child) - location.y()); - setFlowAwareLocationForChild(child, location); + minMarginAfterBaselines.append(minMarginAfterBaseline); + } + + if (style()->flexWrap() != FlexWrapReverse) + return; + + // wrap-reverse flips the cross axis start and end. For baseline alignment, + // this means we need to align the after edge of baseline elements with the + // after edge of the flex line. + child = m_orderIterator.first(); + for (size_t lineNumber = 0; lineNumber < lineContexts.size(); ++lineNumber) { + LayoutUnit minMarginAfterBaseline = minMarginAfterBaselines[lineNumber]; + for (size_t childNumber = 0; + childNumber < lineContexts[lineNumber].numberOfChildren; + ++childNumber, child = m_orderIterator.next()) { + ASSERT(child); + if (alignmentForChild(child) == ItemPositionBaseline && + !hasAutoMarginsInCrossAxis(child) && minMarginAfterBaseline) + adjustAlignmentForChild(child, minMarginAfterBaseline); } -} - -void RenderFlexibleBox::flipForWrapReverse(const Vector& lineContexts, LayoutUnit crossAxisStartEdge) -{ - LayoutUnit contentExtent = crossAxisContentExtent(); - RenderBox* child = m_orderIterator.first(); - for (size_t lineNumber = 0; lineNumber < lineContexts.size(); ++lineNumber) { - for (size_t childNumber = 0; childNumber < lineContexts[lineNumber].numberOfChildren; ++childNumber, child = m_orderIterator.next()) { - ASSERT(child); - LayoutUnit lineCrossAxisExtent = lineContexts[lineNumber].crossAxisExtent; - LayoutUnit originalOffset = lineContexts[lineNumber].crossAxisOffset - crossAxisStartEdge; - LayoutUnit newOffset = contentExtent - originalOffset - lineCrossAxisExtent; - adjustAlignmentForChild(child, newOffset - originalOffset); - } + } +} + +void RenderFlexibleBox::applyStretchAlignmentToChild( + RenderBox* child, + LayoutUnit lineCrossAxisExtent) { + if (!isColumnFlow() && child->style()->logicalHeight().isAuto()) { + // FIXME: If the child has orthogonal flow, then it already has an override + // height set, so use it. + if (!hasOrthogonalFlow(child)) { + LayoutUnit heightBeforeStretching = + needToStretchChildLogicalHeight(child) + ? constrainedChildIntrinsicContentLogicalHeight(child) + : child->logicalHeight(); + LayoutUnit stretchedLogicalHeight = + heightBeforeStretching + + availableAlignmentSpaceForChildBeforeStretching(lineCrossAxisExtent, + child); + ASSERT(!child->needsLayout()); + LayoutUnit desiredLogicalHeight = child->constrainLogicalHeightByMinMax( + stretchedLogicalHeight, + heightBeforeStretching - child->borderAndPaddingLogicalHeight()); + + // FIXME: Can avoid laying out here in some cases. See + // https://webkit.org/b/87905. + if (desiredLogicalHeight != child->logicalHeight()) { + child->setOverrideLogicalContentHeight( + desiredLogicalHeight - child->borderAndPaddingLogicalHeight()); + child->setLogicalHeight(0); + child->forceChildLayout(); + } + } + } else if (isColumnFlow() && child->style()->logicalWidth().isAuto()) { + // FIXME: If the child doesn't have orthogonal flow, then it already has an + // override width set, so use it. + if (hasOrthogonalFlow(child)) { + LayoutUnit childWidth = std::max( + 0, lineCrossAxisExtent - crossAxisMarginExtentForChild(child)); + childWidth = + child->constrainLogicalWidthByMinMax(childWidth, childWidth, this); + + if (childWidth != child->logicalWidth()) { + child->setOverrideLogicalContentWidth( + childWidth - child->borderAndPaddingLogicalWidth()); + child->forceChildLayout(); + } } + } +} + +void RenderFlexibleBox::flipForRightToLeftColumn() { + if (style()->isLeftToRightDirection() || !isColumnFlow()) + return; + + LayoutUnit crossExtent = crossAxisExtent(); + for (RenderBox* child = m_orderIterator.first(); child; + child = m_orderIterator.next()) { + if (child->isOutOfFlowPositioned()) + continue; + LayoutPoint location = flowAwareLocationForChild(child); + // For vertical flows, setFlowAwareLocationForChild will transpose x and y, + // so using the y axis for a column cross axis extent is correct. + location.setY(crossExtent - crossAxisExtentForChild(child) - location.y()); + setFlowAwareLocationForChild(child, location); + } +} + +void RenderFlexibleBox::flipForWrapReverse( + const Vector& lineContexts, + LayoutUnit crossAxisStartEdge) { + LayoutUnit contentExtent = crossAxisContentExtent(); + RenderBox* child = m_orderIterator.first(); + for (size_t lineNumber = 0; lineNumber < lineContexts.size(); ++lineNumber) { + for (size_t childNumber = 0; + childNumber < lineContexts[lineNumber].numberOfChildren; + ++childNumber, child = m_orderIterator.next()) { + ASSERT(child); + LayoutUnit lineCrossAxisExtent = lineContexts[lineNumber].crossAxisExtent; + LayoutUnit originalOffset = + lineContexts[lineNumber].crossAxisOffset - crossAxisStartEdge; + LayoutUnit newOffset = + contentExtent - originalOffset - lineCrossAxisExtent; + adjustAlignmentForChild(child, newOffset - originalOffset); + } + } } -} +} // namespace blink diff --git a/sky/engine/core/rendering/RenderFlexibleBox.h b/sky/engine/core/rendering/RenderFlexibleBox.h index 33d6f00630137..fb4cc2a5e9c01 100644 --- a/sky/engine/core/rendering/RenderFlexibleBox.h +++ b/sky/engine/core/rendering/RenderFlexibleBox.h @@ -37,128 +37,181 @@ namespace blink { class RenderFlexibleBox : public RenderBlock { -public: - RenderFlexibleBox(); - virtual ~RenderFlexibleBox(); - - virtual const char* renderName() const override; - - virtual bool isFlexibleBox() const override final { return true; } - void layout(); - - virtual int baselinePosition(FontBaseline, bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const override; - virtual int firstLineBoxBaseline(FontBaselineOrAuto baselineType) const override; - virtual int inlineBlockBaseline(LineDirectionMode) const override; - - virtual void paintChildren(PaintInfo&, const LayoutPoint&, Vector& layers) override final; - - bool isHorizontalFlow() const; - -protected: - virtual void computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const override; - - virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle) override; - virtual void removeChild(RenderObject*) override; - -private: - enum FlexSign { - PositiveFlexibility, - NegativeFlexibility, - }; - - enum PositionedLayoutMode { - FlipForRowReverse, - NoFlipForRowReverse, - }; - - typedef HashMap InflexibleFlexItemSize; - typedef Vector OrderedFlexItemList; - - struct LineContext; - struct Violation; - - // Use an inline capacity of 8, since flexbox containers usually have less than 8 children. - typedef Vector ChildFrameRects; - - bool hasOrthogonalFlow(RenderBox* child) const; - bool isColumnFlow() const; - bool isLeftToRightFlow() const; - bool isMultiline() const; - Length flexBasisForChild(RenderBox* child) const; - LayoutUnit crossAxisExtentForChild(RenderBox* child) const; - LayoutUnit crossAxisIntrinsicExtentForChild(RenderBox* child) const; - LayoutUnit childIntrinsicHeight(RenderBox* child) const; - LayoutUnit childIntrinsicWidth(RenderBox* child) const; - LayoutUnit mainAxisExtentForChild(RenderBox* child) const; - LayoutUnit crossAxisExtent() const; - LayoutUnit mainAxisExtent() const; - LayoutUnit crossAxisContentExtent() const; - LayoutUnit mainAxisContentExtent(LayoutUnit contentLogicalHeight); - LayoutUnit computeMainAxisExtentForChild(RenderBox* child, SizeType, const Length& size); - LayoutUnit flowAwareBorderStart() const; - LayoutUnit flowAwareBorderEnd() const; - LayoutUnit flowAwareBorderBefore() const; - LayoutUnit flowAwareBorderAfter() const; - LayoutUnit flowAwarePaddingStart() const; - LayoutUnit flowAwarePaddingEnd() const; - LayoutUnit flowAwarePaddingBefore() const; - LayoutUnit flowAwarePaddingAfter() const; - LayoutUnit flowAwareMarginStartForChild(RenderBox* child) const; - LayoutUnit flowAwareMarginEndForChild(RenderBox* child) const; - LayoutUnit flowAwareMarginBeforeForChild(RenderBox* child) const; - LayoutUnit crossAxisMarginExtentForChild(RenderBox* child) const; - LayoutPoint flowAwareLocationForChild(RenderBox* child) const; - // FIXME: Supporting layout deltas. - void setFlowAwareLocationForChild(RenderBox* child, const LayoutPoint&); - void adjustAlignmentForChild(RenderBox* child, LayoutUnit); - ItemPosition alignmentForChild(RenderBox* child) const; - LayoutUnit mainAxisBorderAndPaddingExtentForChild(RenderBox* child) const; - LayoutUnit preferredMainAxisContentExtentForChild(RenderBox* child, bool hasInfiniteLineLength, bool relayoutChildren = false); - bool childPreferredMainAxisContentExtentRequiresLayout(RenderBox* child, bool hasInfiniteLineLength) const; - bool needToStretchChildLogicalHeight(RenderBox* child) const; - - void layoutFlexItems(bool relayoutChildren); - LayoutUnit autoMarginOffsetInMainAxis(const OrderedFlexItemList&, LayoutUnit& availableFreeSpace); - void updateAutoMarginsInMainAxis(RenderBox* child, LayoutUnit autoMarginOffset); - bool hasAutoMarginsInCrossAxis(RenderBox* child) const; - bool updateAutoMarginsInCrossAxis(RenderBox* child, LayoutUnit availableAlignmentSpace); - void repositionLogicalHeightDependentFlexItems(Vector&); - LayoutUnit clientLogicalBottomAfterRepositioning(); - void appendChildFrameRects(ChildFrameRects&); - - LayoutUnit availableAlignmentSpaceForChild(LayoutUnit lineCrossAxisExtent, RenderBox*); - LayoutUnit availableAlignmentSpaceForChildBeforeStretching(LayoutUnit lineCrossAxisExtent, RenderBox*); - LayoutUnit marginBoxAscentForChild(RenderBox*); - - LayoutUnit computeChildMarginValue(Length margin); - void prepareOrderIteratorAndMargins(); - LayoutUnit adjustChildSizeForMinAndMax(RenderBox*, LayoutUnit childSize); - // The hypothetical main size of an item is the flex base size clamped according to its min and max main size properties - bool computeNextFlexLine(OrderedFlexItemList& orderedChildren, LayoutUnit& sumFlexBaseSize, double& totalFlexGrow, double& totalWeightedFlexShrink, LayoutUnit& sumHypotheticalMainSize, bool& hasInfiniteLineLength, bool relayoutChildren); - - bool resolveFlexibleLengths(FlexSign, const OrderedFlexItemList&, LayoutUnit& availableFreeSpace, double& totalFlexGrow, double& totalWeightedFlexShrink, InflexibleFlexItemSize&, Vector& childSizes, bool hasInfiniteLineLength); - void freezeViolations(const Vector&, LayoutUnit& availableFreeSpace, double& totalFlexGrow, double& totalWeightedFlexShrink, InflexibleFlexItemSize&, bool hasInfiniteLineLength); - - void resetAutoMarginsAndLogicalTopInCrossAxis(RenderBox*); - void setLogicalOverrideSize(RenderBox* child, LayoutUnit childPreferredSize); - size_t numberOfInFlowPositionedChildren(const OrderedFlexItemList&) const; - void layoutAndPlaceChildren(LayoutUnit& crossAxisOffset, const OrderedFlexItemList&, const Vector& childSizes, LayoutUnit availableFreeSpace, bool relayoutChildren, Vector&, bool hasInfiniteLineLength); - void layoutColumnReverse(const OrderedFlexItemList&, LayoutUnit crossAxisOffset, LayoutUnit availableFreeSpace); - void alignFlexLines(Vector&); - void alignChildren(const Vector&); - void applyStretchAlignmentToChild(RenderBox*, LayoutUnit lineCrossAxisExtent); - void flipForRightToLeftColumn(); - void flipForWrapReverse(const Vector&, LayoutUnit crossAxisStartEdge); - - // This is used to cache the preferred size for orthogonal flow children so we don't have to relayout to get it - HashMap m_intrinsicSizeAlongMainAxis; - - mutable OrderIterator m_orderIterator; - int m_numberOfInFlowChildrenOnFirstLine; + public: + RenderFlexibleBox(); + virtual ~RenderFlexibleBox(); + + virtual const char* renderName() const override; + + virtual bool isFlexibleBox() const override final { return true; } + void layout(); + + virtual int baselinePosition( + FontBaseline, + bool firstLine, + LineDirectionMode, + LinePositionMode = PositionOnContainingLine) const override; + virtual int firstLineBoxBaseline( + FontBaselineOrAuto baselineType) const override; + virtual int inlineBlockBaseline(LineDirectionMode) const override; + + virtual void paintChildren(PaintInfo&, + const LayoutPoint&, + Vector& layers) override final; + + bool isHorizontalFlow() const; + + protected: + virtual void computeIntrinsicLogicalWidths( + LayoutUnit& minLogicalWidth, + LayoutUnit& maxLogicalWidth) const override; + + virtual void styleDidChange(StyleDifference, + const RenderStyle* oldStyle) override; + virtual void removeChild(RenderObject*) override; + + private: + enum FlexSign { + PositiveFlexibility, + NegativeFlexibility, + }; + + enum PositionedLayoutMode { + FlipForRowReverse, + NoFlipForRowReverse, + }; + + typedef HashMap InflexibleFlexItemSize; + typedef Vector OrderedFlexItemList; + + struct LineContext; + struct Violation; + + // Use an inline capacity of 8, since flexbox containers usually have less + // than 8 children. + typedef Vector ChildFrameRects; + + bool hasOrthogonalFlow(RenderBox* child) const; + bool isColumnFlow() const; + bool isLeftToRightFlow() const; + bool isMultiline() const; + Length flexBasisForChild(RenderBox* child) const; + LayoutUnit crossAxisExtentForChild(RenderBox* child) const; + LayoutUnit crossAxisIntrinsicExtentForChild(RenderBox* child) const; + LayoutUnit childIntrinsicHeight(RenderBox* child) const; + LayoutUnit childIntrinsicWidth(RenderBox* child) const; + LayoutUnit mainAxisExtentForChild(RenderBox* child) const; + LayoutUnit crossAxisExtent() const; + LayoutUnit mainAxisExtent() const; + LayoutUnit crossAxisContentExtent() const; + LayoutUnit mainAxisContentExtent(LayoutUnit contentLogicalHeight); + LayoutUnit computeMainAxisExtentForChild(RenderBox* child, + SizeType, + const Length& size); + LayoutUnit flowAwareBorderStart() const; + LayoutUnit flowAwareBorderEnd() const; + LayoutUnit flowAwareBorderBefore() const; + LayoutUnit flowAwareBorderAfter() const; + LayoutUnit flowAwarePaddingStart() const; + LayoutUnit flowAwarePaddingEnd() const; + LayoutUnit flowAwarePaddingBefore() const; + LayoutUnit flowAwarePaddingAfter() const; + LayoutUnit flowAwareMarginStartForChild(RenderBox* child) const; + LayoutUnit flowAwareMarginEndForChild(RenderBox* child) const; + LayoutUnit flowAwareMarginBeforeForChild(RenderBox* child) const; + LayoutUnit crossAxisMarginExtentForChild(RenderBox* child) const; + LayoutPoint flowAwareLocationForChild(RenderBox* child) const; + // FIXME: Supporting layout deltas. + void setFlowAwareLocationForChild(RenderBox* child, const LayoutPoint&); + void adjustAlignmentForChild(RenderBox* child, LayoutUnit); + ItemPosition alignmentForChild(RenderBox* child) const; + LayoutUnit mainAxisBorderAndPaddingExtentForChild(RenderBox* child) const; + LayoutUnit preferredMainAxisContentExtentForChild( + RenderBox* child, + bool hasInfiniteLineLength, + bool relayoutChildren = false); + bool childPreferredMainAxisContentExtentRequiresLayout( + RenderBox* child, + bool hasInfiniteLineLength) const; + bool needToStretchChildLogicalHeight(RenderBox* child) const; + + void layoutFlexItems(bool relayoutChildren); + LayoutUnit autoMarginOffsetInMainAxis(const OrderedFlexItemList&, + LayoutUnit& availableFreeSpace); + void updateAutoMarginsInMainAxis(RenderBox* child, + LayoutUnit autoMarginOffset); + bool hasAutoMarginsInCrossAxis(RenderBox* child) const; + bool updateAutoMarginsInCrossAxis(RenderBox* child, + LayoutUnit availableAlignmentSpace); + void repositionLogicalHeightDependentFlexItems(Vector&); + LayoutUnit clientLogicalBottomAfterRepositioning(); + void appendChildFrameRects(ChildFrameRects&); + + LayoutUnit availableAlignmentSpaceForChild(LayoutUnit lineCrossAxisExtent, + RenderBox*); + LayoutUnit availableAlignmentSpaceForChildBeforeStretching( + LayoutUnit lineCrossAxisExtent, + RenderBox*); + LayoutUnit marginBoxAscentForChild(RenderBox*); + + LayoutUnit computeChildMarginValue(Length margin); + void prepareOrderIteratorAndMargins(); + LayoutUnit adjustChildSizeForMinAndMax(RenderBox*, LayoutUnit childSize); + // The hypothetical main size of an item is the flex base size clamped + // according to its min and max main size properties + bool computeNextFlexLine(OrderedFlexItemList& orderedChildren, + LayoutUnit& sumFlexBaseSize, + double& totalFlexGrow, + double& totalWeightedFlexShrink, + LayoutUnit& sumHypotheticalMainSize, + bool& hasInfiniteLineLength, + bool relayoutChildren); + + bool resolveFlexibleLengths(FlexSign, + const OrderedFlexItemList&, + LayoutUnit& availableFreeSpace, + double& totalFlexGrow, + double& totalWeightedFlexShrink, + InflexibleFlexItemSize&, + Vector& childSizes, + bool hasInfiniteLineLength); + void freezeViolations(const Vector&, + LayoutUnit& availableFreeSpace, + double& totalFlexGrow, + double& totalWeightedFlexShrink, + InflexibleFlexItemSize&, + bool hasInfiniteLineLength); + + void resetAutoMarginsAndLogicalTopInCrossAxis(RenderBox*); + void setLogicalOverrideSize(RenderBox* child, LayoutUnit childPreferredSize); + size_t numberOfInFlowPositionedChildren(const OrderedFlexItemList&) const; + void layoutAndPlaceChildren(LayoutUnit& crossAxisOffset, + const OrderedFlexItemList&, + const Vector& childSizes, + LayoutUnit availableFreeSpace, + bool relayoutChildren, + Vector&, + bool hasInfiniteLineLength); + void layoutColumnReverse(const OrderedFlexItemList&, + LayoutUnit crossAxisOffset, + LayoutUnit availableFreeSpace); + void alignFlexLines(Vector&); + void alignChildren(const Vector&); + void applyStretchAlignmentToChild(RenderBox*, LayoutUnit lineCrossAxisExtent); + void flipForRightToLeftColumn(); + void flipForWrapReverse(const Vector&, + LayoutUnit crossAxisStartEdge); + + // This is used to cache the preferred size for orthogonal flow children so we + // don't have to relayout to get it + HashMap m_intrinsicSizeAlongMainAxis; + + mutable OrderIterator m_orderIterator; + int m_numberOfInFlowChildrenOnFirstLine; }; DEFINE_RENDER_OBJECT_TYPE_CASTS(RenderFlexibleBox, isFlexibleBox()); -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_RENDERFLEXIBLEBOX_H_ diff --git a/sky/engine/core/rendering/RenderGeometryMap.cpp b/sky/engine/core/rendering/RenderGeometryMap.cpp index 392640b2bdb19..2fb15e888e70f 100644 --- a/sky/engine/core/rendering/RenderGeometryMap.cpp +++ b/sky/engine/core/rendering/RenderGeometryMap.cpp @@ -33,276 +33,323 @@ namespace blink { RenderGeometryMap::RenderGeometryMap(MapCoordinatesFlags flags) - : m_insertionPosition(kNotFound) - , m_nonUniformStepsCount(0) - , m_transformedStepsCount(0) - , m_mapCoordinatesFlags(flags) -{ -} - -RenderGeometryMap::~RenderGeometryMap() -{ -} - -void RenderGeometryMap::mapToContainer(TransformState& transformState, const RenderBox* container) const -{ - // If the mapping includes something like columns, we have to go via renderers. - if (hasNonUniformStep()) { - m_mapping.last().m_renderer->mapLocalToContainer(container, transformState, ApplyContainerFlip | m_mapCoordinatesFlags); - transformState.flatten(); - return; - } + : m_insertionPosition(kNotFound), + m_nonUniformStepsCount(0), + m_transformedStepsCount(0), + m_mapCoordinatesFlags(flags) {} + +RenderGeometryMap::~RenderGeometryMap() {} + +void RenderGeometryMap::mapToContainer(TransformState& transformState, + const RenderBox* container) const { + // If the mapping includes something like columns, we have to go via + // renderers. + if (hasNonUniformStep()) { + m_mapping.last().m_renderer->mapLocalToContainer( + container, transformState, ApplyContainerFlip | m_mapCoordinatesFlags); + transformState.flatten(); + return; + } #if ENABLE(ASSERT) - bool foundContainer = !container || (m_mapping.size() && m_mapping[0].m_renderer == container); + bool foundContainer = + !container || (m_mapping.size() && m_mapping[0].m_renderer == container); #endif - for (int i = m_mapping.size() - 1; i >= 0; --i) { - const RenderGeometryMapStep& currentStep = m_mapping[i]; + for (int i = m_mapping.size() - 1; i >= 0; --i) { + const RenderGeometryMapStep& currentStep = m_mapping[i]; - // If container is the root RenderView (step 0) we want to apply its fixed position offset. - if (i > 0 && currentStep.m_renderer == container) { + // If container is the root RenderView (step 0) we want to apply its fixed + // position offset. + if (i > 0 && currentStep.m_renderer == container) { #if ENABLE(ASSERT) - foundContainer = true; + foundContainer = true; #endif - break; - } - - ASSERT(!i == isTopmostRenderView(currentStep.m_renderer)); - - if (!i) { - // A null container indicates mapping through the root RenderView, so including its transform (the page scale). - if (!container && currentStep.m_transform) - transformState.applyTransform(*currentStep.m_transform.get()); - } else { - TransformState::TransformAccumulation accumulate = currentStep.m_accumulatingTransform ? TransformState::AccumulateTransform : TransformState::FlattenTransform; - if (currentStep.m_transform) - transformState.applyTransform(*currentStep.m_transform.get(), accumulate); - else - transformState.move(currentStep.m_offset.width(), currentStep.m_offset.height(), accumulate); - } + break; } - ASSERT(foundContainer); - transformState.flatten(); + ASSERT(!i == isTopmostRenderView(currentStep.m_renderer)); + + if (!i) { + // A null container indicates mapping through the root RenderView, so + // including its transform (the page scale). + if (!container && currentStep.m_transform) + transformState.applyTransform(*currentStep.m_transform.get()); + } else { + TransformState::TransformAccumulation accumulate = + currentStep.m_accumulatingTransform + ? TransformState::AccumulateTransform + : TransformState::FlattenTransform; + if (currentStep.m_transform) + transformState.applyTransform(*currentStep.m_transform.get(), + accumulate); + else + transformState.move(currentStep.m_offset.width(), + currentStep.m_offset.height(), accumulate); + } + } + + ASSERT(foundContainer); + transformState.flatten(); } -FloatPoint RenderGeometryMap::mapToContainer(const FloatPoint& p, const RenderBox* container) const -{ - FloatPoint result; +FloatPoint RenderGeometryMap::mapToContainer(const FloatPoint& p, + const RenderBox* container) const { + FloatPoint result; - if (!hasTransformStep() && !hasNonUniformStep() && (!container || (m_mapping.size() && container == m_mapping[0].m_renderer))) - result = p + m_accumulatedOffset; - else { - TransformState transformState(TransformState::ApplyTransformDirection, p); - mapToContainer(transformState, container); - result = transformState.lastPlanarPoint(); - } + if (!hasTransformStep() && !hasNonUniformStep() && + (!container || + (m_mapping.size() && container == m_mapping[0].m_renderer))) + result = p + m_accumulatedOffset; + else { + TransformState transformState(TransformState::ApplyTransformDirection, p); + mapToContainer(transformState, container); + result = transformState.lastPlanarPoint(); + } #if ENABLE(ASSERT) - if (m_mapping.size() > 0) { - const RenderObject* lastRenderer = m_mapping.last().m_renderer; - const RenderLayer* layer = lastRenderer->enclosingLayer(); - - // Bounds for invisible layers are intentionally not calculated, and are - // therefore not necessarily expected to be correct here. This is ok, - // because they will be recomputed if the layer becomes visible. - if (!layer) { - FloatPoint rendererMappedResult = lastRenderer->localToContainerPoint(p, container, m_mapCoordinatesFlags); - - ASSERT(roundedIntPoint(rendererMappedResult) == roundedIntPoint(result)); - } + if (m_mapping.size() > 0) { + const RenderObject* lastRenderer = m_mapping.last().m_renderer; + const RenderLayer* layer = lastRenderer->enclosingLayer(); + + // Bounds for invisible layers are intentionally not calculated, and are + // therefore not necessarily expected to be correct here. This is ok, + // because they will be recomputed if the layer becomes visible. + if (!layer) { + FloatPoint rendererMappedResult = lastRenderer->localToContainerPoint( + p, container, m_mapCoordinatesFlags); + + ASSERT(roundedIntPoint(rendererMappedResult) == roundedIntPoint(result)); } + } #endif - return result; + return result; } #ifndef NDEBUG // Handy function to call from gdb while debugging mismatched point/rect errors. -void RenderGeometryMap::dumpSteps() const -{ - fprintf(stderr, "RenderGeometryMap::dumpSteps accumulatedOffset=%d,%d\n", m_accumulatedOffset.width().toInt(), m_accumulatedOffset.height().toInt()); - for (int i = m_mapping.size() - 1; i >= 0; --i) { - fprintf(stderr, " [%d] %s: offset=%d,%d", i, m_mapping[i].m_renderer->debugName().ascii().data(), m_mapping[i].m_offset.width().toInt(), m_mapping[i].m_offset.height().toInt()); - if (m_mapping[i].m_hasTransform) - fprintf(stderr, " hasTransform"); - fprintf(stderr, "\n"); - } +void RenderGeometryMap::dumpSteps() const { + fprintf(stderr, "RenderGeometryMap::dumpSteps accumulatedOffset=%d,%d\n", + m_accumulatedOffset.width().toInt(), + m_accumulatedOffset.height().toInt()); + for (int i = m_mapping.size() - 1; i >= 0; --i) { + fprintf(stderr, " [%d] %s: offset=%d,%d", i, + m_mapping[i].m_renderer->debugName().ascii().data(), + m_mapping[i].m_offset.width().toInt(), + m_mapping[i].m_offset.height().toInt()); + if (m_mapping[i].m_hasTransform) + fprintf(stderr, " hasTransform"); + fprintf(stderr, "\n"); + } } #endif -FloatQuad RenderGeometryMap::mapToContainer(const FloatRect& rect, const RenderBox* container) const -{ - FloatRect result; - - if (!hasTransformStep() && !hasNonUniformStep() && (!container || (m_mapping.size() && container == m_mapping[0].m_renderer))) { - result = rect; - result.move(m_accumulatedOffset); - } else { - TransformState transformState(TransformState::ApplyTransformDirection, rect.center(), rect); - mapToContainer(transformState, container); - result = transformState.lastPlanarQuad().boundingBox(); - } +FloatQuad RenderGeometryMap::mapToContainer(const FloatRect& rect, + const RenderBox* container) const { + FloatRect result; + + if (!hasTransformStep() && !hasNonUniformStep() && + (!container || + (m_mapping.size() && container == m_mapping[0].m_renderer))) { + result = rect; + result.move(m_accumulatedOffset); + } else { + TransformState transformState(TransformState::ApplyTransformDirection, + rect.center(), rect); + mapToContainer(transformState, container); + result = transformState.lastPlanarQuad().boundingBox(); + } #if ENABLE(ASSERT) - if (m_mapping.size() > 0) { - const RenderObject* lastRenderer = m_mapping.last().m_renderer; - - // Bounds for invisible layers are intentionally not calculated, and are - // therefore not necessarily expected to be correct here. This is ok, - // because they will be recomputed if the layer becomes visible. - FloatRect rendererMappedResult = lastRenderer->localToContainerQuad(rect, container, m_mapCoordinatesFlags).boundingBox(); - - // Inspector creates renderers with negative width . - // Taking FloatQuad bounds avoids spurious assertions because of that. - ASSERT(enclosingIntRect(rendererMappedResult) == enclosingIntRect(FloatQuad(result).boundingBox())); - } + if (m_mapping.size() > 0) { + const RenderObject* lastRenderer = m_mapping.last().m_renderer; + + // Bounds for invisible layers are intentionally not calculated, and are + // therefore not necessarily expected to be correct here. This is ok, + // because they will be recomputed if the layer becomes visible. + FloatRect rendererMappedResult = + lastRenderer + ->localToContainerQuad(rect, container, m_mapCoordinatesFlags) + .boundingBox(); + + // Inspector creates renderers with negative width + // . Taking FloatQuad bounds + // avoids spurious assertions because of that. + ASSERT(enclosingIntRect(rendererMappedResult) == + enclosingIntRect(FloatQuad(result).boundingBox())); + } #endif - return result; + return result; } -void RenderGeometryMap::pushMappingsToAncestor(const RenderObject* renderer, const RenderBox* ancestorRenderer) -{ - // We need to push mappings in reverse order here, so do insertions rather than appends. - TemporaryChange positionChange(m_insertionPosition, m_mapping.size()); - do { - renderer = renderer->pushMappingToContainer(ancestorRenderer, *this); - } while (renderer && renderer != ancestorRenderer); - - ASSERT(m_mapping.isEmpty() || isTopmostRenderView(m_mapping[0].m_renderer)); +void RenderGeometryMap::pushMappingsToAncestor( + const RenderObject* renderer, + const RenderBox* ancestorRenderer) { + // We need to push mappings in reverse order here, so do insertions rather + // than appends. + TemporaryChange positionChange(m_insertionPosition, m_mapping.size()); + do { + renderer = renderer->pushMappingToContainer(ancestorRenderer, *this); + } while (renderer && renderer != ancestorRenderer); + + ASSERT(m_mapping.isEmpty() || isTopmostRenderView(m_mapping[0].m_renderer)); } -static bool canMapBetweenRenderers(const RenderObject* renderer, const RenderObject* ancestor) -{ - for (const RenderObject* current = renderer; ; current = current->parent()) { - if (current->hasTransform()) - return false; +static bool canMapBetweenRenderers(const RenderObject* renderer, + const RenderObject* ancestor) { + for (const RenderObject* current = renderer;; current = current->parent()) { + if (current->hasTransform()) + return false; - if (current == ancestor) - break; - } + if (current == ancestor) + break; + } - return true; + return true; } -void RenderGeometryMap::pushMappingsToAncestor(const RenderLayer* layer, const RenderLayer* ancestorLayer) -{ - const RenderObject* renderer = layer->renderer(); - - bool crossDocument = false; - ASSERT(!crossDocument || m_mapCoordinatesFlags & TraverseDocumentBoundaries); - - // We have to visit all the renderers to detect flipped blocks. This might defeat the gains - // from mapping via layers. - bool canConvertInLayerTree = (ancestorLayer && !crossDocument) ? canMapBetweenRenderers(layer->renderer(), ancestorLayer->renderer()) : false; - -// fprintf(stderr, "RenderGeometryMap::pushMappingsToAncestor from layer %p to layer %p, canConvertInLayerTree=%d\n", layer, ancestorLayer, canConvertInLayerTree); - - if (canConvertInLayerTree) { - LayoutPoint layerOffset; - layer->convertToLayerCoords(ancestorLayer, layerOffset); - - // The RenderView must be pushed first. - if (!m_mapping.size()) { - ASSERT(ancestorLayer->renderer()->isRenderView()); - pushMappingsToAncestor(ancestorLayer->renderer(), 0); - } - - TemporaryChange positionChange(m_insertionPosition, m_mapping.size()); - bool accumulatingTransform = layer->renderer()->style()->preserves3D() || ancestorLayer->renderer()->style()->preserves3D(); - push(renderer, toLayoutSize(layerOffset), accumulatingTransform, /*isNonUniform*/ false, /*hasTransform*/ false); - return; +void RenderGeometryMap::pushMappingsToAncestor( + const RenderLayer* layer, + const RenderLayer* ancestorLayer) { + const RenderObject* renderer = layer->renderer(); + + bool crossDocument = false; + ASSERT(!crossDocument || m_mapCoordinatesFlags & TraverseDocumentBoundaries); + + // We have to visit all the renderers to detect flipped blocks. This might + // defeat the gains from mapping via layers. + bool canConvertInLayerTree = + (ancestorLayer && !crossDocument) + ? canMapBetweenRenderers(layer->renderer(), ancestorLayer->renderer()) + : false; + + // fprintf(stderr, "RenderGeometryMap::pushMappingsToAncestor from layer %p + // to layer %p, canConvertInLayerTree=%d\n", layer, ancestorLayer, + // canConvertInLayerTree); + + if (canConvertInLayerTree) { + LayoutPoint layerOffset; + layer->convertToLayerCoords(ancestorLayer, layerOffset); + + // The RenderView must be pushed first. + if (!m_mapping.size()) { + ASSERT(ancestorLayer->renderer()->isRenderView()); + pushMappingsToAncestor(ancestorLayer->renderer(), 0); } - const RenderBox* ancestorRenderer = ancestorLayer ? ancestorLayer->renderer() : 0; - pushMappingsToAncestor(renderer, ancestorRenderer); + + TemporaryChange positionChange(m_insertionPosition, + m_mapping.size()); + bool accumulatingTransform = + layer->renderer()->style()->preserves3D() || + ancestorLayer->renderer()->style()->preserves3D(); + push(renderer, toLayoutSize(layerOffset), accumulatingTransform, + /*isNonUniform*/ false, /*hasTransform*/ false); + return; + } + const RenderBox* ancestorRenderer = + ancestorLayer ? ancestorLayer->renderer() : 0; + pushMappingsToAncestor(renderer, ancestorRenderer); } -void RenderGeometryMap::push(const RenderObject* renderer, const LayoutSize& offsetFromContainer, bool accumulatingTransform, bool isNonUniform, bool hasTransform) -{ -// fprintf(stderr, "RenderGeometryMap::push %p %d,%d isNonUniform=%d\n", renderer, offsetFromContainer.width().toInt(), offsetFromContainer.height().toInt(), isNonUniform); +void RenderGeometryMap::push(const RenderObject* renderer, + const LayoutSize& offsetFromContainer, + bool accumulatingTransform, + bool isNonUniform, + bool hasTransform) { + // fprintf(stderr, "RenderGeometryMap::push %p %d,%d isNonUniform=%d\n", + // renderer, offsetFromContainer.width().toInt(), + // offsetFromContainer.height().toInt(), isNonUniform); - ASSERT(m_insertionPosition != kNotFound); - ASSERT(!renderer->isRenderView() || !m_insertionPosition || m_mapCoordinatesFlags & TraverseDocumentBoundaries); + ASSERT(m_insertionPosition != kNotFound); + ASSERT(!renderer->isRenderView() || !m_insertionPosition || + m_mapCoordinatesFlags & TraverseDocumentBoundaries); - m_mapping.insert(m_insertionPosition, RenderGeometryMapStep(renderer, accumulatingTransform, isNonUniform, hasTransform)); + m_mapping.insert(m_insertionPosition, + RenderGeometryMapStep(renderer, accumulatingTransform, + isNonUniform, hasTransform)); - RenderGeometryMapStep& step = m_mapping[m_insertionPosition]; - step.m_offset = offsetFromContainer; + RenderGeometryMapStep& step = m_mapping[m_insertionPosition]; + step.m_offset = offsetFromContainer; - stepInserted(step); + stepInserted(step); } -void RenderGeometryMap::push(const RenderObject* renderer, const TransformationMatrix& t, bool accumulatingTransform, bool isNonUniform, bool hasTransform) -{ - ASSERT(m_insertionPosition != kNotFound); - ASSERT(!renderer->isRenderView() || !m_insertionPosition || m_mapCoordinatesFlags & TraverseDocumentBoundaries); +void RenderGeometryMap::push(const RenderObject* renderer, + const TransformationMatrix& t, + bool accumulatingTransform, + bool isNonUniform, + bool hasTransform) { + ASSERT(m_insertionPosition != kNotFound); + ASSERT(!renderer->isRenderView() || !m_insertionPosition || + m_mapCoordinatesFlags & TraverseDocumentBoundaries); - m_mapping.insert(m_insertionPosition, RenderGeometryMapStep(renderer, accumulatingTransform, isNonUniform, hasTransform)); + m_mapping.insert(m_insertionPosition, + RenderGeometryMapStep(renderer, accumulatingTransform, + isNonUniform, hasTransform)); - RenderGeometryMapStep& step = m_mapping[m_insertionPosition]; + RenderGeometryMapStep& step = m_mapping[m_insertionPosition]; - if (!t.isIntegerTranslation()) - step.m_transform = adoptPtr(new TransformationMatrix(t)); - else - step.m_offset = LayoutSize(t.e(), t.f()); + if (!t.isIntegerTranslation()) + step.m_transform = adoptPtr(new TransformationMatrix(t)); + else + step.m_offset = LayoutSize(t.e(), t.f()); - stepInserted(step); + stepInserted(step); } -void RenderGeometryMap::popMappingsToAncestor(const RenderBox* ancestorRenderer) -{ - ASSERT(m_mapping.size()); +void RenderGeometryMap::popMappingsToAncestor( + const RenderBox* ancestorRenderer) { + ASSERT(m_mapping.size()); - while (m_mapping.size() && m_mapping.last().m_renderer != ancestorRenderer) { - stepRemoved(m_mapping.last()); - m_mapping.removeLast(); - } + while (m_mapping.size() && m_mapping.last().m_renderer != ancestorRenderer) { + stepRemoved(m_mapping.last()); + m_mapping.removeLast(); + } } -void RenderGeometryMap::popMappingsToAncestor(const RenderLayer* ancestorLayer) -{ - const RenderBox* ancestorRenderer = ancestorLayer ? ancestorLayer->renderer() : 0; - popMappingsToAncestor(ancestorRenderer); +void RenderGeometryMap::popMappingsToAncestor( + const RenderLayer* ancestorLayer) { + const RenderBox* ancestorRenderer = + ancestorLayer ? ancestorLayer->renderer() : 0; + popMappingsToAncestor(ancestorRenderer); } -void RenderGeometryMap::stepInserted(const RenderGeometryMapStep& step) -{ - m_accumulatedOffset += step.m_offset; +void RenderGeometryMap::stepInserted(const RenderGeometryMapStep& step) { + m_accumulatedOffset += step.m_offset; - if (step.m_isNonUniform) - ++m_nonUniformStepsCount; + if (step.m_isNonUniform) + ++m_nonUniformStepsCount; - if (step.m_transform) - ++m_transformedStepsCount; + if (step.m_transform) + ++m_transformedStepsCount; } -void RenderGeometryMap::stepRemoved(const RenderGeometryMapStep& step) -{ - m_accumulatedOffset -= step.m_offset; +void RenderGeometryMap::stepRemoved(const RenderGeometryMapStep& step) { + m_accumulatedOffset -= step.m_offset; - if (step.m_isNonUniform) { - ASSERT(m_nonUniformStepsCount); - --m_nonUniformStepsCount; - } + if (step.m_isNonUniform) { + ASSERT(m_nonUniformStepsCount); + --m_nonUniformStepsCount; + } - if (step.m_transform) { - ASSERT(m_transformedStepsCount); - --m_transformedStepsCount; - } + if (step.m_transform) { + ASSERT(m_transformedStepsCount); + --m_transformedStepsCount; + } } #if ENABLE(ASSERT) -bool RenderGeometryMap::isTopmostRenderView(const RenderObject* renderer) const -{ - if (!renderer->isRenderView()) - return false; - - // If we're not working with multiple RenderViews, then any view is considered - // "topmost" (to preserve original behavior). - return !(m_mapCoordinatesFlags & TraverseDocumentBoundaries); +bool RenderGeometryMap::isTopmostRenderView( + const RenderObject* renderer) const { + if (!renderer->isRenderView()) + return false; + + // If we're not working with multiple RenderViews, then any view is considered + // "topmost" (to preserve original behavior). + return !(m_mapCoordinatesFlags & TraverseDocumentBoundaries); } #endif -} // namespace blink +} // namespace blink diff --git a/sky/engine/core/rendering/RenderGeometryMap.h b/sky/engine/core/rendering/RenderGeometryMap.h index 7fc1b3eb21c3b..fb57bee1ea4c0 100644 --- a/sky/engine/core/rendering/RenderGeometryMap.h +++ b/sky/engine/core/rendering/RenderGeometryMap.h @@ -41,66 +41,82 @@ class RenderBox; class TransformationMatrix; class TransformState; -// Can be used while walking the Renderer tree to cache data about offsets and transforms. +// Can be used while walking the Renderer tree to cache data about offsets and +// transforms. class RenderGeometryMap { - WTF_MAKE_NONCOPYABLE(RenderGeometryMap); -public: - RenderGeometryMap(MapCoordinatesFlags = UseTransforms); - ~RenderGeometryMap(); - - MapCoordinatesFlags mapCoordinatesFlags() const { return m_mapCoordinatesFlags; } - - FloatRect absoluteRect(const FloatRect& rect) const - { - return mapToContainer(rect, 0).boundingBox(); - } - - // Map to a container. Will assert that the container has been pushed onto this map. - // A null container maps through the RenderView (including its scale transform, if any). - // If the container is the RenderView, the scroll offset is applied, but not the scale. - FloatPoint mapToContainer(const FloatPoint&, const RenderBox*) const; - FloatQuad mapToContainer(const FloatRect&, const RenderBox*) const; - - // Called by code walking the renderer or layer trees. - void pushMappingsToAncestor(const RenderLayer*, const RenderLayer* ancestorLayer); - void popMappingsToAncestor(const RenderLayer*); - void pushMappingsToAncestor(const RenderObject*, const RenderBox* ancestorRenderer); - void popMappingsToAncestor(const RenderBox*); - - // The following methods should only be called by renderers inside a call to pushMappingsToAncestor(). - - // Push geometry info between this renderer and some ancestor. The ancestor must be its container() or some - // stacking context between the renderer and its container. - void push(const RenderObject*, const LayoutSize&, bool accumulatingTransform = false, bool isNonUniform = false, bool hasTransform = false); - void push(const RenderObject*, const TransformationMatrix&, bool accumulatingTransform = false, bool isNonUniform = false, bool hasTransform = false); - -private: - void mapToContainer(TransformState&, const RenderBox* container = 0) const; - - void stepInserted(const RenderGeometryMapStep&); - void stepRemoved(const RenderGeometryMapStep&); - - bool hasNonUniformStep() const { return m_nonUniformStepsCount; } - bool hasTransformStep() const { return m_transformedStepsCount; } + WTF_MAKE_NONCOPYABLE(RenderGeometryMap); + + public: + RenderGeometryMap(MapCoordinatesFlags = UseTransforms); + ~RenderGeometryMap(); + + MapCoordinatesFlags mapCoordinatesFlags() const { + return m_mapCoordinatesFlags; + } + + FloatRect absoluteRect(const FloatRect& rect) const { + return mapToContainer(rect, 0).boundingBox(); + } + + // Map to a container. Will assert that the container has been pushed onto + // this map. A null container maps through the RenderView (including its scale + // transform, if any). If the container is the RenderView, the scroll offset + // is applied, but not the scale. + FloatPoint mapToContainer(const FloatPoint&, const RenderBox*) const; + FloatQuad mapToContainer(const FloatRect&, const RenderBox*) const; + + // Called by code walking the renderer or layer trees. + void pushMappingsToAncestor(const RenderLayer*, + const RenderLayer* ancestorLayer); + void popMappingsToAncestor(const RenderLayer*); + void pushMappingsToAncestor(const RenderObject*, + const RenderBox* ancestorRenderer); + void popMappingsToAncestor(const RenderBox*); + + // The following methods should only be called by renderers inside a call to + // pushMappingsToAncestor(). + + // Push geometry info between this renderer and some ancestor. The ancestor + // must be its container() or some stacking context between the renderer and + // its container. + void push(const RenderObject*, + const LayoutSize&, + bool accumulatingTransform = false, + bool isNonUniform = false, + bool hasTransform = false); + void push(const RenderObject*, + const TransformationMatrix&, + bool accumulatingTransform = false, + bool isNonUniform = false, + bool hasTransform = false); + + private: + void mapToContainer(TransformState&, const RenderBox* container = 0) const; + + void stepInserted(const RenderGeometryMapStep&); + void stepRemoved(const RenderGeometryMapStep&); + + bool hasNonUniformStep() const { return m_nonUniformStepsCount; } + bool hasTransformStep() const { return m_transformedStepsCount; } #ifndef NDEBUG - void dumpSteps() const; + void dumpSteps() const; #endif #if ENABLE(ASSERT) - bool isTopmostRenderView(const RenderObject* renderer) const; + bool isTopmostRenderView(const RenderObject* renderer) const; #endif - typedef Vector RenderGeometryMapSteps; + typedef Vector RenderGeometryMapSteps; - size_t m_insertionPosition; - int m_nonUniformStepsCount; - int m_transformedStepsCount; - RenderGeometryMapSteps m_mapping; - LayoutSize m_accumulatedOffset; - MapCoordinatesFlags m_mapCoordinatesFlags; + size_t m_insertionPosition; + int m_nonUniformStepsCount; + int m_transformedStepsCount; + RenderGeometryMapSteps m_mapping; + LayoutSize m_accumulatedOffset; + MapCoordinatesFlags m_mapCoordinatesFlags; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_RENDERGEOMETRYMAP_H_ diff --git a/sky/engine/core/rendering/RenderGeometryMapStep.h b/sky/engine/core/rendering/RenderGeometryMapStep.h index 2fae10655e931..995774c38f9c1 100644 --- a/sky/engine/core/rendering/RenderGeometryMapStep.h +++ b/sky/engine/core/rendering/RenderGeometryMapStep.h @@ -37,32 +37,34 @@ class RenderObject; // Stores data about how to map from one renderer to its container. struct RenderGeometryMapStep { - RenderGeometryMapStep(const RenderGeometryMapStep& o) - : m_renderer(o.m_renderer) - , m_offset(o.m_offset) - , m_accumulatingTransform(o.m_accumulatingTransform) - , m_isNonUniform(o.m_isNonUniform) - , m_hasTransform(o.m_hasTransform) - { - ASSERT(!o.m_transform); - } - RenderGeometryMapStep(const RenderObject* renderer, bool accumulatingTransform, bool isNonUniform, bool hasTransform) - : m_renderer(renderer) - , m_accumulatingTransform(accumulatingTransform) - , m_isNonUniform(isNonUniform) - , m_hasTransform(hasTransform) - { - } - const RenderObject* m_renderer; - LayoutSize m_offset; - OwnPtr m_transform; // Includes offset if non-null. - bool m_accumulatingTransform; - bool m_isNonUniform; // Mapping depends on the input point, e.g. because of CSS columns. - bool m_hasTransform; + RenderGeometryMapStep(const RenderGeometryMapStep& o) + : m_renderer(o.m_renderer), + m_offset(o.m_offset), + m_accumulatingTransform(o.m_accumulatingTransform), + m_isNonUniform(o.m_isNonUniform), + m_hasTransform(o.m_hasTransform) { + ASSERT(!o.m_transform); + } + RenderGeometryMapStep(const RenderObject* renderer, + bool accumulatingTransform, + bool isNonUniform, + bool hasTransform) + : m_renderer(renderer), + m_accumulatingTransform(accumulatingTransform), + m_isNonUniform(isNonUniform), + m_hasTransform(hasTransform) {} + const RenderObject* m_renderer; + LayoutSize m_offset; + OwnPtr m_transform; // Includes offset if non-null. + bool m_accumulatingTransform; + bool m_isNonUniform; // Mapping depends on the input point, e.g. because of + // CSS columns. + bool m_hasTransform; }; -} // namespace blink +} // namespace blink -WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(blink::RenderGeometryMapStep); +WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS( + blink::RenderGeometryMapStep); #endif // SKY_ENGINE_CORE_RENDERING_RENDERGEOMETRYMAPSTEP_H_ diff --git a/sky/engine/core/rendering/RenderInline.cpp b/sky/engine/core/rendering/RenderInline.cpp index d520be791505b..5000d49515e59 100644 --- a/sky/engine/core/rendering/RenderInline.cpp +++ b/sky/engine/core/rendering/RenderInline.cpp @@ -1,7 +1,8 @@ /* * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) - * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. + * All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -37,638 +38,719 @@ namespace blink { struct SameSizeAsRenderInline : public RenderBoxModelObject { - virtual ~SameSizeAsRenderInline() { } - RenderObjectChildList m_children; - RenderLineBoxList m_lineBoxes; + virtual ~SameSizeAsRenderInline() {} + RenderObjectChildList m_children; + RenderLineBoxList m_lineBoxes; }; -COMPILE_ASSERT(sizeof(RenderInline) == sizeof(SameSizeAsRenderInline), RenderInline_should_stay_small); - -RenderInline::RenderInline() -{ -} - -void RenderInline::willBeDestroyed() -{ - // Make sure to destroy anonymous children first while they are still connected to the rest of the tree, so that they will - // properly dirty line boxes that they are removed from. Effects that do :before/:after only on hover could crash otherwise. - children()->destroyLeftoverChildren(); - - if (!documentBeingDestroyed()) { - if (firstLineBox()) { - // If line boxes are contained inside a root, that means we're an inline. - // In that case, we need to remove all the line boxes so that the parent - // lines aren't pointing to deleted children. If the first line box does - // not have a parent that means they are either already disconnected or - // root lines that can just be destroyed without disconnecting. - if (firstLineBox()->parent()) { - for (InlineFlowBox* box = firstLineBox(); box; box = box->nextLineBox()) - box->remove(); - } - } else if (parent()) - parent()->dirtyLinesFromChangedChild(this); - } - - m_lineBoxes.deleteLineBoxes(); +COMPILE_ASSERT(sizeof(RenderInline) == sizeof(SameSizeAsRenderInline), + RenderInline_should_stay_small); - RenderBoxModelObject::willBeDestroyed(); -} +RenderInline::RenderInline() {} -void RenderInline::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) -{ - RenderBoxModelObject::styleDidChange(diff, oldStyle); - - if (!alwaysCreateLineBoxes()) { - RenderStyle* newStyle = style(); - bool alwaysCreateLineBoxesNew = hasBoxDecorationBackground() || newStyle->hasPadding() || newStyle->hasMargin() || newStyle->hasOutline(); - if (oldStyle && alwaysCreateLineBoxesNew) { - dirtyLineBoxes(false); - setNeedsLayout(); - } - setAlwaysCreateLineBoxes(alwaysCreateLineBoxesNew); - } -} - -void RenderInline::updateAlwaysCreateLineBoxes(bool fullLayout) -{ - // Once we have been tainted once, just assume it will happen again. This way effects like hover highlighting that change the - // background color will only cause a layout on the first rollover. - if (alwaysCreateLineBoxes()) - return; - - RenderStyle* parentStyle = parent()->style(); - RenderInline* parentRenderInline = parent()->isRenderInline() ? toRenderInline(parent()) : 0; - bool alwaysCreateLineBoxesNew = (parentRenderInline && parentRenderInline->alwaysCreateLineBoxes()) - || (parentRenderInline && parentStyle->verticalAlign() != BASELINE) - || style()->verticalAlign() != BASELINE - || style()->textEmphasisMark() != TextEmphasisMarkNone - || !parentStyle->font().fontMetrics().hasIdenticalAscentDescentAndLineGap(style()->font().fontMetrics()) - || parentStyle->lineHeight() != style()->lineHeight(); - - if (alwaysCreateLineBoxesNew) { - if (!fullLayout) - dirtyLineBoxes(false); - setAlwaysCreateLineBoxes(); - } -} - -LayoutRect RenderInline::localCaretRect(InlineBox* inlineBox, int, LayoutUnit* extraWidthToEndOfLine) -{ - if (firstChild()) { - // This condition is possible if the RenderInline is at an editing boundary, - // i.e. the VisiblePosition is: - // | - // FIXME: need to figure out how to make this return a valid rect, note that - // there are no line boxes created in the above case. - return LayoutRect(); - } - - ASSERT_UNUSED(inlineBox, !inlineBox); - - if (extraWidthToEndOfLine) - *extraWidthToEndOfLine = 0; - - LayoutRect caretRect = localCaretRectForEmptyElement(borderAndPaddingWidth(), 0); - - if (InlineBox* firstBox = firstLineBox()) - caretRect.moveBy(roundedLayoutPoint(firstBox->topLeft())); - - return caretRect; -} - -void RenderInline::addChild(RenderObject* newChild, RenderObject* beforeChild) -{ - RenderBoxModelObject::addChild(newChild, beforeChild); - newChild->setNeedsLayoutAndPrefWidthsRecalc(); -} - -void RenderInline::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, Vector& layers) -{ - m_lineBoxes.paint(this, paintInfo, paintOffset, layers); -} - -template -void RenderInline::generateLineBoxRects(GeneratorContext& yield) const -{ - if (!alwaysCreateLineBoxes()) - generateCulledLineBoxRects(yield, this); - else if (InlineFlowBox* curr = firstLineBox()) { - for (; curr; curr = curr->nextLineBox()) - yield(FloatRect(curr->topLeft(), curr->size())); - } else - yield(FloatRect()); -} +void RenderInline::willBeDestroyed() { + // Make sure to destroy anonymous children first while they are still + // connected to the rest of the tree, so that they will properly dirty line + // boxes that they are removed from. Effects that do :before/:after only on + // hover could crash otherwise. + children()->destroyLeftoverChildren(); -template -void RenderInline::generateCulledLineBoxRects(GeneratorContext& yield, const RenderInline* container) const -{ - if (!culledInlineFirstLineBox()) { - yield(FloatRect()); - return; + if (!documentBeingDestroyed()) { + if (firstLineBox()) { + // If line boxes are contained inside a root, that means we're an inline. + // In that case, we need to remove all the line boxes so that the parent + // lines aren't pointing to deleted children. If the first line box does + // not have a parent that means they are either already disconnected or + // root lines that can just be destroyed without disconnecting. + if (firstLineBox()->parent()) { + for (InlineFlowBox* box = firstLineBox(); box; box = box->nextLineBox()) + box->remove(); + } + } else if (parent()) + parent()->dirtyLinesFromChangedChild(this); + } + + m_lineBoxes.deleteLineBoxes(); + + RenderBoxModelObject::willBeDestroyed(); +} + +void RenderInline::styleDidChange(StyleDifference diff, + const RenderStyle* oldStyle) { + RenderBoxModelObject::styleDidChange(diff, oldStyle); + + if (!alwaysCreateLineBoxes()) { + RenderStyle* newStyle = style(); + bool alwaysCreateLineBoxesNew = + hasBoxDecorationBackground() || newStyle->hasPadding() || + newStyle->hasMargin() || newStyle->hasOutline(); + if (oldStyle && alwaysCreateLineBoxesNew) { + dirtyLineBoxes(false); + setNeedsLayout(); } - - for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) { - if (curr->isFloatingOrOutOfFlowPositioned()) - continue; - - // We want to get the margin box in the inline direction, and then use our font ascent/descent in the block - // direction (aligned to the root box's baseline). - if (curr->isBox()) { - RenderBox* currBox = toRenderBox(curr); - if (currBox->inlineBoxWrapper()) { - RootInlineBox& rootBox = currBox->inlineBoxWrapper()->root(); - int logicalTop = rootBox.logicalTop() + (rootBox.renderer().style(rootBox.isFirstLineStyle())->font().fontMetrics().ascent() - container->style(rootBox.isFirstLineStyle())->font().fontMetrics().ascent()); - int logicalHeight = container->style(rootBox.isFirstLineStyle())->font().fontMetrics().height(); - yield(FloatRect(currBox->inlineBoxWrapper()->x() - currBox->marginLeft(), logicalTop, (currBox->width() + currBox->marginWidth()).toFloat(), logicalHeight)); - } - } else if (curr->isRenderInline()) { - // If the child doesn't need line boxes either, then we can recur. - RenderInline* currInline = toRenderInline(curr); - if (!currInline->alwaysCreateLineBoxes()) - currInline->generateCulledLineBoxRects(yield, container); - else { - for (InlineFlowBox* childLine = currInline->firstLineBox(); childLine; childLine = childLine->nextLineBox()) { - RootInlineBox& rootBox = childLine->root(); - int logicalTop = rootBox.logicalTop() + (rootBox.renderer().style(rootBox.isFirstLineStyle())->font().fontMetrics().ascent() - container->style(rootBox.isFirstLineStyle())->font().fontMetrics().ascent()); - int logicalHeight = container->style(rootBox.isFirstLineStyle())->font().fontMetrics().height(); - yield(FloatRect(childLine->x() - childLine->marginLogicalLeft(), - logicalTop, - childLine->logicalWidth() + childLine->marginLogicalLeft() + childLine->marginLogicalRight(), - logicalHeight)); - } - } - } else if (curr->isText()) { - RenderText* currText = toRenderText(curr); - for (InlineTextBox* childText = currText->firstTextBox(); childText; childText = childText->nextTextBox()) { - RootInlineBox& rootBox = childText->root(); - int logicalTop = rootBox.logicalTop() + (rootBox.renderer().style(rootBox.isFirstLineStyle())->font().fontMetrics().ascent() - container->style(rootBox.isFirstLineStyle())->font().fontMetrics().ascent()); - int logicalHeight = container->style(rootBox.isFirstLineStyle())->font().fontMetrics().height(); - yield(FloatRect(childText->x(), logicalTop, childText->logicalWidth(), logicalHeight)); - } + setAlwaysCreateLineBoxes(alwaysCreateLineBoxesNew); + } +} + +void RenderInline::updateAlwaysCreateLineBoxes(bool fullLayout) { + // Once we have been tainted once, just assume it will happen again. This way + // effects like hover highlighting that change the background color will only + // cause a layout on the first rollover. + if (alwaysCreateLineBoxes()) + return; + + RenderStyle* parentStyle = parent()->style(); + RenderInline* parentRenderInline = + parent()->isRenderInline() ? toRenderInline(parent()) : 0; + bool alwaysCreateLineBoxesNew = + (parentRenderInline && parentRenderInline->alwaysCreateLineBoxes()) || + (parentRenderInline && parentStyle->verticalAlign() != BASELINE) || + style()->verticalAlign() != BASELINE || + style()->textEmphasisMark() != TextEmphasisMarkNone || + !parentStyle->font().fontMetrics().hasIdenticalAscentDescentAndLineGap( + style()->font().fontMetrics()) || + parentStyle->lineHeight() != style()->lineHeight(); + + if (alwaysCreateLineBoxesNew) { + if (!fullLayout) + dirtyLineBoxes(false); + setAlwaysCreateLineBoxes(); + } +} + +LayoutRect RenderInline::localCaretRect(InlineBox* inlineBox, + int, + LayoutUnit* extraWidthToEndOfLine) { + if (firstChild()) { + // This condition is possible if the RenderInline is at an editing boundary, + // i.e. the VisiblePosition is: + // | + // + // FIXME: need to figure out how to make this return a valid rect, note that + // there are no line boxes created in the above case. + return LayoutRect(); + } + + ASSERT_UNUSED(inlineBox, !inlineBox); + + if (extraWidthToEndOfLine) + *extraWidthToEndOfLine = 0; + + LayoutRect caretRect = + localCaretRectForEmptyElement(borderAndPaddingWidth(), 0); + + if (InlineBox* firstBox = firstLineBox()) + caretRect.moveBy(roundedLayoutPoint(firstBox->topLeft())); + + return caretRect; +} + +void RenderInline::addChild(RenderObject* newChild, RenderObject* beforeChild) { + RenderBoxModelObject::addChild(newChild, beforeChild); + newChild->setNeedsLayoutAndPrefWidthsRecalc(); +} + +void RenderInline::paint(PaintInfo& paintInfo, + const LayoutPoint& paintOffset, + Vector& layers) { + m_lineBoxes.paint(this, paintInfo, paintOffset, layers); +} + +template +void RenderInline::generateLineBoxRects(GeneratorContext& yield) const { + if (!alwaysCreateLineBoxes()) + generateCulledLineBoxRects(yield, this); + else if (InlineFlowBox* curr = firstLineBox()) { + for (; curr; curr = curr->nextLineBox()) + yield(FloatRect(curr->topLeft(), curr->size())); + } else + yield(FloatRect()); +} + +template +void RenderInline::generateCulledLineBoxRects( + GeneratorContext& yield, + const RenderInline* container) const { + if (!culledInlineFirstLineBox()) { + yield(FloatRect()); + return; + } + + for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) { + if (curr->isFloatingOrOutOfFlowPositioned()) + continue; + + // We want to get the margin box in the inline direction, and then use our + // font ascent/descent in the block direction (aligned to the root box's + // baseline). + if (curr->isBox()) { + RenderBox* currBox = toRenderBox(curr); + if (currBox->inlineBoxWrapper()) { + RootInlineBox& rootBox = currBox->inlineBoxWrapper()->root(); + int logicalTop = + rootBox.logicalTop() + (rootBox.renderer() + .style(rootBox.isFirstLineStyle()) + ->font() + .fontMetrics() + .ascent() - + container->style(rootBox.isFirstLineStyle()) + ->font() + .fontMetrics() + .ascent()); + int logicalHeight = container->style(rootBox.isFirstLineStyle()) + ->font() + .fontMetrics() + .height(); + yield(FloatRect( + currBox->inlineBoxWrapper()->x() - currBox->marginLeft(), + logicalTop, (currBox->width() + currBox->marginWidth()).toFloat(), + logicalHeight)); + } + } else if (curr->isRenderInline()) { + // If the child doesn't need line boxes either, then we can recur. + RenderInline* currInline = toRenderInline(curr); + if (!currInline->alwaysCreateLineBoxes()) + currInline->generateCulledLineBoxRects(yield, container); + else { + for (InlineFlowBox* childLine = currInline->firstLineBox(); childLine; + childLine = childLine->nextLineBox()) { + RootInlineBox& rootBox = childLine->root(); + int logicalTop = rootBox.logicalTop() + + (rootBox.renderer() + .style(rootBox.isFirstLineStyle()) + ->font() + .fontMetrics() + .ascent() - + container->style(rootBox.isFirstLineStyle()) + ->font() + .fontMetrics() + .ascent()); + int logicalHeight = container->style(rootBox.isFirstLineStyle()) + ->font() + .fontMetrics() + .height(); + yield(FloatRect( + childLine->x() - childLine->marginLogicalLeft(), logicalTop, + childLine->logicalWidth() + childLine->marginLogicalLeft() + + childLine->marginLogicalRight(), + logicalHeight)); } + } + } else if (curr->isText()) { + RenderText* currText = toRenderText(curr); + for (InlineTextBox* childText = currText->firstTextBox(); childText; + childText = childText->nextTextBox()) { + RootInlineBox& rootBox = childText->root(); + int logicalTop = + rootBox.logicalTop() + (rootBox.renderer() + .style(rootBox.isFirstLineStyle()) + ->font() + .fontMetrics() + .ascent() - + container->style(rootBox.isFirstLineStyle()) + ->font() + .fontMetrics() + .ascent()); + int logicalHeight = container->style(rootBox.isFirstLineStyle()) + ->font() + .fontMetrics() + .height(); + yield(FloatRect(childText->x(), logicalTop, childText->logicalWidth(), + logicalHeight)); + } } + } } namespace { class AbsoluteRectsGeneratorContext { -public: - AbsoluteRectsGeneratorContext(Vector& rects, const LayoutPoint& accumulatedOffset) - : m_rects(rects) - , m_accumulatedOffset(accumulatedOffset) { } - - void operator()(const FloatRect& rect) - { - IntRect intRect = enclosingIntRect(rect); - intRect.move(m_accumulatedOffset.x(), m_accumulatedOffset.y()); - m_rects.append(intRect); - } -private: - Vector& m_rects; - const LayoutPoint& m_accumulatedOffset; + public: + AbsoluteRectsGeneratorContext(Vector& rects, + const LayoutPoint& accumulatedOffset) + : m_rects(rects), m_accumulatedOffset(accumulatedOffset) {} + + void operator()(const FloatRect& rect) { + IntRect intRect = enclosingIntRect(rect); + intRect.move(m_accumulatedOffset.x(), m_accumulatedOffset.y()); + m_rects.append(intRect); + } + + private: + Vector& m_rects; + const LayoutPoint& m_accumulatedOffset; }; class AbsoluteQuadsGeneratorContext { -public: - AbsoluteQuadsGeneratorContext(const RenderInline* renderer, Vector& quads) - : m_quads(quads) - , m_geometryMap() - { - m_geometryMap.pushMappingsToAncestor(renderer, 0); - } - - void operator()(const FloatRect& rect) - { - m_quads.append(m_geometryMap.absoluteRect(rect)); - } -private: - Vector& m_quads; - RenderGeometryMap m_geometryMap; + public: + AbsoluteQuadsGeneratorContext(const RenderInline* renderer, + Vector& quads) + : m_quads(quads), m_geometryMap() { + m_geometryMap.pushMappingsToAncestor(renderer, 0); + } + + void operator()(const FloatRect& rect) { + m_quads.append(m_geometryMap.absoluteRect(rect)); + } + + private: + Vector& m_quads; + RenderGeometryMap m_geometryMap; }; -} // unnamed namespace +} // unnamed namespace -void RenderInline::absoluteQuads(Vector& quads) const -{ - AbsoluteQuadsGeneratorContext context(this, quads); - generateLineBoxRects(context); +void RenderInline::absoluteQuads(Vector& quads) const { + AbsoluteQuadsGeneratorContext context(this, quads); + generateLineBoxRects(context); } -LayoutUnit RenderInline::offsetLeft() const -{ - LayoutPoint topLeft; - if (InlineBox* firstBox = firstLineBoxIncludingCulling()) - topLeft = flooredLayoutPoint(firstBox->topLeft()); - return adjustedPositionRelativeToOffsetParent(topLeft).x(); +LayoutUnit RenderInline::offsetLeft() const { + LayoutPoint topLeft; + if (InlineBox* firstBox = firstLineBoxIncludingCulling()) + topLeft = flooredLayoutPoint(firstBox->topLeft()); + return adjustedPositionRelativeToOffsetParent(topLeft).x(); } -LayoutUnit RenderInline::offsetTop() const -{ - LayoutPoint topLeft; - if (InlineBox* firstBox = firstLineBoxIncludingCulling()) - topLeft = flooredLayoutPoint(firstBox->topLeft()); - return adjustedPositionRelativeToOffsetParent(topLeft).y(); +LayoutUnit RenderInline::offsetTop() const { + LayoutPoint topLeft; + if (InlineBox* firstBox = firstLineBoxIncludingCulling()) + topLeft = flooredLayoutPoint(firstBox->topLeft()); + return adjustedPositionRelativeToOffsetParent(topLeft).y(); } -static LayoutUnit computeMargin(const RenderInline* renderer, const Length& margin) -{ - if (margin.isAuto()) - return 0; - if (margin.isFixed()) - return margin.value(); - if (margin.isPercent()) - return minimumValueForLength(margin, std::max(0, renderer->containingBlock()->availableLogicalWidth())); +static LayoutUnit computeMargin(const RenderInline* renderer, + const Length& margin) { + if (margin.isAuto()) return 0; + if (margin.isFixed()) + return margin.value(); + if (margin.isPercent()) + return minimumValueForLength( + margin, std::max( + 0, renderer->containingBlock()->availableLogicalWidth())); + return 0; } -LayoutUnit RenderInline::marginLeft() const -{ - return computeMargin(this, style()->marginLeft()); +LayoutUnit RenderInline::marginLeft() const { + return computeMargin(this, style()->marginLeft()); } -LayoutUnit RenderInline::marginRight() const -{ - return computeMargin(this, style()->marginRight()); +LayoutUnit RenderInline::marginRight() const { + return computeMargin(this, style()->marginRight()); } -LayoutUnit RenderInline::marginTop() const -{ - return computeMargin(this, style()->marginTop()); +LayoutUnit RenderInline::marginTop() const { + return computeMargin(this, style()->marginTop()); } -LayoutUnit RenderInline::marginBottom() const -{ - return computeMargin(this, style()->marginBottom()); +LayoutUnit RenderInline::marginBottom() const { + return computeMargin(this, style()->marginBottom()); } -LayoutUnit RenderInline::marginStart(const RenderStyle* otherStyle) const -{ - return computeMargin(this, style()->marginStartUsing(otherStyle ? otherStyle : style())); +LayoutUnit RenderInline::marginStart(const RenderStyle* otherStyle) const { + return computeMargin( + this, style()->marginStartUsing(otherStyle ? otherStyle : style())); } -LayoutUnit RenderInline::marginEnd(const RenderStyle* otherStyle) const -{ - return computeMargin(this, style()->marginEndUsing(otherStyle ? otherStyle : style())); +LayoutUnit RenderInline::marginEnd(const RenderStyle* otherStyle) const { + return computeMargin( + this, style()->marginEndUsing(otherStyle ? otherStyle : style())); } -LayoutUnit RenderInline::marginBefore(const RenderStyle* otherStyle) const -{ - return computeMargin(this, style()->marginBeforeUsing(otherStyle ? otherStyle : style())); +LayoutUnit RenderInline::marginBefore(const RenderStyle* otherStyle) const { + return computeMargin( + this, style()->marginBeforeUsing(otherStyle ? otherStyle : style())); } -LayoutUnit RenderInline::marginAfter(const RenderStyle* otherStyle) const -{ - return computeMargin(this, style()->marginAfterUsing(otherStyle ? otherStyle : style())); +LayoutUnit RenderInline::marginAfter(const RenderStyle* otherStyle) const { + return computeMargin( + this, style()->marginAfterUsing(otherStyle ? otherStyle : style())); } -const char* RenderInline::renderName() const -{ - return "RenderInline"; +const char* RenderInline::renderName() const { + return "RenderInline"; } -bool RenderInline::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, - const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset) -{ - return m_lineBoxes.hitTest(this, request, result, locationInContainer, accumulatedOffset); +bool RenderInline::nodeAtPoint(const HitTestRequest& request, + HitTestResult& result, + const HitTestLocation& locationInContainer, + const LayoutPoint& accumulatedOffset) { + return m_lineBoxes.hitTest(this, request, result, locationInContainer, + accumulatedOffset); } namespace { class HitTestCulledInlinesGeneratorContext { -public: - HitTestCulledInlinesGeneratorContext(Region& region, const HitTestLocation& location) : m_intersected(false), m_region(region), m_location(location) { } - void operator()(const FloatRect& rect) - { - m_intersected = m_intersected || m_location.intersects(rect); - m_region.unite(enclosingIntRect(rect)); - } - bool intersected() const { return m_intersected; } -private: - bool m_intersected; - Region& m_region; - const HitTestLocation& m_location; + public: + HitTestCulledInlinesGeneratorContext(Region& region, + const HitTestLocation& location) + : m_intersected(false), m_region(region), m_location(location) {} + void operator()(const FloatRect& rect) { + m_intersected = m_intersected || m_location.intersects(rect); + m_region.unite(enclosingIntRect(rect)); + } + bool intersected() const { return m_intersected; } + + private: + bool m_intersected; + Region& m_region; + const HitTestLocation& m_location; }; -} // unnamed namespace +} // unnamed namespace -bool RenderInline::hitTestCulledInline(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset) -{ - ASSERT(result.isRectBasedTest() && !alwaysCreateLineBoxes()); - if (!visibleToHitTestRequest(request)) - return false; +bool RenderInline::hitTestCulledInline( + const HitTestRequest& request, + HitTestResult& result, + const HitTestLocation& locationInContainer, + const LayoutPoint& accumulatedOffset) { + ASSERT(result.isRectBasedTest() && !alwaysCreateLineBoxes()); + if (!visibleToHitTestRequest(request)) + return false; - HitTestLocation tmpLocation(locationInContainer, -toLayoutSize(accumulatedOffset)); + HitTestLocation tmpLocation(locationInContainer, + -toLayoutSize(accumulatedOffset)); - Region regionResult; - HitTestCulledInlinesGeneratorContext context(regionResult, tmpLocation); - generateCulledLineBoxRects(context, this); + Region regionResult; + HitTestCulledInlinesGeneratorContext context(regionResult, tmpLocation); + generateCulledLineBoxRects(context, this); - if (context.intersected()) { - updateHitTestResult(result, tmpLocation.point()); - return regionResult.contains(tmpLocation.boundingBox()); - } - return false; + if (context.intersected()) { + updateHitTestResult(result, tmpLocation.point()); + return regionResult.contains(tmpLocation.boundingBox()); + } + return false; } -PositionWithAffinity RenderInline::positionForPoint(const LayoutPoint& point) -{ - // FIXME(sky): Now that we don't have continuations, can this whole function just be the following? - // return containingBlock()->positionForPoint(point); +PositionWithAffinity RenderInline::positionForPoint(const LayoutPoint& point) { + // FIXME(sky): Now that we don't have continuations, can this whole function + // just be the following? return containingBlock()->positionForPoint(point); - // FIXME: Does not deal with relative positioned inlines (should it?) - RenderBlock* cb = containingBlock(); - if (firstLineBox()) { - // This inline actually has a line box. We must have clicked in the border/padding of one of these boxes. We - // should try to find a result by asking our containing block. - return cb->positionForPoint(point); - } + // FIXME: Does not deal with relative positioned inlines (should it?) + RenderBlock* cb = containingBlock(); + if (firstLineBox()) { + // This inline actually has a line box. We must have clicked in the + // border/padding of one of these boxes. We should try to find a result by + // asking our containing block. + return cb->positionForPoint(point); + } - // Translate the coords from the pre-anonymous block to the post-anonymous block. - return RenderBoxModelObject::positionForPoint(point); + // Translate the coords from the pre-anonymous block to the post-anonymous + // block. + return RenderBoxModelObject::positionForPoint(point); } namespace { class LinesBoundingBoxGeneratorContext { -public: - LinesBoundingBoxGeneratorContext(FloatRect& rect) : m_rect(rect) { } - void operator()(const FloatRect& rect) - { - m_rect.uniteIfNonZero(rect); - } -private: - FloatRect& m_rect; -}; - -} // unnamed namespace + public: + LinesBoundingBoxGeneratorContext(FloatRect& rect) : m_rect(rect) {} + void operator()(const FloatRect& rect) { m_rect.uniteIfNonZero(rect); } -IntRect RenderInline::linesBoundingBox() const -{ - if (!alwaysCreateLineBoxes()) { - ASSERT(!firstLineBox()); - FloatRect floatResult; - LinesBoundingBoxGeneratorContext context(floatResult); - generateCulledLineBoxRects(context, this); - return enclosingIntRect(floatResult); - } - - IntRect result; - - // See , for an unknown reason the linked list here is sometimes inconsistent, first is non-zero and last is zero. We have been - // unable to reproduce this at all (and consequently unable to figure ot why this is happening). The assert will hopefully catch the problem in debug - // builds and help us someday figure out why. We also put in a redundant check of lastLineBox() to avoid the crash for now. - ASSERT(!firstLineBox() == !lastLineBox()); // Either both are null or both exist. - if (firstLineBox() && lastLineBox()) { - // Return the width of the minimal left side and the maximal right side. - float logicalLeftSide = 0; - float logicalRightSide = 0; - for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) { - if (curr == firstLineBox() || curr->logicalLeft() < logicalLeftSide) - logicalLeftSide = curr->logicalLeft(); - if (curr == firstLineBox() || curr->logicalRight() > logicalRightSide) - logicalRightSide = curr->logicalRight(); - } - - float x = logicalLeftSide; - float y = firstLineBox()->y(); - float width = logicalRightSide - logicalLeftSide; - float height = lastLineBox()->logicalBottom() - y; - result = enclosingIntRect(FloatRect(x, y, width, height)); - } - - return result; -} - -InlineBox* RenderInline::culledInlineFirstLineBox() const -{ - for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) { - if (curr->isFloatingOrOutOfFlowPositioned()) - continue; - - // We want to get the margin box in the inline direction, and then use our font ascent/descent in the block - // direction (aligned to the root box's baseline). - if (curr->isBox()) - return toRenderBox(curr)->inlineBoxWrapper(); - if (curr->isRenderInline()) { - RenderInline* currInline = toRenderInline(curr); - InlineBox* result = currInline->firstLineBoxIncludingCulling(); - if (result) - return result; - } else if (curr->isText()) { - RenderText* currText = toRenderText(curr); - if (currText->firstTextBox()) - return currText->firstTextBox(); - } - } - return 0; -} - -InlineBox* RenderInline::culledInlineLastLineBox() const -{ - for (RenderObject* curr = lastChild(); curr; curr = curr->previousSibling()) { - if (curr->isFloatingOrOutOfFlowPositioned()) - continue; + private: + FloatRect& m_rect; +}; - // We want to get the margin box in the inline direction, and then use our font ascent/descent in the block - // direction (aligned to the root box's baseline). - if (curr->isBox()) - return toRenderBox(curr)->inlineBoxWrapper(); - if (curr->isRenderInline()) { - RenderInline* currInline = toRenderInline(curr); - InlineBox* result = currInline->lastLineBoxIncludingCulling(); - if (result) - return result; - } else if (curr->isText()) { - RenderText* currText = toRenderText(curr); - if (currText->lastTextBox()) - return currText->lastTextBox(); - } - } - return 0; -} +} // unnamed namespace -LayoutRect RenderInline::culledInlineVisualOverflowBoundingBox() const -{ +IntRect RenderInline::linesBoundingBox() const { + if (!alwaysCreateLineBoxes()) { + ASSERT(!firstLineBox()); FloatRect floatResult; LinesBoundingBoxGeneratorContext context(floatResult); generateCulledLineBoxRects(context, this); - LayoutRect result(enclosingLayoutRect(floatResult)); - for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) { - if (curr->isFloatingOrOutOfFlowPositioned()) - continue; - - // For overflow we just have to propagate by hand and recompute it all. - if (curr->isBox()) { - RenderBox* currBox = toRenderBox(curr); - if (!currBox->hasSelfPaintingLayer() && currBox->inlineBoxWrapper()) { - LayoutRect logicalRect = currBox->visualOverflowRect(); - logicalRect.moveBy(currBox->location()); - result.uniteIfNonZero(logicalRect); - } - } else if (curr->isRenderInline()) { - // If the child doesn't need line boxes either, then we can recur. - RenderInline* currInline = toRenderInline(curr); - if (!currInline->alwaysCreateLineBoxes()) - result.uniteIfNonZero(currInline->culledInlineVisualOverflowBoundingBox()); - else - result.uniteIfNonZero(currInline->linesVisualOverflowBoundingBox()); - } else if (curr->isText()) { - // FIXME; Overflow from text boxes is lost. We will need to cache this information in - // InlineTextBoxes. - RenderText* currText = toRenderText(curr); - result.uniteIfNonZero(currText->linesVisualOverflowBoundingBox()); - } - } - return result; -} - -LayoutRect RenderInline::linesVisualOverflowBoundingBox() const -{ - if (!alwaysCreateLineBoxes()) - return culledInlineVisualOverflowBoundingBox(); - - if (!firstLineBox() || !lastLineBox()) - return LayoutRect(); - + return enclosingIntRect(floatResult); + } + + IntRect result; + + // See , for an unknown reason the linked list here is + // sometimes inconsistent, first is non-zero and last is zero. We have been + // unable to reproduce this at all (and consequently unable to figure ot why + // this is happening). The assert will hopefully catch the problem in debug + // builds and help us someday figure out why. We also put in a redundant + // check of lastLineBox() to avoid the crash for now. + ASSERT(!firstLineBox() == + !lastLineBox()); // Either both are null or both exist. + if (firstLineBox() && lastLineBox()) { // Return the width of the minimal left side and the maximal right side. - LayoutUnit logicalLeftSide = LayoutUnit::max(); - LayoutUnit logicalRightSide = LayoutUnit::min(); - for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) { - logicalLeftSide = std::min(logicalLeftSide, curr->logicalLeftVisualOverflow()); - logicalRightSide = std::max(logicalRightSide, curr->logicalRightVisualOverflow()); + float logicalLeftSide = 0; + float logicalRightSide = 0; + for (InlineFlowBox* curr = firstLineBox(); curr; + curr = curr->nextLineBox()) { + if (curr == firstLineBox() || curr->logicalLeft() < logicalLeftSide) + logicalLeftSide = curr->logicalLeft(); + if (curr == firstLineBox() || curr->logicalRight() > logicalRightSide) + logicalRightSide = curr->logicalRight(); } - RootInlineBox& firstRootBox = firstLineBox()->root(); - RootInlineBox& lastRootBox = lastLineBox()->root(); - - LayoutUnit logicalTop = firstLineBox()->logicalTopVisualOverflow(firstRootBox.lineTop()); - LayoutUnit logicalWidth = logicalRightSide - logicalLeftSide; - LayoutUnit logicalHeight = lastLineBox()->logicalBottomVisualOverflow(lastRootBox.lineBottom()) - logicalTop; - - LayoutRect rect(logicalLeftSide, logicalTop, logicalWidth, logicalHeight); - return rect; -} - -void RenderInline::mapLocalToContainer(const RenderBox* paintInvalidationContainer, TransformState& transformState, MapCoordinatesFlags mode) const -{ - bool containerSkipped; - RenderObject* o = container(paintInvalidationContainer, &containerSkipped); - if (!o) - return; - - if (mode & ApplyContainerFlip && o->isBox()) { - mode &= ~ApplyContainerFlip; + float x = logicalLeftSide; + float y = firstLineBox()->y(); + float width = logicalRightSide - logicalLeftSide; + float height = lastLineBox()->logicalBottom() - y; + result = enclosingIntRect(FloatRect(x, y, width, height)); + } + + return result; +} + +InlineBox* RenderInline::culledInlineFirstLineBox() const { + for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) { + if (curr->isFloatingOrOutOfFlowPositioned()) + continue; + + // We want to get the margin box in the inline direction, and then use our + // font ascent/descent in the block direction (aligned to the root box's + // baseline). + if (curr->isBox()) + return toRenderBox(curr)->inlineBoxWrapper(); + if (curr->isRenderInline()) { + RenderInline* currInline = toRenderInline(curr); + InlineBox* result = currInline->firstLineBoxIncludingCulling(); + if (result) + return result; + } else if (curr->isText()) { + RenderText* currText = toRenderText(curr); + if (currText->firstTextBox()) + return currText->firstTextBox(); } - - LayoutSize containerOffset = offsetFromContainer(o, roundedLayoutPoint(transformState.mappedPoint())); - - bool preserve3D = mode & UseTransforms && (o->style()->preserves3D() || style()->preserves3D()); - if (mode & UseTransforms && shouldUseTransformFromContainer(o)) { - TransformationMatrix t; - getTransformFromContainer(o, containerOffset, t); - transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); - } else - transformState.move(containerOffset.width(), containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); - - if (containerSkipped) { - // There can't be a transform between paintInvalidationContainer and o, because transforms create containers, so it should be safe - // to just subtract the delta between the paintInvalidationContainer and o. - LayoutSize containerOffset = paintInvalidationContainer->offsetFromAncestorContainer(o); - transformState.move(-containerOffset.width(), -containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); - return; + } + return 0; +} + +InlineBox* RenderInline::culledInlineLastLineBox() const { + for (RenderObject* curr = lastChild(); curr; curr = curr->previousSibling()) { + if (curr->isFloatingOrOutOfFlowPositioned()) + continue; + + // We want to get the margin box in the inline direction, and then use our + // font ascent/descent in the block direction (aligned to the root box's + // baseline). + if (curr->isBox()) + return toRenderBox(curr)->inlineBoxWrapper(); + if (curr->isRenderInline()) { + RenderInline* currInline = toRenderInline(curr); + InlineBox* result = currInline->lastLineBoxIncludingCulling(); + if (result) + return result; + } else if (curr->isText()) { + RenderText* currText = toRenderText(curr); + if (currText->lastTextBox()) + return currText->lastTextBox(); } - - o->mapLocalToContainer(paintInvalidationContainer, transformState, mode); -} - -void RenderInline::updateHitTestResult(HitTestResult& result, const LayoutPoint& point) -{ -} - -void RenderInline::dirtyLineBoxes(bool fullLayout) -{ - if (fullLayout) { - m_lineBoxes.deleteLineBoxes(); - return; + } + return 0; +} + +LayoutRect RenderInline::culledInlineVisualOverflowBoundingBox() const { + FloatRect floatResult; + LinesBoundingBoxGeneratorContext context(floatResult); + generateCulledLineBoxRects(context, this); + LayoutRect result(enclosingLayoutRect(floatResult)); + for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) { + if (curr->isFloatingOrOutOfFlowPositioned()) + continue; + + // For overflow we just have to propagate by hand and recompute it all. + if (curr->isBox()) { + RenderBox* currBox = toRenderBox(curr); + if (!currBox->hasSelfPaintingLayer() && currBox->inlineBoxWrapper()) { + LayoutRect logicalRect = currBox->visualOverflowRect(); + logicalRect.moveBy(currBox->location()); + result.uniteIfNonZero(logicalRect); + } + } else if (curr->isRenderInline()) { + // If the child doesn't need line boxes either, then we can recur. + RenderInline* currInline = toRenderInline(curr); + if (!currInline->alwaysCreateLineBoxes()) + result.uniteIfNonZero( + currInline->culledInlineVisualOverflowBoundingBox()); + else + result.uniteIfNonZero(currInline->linesVisualOverflowBoundingBox()); + } else if (curr->isText()) { + // FIXME; Overflow from text boxes is lost. We will need to cache this + // information in InlineTextBoxes. + RenderText* currText = toRenderText(curr); + result.uniteIfNonZero(currText->linesVisualOverflowBoundingBox()); } + } + return result; +} + +LayoutRect RenderInline::linesVisualOverflowBoundingBox() const { + if (!alwaysCreateLineBoxes()) + return culledInlineVisualOverflowBoundingBox(); + + if (!firstLineBox() || !lastLineBox()) + return LayoutRect(); + + // Return the width of the minimal left side and the maximal right side. + LayoutUnit logicalLeftSide = LayoutUnit::max(); + LayoutUnit logicalRightSide = LayoutUnit::min(); + for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) { + logicalLeftSide = + std::min(logicalLeftSide, curr->logicalLeftVisualOverflow()); + logicalRightSide = + std::max(logicalRightSide, curr->logicalRightVisualOverflow()); + } + + RootInlineBox& firstRootBox = firstLineBox()->root(); + RootInlineBox& lastRootBox = lastLineBox()->root(); + + LayoutUnit logicalTop = + firstLineBox()->logicalTopVisualOverflow(firstRootBox.lineTop()); + LayoutUnit logicalWidth = logicalRightSide - logicalLeftSide; + LayoutUnit logicalHeight = + lastLineBox()->logicalBottomVisualOverflow(lastRootBox.lineBottom()) - + logicalTop; + + LayoutRect rect(logicalLeftSide, logicalTop, logicalWidth, logicalHeight); + return rect; +} + +void RenderInline::mapLocalToContainer( + const RenderBox* paintInvalidationContainer, + TransformState& transformState, + MapCoordinatesFlags mode) const { + bool containerSkipped; + RenderObject* o = container(paintInvalidationContainer, &containerSkipped); + if (!o) + return; + + if (mode & ApplyContainerFlip && o->isBox()) { + mode &= ~ApplyContainerFlip; + } + + LayoutSize containerOffset = + offsetFromContainer(o, roundedLayoutPoint(transformState.mappedPoint())); + + bool preserve3D = mode & UseTransforms && + (o->style()->preserves3D() || style()->preserves3D()); + if (mode & UseTransforms && shouldUseTransformFromContainer(o)) { + TransformationMatrix t; + getTransformFromContainer(o, containerOffset, t); + transformState.applyTransform(t, preserve3D + ? TransformState::AccumulateTransform + : TransformState::FlattenTransform); + } else + transformState.move(containerOffset.width(), containerOffset.height(), + preserve3D ? TransformState::AccumulateTransform + : TransformState::FlattenTransform); + + if (containerSkipped) { + // There can't be a transform between paintInvalidationContainer and o, + // because transforms create containers, so it should be safe to just + // subtract the delta between the paintInvalidationContainer and o. + LayoutSize containerOffset = + paintInvalidationContainer->offsetFromAncestorContainer(o); + transformState.move(-containerOffset.width(), -containerOffset.height(), + preserve3D ? TransformState::AccumulateTransform + : TransformState::FlattenTransform); + return; + } + + o->mapLocalToContainer(paintInvalidationContainer, transformState, mode); +} + +void RenderInline::updateHitTestResult(HitTestResult& result, + const LayoutPoint& point) {} + +void RenderInline::dirtyLineBoxes(bool fullLayout) { + if (fullLayout) { + m_lineBoxes.deleteLineBoxes(); + return; + } - if (!alwaysCreateLineBoxes()) { - // We have to grovel into our children in order to dirty the appropriate lines. - for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) { - if (curr->isFloatingOrOutOfFlowPositioned()) - continue; - if (curr->isBox() && !curr->needsLayout()) { - RenderBox* currBox = toRenderBox(curr); - if (currBox->inlineBoxWrapper()) - currBox->inlineBoxWrapper()->root().markDirty(); - } else if (!curr->selfNeedsLayout()) { - if (curr->isRenderInline()) { - RenderInline* currInline = toRenderInline(curr); - for (InlineFlowBox* childLine = currInline->firstLineBox(); childLine; childLine = childLine->nextLineBox()) - childLine->root().markDirty(); - } else if (curr->isText()) { - RenderText* currText = toRenderText(curr); - for (InlineTextBox* childText = currText->firstTextBox(); childText; childText = childText->nextTextBox()) - childText->root().markDirty(); - } - } + if (!alwaysCreateLineBoxes()) { + // We have to grovel into our children in order to dirty the appropriate + // lines. + for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) { + if (curr->isFloatingOrOutOfFlowPositioned()) + continue; + if (curr->isBox() && !curr->needsLayout()) { + RenderBox* currBox = toRenderBox(curr); + if (currBox->inlineBoxWrapper()) + currBox->inlineBoxWrapper()->root().markDirty(); + } else if (!curr->selfNeedsLayout()) { + if (curr->isRenderInline()) { + RenderInline* currInline = toRenderInline(curr); + for (InlineFlowBox* childLine = currInline->firstLineBox(); childLine; + childLine = childLine->nextLineBox()) + childLine->root().markDirty(); + } else if (curr->isText()) { + RenderText* currText = toRenderText(curr); + for (InlineTextBox* childText = currText->firstTextBox(); childText; + childText = childText->nextTextBox()) + childText->root().markDirty(); } - } else - m_lineBoxes.dirtyLineBoxes(); + } + } + } else + m_lineBoxes.dirtyLineBoxes(); } -void RenderInline::deleteLineBoxTree() -{ - m_lineBoxes.deleteLineBoxTree(); +void RenderInline::deleteLineBoxTree() { + m_lineBoxes.deleteLineBoxTree(); } -InlineFlowBox* RenderInline::createInlineFlowBox() -{ - return new InlineFlowBox(*this); +InlineFlowBox* RenderInline::createInlineFlowBox() { + return new InlineFlowBox(*this); } -InlineFlowBox* RenderInline::createAndAppendInlineFlowBox() -{ - setAlwaysCreateLineBoxes(); - InlineFlowBox* flowBox = createInlineFlowBox(); - m_lineBoxes.appendLineBox(flowBox); - return flowBox; +InlineFlowBox* RenderInline::createAndAppendInlineFlowBox() { + setAlwaysCreateLineBoxes(); + InlineFlowBox* flowBox = createInlineFlowBox(); + m_lineBoxes.appendLineBox(flowBox); + return flowBox; } -LayoutUnit RenderInline::lineHeight(bool firstLine, LineDirectionMode /*direction*/, LinePositionMode /*linePositionMode*/) const -{ - return style()->computedLineHeight(); +LayoutUnit RenderInline::lineHeight( + bool firstLine, + LineDirectionMode /*direction*/, + LinePositionMode /*linePositionMode*/) const { + return style()->computedLineHeight(); } -int RenderInline::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const -{ - ASSERT(linePositionMode == PositionOnContainingLine); - const FontMetrics& fontMetrics = style(firstLine)->fontMetrics(); - return fontMetrics.ascent(baselineType) + (lineHeight(firstLine, direction, linePositionMode) - fontMetrics.height()) / 2; +int RenderInline::baselinePosition(FontBaseline baselineType, + bool firstLine, + LineDirectionMode direction, + LinePositionMode linePositionMode) const { + ASSERT(linePositionMode == PositionOnContainingLine); + const FontMetrics& fontMetrics = style(firstLine)->fontMetrics(); + return fontMetrics.ascent(baselineType) + + (lineHeight(firstLine, direction, linePositionMode) - + fontMetrics.height()) / + 2; } namespace { -class AbsoluteRectsIgnoringEmptyRectsGeneratorContext : public AbsoluteRectsGeneratorContext { -public: - AbsoluteRectsIgnoringEmptyRectsGeneratorContext(Vector& rects, const LayoutPoint& accumulatedOffset) - : AbsoluteRectsGeneratorContext(rects, accumulatedOffset) { } - - void operator()(const FloatRect& rect) - { - if (!rect.isEmpty()) - AbsoluteRectsGeneratorContext::operator()(rect); - } +class AbsoluteRectsIgnoringEmptyRectsGeneratorContext + : public AbsoluteRectsGeneratorContext { + public: + AbsoluteRectsIgnoringEmptyRectsGeneratorContext( + Vector& rects, + const LayoutPoint& accumulatedOffset) + : AbsoluteRectsGeneratorContext(rects, accumulatedOffset) {} + + void operator()(const FloatRect& rect) { + if (!rect.isEmpty()) + AbsoluteRectsGeneratorContext::operator()(rect); + } }; -} // unnamed namespace +} // unnamed namespace -void RenderInline::addFocusRingRects(Vector& rects, const LayoutPoint& additionalOffset, const RenderBox* paintContainer) const -{ - AbsoluteRectsIgnoringEmptyRectsGeneratorContext context(rects, additionalOffset); - generateLineBoxRects(context); +void RenderInline::addFocusRingRects(Vector& rects, + const LayoutPoint& additionalOffset, + const RenderBox* paintContainer) const { + AbsoluteRectsIgnoringEmptyRectsGeneratorContext context(rects, + additionalOffset); + generateLineBoxRects(context); - addChildFocusRingRects(rects, additionalOffset, paintContainer); + addChildFocusRingRects(rects, additionalOffset, paintContainer); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/core/rendering/RenderInline.h b/sky/engine/core/rendering/RenderInline.h index 563cf57c696c8..deceb0f6f2d8e 100644 --- a/sky/engine/core/rendering/RenderInline.h +++ b/sky/engine/core/rendering/RenderInline.h @@ -1,7 +1,8 @@ /* * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) - * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. + * All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -30,115 +31,177 @@ namespace blink { class RenderInline : public RenderBoxModelObject { -public: - explicit RenderInline(); - - RenderObject* firstChild() const { ASSERT(children() == virtualChildren()); return children()->firstChild(); } - RenderObject* lastChild() const { ASSERT(children() == virtualChildren()); return children()->lastChild(); } - - // If you have a RenderInline, use firstChild or lastChild instead. - void slowFirstChild() const = delete; - void slowLastChild() const = delete; - - virtual void addChild(RenderObject* newChild, RenderObject* beforeChild = 0) override; - - virtual LayoutUnit marginLeft() const override final; - virtual LayoutUnit marginRight() const override final; - virtual LayoutUnit marginTop() const override final; - virtual LayoutUnit marginBottom() const override final; - virtual LayoutUnit marginBefore(const RenderStyle* otherStyle = 0) const override final; - virtual LayoutUnit marginAfter(const RenderStyle* otherStyle = 0) const override final; - virtual LayoutUnit marginStart(const RenderStyle* otherStyle = 0) const override final; - virtual LayoutUnit marginEnd(const RenderStyle* otherStyle = 0) const override final; - - virtual void absoluteQuads(Vector&) const override; - - IntRect linesBoundingBox() const; - LayoutRect linesVisualOverflowBoundingBox() const; - - InlineFlowBox* createAndAppendInlineFlowBox(); - - void dirtyLineBoxes(bool fullLayout); - void deleteLineBoxTree(); - - RenderLineBoxList* lineBoxes() { return &m_lineBoxes; } - const RenderLineBoxList* lineBoxes() const { return &m_lineBoxes; } - - InlineFlowBox* firstLineBox() const { return m_lineBoxes.firstLineBox(); } - InlineFlowBox* lastLineBox() const { return m_lineBoxes.lastLineBox(); } - InlineBox* firstLineBoxIncludingCulling() const { return alwaysCreateLineBoxes() ? firstLineBox() : culledInlineFirstLineBox(); } - InlineBox* lastLineBoxIncludingCulling() const { return alwaysCreateLineBoxes() ? lastLineBox() : culledInlineLastLineBox(); } - - virtual void addFocusRingRects(Vector&, const LayoutPoint& additionalOffset, const RenderBox* paintContainer = 0) const override final; - - bool alwaysCreateLineBoxes() const { return alwaysCreateLineBoxesForRenderInline(); } - void setAlwaysCreateLineBoxes(bool alwaysCreateLineBoxes = true) { setAlwaysCreateLineBoxesForRenderInline(alwaysCreateLineBoxes); } - void updateAlwaysCreateLineBoxes(bool fullLayout); - - virtual LayoutRect localCaretRect(InlineBox*, int, LayoutUnit* extraWidthToEndOfLine) override final; - - bool hitTestCulledInline(const HitTestRequest&, HitTestResult&, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset); - -protected: - virtual void willBeDestroyed() override; - - virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle) override; - -private: - virtual RenderObjectChildList* virtualChildren() override final { return children(); } - virtual const RenderObjectChildList* virtualChildren() const override final { return children(); } - const RenderObjectChildList* children() const { return &m_children; } - RenderObjectChildList* children() { return &m_children; } - - virtual const char* renderName() const override; - - virtual bool isRenderInline() const override final { return true; } - - LayoutRect culledInlineVisualOverflowBoundingBox() const; - InlineBox* culledInlineFirstLineBox() const; - InlineBox* culledInlineLastLineBox() const; - - template - void generateLineBoxRects(GeneratorContext& yield) const; - template - void generateCulledLineBoxRects(GeneratorContext& yield, const RenderInline* container) const; - - virtual void layout() override final { ASSERT_NOT_REACHED(); } // Do nothing for layout() - - virtual void paint(PaintInfo&, const LayoutPoint&, Vector& layers) override final; - - virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset) override final; - - virtual LayoutUnit offsetLeft() const override final; - virtual LayoutUnit offsetTop() const override final; - virtual LayoutUnit offsetWidth() const override final { return linesBoundingBox().width(); } - virtual LayoutUnit offsetHeight() const override final { return linesBoundingBox().height(); } - - virtual void mapLocalToContainer(const RenderBox* paintInvalidationContainer, TransformState&, MapCoordinatesFlags = ApplyContainerFlip) const override; - - virtual PositionWithAffinity positionForPoint(const LayoutPoint&) override final; - - virtual IntRect borderBoundingBox() const override final - { - IntRect boundingBox = linesBoundingBox(); - return IntRect(0, 0, boundingBox.width(), boundingBox.height()); - } - - virtual InlineFlowBox* createInlineFlowBox(); // Subclassed by SVG and Ruby - - virtual void dirtyLinesFromChangedChild(RenderObject* child) override final { m_lineBoxes.dirtyLinesFromChangedChild(this, child); } - - virtual LayoutUnit lineHeight(bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const override final; - virtual int baselinePosition(FontBaseline, bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const override final; - - virtual void updateHitTestResult(HitTestResult&, const LayoutPoint&) override final; - - RenderObjectChildList m_children; - RenderLineBoxList m_lineBoxes; // All of the line boxes created for this inline flow. For example, Hello
world.
will have two line boxes. + public: + explicit RenderInline(); + + RenderObject* firstChild() const { + ASSERT(children() == virtualChildren()); + return children()->firstChild(); + } + RenderObject* lastChild() const { + ASSERT(children() == virtualChildren()); + return children()->lastChild(); + } + + // If you have a RenderInline, use firstChild or lastChild instead. + void slowFirstChild() const = delete; + void slowLastChild() const = delete; + + virtual void addChild(RenderObject* newChild, + RenderObject* beforeChild = 0) override; + + virtual LayoutUnit marginLeft() const override final; + virtual LayoutUnit marginRight() const override final; + virtual LayoutUnit marginTop() const override final; + virtual LayoutUnit marginBottom() const override final; + virtual LayoutUnit marginBefore( + const RenderStyle* otherStyle = 0) const override final; + virtual LayoutUnit marginAfter( + const RenderStyle* otherStyle = 0) const override final; + virtual LayoutUnit marginStart( + const RenderStyle* otherStyle = 0) const override final; + virtual LayoutUnit marginEnd( + const RenderStyle* otherStyle = 0) const override final; + + virtual void absoluteQuads(Vector&) const override; + + IntRect linesBoundingBox() const; + LayoutRect linesVisualOverflowBoundingBox() const; + + InlineFlowBox* createAndAppendInlineFlowBox(); + + void dirtyLineBoxes(bool fullLayout); + void deleteLineBoxTree(); + + RenderLineBoxList* lineBoxes() { return &m_lineBoxes; } + const RenderLineBoxList* lineBoxes() const { return &m_lineBoxes; } + + InlineFlowBox* firstLineBox() const { return m_lineBoxes.firstLineBox(); } + InlineFlowBox* lastLineBox() const { return m_lineBoxes.lastLineBox(); } + InlineBox* firstLineBoxIncludingCulling() const { + return alwaysCreateLineBoxes() ? firstLineBox() + : culledInlineFirstLineBox(); + } + InlineBox* lastLineBoxIncludingCulling() const { + return alwaysCreateLineBoxes() ? lastLineBox() : culledInlineLastLineBox(); + } + + virtual void addFocusRingRects( + Vector&, + const LayoutPoint& additionalOffset, + const RenderBox* paintContainer = 0) const override final; + + bool alwaysCreateLineBoxes() const { + return alwaysCreateLineBoxesForRenderInline(); + } + void setAlwaysCreateLineBoxes(bool alwaysCreateLineBoxes = true) { + setAlwaysCreateLineBoxesForRenderInline(alwaysCreateLineBoxes); + } + void updateAlwaysCreateLineBoxes(bool fullLayout); + + virtual LayoutRect localCaretRect( + InlineBox*, + int, + LayoutUnit* extraWidthToEndOfLine) override final; + + bool hitTestCulledInline(const HitTestRequest&, + HitTestResult&, + const HitTestLocation& locationInContainer, + const LayoutPoint& accumulatedOffset); + + protected: + virtual void willBeDestroyed() override; + + virtual void styleDidChange(StyleDifference, + const RenderStyle* oldStyle) override; + + private: + virtual RenderObjectChildList* virtualChildren() override final { + return children(); + } + virtual const RenderObjectChildList* virtualChildren() const override final { + return children(); + } + const RenderObjectChildList* children() const { return &m_children; } + RenderObjectChildList* children() { return &m_children; } + + virtual const char* renderName() const override; + + virtual bool isRenderInline() const override final { return true; } + + LayoutRect culledInlineVisualOverflowBoundingBox() const; + InlineBox* culledInlineFirstLineBox() const; + InlineBox* culledInlineLastLineBox() const; + + template + void generateLineBoxRects(GeneratorContext& yield) const; + template + void generateCulledLineBoxRects(GeneratorContext& yield, + const RenderInline* container) const; + + virtual void layout() override final { + ASSERT_NOT_REACHED(); + } // Do nothing for layout() + + virtual void paint(PaintInfo&, + const LayoutPoint&, + Vector& layers) override final; + + virtual bool nodeAtPoint(const HitTestRequest&, + HitTestResult&, + const HitTestLocation& locationInContainer, + const LayoutPoint& accumulatedOffset) override final; + + virtual LayoutUnit offsetLeft() const override final; + virtual LayoutUnit offsetTop() const override final; + virtual LayoutUnit offsetWidth() const override final { + return linesBoundingBox().width(); + } + virtual LayoutUnit offsetHeight() const override final { + return linesBoundingBox().height(); + } + + virtual void mapLocalToContainer( + const RenderBox* paintInvalidationContainer, + TransformState&, + MapCoordinatesFlags = ApplyContainerFlip) const override; + + virtual PositionWithAffinity positionForPoint( + const LayoutPoint&) override final; + + virtual IntRect borderBoundingBox() const override final { + IntRect boundingBox = linesBoundingBox(); + return IntRect(0, 0, boundingBox.width(), boundingBox.height()); + } + + virtual InlineFlowBox* createInlineFlowBox(); // Subclassed by SVG and Ruby + + virtual void dirtyLinesFromChangedChild(RenderObject* child) override final { + m_lineBoxes.dirtyLinesFromChangedChild(this, child); + } + + virtual LayoutUnit lineHeight( + bool firstLine, + LineDirectionMode, + LinePositionMode = PositionOnContainingLine) const override final; + virtual int baselinePosition( + FontBaseline, + bool firstLine, + LineDirectionMode, + LinePositionMode = PositionOnContainingLine) const override final; + + virtual void updateHitTestResult(HitTestResult&, + const LayoutPoint&) override final; + + RenderObjectChildList m_children; + RenderLineBoxList m_lineBoxes; // All of the line boxes created for this + // inline flow. For example, + // Hello
world.
will have two + // line boxes. }; DEFINE_RENDER_OBJECT_TYPE_CASTS(RenderInline, isRenderInline()); -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_RENDERINLINE_H_ diff --git a/sky/engine/core/rendering/RenderLayer.cpp b/sky/engine/core/rendering/RenderLayer.cpp index 50fea28dc780a..1f4c41066eae0 100644 --- a/sky/engine/core/rendering/RenderLayer.cpp +++ b/sky/engine/core/rendering/RenderLayer.cpp @@ -1,5 +1,6 @@ /* - * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. + * All rights reserved. * * Portions are Copyright (C) 1998 Netscape Communications Corporation. * @@ -24,7 +25,7 @@ * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Alternatively, the contents of this file may be used under the terms * of either the Mozilla Public License Version 1.1, found at @@ -66,453 +67,470 @@ namespace blink { RenderLayer::RenderLayer(RenderBox* renderer, LayerType type) - : m_layerType(type) - , m_isRootLayer(renderer->isRenderView()) - , m_3DTransformedDescendantStatusDirty(true) - , m_has3DTransformedDescendant(false) - , m_renderer(renderer) - , m_parent(0) - , m_previous(0) - , m_next(0) - , m_first(0) - , m_last(0) - , m_clipper(*renderer) -{ - m_stackingNode = adoptPtr(new RenderLayerStackingNode(this)); - m_isSelfPaintingLayer = shouldBeSelfPaintingLayer(); + : m_layerType(type), + m_isRootLayer(renderer->isRenderView()), + m_3DTransformedDescendantStatusDirty(true), + m_has3DTransformedDescendant(false), + m_renderer(renderer), + m_parent(0), + m_previous(0), + m_next(0), + m_first(0), + m_last(0), + m_clipper(*renderer) { + m_stackingNode = adoptPtr(new RenderLayerStackingNode(this)); + m_isSelfPaintingLayer = shouldBeSelfPaintingLayer(); } -RenderLayer::~RenderLayer() -{ -} +RenderLayer::~RenderLayer() {} -void RenderLayer::updateLayerPositionsAfterLayout() -{ - m_clipper.clearClipRectsIncludingDescendants(); +void RenderLayer::updateLayerPositionsAfterLayout() { + m_clipper.clearClipRectsIncludingDescendants(); } +void RenderLayer::dirty3DTransformedDescendantStatus() { + RenderLayerStackingNode* stackingNode = + m_stackingNode->ancestorStackingContextNode(); + if (!stackingNode) + return; -void RenderLayer::dirty3DTransformedDescendantStatus() -{ - RenderLayerStackingNode* stackingNode = m_stackingNode->ancestorStackingContextNode(); - if (!stackingNode) - return; + stackingNode->layer()->m_3DTransformedDescendantStatusDirty = true; + // This propagates up through preserve-3d hierarchies to the enclosing + // flattening layer. Note that preserves3D() creates stacking context, so we + // can just run up the stacking containers. + while (stackingNode && + stackingNode->layer()->renderer()->style()->preserves3D()) { stackingNode->layer()->m_3DTransformedDescendantStatusDirty = true; - - // This propagates up through preserve-3d hierarchies to the enclosing flattening layer. - // Note that preserves3D() creates stacking context, so we can just run up the stacking containers. - while (stackingNode && stackingNode->layer()->renderer()->style()->preserves3D()) { - stackingNode->layer()->m_3DTransformedDescendantStatusDirty = true; - stackingNode = stackingNode->ancestorStackingContextNode(); - } + stackingNode = stackingNode->ancestorStackingContextNode(); + } } // Return true if this layer or any preserve-3d descendants have 3d. -bool RenderLayer::update3DTransformedDescendantStatus() -{ - if (m_3DTransformedDescendantStatusDirty) { - m_has3DTransformedDescendant = false; +bool RenderLayer::update3DTransformedDescendantStatus() { + if (m_3DTransformedDescendantStatusDirty) { + m_has3DTransformedDescendant = false; - m_stackingNode->updateZOrderLists(); + m_stackingNode->updateZOrderLists(); - // Transformed or preserve-3d descendants can only be in the z-order lists, not - // in the normal flow list, so we only need to check those. - RenderLayerStackingNodeIterator iterator(*m_stackingNode.get(), PositiveZOrderChildren); - while (RenderLayerStackingNode* node = iterator.next()) - m_has3DTransformedDescendant |= node->layer()->update3DTransformedDescendantStatus(); + // Transformed or preserve-3d descendants can only be in the z-order lists, + // not in the normal flow list, so we only need to check those. + RenderLayerStackingNodeIterator iterator(*m_stackingNode.get(), + PositiveZOrderChildren); + while (RenderLayerStackingNode* node = iterator.next()) + m_has3DTransformedDescendant |= + node->layer()->update3DTransformedDescendantStatus(); - m_3DTransformedDescendantStatusDirty = false; - } + m_3DTransformedDescendantStatusDirty = false; + } - // If we live in a 3d hierarchy, then the layer at the root of that hierarchy needs - // the m_has3DTransformedDescendant set. - if (renderer()->style()->preserves3D()) - return renderer()->has3DTransform() || m_has3DTransformedDescendant; + // If we live in a 3d hierarchy, then the layer at the root of that hierarchy + // needs the m_has3DTransformedDescendant set. + if (renderer()->style()->preserves3D()) + return renderer()->has3DTransform() || m_has3DTransformedDescendant; - return renderer()->has3DTransform(); + return renderer()->has3DTransform(); } -IntSize RenderLayer::size() const -{ - // FIXME: Is snapping the size really needed here? - RenderBox* box = renderer(); - return pixelSnappedIntSize(box->size(), box->location()); +IntSize RenderLayer::size() const { + // FIXME: Is snapping the size really needed here? + RenderBox* box = renderer(); + return pixelSnappedIntSize(box->size(), box->location()); } -LayoutPoint RenderLayer::location() const -{ - LayoutPoint localPoint; - LayoutSize inlineBoundingBoxOffset; // We don't put this into the RenderLayer x/y for inlines, so we need to subtract it out when done. - - if (renderer()->isInline() && renderer()->isRenderInline()) { - RenderInline* inlineFlow = toRenderInline(renderer()); - IntRect lineBox = inlineFlow->linesBoundingBox(); - inlineBoundingBoxOffset = toSize(lineBox.location()); - localPoint += inlineBoundingBoxOffset; - } else { - localPoint += renderer()->locationOffset(); +LayoutPoint RenderLayer::location() const { + LayoutPoint localPoint; + LayoutSize inlineBoundingBoxOffset; // We don't put this into the RenderLayer + // x/y for inlines, so we need to + // subtract it out when done. + + if (renderer()->isInline() && renderer()->isRenderInline()) { + RenderInline* inlineFlow = toRenderInline(renderer()); + IntRect lineBox = inlineFlow->linesBoundingBox(); + inlineBoundingBoxOffset = toSize(lineBox.location()); + localPoint += inlineBoundingBoxOffset; + } else { + localPoint += renderer()->locationOffset(); + } + + if (!renderer()->isOutOfFlowPositioned() && renderer()->parent()) { + // We must adjust our position by walking up the render tree looking for the + // nearest enclosing object with a layer. + RenderObject* curr = renderer()->parent(); + while (curr && !curr->hasLayer()) { + if (curr->isBox()) { + // Rows and cells share the same coordinate space (that of the section). + // Omit them when computing our xpos/ypos. + localPoint += toRenderBox(curr)->locationOffset(); + } + curr = curr->parent(); } + } - if (!renderer()->isOutOfFlowPositioned() && renderer()->parent()) { - // We must adjust our position by walking up the render tree looking for the - // nearest enclosing object with a layer. - RenderObject* curr = renderer()->parent(); - while (curr && !curr->hasLayer()) { - if (curr->isBox()) { - // Rows and cells share the same coordinate space (that of the section). - // Omit them when computing our xpos/ypos. - localPoint += toRenderBox(curr)->locationOffset(); - } - curr = curr->parent(); - } - } - - // FIXME: We'd really like to just get rid of the concept of a layer rectangle and rely on the renderers. - localPoint -= inlineBoundingBoxOffset; + // FIXME: We'd really like to just get rid of the concept of a layer rectangle + // and rely on the renderers. + localPoint -= inlineBoundingBoxOffset; - return localPoint; + return localPoint; } -RenderLayer* RenderLayer::enclosingPositionedAncestor() const -{ - RenderLayer* curr = parent(); - while (curr && !curr->isPositionedContainer()) - curr = curr->parent(); +RenderLayer* RenderLayer::enclosingPositionedAncestor() const { + RenderLayer* curr = parent(); + while (curr && !curr->isPositionedContainer()) + curr = curr->parent(); - return curr; + return curr; } -const RenderLayer* RenderLayer::compositingContainer() const -{ - if (stackingNode()->isNormalFlowOnly()) - return parent(); - if (RenderLayerStackingNode* ancestorStackingNode = stackingNode()->ancestorStackingContextNode()) - return ancestorStackingNode->layer(); - return 0; +const RenderLayer* RenderLayer::compositingContainer() const { + if (stackingNode()->isNormalFlowOnly()) + return parent(); + if (RenderLayerStackingNode* ancestorStackingNode = + stackingNode()->ancestorStackingContextNode()) + return ancestorStackingNode->layer(); + return 0; } -void* RenderLayer::operator new(size_t sz) -{ - return partitionAlloc(Partitions::getRenderingPartition(), sz); +void* RenderLayer::operator new(size_t sz) { + return partitionAlloc(Partitions::getRenderingPartition(), sz); } -void RenderLayer::operator delete(void* ptr) -{ - partitionFree(ptr); +void RenderLayer::operator delete(void* ptr) { + partitionFree(ptr); } -void RenderLayer::addChild(RenderLayer* child, RenderLayer* beforeChild) -{ - RenderLayer* prevSibling = beforeChild ? beforeChild->previousSibling() : lastChild(); - if (prevSibling) { - child->setPreviousSibling(prevSibling); - prevSibling->setNextSibling(child); - ASSERT(prevSibling != child); - } else - setFirstChild(child); - - if (beforeChild) { - beforeChild->setPreviousSibling(child); - child->setNextSibling(beforeChild); - ASSERT(beforeChild != child); - } else - setLastChild(child); - - child->m_parent = this; - - if (child->stackingNode()->isNormalFlowOnly()) - m_stackingNode->dirtyNormalFlowList(); - - if (!child->stackingNode()->isNormalFlowOnly() || child->firstChild()) { - // Dirty the z-order list in which we are contained. The ancestorStackingContextNode() can be null in the - // case where we're building up generated content layers. This is ok, since the lists will start - // off dirty in that case anyway. - child->stackingNode()->dirtyStackingContextZOrderLists(); - } +void RenderLayer::addChild(RenderLayer* child, RenderLayer* beforeChild) { + RenderLayer* prevSibling = + beforeChild ? beforeChild->previousSibling() : lastChild(); + if (prevSibling) { + child->setPreviousSibling(prevSibling); + prevSibling->setNextSibling(child); + ASSERT(prevSibling != child); + } else + setFirstChild(child); + + if (beforeChild) { + beforeChild->setPreviousSibling(child); + child->setNextSibling(beforeChild); + ASSERT(beforeChild != child); + } else + setLastChild(child); + + child->m_parent = this; + + if (child->stackingNode()->isNormalFlowOnly()) + m_stackingNode->dirtyNormalFlowList(); + + if (!child->stackingNode()->isNormalFlowOnly() || child->firstChild()) { + // Dirty the z-order list in which we are contained. The + // ancestorStackingContextNode() can be null in the case where we're + // building up generated content layers. This is ok, since the lists will + // start off dirty in that case anyway. + child->stackingNode()->dirtyStackingContextZOrderLists(); + } } -RenderLayer* RenderLayer::removeChild(RenderLayer* oldChild) -{ - if (oldChild->previousSibling()) - oldChild->previousSibling()->setNextSibling(oldChild->nextSibling()); - if (oldChild->nextSibling()) - oldChild->nextSibling()->setPreviousSibling(oldChild->previousSibling()); - - if (m_first == oldChild) - m_first = oldChild->nextSibling(); - if (m_last == oldChild) - m_last = oldChild->previousSibling(); - - if (oldChild->stackingNode()->isNormalFlowOnly()) - m_stackingNode->dirtyNormalFlowList(); - if (!oldChild->stackingNode()->isNormalFlowOnly() || oldChild->firstChild()) { - // Dirty the z-order list in which we are contained. When called via the - // reattachment process in removeOnlyThisLayer, the layer may already be disconnected - // from the main layer tree, so we need to null-check the - // |stackingContext| value. - oldChild->stackingNode()->dirtyStackingContextZOrderLists(); - } - - oldChild->setPreviousSibling(0); - oldChild->setNextSibling(0); - oldChild->m_parent = 0; - - return oldChild; +RenderLayer* RenderLayer::removeChild(RenderLayer* oldChild) { + if (oldChild->previousSibling()) + oldChild->previousSibling()->setNextSibling(oldChild->nextSibling()); + if (oldChild->nextSibling()) + oldChild->nextSibling()->setPreviousSibling(oldChild->previousSibling()); + + if (m_first == oldChild) + m_first = oldChild->nextSibling(); + if (m_last == oldChild) + m_last = oldChild->previousSibling(); + + if (oldChild->stackingNode()->isNormalFlowOnly()) + m_stackingNode->dirtyNormalFlowList(); + if (!oldChild->stackingNode()->isNormalFlowOnly() || oldChild->firstChild()) { + // Dirty the z-order list in which we are contained. When called via the + // reattachment process in removeOnlyThisLayer, the layer may already be + // disconnected from the main layer tree, so we need to null-check the + // |stackingContext| value. + oldChild->stackingNode()->dirtyStackingContextZOrderLists(); + } + + oldChild->setPreviousSibling(0); + oldChild->setNextSibling(0); + oldChild->m_parent = 0; + + return oldChild; } -void RenderLayer::removeOnlyThisLayer() -{ - if (!m_parent) - return; +void RenderLayer::removeOnlyThisLayer() { + if (!m_parent) + return; - m_clipper.clearClipRectsIncludingDescendants(); + m_clipper.clearClipRectsIncludingDescendants(); - RenderLayer* nextSib = nextSibling(); + RenderLayer* nextSib = nextSibling(); - // Now walk our kids and reattach them to our parent. - RenderLayer* current = m_first; - while (current) { - RenderLayer* next = current->nextSibling(); - removeChild(current); - m_parent->addChild(current, nextSib); + // Now walk our kids and reattach them to our parent. + RenderLayer* current = m_first; + while (current) { + RenderLayer* next = current->nextSibling(); + removeChild(current); + m_parent->addChild(current, nextSib); - // FIXME: We should call a specialized version of this function. - current->updateLayerPositionsAfterLayout(); - current = next; - } + // FIXME: We should call a specialized version of this function. + current->updateLayerPositionsAfterLayout(); + current = next; + } + + // Remove us from the parent. + m_parent->removeChild(this); + m_renderer->destroyLayer(); +} - // Remove us from the parent. - m_parent->removeChild(this); - m_renderer->destroyLayer(); +void RenderLayer::insertOnlyThisLayer() { + if (!m_parent && renderer()->parent()) { + // We need to connect ourselves when our renderer() has a parent. + // Find our enclosingLayer and add ourselves. + RenderLayer* parentLayer = renderer()->parent()->enclosingLayer(); + ASSERT(parentLayer); + RenderLayer* beforeChild = + renderer()->parent()->findNextLayer(parentLayer, renderer()); + parentLayer->addChild(this, beforeChild); + } + + // Remove all descendant layers from the hierarchy and add them to the new + // position. + for (RenderObject* curr = renderer()->slowFirstChild(); curr; + curr = curr->nextSibling()) + curr->moveLayers(m_parent, this); + + // Clear out all the clip rects. + m_clipper.clearClipRectsIncludingDescendants(); } -void RenderLayer::insertOnlyThisLayer() -{ - if (!m_parent && renderer()->parent()) { - // We need to connect ourselves when our renderer() has a parent. - // Find our enclosingLayer and add ourselves. - RenderLayer* parentLayer = renderer()->parent()->enclosingLayer(); - ASSERT(parentLayer); - RenderLayer* beforeChild = renderer()->parent()->findNextLayer(parentLayer, renderer()); - parentLayer->addChild(this, beforeChild); +// Returns the layer reached on the walk up towards the ancestor. +static inline const RenderLayer* accumulateOffsetTowardsAncestor( + const RenderLayer* layer, + const RenderLayer* ancestorLayer, + LayoutPoint& location) { + ASSERT(ancestorLayer != layer); + + const RenderBox* renderer = layer->renderer(); + EPosition position = renderer->style()->position(); + + RenderLayer* parentLayer; + if (position == AbsolutePosition) { + // Do what enclosingPositionedAncestor() does, but check for ancestorLayer + // along the way. + parentLayer = layer->parent(); + bool foundAncestorFirst = false; + while (parentLayer) { + // RenderFlowThread is a positioned container, child of RenderView, + // positioned at (0,0). This implies that, for out-of-flow positioned + // elements inside a RenderFlowThread, we are bailing out before reaching + // root layer. + if (parentLayer->isPositionedContainer()) + break; + + if (parentLayer == ancestorLayer) { + foundAncestorFirst = true; + break; + } + + parentLayer = parentLayer->parent(); } - // Remove all descendant layers from the hierarchy and add them to the new position. - for (RenderObject* curr = renderer()->slowFirstChild(); curr; curr = curr->nextSibling()) - curr->moveLayers(m_parent, this); + if (foundAncestorFirst) { + // Found ancestorLayer before the abs. positioned container, so compute + // offset of both relative to enclosingPositionedAncestor and subtract. + RenderLayer* positionedAncestor = + parentLayer->enclosingPositionedAncestor(); - // Clear out all the clip rects. - m_clipper.clearClipRectsIncludingDescendants(); -} + LayoutPoint thisCoords; + layer->convertToLayerCoords(positionedAncestor, thisCoords); -// Returns the layer reached on the walk up towards the ancestor. -static inline const RenderLayer* accumulateOffsetTowardsAncestor(const RenderLayer* layer, const RenderLayer* ancestorLayer, LayoutPoint& location) -{ - ASSERT(ancestorLayer != layer); - - const RenderBox* renderer = layer->renderer(); - EPosition position = renderer->style()->position(); - - RenderLayer* parentLayer; - if (position == AbsolutePosition) { - // Do what enclosingPositionedAncestor() does, but check for ancestorLayer along the way. - parentLayer = layer->parent(); - bool foundAncestorFirst = false; - while (parentLayer) { - // RenderFlowThread is a positioned container, child of RenderView, positioned at (0,0). - // This implies that, for out-of-flow positioned elements inside a RenderFlowThread, - // we are bailing out before reaching root layer. - if (parentLayer->isPositionedContainer()) - break; - - if (parentLayer == ancestorLayer) { - foundAncestorFirst = true; - break; - } - - parentLayer = parentLayer->parent(); - } - - if (foundAncestorFirst) { - // Found ancestorLayer before the abs. positioned container, so compute offset of both relative - // to enclosingPositionedAncestor and subtract. - RenderLayer* positionedAncestor = parentLayer->enclosingPositionedAncestor(); - - LayoutPoint thisCoords; - layer->convertToLayerCoords(positionedAncestor, thisCoords); - - LayoutPoint ancestorCoords; - ancestorLayer->convertToLayerCoords(positionedAncestor, ancestorCoords); - - location += (thisCoords - ancestorCoords); - return ancestorLayer; - } - } else - parentLayer = layer->parent(); - - if (!parentLayer) - return 0; - - location += toSize(layer->location()); - return parentLayer; -} + LayoutPoint ancestorCoords; + ancestorLayer->convertToLayerCoords(positionedAncestor, ancestorCoords); -void RenderLayer::convertToLayerCoords(const RenderLayer* ancestorLayer, LayoutPoint& location) const -{ - if (ancestorLayer == this) - return; + location += (thisCoords - ancestorCoords); + return ancestorLayer; + } + } else + parentLayer = layer->parent(); - const RenderLayer* currLayer = this; - while (currLayer && currLayer != ancestorLayer) - currLayer = accumulateOffsetTowardsAncestor(currLayer, ancestorLayer, location); + if (!parentLayer) + return 0; + + location += toSize(layer->location()); + return parentLayer; } -void RenderLayer::convertToLayerCoords(const RenderLayer* ancestorLayer, LayoutRect& rect) const -{ - LayoutPoint delta; - convertToLayerCoords(ancestorLayer, delta); - rect.move(-delta.x(), -delta.y()); +void RenderLayer::convertToLayerCoords(const RenderLayer* ancestorLayer, + LayoutPoint& location) const { + if (ancestorLayer == this) + return; + + const RenderLayer* currLayer = this; + while (currLayer && currLayer != ancestorLayer) + currLayer = + accumulateOffsetTowardsAncestor(currLayer, ancestorLayer, location); } -void RenderLayer::clipToRect(const LayerPaintingInfo& localPaintingInfo, GraphicsContext* context, const ClipRect& clipRect, - BorderRadiusClippingRule rule) -{ - if (clipRect.rect() == localPaintingInfo.paintDirtyRect && !clipRect.hasRadius()) - return; - context->save(); - context->clip(pixelSnappedIntRect(clipRect.rect())); +void RenderLayer::convertToLayerCoords(const RenderLayer* ancestorLayer, + LayoutRect& rect) const { + LayoutPoint delta; + convertToLayerCoords(ancestorLayer, delta); + rect.move(-delta.x(), -delta.y()); } -void RenderLayer::restoreClip(GraphicsContext* context, const LayoutRect& paintDirtyRect, const ClipRect& clipRect) -{ - if (clipRect.rect() == paintDirtyRect && !clipRect.hasRadius()) - return; - context->restore(); +void RenderLayer::clipToRect(const LayerPaintingInfo& localPaintingInfo, + GraphicsContext* context, + const ClipRect& clipRect, + BorderRadiusClippingRule rule) { + if (clipRect.rect() == localPaintingInfo.paintDirtyRect && + !clipRect.hasRadius()) + return; + context->save(); + context->clip(pixelSnappedIntRect(clipRect.rect())); } -bool RenderLayer::intersectsDamageRect(const LayoutRect& layerBounds, const LayoutRect& damageRect, const RenderLayer* rootLayer, const LayoutPoint* offsetFromRoot) const -{ - // Always examine the canvas and the root. - if (isRootLayer()) - return true; +void RenderLayer::restoreClip(GraphicsContext* context, + const LayoutRect& paintDirtyRect, + const ClipRect& clipRect) { + if (clipRect.rect() == paintDirtyRect && !clipRect.hasRadius()) + return; + context->restore(); +} - // Otherwise we need to compute the bounding box of this single layer and see if it intersects - // the damage rect. - return physicalBoundingBox(rootLayer, offsetFromRoot).intersects(damageRect); +bool RenderLayer::intersectsDamageRect( + const LayoutRect& layerBounds, + const LayoutRect& damageRect, + const RenderLayer* rootLayer, + const LayoutPoint* offsetFromRoot) const { + // Always examine the canvas and the root. + if (isRootLayer()) + return true; + + // Otherwise we need to compute the bounding box of this single layer and see + // if it intersects the damage rect. + return physicalBoundingBox(rootLayer, offsetFromRoot).intersects(damageRect); } -LayoutRect RenderLayer::logicalBoundingBox() const -{ - // There are three special cases we need to consider. - // (1) Inline Flows. For inline flows we will create a bounding box that fully encompasses all of the lines occupied by the - // inline. In other words, if some wraps to three lines, we'll create a bounding box that fully encloses the - // line boxes of all three lines (including overflow on those lines). - // (2) Left/Top Overflow. The width/height of layers already includes right/bottom overflow. However, in the case of left/top - // overflow, we have to create a bounding box that will extend to include this overflow. - // (3) Floats. When a layer has overhanging floats that it paints, we need to make sure to include these overhanging floats - // as part of our bounding box. We do this because we are the responsible layer for both hit testing and painting those - // floats. - LayoutRect result; - if (renderer()->isInline() && renderer()->isRenderInline()) { - result = toRenderInline(renderer())->linesVisualOverflowBoundingBox(); - } else { - RenderBox* box = renderer(); - result = box->borderBoxRect(); - result.unite(box->visualOverflowRect()); - } +LayoutRect RenderLayer::logicalBoundingBox() const { + // There are three special cases we need to consider. + // (1) Inline Flows. For inline flows we will create a bounding box that + // fully encompasses all of the lines occupied by the inline. In other words, + // if some wraps to three lines, we'll create a bounding box that fully + // encloses the line boxes of all three lines (including overflow on those + // lines). (2) Left/Top Overflow. The width/height of layers already includes + // right/bottom overflow. However, in the case of left/top overflow, we have + // to create a bounding box that will extend to include this overflow. (3) + // Floats. When a layer has overhanging floats that it paints, we need to + // make sure to include these overhanging floats as part of our bounding box. + // We do this because we are the responsible layer for both hit testing and + // painting those floats. + LayoutRect result; + if (renderer()->isInline() && renderer()->isRenderInline()) { + result = toRenderInline(renderer())->linesVisualOverflowBoundingBox(); + } else { + RenderBox* box = renderer(); + result = box->borderBoxRect(); + result.unite(box->visualOverflowRect()); + } - return result; + return result; } -LayoutRect RenderLayer::physicalBoundingBox(const RenderLayer* ancestorLayer, const LayoutPoint* offsetFromRoot) const -{ - LayoutPoint delta; - if (offsetFromRoot) - delta = *offsetFromRoot; - else - convertToLayerCoords(ancestorLayer, delta); - - LayoutRect result = logicalBoundingBox(); - result.moveBy(delta); - return result; +LayoutRect RenderLayer::physicalBoundingBox( + const RenderLayer* ancestorLayer, + const LayoutPoint* offsetFromRoot) const { + LayoutPoint delta; + if (offsetFromRoot) + delta = *offsetFromRoot; + else + convertToLayerCoords(ancestorLayer, delta); + + LayoutRect result = logicalBoundingBox(); + result.moveBy(delta); + return result; } -static void expandRectForReflectionAndStackingChildren(const RenderLayer* ancestorLayer, LayoutRect& result) -{ - ASSERT(ancestorLayer->stackingNode()->isStackingContext() || !ancestorLayer->stackingNode()->hasPositiveZOrderList()); +static void expandRectForReflectionAndStackingChildren( + const RenderLayer* ancestorLayer, + LayoutRect& result) { + ASSERT(ancestorLayer->stackingNode()->isStackingContext() || + !ancestorLayer->stackingNode()->hasPositiveZOrderList()); #if ENABLE(ASSERT) - LayerListMutationDetector mutationChecker(const_cast(ancestorLayer)->stackingNode()); + LayerListMutationDetector mutationChecker( + const_cast(ancestorLayer)->stackingNode()); #endif - RenderLayerStackingNodeIterator iterator(*ancestorLayer->stackingNode(), AllChildren); - while (RenderLayerStackingNode* node = iterator.next()) { - result.unite(node->layer()->boundingBoxForCompositing(ancestorLayer)); - } + RenderLayerStackingNodeIterator iterator(*ancestorLayer->stackingNode(), + AllChildren); + while (RenderLayerStackingNode* node = iterator.next()) { + result.unite(node->layer()->boundingBoxForCompositing(ancestorLayer)); + } } -LayoutRect RenderLayer::physicalBoundingBoxIncludingReflectionAndStackingChildren(const RenderLayer* ancestorLayer, const LayoutPoint& offsetFromRoot) const -{ - LayoutPoint origin; - LayoutRect result = physicalBoundingBox(ancestorLayer, &origin); +LayoutRect +RenderLayer::physicalBoundingBoxIncludingReflectionAndStackingChildren( + const RenderLayer* ancestorLayer, + const LayoutPoint& offsetFromRoot) const { + LayoutPoint origin; + LayoutRect result = physicalBoundingBox(ancestorLayer, &origin); - const_cast(this)->stackingNode()->updateLayerListsIfNeeded(); + const_cast(this)->stackingNode()->updateLayerListsIfNeeded(); - expandRectForReflectionAndStackingChildren(this, result); + expandRectForReflectionAndStackingChildren(this, result); - result.moveBy(offsetFromRoot); - return result; + result.moveBy(offsetFromRoot); + return result; } -LayoutRect RenderLayer::boundingBoxForCompositing(const RenderLayer* ancestorLayer) const -{ - if (!isSelfPaintingLayer()) - return LayoutRect(); +LayoutRect RenderLayer::boundingBoxForCompositing( + const RenderLayer* ancestorLayer) const { + if (!isSelfPaintingLayer()) + return LayoutRect(); - if (!ancestorLayer) - ancestorLayer = this; + if (!ancestorLayer) + ancestorLayer = this; - LayoutRect localClipRect = clipper().localClipRect(); - if (localClipRect != PaintInfo::infiniteRect()) { - if (renderer()->transform()) - localClipRect = renderer()->transform()->mapRect(localClipRect); + LayoutRect localClipRect = clipper().localClipRect(); + if (localClipRect != PaintInfo::infiniteRect()) { + if (renderer()->transform()) + localClipRect = renderer()->transform()->mapRect(localClipRect); - LayoutPoint delta; - convertToLayerCoords(ancestorLayer, delta); - localClipRect.moveBy(delta); - return localClipRect; - } + LayoutPoint delta; + convertToLayerCoords(ancestorLayer, delta); + localClipRect.moveBy(delta); + return localClipRect; + } - LayoutPoint origin; - LayoutRect result = physicalBoundingBox(ancestorLayer, &origin); + LayoutPoint origin; + LayoutRect result = physicalBoundingBox(ancestorLayer, &origin); - const_cast(this)->stackingNode()->updateLayerListsIfNeeded(); + const_cast(this)->stackingNode()->updateLayerListsIfNeeded(); - expandRectForReflectionAndStackingChildren(this, result); + expandRectForReflectionAndStackingChildren(this, result); - if (renderer()->transform()) - result = renderer()->transform()->mapRect(result); + if (renderer()->transform()) + result = renderer()->transform()->mapRect(result); - LayoutPoint delta; - convertToLayerCoords(ancestorLayer, delta); - result.moveBy(delta); - return result; + LayoutPoint delta; + convertToLayerCoords(ancestorLayer, delta); + result.moveBy(delta); + return result; } -bool RenderLayer::shouldBeSelfPaintingLayer() const -{ - return m_layerType == NormalLayer; +bool RenderLayer::shouldBeSelfPaintingLayer() const { + return m_layerType == NormalLayer; } -void RenderLayer::styleChanged(StyleDifference diff, const RenderStyle* oldStyle) -{ - m_stackingNode->updateIsNormalFlowOnly(); - m_stackingNode->updateStackingNodesAfterStyleChange(oldStyle); +void RenderLayer::styleChanged(StyleDifference diff, + const RenderStyle* oldStyle) { + m_stackingNode->updateIsNormalFlowOnly(); + m_stackingNode->updateStackingNodesAfterStyleChange(oldStyle); - // Overlay scrollbars can make this layer self-painting so we need - // to recompute the bit once scrollbars have been updated. - m_isSelfPaintingLayer = shouldBeSelfPaintingLayer(); + // Overlay scrollbars can make this layer self-painting so we need + // to recompute the bit once scrollbars have been updated. + m_isSelfPaintingLayer = shouldBeSelfPaintingLayer(); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/core/rendering/RenderLayer.h b/sky/engine/core/rendering/RenderLayer.h index 7430887e1d6b6..b5216cf474f10 100644 --- a/sky/engine/core/rendering/RenderLayer.h +++ b/sky/engine/core/rendering/RenderLayer.h @@ -25,7 +25,7 @@ * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Alternatively, the contents of this file may be used under the terms * of either the Mozilla Public License Version 1.1, found at @@ -57,127 +57,150 @@ namespace blink { class RenderStyle; -enum BorderRadiusClippingRule { IncludeSelfForBorderRadius, DoNotIncludeSelfForBorderRadius }; +enum BorderRadiusClippingRule { + IncludeSelfForBorderRadius, + DoNotIncludeSelfForBorderRadius +}; enum IncludeSelfOrNot { IncludeSelf, ExcludeSelf }; class RenderLayer { - WTF_MAKE_NONCOPYABLE(RenderLayer); -public: - RenderLayer(RenderBox*, LayerType); - ~RenderLayer(); - - RenderBox* renderer() const { return m_renderer; } - RenderLayer* parent() const { return m_parent; } - RenderLayer* previousSibling() const { return m_previous; } - RenderLayer* nextSibling() const { return m_next; } - RenderLayer* firstChild() const { return m_first; } - RenderLayer* lastChild() const { return m_last; } - - void addChild(RenderLayer* newChild, RenderLayer* beforeChild = 0); - RenderLayer* removeChild(RenderLayer*); - - void removeOnlyThisLayer(); - void insertOnlyThisLayer(); - - void styleChanged(StyleDifference, const RenderStyle* oldStyle); - bool isSelfPaintingLayer() const { return m_isSelfPaintingLayer; } - void setLayerType(LayerType layerType) { m_layerType = layerType; } - - const RenderLayer* root() const - { - const RenderLayer* curr = this; - while (curr->parent()) - curr = curr->parent(); - return curr; - } - - LayoutPoint location() const; - IntSize size() const; - LayoutRect rect() const { return LayoutRect(location(), size()); } - - bool isRootLayer() const { return m_isRootLayer; } - - void updateLayerPositionsAfterLayout(); - - RenderLayerStackingNode* stackingNode() { return m_stackingNode.get(); } - const RenderLayerStackingNode* stackingNode() const { return m_stackingNode.get(); } - - // Gets the nearest enclosing positioned ancestor layer (also includes - // the layer and the root layer). - RenderLayer* enclosingPositionedAncestor() const; - - const RenderLayer* compositingContainer() const; - - void convertToLayerCoords(const RenderLayer* ancestorLayer, LayoutPoint&) const; - void convertToLayerCoords(const RenderLayer* ancestorLayer, LayoutRect&) const; - - // Pass offsetFromRoot if known. - bool intersectsDamageRect(const LayoutRect& layerBounds, const LayoutRect& damageRect, const RenderLayer* rootLayer, const LayoutPoint* offsetFromRoot = 0) const; - - // Bounding box relative to some ancestor layer. Pass offsetFromRoot if known. - LayoutRect physicalBoundingBox(const RenderLayer* ancestorLayer, const LayoutPoint* offsetFromRoot = 0) const; - LayoutRect physicalBoundingBoxIncludingReflectionAndStackingChildren(const RenderLayer* ancestorLayer, const LayoutPoint& offsetFromRoot) const; - LayoutRect boundingBoxForCompositing(const RenderLayer* ancestorLayer = 0) const; - - bool has3DTransformedDescendant() const { return m_has3DTransformedDescendant; } - // Both updates the status, and returns true if descendants of this have 3d. - bool update3DTransformedDescendantStatus(); - - void* operator new(size_t); - // Only safe to call from RenderBox::destroyLayer() - void operator delete(void*); - - RenderLayerClipper& clipper() { return m_clipper; } - const RenderLayerClipper& clipper() const { return m_clipper; } - - inline bool isPositionedContainer() const - { - // FIXME: This is not in sync with containingBlock. - return isRootLayer() || renderer()->isPositioned() || renderer()->hasTransform(); - } - - void clipToRect(const LayerPaintingInfo&, GraphicsContext*, const ClipRect&, BorderRadiusClippingRule = IncludeSelfForBorderRadius); - void restoreClip(GraphicsContext*, const LayoutRect& paintDirtyRect, const ClipRect&); - - // Bounding box in the coordinates of this layer. - LayoutRect logicalBoundingBox() const; - - void setNextSibling(RenderLayer* next) { m_next = next; } - void setPreviousSibling(RenderLayer* prev) { m_previous = prev; } - void setFirstChild(RenderLayer* first) { m_first = first; } - void setLastChild(RenderLayer* last) { m_last = last; } - - bool shouldBeSelfPaintingLayer() const; - - void dirty3DTransformedDescendantStatus(); - -private: - LayerType m_layerType; - - // Self-painting layer is an optimization where we avoid the heavy RenderLayer painting - // machinery for a RenderLayer allocated only to handle the overflow clip case. - // FIXME(crbug.com/332791): Self-painting layer should be merged into the overflow-only concept. - unsigned m_isSelfPaintingLayer : 1; - - const unsigned m_isRootLayer : 1; - - unsigned m_3DTransformedDescendantStatusDirty : 1; - // Set on a stacking context layer that has 3D descendants anywhere - // in a preserves3D hierarchy. Hint to do 3D-aware hit testing. - unsigned m_has3DTransformedDescendant : 1; - - RenderBox* m_renderer; - - RenderLayer* m_parent; - RenderLayer* m_previous; - RenderLayer* m_next; - RenderLayer* m_first; - RenderLayer* m_last; - - RenderLayerClipper m_clipper; // FIXME: Lazily allocate? - OwnPtr m_stackingNode; + WTF_MAKE_NONCOPYABLE(RenderLayer); + + public: + RenderLayer(RenderBox*, LayerType); + ~RenderLayer(); + + RenderBox* renderer() const { return m_renderer; } + RenderLayer* parent() const { return m_parent; } + RenderLayer* previousSibling() const { return m_previous; } + RenderLayer* nextSibling() const { return m_next; } + RenderLayer* firstChild() const { return m_first; } + RenderLayer* lastChild() const { return m_last; } + + void addChild(RenderLayer* newChild, RenderLayer* beforeChild = 0); + RenderLayer* removeChild(RenderLayer*); + + void removeOnlyThisLayer(); + void insertOnlyThisLayer(); + + void styleChanged(StyleDifference, const RenderStyle* oldStyle); + bool isSelfPaintingLayer() const { return m_isSelfPaintingLayer; } + void setLayerType(LayerType layerType) { m_layerType = layerType; } + + const RenderLayer* root() const { + const RenderLayer* curr = this; + while (curr->parent()) + curr = curr->parent(); + return curr; + } + + LayoutPoint location() const; + IntSize size() const; + LayoutRect rect() const { return LayoutRect(location(), size()); } + + bool isRootLayer() const { return m_isRootLayer; } + + void updateLayerPositionsAfterLayout(); + + RenderLayerStackingNode* stackingNode() { return m_stackingNode.get(); } + const RenderLayerStackingNode* stackingNode() const { + return m_stackingNode.get(); + } + + // Gets the nearest enclosing positioned ancestor layer (also includes + // the layer and the root layer). + RenderLayer* enclosingPositionedAncestor() const; + + const RenderLayer* compositingContainer() const; + + void convertToLayerCoords(const RenderLayer* ancestorLayer, + LayoutPoint&) const; + void convertToLayerCoords(const RenderLayer* ancestorLayer, + LayoutRect&) const; + + // Pass offsetFromRoot if known. + bool intersectsDamageRect(const LayoutRect& layerBounds, + const LayoutRect& damageRect, + const RenderLayer* rootLayer, + const LayoutPoint* offsetFromRoot = 0) const; + + // Bounding box relative to some ancestor layer. Pass offsetFromRoot if known. + LayoutRect physicalBoundingBox(const RenderLayer* ancestorLayer, + const LayoutPoint* offsetFromRoot = 0) const; + LayoutRect physicalBoundingBoxIncludingReflectionAndStackingChildren( + const RenderLayer* ancestorLayer, + const LayoutPoint& offsetFromRoot) const; + LayoutRect boundingBoxForCompositing( + const RenderLayer* ancestorLayer = 0) const; + + bool has3DTransformedDescendant() const { + return m_has3DTransformedDescendant; + } + // Both updates the status, and returns true if descendants of this have 3d. + bool update3DTransformedDescendantStatus(); + + void* operator new(size_t); + // Only safe to call from RenderBox::destroyLayer() + void operator delete(void*); + + RenderLayerClipper& clipper() { return m_clipper; } + const RenderLayerClipper& clipper() const { return m_clipper; } + + inline bool isPositionedContainer() const { + // FIXME: This is not in sync with containingBlock. + return isRootLayer() || renderer()->isPositioned() || + renderer()->hasTransform(); + } + + void clipToRect(const LayerPaintingInfo&, + GraphicsContext*, + const ClipRect&, + BorderRadiusClippingRule = IncludeSelfForBorderRadius); + void restoreClip(GraphicsContext*, + const LayoutRect& paintDirtyRect, + const ClipRect&); + + // Bounding box in the coordinates of this layer. + LayoutRect logicalBoundingBox() const; + + void setNextSibling(RenderLayer* next) { m_next = next; } + void setPreviousSibling(RenderLayer* prev) { m_previous = prev; } + void setFirstChild(RenderLayer* first) { m_first = first; } + void setLastChild(RenderLayer* last) { m_last = last; } + + bool shouldBeSelfPaintingLayer() const; + + void dirty3DTransformedDescendantStatus(); + + private: + LayerType m_layerType; + + // Self-painting layer is an optimization where we avoid the heavy RenderLayer + // painting machinery for a RenderLayer allocated only to handle the overflow + // clip case. + // FIXME(crbug.com/332791): Self-painting layer should be merged into the + // overflow-only concept. + unsigned m_isSelfPaintingLayer : 1; + + const unsigned m_isRootLayer : 1; + + unsigned m_3DTransformedDescendantStatusDirty : 1; + // Set on a stacking context layer that has 3D descendants anywhere + // in a preserves3D hierarchy. Hint to do 3D-aware hit testing. + unsigned m_has3DTransformedDescendant : 1; + + RenderBox* m_renderer; + + RenderLayer* m_parent; + RenderLayer* m_previous; + RenderLayer* m_next; + RenderLayer* m_first; + RenderLayer* m_last; + + RenderLayerClipper m_clipper; // FIXME: Lazily allocate? + OwnPtr m_stackingNode; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_RENDERLAYER_H_ diff --git a/sky/engine/core/rendering/RenderLayerClipper.cpp b/sky/engine/core/rendering/RenderLayerClipper.cpp index 34af475c49638..47f3b34e99d09 100644 --- a/sky/engine/core/rendering/RenderLayerClipper.cpp +++ b/sky/engine/core/rendering/RenderLayerClipper.cpp @@ -1,5 +1,6 @@ /* - * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. + * All rights reserved. * * Portions are Copyright (C) 1998 Netscape Communications Corporation. * @@ -24,7 +25,7 @@ * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Alternatively, the contents of this file may be used under the terms * of either the Mozilla Public License Version 1.1, found at @@ -49,240 +50,261 @@ namespace blink { RenderLayerClipper::RenderLayerClipper(RenderBox& renderer) - : m_renderer(renderer) -{ -} + : m_renderer(renderer) {} -ClipRects* RenderLayerClipper::clipRectsIfCached(const ClipRectsContext& context) const -{ - ASSERT(context.usesCache()); - if (!m_cache) - return 0; - ClipRectsCache::Entry& entry = m_cache->get(context.cacheSlot); - // FIXME: We used to ASSERT that we always got a consistent root layer. - // We should add a test that has an inconsistent root. See - // http://crbug.com/366118 for an example. - if (context.rootLayer != entry.root) - return 0; +ClipRects* RenderLayerClipper::clipRectsIfCached( + const ClipRectsContext& context) const { + ASSERT(context.usesCache()); + if (!m_cache) + return 0; + ClipRectsCache::Entry& entry = m_cache->get(context.cacheSlot); + // FIXME: We used to ASSERT that we always got a consistent root layer. + // We should add a test that has an inconsistent root. See + // http://crbug.com/366118 for an example. + if (context.rootLayer != entry.root) + return 0; #ifdef CHECK_CACHED_CLIP_RECTS - // This code is useful to check cached clip rects, but is too expensive to leave enabled in debug builds by default. - ClipRectsContext tempContext(context); - tempContext.cacheSlot = UncachedClipRects; - ClipRects clipRects; - calculateClipRects(tempContext, clipRects); - ASSERT(clipRects == *entry.clipRects); + // This code is useful to check cached clip rects, but is too expensive to + // leave enabled in debug builds by default. + ClipRectsContext tempContext(context); + tempContext.cacheSlot = UncachedClipRects; + ClipRects clipRects; + calculateClipRects(tempContext, clipRects); + ASSERT(clipRects == *entry.clipRects); #endif - return entry.clipRects.get(); + return entry.clipRects.get(); } -ClipRects* RenderLayerClipper::storeClipRectsInCache(const ClipRectsContext& context, ClipRects* parentClipRects, const ClipRects& clipRects) const -{ - ClipRectsCache::Entry& entry = cache().get(context.cacheSlot); - entry.root = context.rootLayer; - - if (parentClipRects) { - // If our clip rects match the clip rects of our parent, we share storage. - if (clipRects == *parentClipRects) { - entry.clipRects = parentClipRects; - return parentClipRects; - } +ClipRects* RenderLayerClipper::storeClipRectsInCache( + const ClipRectsContext& context, + ClipRects* parentClipRects, + const ClipRects& clipRects) const { + ClipRectsCache::Entry& entry = cache().get(context.cacheSlot); + entry.root = context.rootLayer; + + if (parentClipRects) { + // If our clip rects match the clip rects of our parent, we share storage. + if (clipRects == *parentClipRects) { + entry.clipRects = parentClipRects; + return parentClipRects; } + } - entry.clipRects = ClipRects::create(clipRects); - return entry.clipRects.get(); + entry.clipRects = ClipRects::create(clipRects); + return entry.clipRects.get(); } -ClipRects* RenderLayerClipper::getClipRects(const ClipRectsContext& context) const -{ - if (ClipRects* result = clipRectsIfCached(context)) - return result; - - // Note that it's important that we call getClipRects on our parent - // before we call calculateClipRects so that calculateClipRects will hit - // the cache. - ClipRects* parentClipRects = 0; - if (context.rootLayer != m_renderer.layer() && m_renderer.layer()->parent()) - parentClipRects = m_renderer.layer()->parent()->clipper().getClipRects(context); - - ClipRects clipRects; - calculateClipRects(context, clipRects); - return storeClipRectsInCache(context, parentClipRects, clipRects); +ClipRects* RenderLayerClipper::getClipRects( + const ClipRectsContext& context) const { + if (ClipRects* result = clipRectsIfCached(context)) + return result; + + // Note that it's important that we call getClipRects on our parent + // before we call calculateClipRects so that calculateClipRects will hit + // the cache. + ClipRects* parentClipRects = 0; + if (context.rootLayer != m_renderer.layer() && m_renderer.layer()->parent()) + parentClipRects = + m_renderer.layer()->parent()->clipper().getClipRects(context); + + ClipRects clipRects; + calculateClipRects(context, clipRects); + return storeClipRectsInCache(context, parentClipRects, clipRects); } -void RenderLayerClipper::clearClipRectsIncludingDescendants() -{ - m_cache = nullptr; +void RenderLayerClipper::clearClipRectsIncludingDescendants() { + m_cache = nullptr; - for (RenderLayer* layer = m_renderer.layer()->firstChild(); layer; layer = layer->nextSibling()) - layer->clipper().clearClipRectsIncludingDescendants(); + for (RenderLayer* layer = m_renderer.layer()->firstChild(); layer; + layer = layer->nextSibling()) + layer->clipper().clearClipRectsIncludingDescendants(); } -void RenderLayerClipper::clearClipRectsIncludingDescendants(ClipRectsCacheSlot cacheSlot) -{ - if (m_cache) - m_cache->clear(cacheSlot); +void RenderLayerClipper::clearClipRectsIncludingDescendants( + ClipRectsCacheSlot cacheSlot) { + if (m_cache) + m_cache->clear(cacheSlot); - for (RenderLayer* layer = m_renderer.layer()->firstChild(); layer; layer = layer->nextSibling()) - layer->clipper().clearClipRectsIncludingDescendants(cacheSlot); + for (RenderLayer* layer = m_renderer.layer()->firstChild(); layer; + layer = layer->nextSibling()) + layer->clipper().clearClipRectsIncludingDescendants(cacheSlot); } -LayoutRect RenderLayerClipper::localClipRect() const -{ - // FIXME: border-radius not accounted for. - RenderLayer* clippingRootLayer = clippingRootForPainting(); - LayoutRect layerBounds; - ClipRect backgroundRect; - ClipRectsContext context(clippingRootLayer, PaintingClipRects); - calculateRects(context, PaintInfo::infiniteRect(), layerBounds, backgroundRect); - - LayoutRect clipRect = backgroundRect.rect(); - if (clipRect == PaintInfo::infiniteRect()) - return clipRect; +LayoutRect RenderLayerClipper::localClipRect() const { + // FIXME: border-radius not accounted for. + RenderLayer* clippingRootLayer = clippingRootForPainting(); + LayoutRect layerBounds; + ClipRect backgroundRect; + ClipRectsContext context(clippingRootLayer, PaintingClipRects); + calculateRects(context, PaintInfo::infiniteRect(), layerBounds, + backgroundRect); + + LayoutRect clipRect = backgroundRect.rect(); + if (clipRect == PaintInfo::infiniteRect()) + return clipRect; - LayoutPoint clippingRootOffset; - m_renderer.layer()->convertToLayerCoords(clippingRootLayer, clippingRootOffset); - clipRect.moveBy(-clippingRootOffset); + LayoutPoint clippingRootOffset; + m_renderer.layer()->convertToLayerCoords(clippingRootLayer, + clippingRootOffset); + clipRect.moveBy(-clippingRootOffset); - return clipRect; + return clipRect; } -void RenderLayerClipper::calculateRects(const ClipRectsContext& context, const LayoutRect& paintDirtyRect, LayoutRect& layerBounds, - ClipRect& backgroundRect, const LayoutPoint* offsetFromRoot) const -{ - bool isClippingRoot = m_renderer.layer() == context.rootLayer; - - if (!isClippingRoot && m_renderer.layer()->parent()) { - backgroundRect = backgroundClipRect(context); - backgroundRect.move(roundedIntSize(context.subPixelAccumulation)); - backgroundRect.intersect(paintDirtyRect); +void RenderLayerClipper::calculateRects( + const ClipRectsContext& context, + const LayoutRect& paintDirtyRect, + LayoutRect& layerBounds, + ClipRect& backgroundRect, + const LayoutPoint* offsetFromRoot) const { + bool isClippingRoot = m_renderer.layer() == context.rootLayer; + + if (!isClippingRoot && m_renderer.layer()->parent()) { + backgroundRect = backgroundClipRect(context); + backgroundRect.move(roundedIntSize(context.subPixelAccumulation)); + backgroundRect.intersect(paintDirtyRect); + } else { + backgroundRect = paintDirtyRect; + } + + LayoutPoint offset; + if (offsetFromRoot) + offset = *offsetFromRoot; + else + m_renderer.layer()->convertToLayerCoords(context.rootLayer, offset); + layerBounds = LayoutRect(offset, m_renderer.layer()->size()); + + // Update the clip rects that will be passed to child layers. + if (m_renderer.hasOverflowClip()) { + // If we establish an overflow clip at all, then go ahead and make sure our + // background rect is intersected with our layer's bounds including our + // visual overflow, since any visual overflow like box-shadow or + // border-outset is not clipped by overflow:auto/hidden. + if (m_renderer.hasVisualOverflow()) { + // FIXME: Perhaps we should be propagating the borderbox as the clip rect + // for children, even though + // we may need to inflate our clip specifically for shadows or + // outsets. + // FIXME: Does not do the right thing with CSS regions yet, since we don't + // yet factor in the individual region boxes as overflow. + LayoutRect layerBoundsWithVisualOverflow = + m_renderer.visualOverflowRect(); + layerBoundsWithVisualOverflow.moveBy(offset); + backgroundRect.intersect(layerBoundsWithVisualOverflow); } else { - backgroundRect = paintDirtyRect; - } - - LayoutPoint offset; - if (offsetFromRoot) - offset = *offsetFromRoot; - else - m_renderer.layer()->convertToLayerCoords(context.rootLayer, offset); - layerBounds = LayoutRect(offset, m_renderer.layer()->size()); - - // Update the clip rects that will be passed to child layers. - if (m_renderer.hasOverflowClip()) { - // If we establish an overflow clip at all, then go ahead and make sure our background - // rect is intersected with our layer's bounds including our visual overflow, - // since any visual overflow like box-shadow or border-outset is not clipped by overflow:auto/hidden. - if (m_renderer.hasVisualOverflow()) { - // FIXME: Perhaps we should be propagating the borderbox as the clip rect for children, even though - // we may need to inflate our clip specifically for shadows or outsets. - // FIXME: Does not do the right thing with CSS regions yet, since we don't yet factor in the - // individual region boxes as overflow. - LayoutRect layerBoundsWithVisualOverflow = m_renderer.visualOverflowRect(); - layerBoundsWithVisualOverflow.moveBy(offset); - backgroundRect.intersect(layerBoundsWithVisualOverflow); - } else { - LayoutRect bounds = m_renderer.borderBoxRect(); - bounds.moveBy(offset); - backgroundRect.intersect(bounds); - } - } - - // CSS clip (different than clipping due to overflow) can clip to any box, even if it falls outside of the border box. - if (m_renderer.hasClip()) { - // Clip applies to *us* as well, so go ahead and update the damageRect. - LayoutRect newPosClip = m_renderer.clipRect(offset); - backgroundRect.intersect(newPosClip); + LayoutRect bounds = m_renderer.borderBoxRect(); + bounds.moveBy(offset); + backgroundRect.intersect(bounds); } + } + + // CSS clip (different than clipping due to overflow) can clip to any box, + // even if it falls outside of the border box. + if (m_renderer.hasClip()) { + // Clip applies to *us* as well, so go ahead and update the damageRect. + LayoutRect newPosClip = m_renderer.clipRect(offset); + backgroundRect.intersect(newPosClip); + } } -void RenderLayerClipper::calculateClipRects(const ClipRectsContext& context, ClipRects& clipRects) const -{ - if (!m_renderer.layer()->parent()) { - // The root layer's clip rect is always infinite. - clipRects.reset(PaintInfo::infiniteRect()); - return; - } - - bool isClippingRoot = m_renderer.layer() == context.rootLayer; - - // For transformed layers, the root layer was shifted to be us, so there is no need to - // examine the parent. We want to cache clip rects with us as the root. - RenderLayer* parentLayer = !isClippingRoot ? m_renderer.layer()->parent() : 0; - - // Ensure that our parent's clip has been calculated so that we can examine the values. - if (parentLayer) { - // FIXME: Why don't we just call getClipRects here? - if (context.usesCache() && parentLayer->clipper().cachedClipRects(context)) { - clipRects = *parentLayer->clipper().cachedClipRects(context); - } else { - parentLayer->clipper().calculateClipRects(context, clipRects); - } +void RenderLayerClipper::calculateClipRects(const ClipRectsContext& context, + ClipRects& clipRects) const { + if (!m_renderer.layer()->parent()) { + // The root layer's clip rect is always infinite. + clipRects.reset(PaintInfo::infiniteRect()); + return; + } + + bool isClippingRoot = m_renderer.layer() == context.rootLayer; + + // For transformed layers, the root layer was shifted to be us, so there is no + // need to examine the parent. We want to cache clip rects with us as the + // root. + RenderLayer* parentLayer = !isClippingRoot ? m_renderer.layer()->parent() : 0; + + // Ensure that our parent's clip has been calculated so that we can examine + // the values. + if (parentLayer) { + // FIXME: Why don't we just call getClipRects here? + if (context.usesCache() && + parentLayer->clipper().cachedClipRects(context)) { + clipRects = *parentLayer->clipper().cachedClipRects(context); } else { - clipRects.reset(PaintInfo::infiniteRect()); - } - - if (m_renderer.style()->position() == AbsolutePosition) { - clipRects.setOverflowClipRect(clipRects.posClipRect()); - } - - // This offset cannot use convertToLayerCoords, because sometimes our rootLayer may be across - // some transformed layer boundary, for example, in the RenderLayerCompositor overlapMap, where - // clipRects are needed in view space. - LayoutPoint offset = roundedLayoutPoint(m_renderer.localToContainerPoint(FloatPoint(), context.rootLayer->renderer())); - if (m_renderer.hasOverflowClip()) { - ClipRect newOverflowClip = m_renderer.overflowClipRect(offset); - newOverflowClip.setHasRadius(m_renderer.style()->hasBorderRadius()); - clipRects.setOverflowClipRect(intersection(newOverflowClip, clipRects.overflowClipRect())); - if (m_renderer.isPositioned()) - clipRects.setPosClipRect(intersection(newOverflowClip, clipRects.posClipRect())); - } - - if (m_renderer.hasClip()) { - LayoutRect newClip = m_renderer.clipRect(offset); - clipRects.setPosClipRect(intersection(newClip, clipRects.posClipRect())); - clipRects.setOverflowClipRect(intersection(newClip, clipRects.overflowClipRect())); + parentLayer->clipper().calculateClipRects(context, clipRects); } + } else { + clipRects.reset(PaintInfo::infiniteRect()); + } + + if (m_renderer.style()->position() == AbsolutePosition) { + clipRects.setOverflowClipRect(clipRects.posClipRect()); + } + + // This offset cannot use convertToLayerCoords, because sometimes our + // rootLayer may be across some transformed layer boundary, for example, in + // the RenderLayerCompositor overlapMap, where clipRects are needed in view + // space. + LayoutPoint offset = roundedLayoutPoint(m_renderer.localToContainerPoint( + FloatPoint(), context.rootLayer->renderer())); + if (m_renderer.hasOverflowClip()) { + ClipRect newOverflowClip = m_renderer.overflowClipRect(offset); + newOverflowClip.setHasRadius(m_renderer.style()->hasBorderRadius()); + clipRects.setOverflowClipRect( + intersection(newOverflowClip, clipRects.overflowClipRect())); + if (m_renderer.isPositioned()) + clipRects.setPosClipRect( + intersection(newOverflowClip, clipRects.posClipRect())); + } + + if (m_renderer.hasClip()) { + LayoutRect newClip = m_renderer.clipRect(offset); + clipRects.setPosClipRect(intersection(newClip, clipRects.posClipRect())); + clipRects.setOverflowClipRect( + intersection(newClip, clipRects.overflowClipRect())); + } } -ClipRect RenderLayerClipper::backgroundClipRect(const ClipRectsContext& context) const -{ - ASSERT(m_renderer.layer()->parent()); +ClipRect RenderLayerClipper::backgroundClipRect( + const ClipRectsContext& context) const { + ASSERT(m_renderer.layer()->parent()); - ClipRects parentClipRects; - if (m_renderer.layer() == context.rootLayer) - parentClipRects.reset(PaintInfo::infiniteRect()); - else - m_renderer.layer()->parent()->clipper().getOrCalculateClipRects(context, parentClipRects); + ClipRects parentClipRects; + if (m_renderer.layer() == context.rootLayer) + parentClipRects.reset(PaintInfo::infiniteRect()); + else + m_renderer.layer()->parent()->clipper().getOrCalculateClipRects( + context, parentClipRects); - if (m_renderer.style()->position() == AbsolutePosition) - return parentClipRects.posClipRect(); - return parentClipRects.overflowClipRect(); + if (m_renderer.style()->position() == AbsolutePosition) + return parentClipRects.posClipRect(); + return parentClipRects.overflowClipRect(); } -void RenderLayerClipper::getOrCalculateClipRects(const ClipRectsContext& context, ClipRects& clipRects) const -{ - if (context.usesCache()) - clipRects = *getClipRects(context); - else - calculateClipRects(context, clipRects); +void RenderLayerClipper::getOrCalculateClipRects( + const ClipRectsContext& context, + ClipRects& clipRects) const { + if (context.usesCache()) + clipRects = *getClipRects(context); + else + calculateClipRects(context, clipRects); } -RenderLayer* RenderLayerClipper::clippingRootForPainting() const -{ - const RenderLayer* current = m_renderer.layer(); - while (current) { - if (current->isRootLayer()) - return const_cast(current); - - current = current->compositingContainer(); - ASSERT(current); - if (current->renderer()->transform()) - return const_cast(current); - } +RenderLayer* RenderLayerClipper::clippingRootForPainting() const { + const RenderLayer* current = m_renderer.layer(); + while (current) { + if (current->isRootLayer()) + return const_cast(current); - ASSERT_NOT_REACHED(); - return 0; + current = current->compositingContainer(); + ASSERT(current); + if (current->renderer()->transform()) + return const_cast(current); + } + + ASSERT_NOT_REACHED(); + return 0; } -} // namespace blink +} // namespace blink diff --git a/sky/engine/core/rendering/RenderLayerClipper.h b/sky/engine/core/rendering/RenderLayerClipper.h index 883dbf83ba3e0..f5b4b23e949ce 100644 --- a/sky/engine/core/rendering/RenderLayerClipper.h +++ b/sky/engine/core/rendering/RenderLayerClipper.h @@ -25,7 +25,7 @@ * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Alternatively, the contents of this file may be used under the terms * of either the Mozilla Public License Version 1.1, found at @@ -53,76 +53,77 @@ namespace blink { class RenderLayer; class ClipRectsContext { -public: - ClipRectsContext(const RenderLayer* root, ClipRectsCacheSlot slot, const LayoutSize& accumulation = LayoutSize()) - : rootLayer(root) - , cacheSlot(slot) - , subPixelAccumulation(accumulation) - { - } - - bool usesCache() const - { - return cacheSlot != UncachedClipRects; - } - - const RenderLayer* const rootLayer; - -private: - friend class RenderLayerClipper; - - ClipRectsCacheSlot cacheSlot; - LayoutSize subPixelAccumulation; + public: + ClipRectsContext(const RenderLayer* root, + ClipRectsCacheSlot slot, + const LayoutSize& accumulation = LayoutSize()) + : rootLayer(root), cacheSlot(slot), subPixelAccumulation(accumulation) {} + + bool usesCache() const { return cacheSlot != UncachedClipRects; } + + const RenderLayer* const rootLayer; + + private: + friend class RenderLayerClipper; + + ClipRectsCacheSlot cacheSlot; + LayoutSize subPixelAccumulation; }; class RenderLayerClipper { - WTF_MAKE_NONCOPYABLE(RenderLayerClipper); -public: - explicit RenderLayerClipper(RenderBox&); + WTF_MAKE_NONCOPYABLE(RenderLayerClipper); + + public: + explicit RenderLayerClipper(RenderBox&); - void clearClipRectsIncludingDescendants(); - void clearClipRectsIncludingDescendants(ClipRectsCacheSlot); + void clearClipRectsIncludingDescendants(); + void clearClipRectsIncludingDescendants(ClipRectsCacheSlot); - LayoutRect localClipRect() const; // Returns the background clip rect of the layer in the local coordinate space. + LayoutRect localClipRect() const; // Returns the background clip rect of the + // layer in the local coordinate space. - ClipRects* getClipRects(const ClipRectsContext&) const; + ClipRects* getClipRects(const ClipRectsContext&) const; - ClipRect backgroundClipRect(const ClipRectsContext&) const; + ClipRect backgroundClipRect(const ClipRectsContext&) const; - // This method figures out our layerBounds in coordinates relative to - // |rootLayer|. It also computes our clip rects for painting/event handling. - void calculateRects(const ClipRectsContext&, const LayoutRect& paintDirtyRect, LayoutRect& layerBounds, - ClipRect& backgroundRect, const LayoutPoint* offsetFromRoot = 0) const; + // This method figures out our layerBounds in coordinates relative to + // |rootLayer|. It also computes our clip rects for painting/event handling. + void calculateRects(const ClipRectsContext&, + const LayoutRect& paintDirtyRect, + LayoutRect& layerBounds, + ClipRect& backgroundRect, + const LayoutPoint* offsetFromRoot = 0) const; -private: - void calculateClipRects(const ClipRectsContext&, ClipRects&) const; + private: + void calculateClipRects(const ClipRectsContext&, ClipRects&) const; - ClipRects* clipRectsIfCached(const ClipRectsContext&) const; - ClipRects* storeClipRectsInCache(const ClipRectsContext&, ClipRects* parentClipRects, const ClipRects&) const; + ClipRects* clipRectsIfCached(const ClipRectsContext&) const; + ClipRects* storeClipRectsInCache(const ClipRectsContext&, + ClipRects* parentClipRects, + const ClipRects&) const; - // cachedClipRects looks buggy: It doesn't check whether context.rootLayer and entry.root match. - // FIXME: Move callers to clipRectsIfCached, which does the proper checks. - ClipRects* cachedClipRects(const ClipRectsContext& context) const - { - return m_cache ? m_cache->get(context.cacheSlot).clipRects.get() : 0; - } + // cachedClipRects looks buggy: It doesn't check whether context.rootLayer and + // entry.root match. + // FIXME: Move callers to clipRectsIfCached, which does the proper checks. + ClipRects* cachedClipRects(const ClipRectsContext& context) const { + return m_cache ? m_cache->get(context.cacheSlot).clipRects.get() : 0; + } - void getOrCalculateClipRects(const ClipRectsContext&, ClipRects&) const; + void getOrCalculateClipRects(const ClipRectsContext&, ClipRects&) const; - RenderLayer* clippingRootForPainting() const; + RenderLayer* clippingRootForPainting() const; - ClipRectsCache& cache() const - { - if (!m_cache) - m_cache = adoptPtr(new ClipRectsCache); - return *m_cache; - } + ClipRectsCache& cache() const { + if (!m_cache) + m_cache = adoptPtr(new ClipRectsCache); + return *m_cache; + } - // FIXME: Could this be a RenderBox? - RenderBox& m_renderer; - mutable OwnPtr m_cache; + // FIXME: Could this be a RenderBox? + RenderBox& m_renderer; + mutable OwnPtr m_cache; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_RENDERLAYERCLIPPER_H_ diff --git a/sky/engine/core/rendering/RenderLayerStackingNode.cpp b/sky/engine/core/rendering/RenderLayerStackingNode.cpp index 2267740821e7c..f2245fc101fb5 100644 --- a/sky/engine/core/rendering/RenderLayerStackingNode.cpp +++ b/sky/engine/core/rendering/RenderLayerStackingNode.cpp @@ -1,5 +1,6 @@ /* - * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. + * All rights reserved. * * Portions are Copyright (C) 1998 Netscape Communications Corporation. * @@ -24,7 +25,7 @@ * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Alternatively, the contents of this file may be used under the terms * of either the Mozilla Public License Version 1.1, found at @@ -53,218 +54,213 @@ namespace blink { // in order to determine if we shoulBeNormalFlowOnly() we have to ask the render // layer about some of its state. RenderLayerStackingNode::RenderLayerStackingNode(RenderLayer* layer) - : m_layer(layer) - , m_normalFlowListDirty(true) + : m_layer(layer), + m_normalFlowListDirty(true) #if ENABLE(ASSERT) - , m_layerListMutationAllowed(true) - , m_stackingParent(0) + , + m_layerListMutationAllowed(true), + m_stackingParent(0) #endif { - m_isNormalFlowOnly = shouldBeNormalFlowOnly(); + m_isNormalFlowOnly = shouldBeNormalFlowOnly(); - // Non-stacking contexts should have empty z-order lists. As this is already the case, - // there is no need to dirty / recompute these lists. - m_zOrderListsDirty = isStackingContext(); + // Non-stacking contexts should have empty z-order lists. As this is already + // the case, there is no need to dirty / recompute these lists. + m_zOrderListsDirty = isStackingContext(); } -RenderLayerStackingNode::~RenderLayerStackingNode() -{ +RenderLayerStackingNode::~RenderLayerStackingNode() { #if ENABLE(ASSERT) - if (!renderer()->documentBeingDestroyed()) { - ASSERT(!isInStackingParentZOrderLists()); - ASSERT(!isInStackingParentNormalFlowList()); + if (!renderer()->documentBeingDestroyed()) { + ASSERT(!isInStackingParentZOrderLists()); + ASSERT(!isInStackingParentNormalFlowList()); - updateStackingParentForZOrderLists(0); - updateStackingParentForNormalFlowList(0); - } + updateStackingParentForZOrderLists(0); + updateStackingParentForNormalFlowList(0); + } #endif } // Helper for the sorting of layers by z-index. -static inline bool compareZIndex(RenderLayerStackingNode* first, RenderLayerStackingNode* second) -{ - return first->zIndex() < second->zIndex(); +static inline bool compareZIndex(RenderLayerStackingNode* first, + RenderLayerStackingNode* second) { + return first->zIndex() < second->zIndex(); } -void RenderLayerStackingNode::dirtyZOrderLists() -{ - ASSERT(m_layerListMutationAllowed); - ASSERT(isStackingContext()); +void RenderLayerStackingNode::dirtyZOrderLists() { + ASSERT(m_layerListMutationAllowed); + ASSERT(isStackingContext()); #if ENABLE(ASSERT) - updateStackingParentForZOrderLists(0); + updateStackingParentForZOrderLists(0); #endif - if (m_zOrderList) - m_zOrderList->clear(); - m_zOrderListsDirty = true; + if (m_zOrderList) + m_zOrderList->clear(); + m_zOrderListsDirty = true; } -void RenderLayerStackingNode::dirtyStackingContextZOrderLists() -{ - if (RenderLayerStackingNode* stackingNode = ancestorStackingContextNode()) - stackingNode->dirtyZOrderLists(); +void RenderLayerStackingNode::dirtyStackingContextZOrderLists() { + if (RenderLayerStackingNode* stackingNode = ancestorStackingContextNode()) + stackingNode->dirtyZOrderLists(); } -void RenderLayerStackingNode::dirtyNormalFlowList() -{ - ASSERT(m_layerListMutationAllowed); +void RenderLayerStackingNode::dirtyNormalFlowList() { + ASSERT(m_layerListMutationAllowed); #if ENABLE(ASSERT) - updateStackingParentForNormalFlowList(0); + updateStackingParentForNormalFlowList(0); #endif - if (m_normalFlowList) - m_normalFlowList->clear(); - m_normalFlowListDirty = true; + if (m_normalFlowList) + m_normalFlowList->clear(); + m_normalFlowListDirty = true; } -void RenderLayerStackingNode::rebuildZOrderLists() -{ - ASSERT(m_layerListMutationAllowed); - ASSERT(isDirtyStackingContext()); +void RenderLayerStackingNode::rebuildZOrderLists() { + ASSERT(m_layerListMutationAllowed); + ASSERT(isDirtyStackingContext()); - for (RenderLayer* child = layer()->firstChild(); child; child = child->nextSibling()) - child->stackingNode()->collectLayers(m_zOrderList); + for (RenderLayer* child = layer()->firstChild(); child; + child = child->nextSibling()) + child->stackingNode()->collectLayers(m_zOrderList); - if (m_zOrderList) - std::stable_sort(m_zOrderList->begin(), m_zOrderList->end(), compareZIndex); + if (m_zOrderList) + std::stable_sort(m_zOrderList->begin(), m_zOrderList->end(), compareZIndex); #if ENABLE(ASSERT) - updateStackingParentForZOrderLists(this); + updateStackingParentForZOrderLists(this); #endif - m_zOrderListsDirty = false; + m_zOrderListsDirty = false; } -void RenderLayerStackingNode::updateNormalFlowList() -{ - if (!m_normalFlowListDirty) - return; +void RenderLayerStackingNode::updateNormalFlowList() { + if (!m_normalFlowListDirty) + return; - ASSERT(m_layerListMutationAllowed); + ASSERT(m_layerListMutationAllowed); - for (RenderLayer* child = layer()->firstChild(); child; child = child->nextSibling()) { - if (child->stackingNode()->isNormalFlowOnly()) { - if (!m_normalFlowList) - m_normalFlowList = adoptPtr(new Vector); - m_normalFlowList->append(child->stackingNode()); - } + for (RenderLayer* child = layer()->firstChild(); child; + child = child->nextSibling()) { + if (child->stackingNode()->isNormalFlowOnly()) { + if (!m_normalFlowList) + m_normalFlowList = adoptPtr(new Vector); + m_normalFlowList->append(child->stackingNode()); } + } #if ENABLE(ASSERT) - updateStackingParentForNormalFlowList(this); + updateStackingParentForNormalFlowList(this); #endif - m_normalFlowListDirty = false; + m_normalFlowListDirty = false; } -void RenderLayerStackingNode::collectLayers(OwnPtr >& buffer) -{ - if (!isNormalFlowOnly()) { - if (!buffer) - buffer = adoptPtr(new Vector); - buffer->append(this); - } - - if (!isStackingContext()) { - for (RenderLayer* child = layer()->firstChild(); child; child = child->nextSibling()) - child->stackingNode()->collectLayers(buffer); - } +void RenderLayerStackingNode::collectLayers( + OwnPtr>& buffer) { + if (!isNormalFlowOnly()) { + if (!buffer) + buffer = adoptPtr(new Vector); + buffer->append(this); + } + + if (!isStackingContext()) { + for (RenderLayer* child = layer()->firstChild(); child; + child = child->nextSibling()) + child->stackingNode()->collectLayers(buffer); + } } #if ENABLE(ASSERT) -bool RenderLayerStackingNode::isInStackingParentZOrderLists() const -{ - if (!m_stackingParent || m_stackingParent->zOrderListsDirty()) - return false; +bool RenderLayerStackingNode::isInStackingParentZOrderLists() const { + if (!m_stackingParent || m_stackingParent->zOrderListsDirty()) + return false; - if (m_stackingParent->zOrderList() && m_stackingParent->zOrderList()->find(this) != kNotFound) - return true; + if (m_stackingParent->zOrderList() && + m_stackingParent->zOrderList()->find(this) != kNotFound) + return true; - return false; + return false; } -bool RenderLayerStackingNode::isInStackingParentNormalFlowList() const -{ - if (!m_stackingParent || m_stackingParent->normalFlowListDirty()) - return false; +bool RenderLayerStackingNode::isInStackingParentNormalFlowList() const { + if (!m_stackingParent || m_stackingParent->normalFlowListDirty()) + return false; - return (m_stackingParent->normalFlowList() && m_stackingParent->normalFlowList()->find(this) != kNotFound); + return (m_stackingParent->normalFlowList() && + m_stackingParent->normalFlowList()->find(this) != kNotFound); } -void RenderLayerStackingNode::updateStackingParentForZOrderLists(RenderLayerStackingNode* stackingParent) -{ - if (m_zOrderList) { - for (size_t i = 0; i < m_zOrderList->size(); ++i) - m_zOrderList->at(i)->setStackingParent(stackingParent); - } +void RenderLayerStackingNode::updateStackingParentForZOrderLists( + RenderLayerStackingNode* stackingParent) { + if (m_zOrderList) { + for (size_t i = 0; i < m_zOrderList->size(); ++i) + m_zOrderList->at(i)->setStackingParent(stackingParent); + } } -void RenderLayerStackingNode::updateStackingParentForNormalFlowList(RenderLayerStackingNode* stackingParent) -{ - if (m_normalFlowList) { - for (size_t i = 0; i < m_normalFlowList->size(); ++i) - m_normalFlowList->at(i)->setStackingParent(stackingParent); - } +void RenderLayerStackingNode::updateStackingParentForNormalFlowList( + RenderLayerStackingNode* stackingParent) { + if (m_normalFlowList) { + for (size_t i = 0; i < m_normalFlowList->size(); ++i) + m_normalFlowList->at(i)->setStackingParent(stackingParent); + } } #endif -void RenderLayerStackingNode::updateLayerListsIfNeeded() -{ - updateZOrderLists(); - updateNormalFlowList(); +void RenderLayerStackingNode::updateLayerListsIfNeeded() { + updateZOrderLists(); + updateNormalFlowList(); } -void RenderLayerStackingNode::updateStackingNodesAfterStyleChange(const RenderStyle* oldStyle) -{ - bool wasStackingContext = oldStyle ? !oldStyle->hasAutoZIndex() : false; - unsigned oldZIndex = oldStyle ? oldStyle->zIndex() : 0; +void RenderLayerStackingNode::updateStackingNodesAfterStyleChange( + const RenderStyle* oldStyle) { + bool wasStackingContext = oldStyle ? !oldStyle->hasAutoZIndex() : false; + unsigned oldZIndex = oldStyle ? oldStyle->zIndex() : 0; - bool isStackingContext = this->isStackingContext(); - if (isStackingContext == wasStackingContext && oldZIndex == zIndex()) - return; + bool isStackingContext = this->isStackingContext(); + if (isStackingContext == wasStackingContext && oldZIndex == zIndex()) + return; - dirtyStackingContextZOrderLists(); + dirtyStackingContextZOrderLists(); - if (isStackingContext) - dirtyZOrderLists(); - else - clearZOrderLists(); + if (isStackingContext) + dirtyZOrderLists(); + else + clearZOrderLists(); } // FIXME: Rename shouldBeNormalFlowOnly to something more accurate now that CSS // 2.1 defines the term "normal flow". -bool RenderLayerStackingNode::shouldBeNormalFlowOnly() const -{ - return !isStackingContext() && !renderer()->isPositioned(); +bool RenderLayerStackingNode::shouldBeNormalFlowOnly() const { + return !isStackingContext() && !renderer()->isPositioned(); } -void RenderLayerStackingNode::updateIsNormalFlowOnly() -{ - bool isNormalFlowOnly = shouldBeNormalFlowOnly(); - if (isNormalFlowOnly == this->isNormalFlowOnly()) - return; - - m_isNormalFlowOnly = isNormalFlowOnly; - if (RenderLayer* p = layer()->parent()) - p->stackingNode()->dirtyNormalFlowList(); - dirtyStackingContextZOrderLists(); +void RenderLayerStackingNode::updateIsNormalFlowOnly() { + bool isNormalFlowOnly = shouldBeNormalFlowOnly(); + if (isNormalFlowOnly == this->isNormalFlowOnly()) + return; + + m_isNormalFlowOnly = isNormalFlowOnly; + if (RenderLayer* p = layer()->parent()) + p->stackingNode()->dirtyNormalFlowList(); + dirtyStackingContextZOrderLists(); } -RenderLayerStackingNode* RenderLayerStackingNode::ancestorStackingContextNode() const -{ - for (RenderLayer* ancestor = layer()->parent(); ancestor; ancestor = ancestor->parent()) { - RenderLayerStackingNode* stackingNode = ancestor->stackingNode(); - if (stackingNode->isStackingContext()) - return stackingNode; - } - return 0; +RenderLayerStackingNode* RenderLayerStackingNode::ancestorStackingContextNode() + const { + for (RenderLayer* ancestor = layer()->parent(); ancestor; + ancestor = ancestor->parent()) { + RenderLayerStackingNode* stackingNode = ancestor->stackingNode(); + if (stackingNode->isStackingContext()) + return stackingNode; + } + return 0; } -RenderBox* RenderLayerStackingNode::renderer() const -{ - return m_layer->renderer(); +RenderBox* RenderLayerStackingNode::renderer() const { + return m_layer->renderer(); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/core/rendering/RenderLayerStackingNode.h b/sky/engine/core/rendering/RenderLayerStackingNode.h index 03d6d3814e50e..97cef98b7f156 100644 --- a/sky/engine/core/rendering/RenderLayerStackingNode.h +++ b/sky/engine/core/rendering/RenderLayerStackingNode.h @@ -25,7 +25,7 @@ * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Alternatively, the contents of this file may be used under the terms * of either the Mozilla Public License Version 1.1, found at @@ -56,151 +56,161 @@ class RenderLayer; class RenderStyle; class RenderLayerStackingNode { - WTF_MAKE_NONCOPYABLE(RenderLayerStackingNode); -public: - explicit RenderLayerStackingNode(RenderLayer*); - ~RenderLayerStackingNode(); + WTF_MAKE_NONCOPYABLE(RenderLayerStackingNode); - unsigned zIndex() const { return renderer()->style()->zIndex(); } + public: + explicit RenderLayerStackingNode(RenderLayer*); + ~RenderLayerStackingNode(); - // A stacking context is a layer that has a non-auto z-index. - bool isStackingContext() const { return !renderer()->style()->hasAutoZIndex(); } + unsigned zIndex() const { return renderer()->style()->zIndex(); } - // Update our normal and z-index lists. - void updateLayerListsIfNeeded(); + // A stacking context is a layer that has a non-auto z-index. + bool isStackingContext() const { + return !renderer()->style()->hasAutoZIndex(); + } - bool zOrderListsDirty() const { return m_zOrderListsDirty; } - void dirtyZOrderLists(); - void updateZOrderLists(); - void clearZOrderLists(); - void dirtyStackingContextZOrderLists(); + // Update our normal and z-index lists. + void updateLayerListsIfNeeded(); - bool hasPositiveZOrderList() const { return zOrderList() && zOrderList()->size(); } + bool zOrderListsDirty() const { return m_zOrderListsDirty; } + void dirtyZOrderLists(); + void updateZOrderLists(); + void clearZOrderLists(); + void dirtyStackingContextZOrderLists(); - // FIXME: should check for dirtiness here? - bool isNormalFlowOnly() const { return m_isNormalFlowOnly; } - void updateIsNormalFlowOnly(); - bool normalFlowListDirty() const { return m_normalFlowListDirty; } - void dirtyNormalFlowList(); + bool hasPositiveZOrderList() const { + return zOrderList() && zOrderList()->size(); + } - void updateStackingNodesAfterStyleChange(const RenderStyle* oldStyle); + // FIXME: should check for dirtiness here? + bool isNormalFlowOnly() const { return m_isNormalFlowOnly; } + void updateIsNormalFlowOnly(); + bool normalFlowListDirty() const { return m_normalFlowListDirty; } + void dirtyNormalFlowList(); - RenderLayerStackingNode* ancestorStackingContextNode() const; + void updateStackingNodesAfterStyleChange(const RenderStyle* oldStyle); - // Gets the enclosing stacking context for this node, possibly the node - // itself, if it is a stacking context. - RenderLayerStackingNode* enclosingStackingContextNode() { return isStackingContext() ? this : ancestorStackingContextNode(); } + RenderLayerStackingNode* ancestorStackingContextNode() const; - RenderLayer* layer() const { return m_layer; } + // Gets the enclosing stacking context for this node, possibly the node + // itself, if it is a stacking context. + RenderLayerStackingNode* enclosingStackingContextNode() { + return isStackingContext() ? this : ancestorStackingContextNode(); + } + + RenderLayer* layer() const { return m_layer; } #if ENABLE(ASSERT) - bool layerListMutationAllowed() const { return m_layerListMutationAllowed; } - void setLayerListMutationAllowed(bool flag) { m_layerListMutationAllowed = flag; } + bool layerListMutationAllowed() const { return m_layerListMutationAllowed; } + void setLayerListMutationAllowed(bool flag) { + m_layerListMutationAllowed = flag; + } #endif -private: - friend class RenderLayerStackingNodeIterator; - friend class RenderLayerStackingNodeReverseIterator; - friend class RenderTreeAsText; + private: + friend class RenderLayerStackingNodeIterator; + friend class RenderLayerStackingNodeReverseIterator; + friend class RenderTreeAsText; - Vector* zOrderList() const - { - ASSERT(!m_zOrderListsDirty); - ASSERT(isStackingContext() || !m_zOrderList); - return m_zOrderList.get(); - } + Vector* zOrderList() const { + ASSERT(!m_zOrderListsDirty); + ASSERT(isStackingContext() || !m_zOrderList); + return m_zOrderList.get(); + } - Vector* normalFlowList() const - { - ASSERT(!m_normalFlowListDirty); - return m_normalFlowList.get(); - } + Vector* normalFlowList() const { + ASSERT(!m_normalFlowListDirty); + return m_normalFlowList.get(); + } - void rebuildZOrderLists(); - void collectLayers(OwnPtr >& zOrderList); + void rebuildZOrderLists(); + void collectLayers(OwnPtr>& zOrderList); #if ENABLE(ASSERT) - bool isInStackingParentZOrderLists() const; - bool isInStackingParentNormalFlowList() const; - void updateStackingParentForZOrderLists(RenderLayerStackingNode* stackingParent); - void updateStackingParentForNormalFlowList(RenderLayerStackingNode* stackingParent); - void setStackingParent(RenderLayerStackingNode* stackingParent) { m_stackingParent = stackingParent; } + bool isInStackingParentZOrderLists() const; + bool isInStackingParentNormalFlowList() const; + void updateStackingParentForZOrderLists( + RenderLayerStackingNode* stackingParent); + void updateStackingParentForNormalFlowList( + RenderLayerStackingNode* stackingParent); + void setStackingParent(RenderLayerStackingNode* stackingParent) { + m_stackingParent = stackingParent; + } #endif - bool shouldBeNormalFlowOnly() const; + bool shouldBeNormalFlowOnly() const; - void updateNormalFlowList(); + void updateNormalFlowList(); - bool isDirtyStackingContext() const { return m_zOrderListsDirty && isStackingContext(); } + bool isDirtyStackingContext() const { + return m_zOrderListsDirty && isStackingContext(); + } - // FIXME: Investigate changing this to Renderbox. - RenderBox* renderer() const; + // FIXME: Investigate changing this to Renderbox. + RenderBox* renderer() const; - RenderLayer* m_layer; + RenderLayer* m_layer; - // m_zOrderList holds a sorted list of all the descendant nodes within - // that have z-indices of 0 or greater (auto will count as 0). - OwnPtr > m_zOrderList; + // m_zOrderList holds a sorted list of all the descendant nodes within + // that have z-indices of 0 or greater (auto will count as 0). + OwnPtr> m_zOrderList; - // This list contains child nodes that cannot create stacking contexts. - OwnPtr > m_normalFlowList; + // This list contains child nodes that cannot create stacking contexts. + OwnPtr> m_normalFlowList; - unsigned m_zOrderListsDirty : 1; - unsigned m_normalFlowListDirty: 1; - unsigned m_isNormalFlowOnly : 1; + unsigned m_zOrderListsDirty : 1; + unsigned m_normalFlowListDirty : 1; + unsigned m_isNormalFlowOnly : 1; #if ENABLE(ASSERT) - unsigned m_layerListMutationAllowed : 1; - RenderLayerStackingNode* m_stackingParent; + unsigned m_layerListMutationAllowed : 1; + RenderLayerStackingNode* m_stackingParent; #endif }; -inline void RenderLayerStackingNode::clearZOrderLists() -{ - ASSERT(!isStackingContext()); +inline void RenderLayerStackingNode::clearZOrderLists() { + ASSERT(!isStackingContext()); #if ENABLE(ASSERT) - updateStackingParentForZOrderLists(0); + updateStackingParentForZOrderLists(0); #endif - m_zOrderList.clear(); + m_zOrderList.clear(); } -inline void RenderLayerStackingNode::updateZOrderLists() -{ - if (!m_zOrderListsDirty) - return; +inline void RenderLayerStackingNode::updateZOrderLists() { + if (!m_zOrderListsDirty) + return; - if (!isStackingContext()) { - clearZOrderLists(); - m_zOrderListsDirty = false; - return; - } + if (!isStackingContext()) { + clearZOrderLists(); + m_zOrderListsDirty = false; + return; + } - rebuildZOrderLists(); + rebuildZOrderLists(); } #if ENABLE(ASSERT) class LayerListMutationDetector { -public: - explicit LayerListMutationDetector(RenderLayerStackingNode* stackingNode) - : m_stackingNode(stackingNode) - , m_previousMutationAllowedState(stackingNode->layerListMutationAllowed()) - { - m_stackingNode->setLayerListMutationAllowed(false); - } - - ~LayerListMutationDetector() - { - m_stackingNode->setLayerListMutationAllowed(m_previousMutationAllowedState); - } - -private: - RenderLayerStackingNode* m_stackingNode; - bool m_previousMutationAllowedState; + public: + explicit LayerListMutationDetector(RenderLayerStackingNode* stackingNode) + : m_stackingNode(stackingNode), + m_previousMutationAllowedState( + stackingNode->layerListMutationAllowed()) { + m_stackingNode->setLayerListMutationAllowed(false); + } + + ~LayerListMutationDetector() { + m_stackingNode->setLayerListMutationAllowed(m_previousMutationAllowedState); + } + + private: + RenderLayerStackingNode* m_stackingNode; + bool m_previousMutationAllowedState; }; #endif -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_RENDERLAYERSTACKINGNODE_H_ diff --git a/sky/engine/core/rendering/RenderLayerStackingNodeIterator.cpp b/sky/engine/core/rendering/RenderLayerStackingNodeIterator.cpp index ea4928de98356..269b8edb928f1 100644 --- a/sky/engine/core/rendering/RenderLayerStackingNodeIterator.cpp +++ b/sky/engine/core/rendering/RenderLayerStackingNodeIterator.cpp @@ -35,77 +35,74 @@ namespace blink { -RenderLayerStackingNode* RenderLayerStackingNodeIterator::next() -{ - if (m_remainingChildren & NormalFlowChildren) { - Vector* normalFlowList = m_root.normalFlowList(); - if (normalFlowList && m_index < normalFlowList->size()) - return normalFlowList->at(m_index++); - - m_index = 0; - m_remainingChildren &= ~NormalFlowChildren; - } - - if (m_remainingChildren & PositiveZOrderChildren) { - Vector* zOrderList = m_root.zOrderList(); - if (zOrderList && m_index < zOrderList->size()) - return zOrderList->at(m_index++); - - m_index = 0; - m_remainingChildren &= ~PositiveZOrderChildren; - } - - return 0; +RenderLayerStackingNode* RenderLayerStackingNodeIterator::next() { + if (m_remainingChildren & NormalFlowChildren) { + Vector* normalFlowList = m_root.normalFlowList(); + if (normalFlowList && m_index < normalFlowList->size()) + return normalFlowList->at(m_index++); + + m_index = 0; + m_remainingChildren &= ~NormalFlowChildren; + } + + if (m_remainingChildren & PositiveZOrderChildren) { + Vector* zOrderList = m_root.zOrderList(); + if (zOrderList && m_index < zOrderList->size()) + return zOrderList->at(m_index++); + + m_index = 0; + m_remainingChildren &= ~PositiveZOrderChildren; + } + + return 0; } -RenderLayerStackingNode* RenderLayerStackingNodeReverseIterator::next() -{ - if (m_remainingChildren & NormalFlowChildren) { - Vector* normalFlowList = m_root.normalFlowList(); - if (normalFlowList && m_index >= 0) - return normalFlowList->at(m_index--); +RenderLayerStackingNode* RenderLayerStackingNodeReverseIterator::next() { + if (m_remainingChildren & NormalFlowChildren) { + Vector* normalFlowList = m_root.normalFlowList(); + if (normalFlowList && m_index >= 0) + return normalFlowList->at(m_index--); - m_remainingChildren &= ~NormalFlowChildren; - setIndexToLastItem(); - } + m_remainingChildren &= ~NormalFlowChildren; + setIndexToLastItem(); + } - if (m_remainingChildren & PositiveZOrderChildren) { - Vector* zOrderList = m_root.zOrderList(); - if (zOrderList && m_index >= 0) - return zOrderList->at(m_index--); + if (m_remainingChildren & PositiveZOrderChildren) { + Vector* zOrderList = m_root.zOrderList(); + if (zOrderList && m_index >= 0) + return zOrderList->at(m_index--); - m_remainingChildren &= ~PositiveZOrderChildren; - setIndexToLastItem(); - } + m_remainingChildren &= ~PositiveZOrderChildren; + setIndexToLastItem(); + } - return 0; + return 0; } -void RenderLayerStackingNodeReverseIterator::setIndexToLastItem() -{ - if (m_remainingChildren & NormalFlowChildren) { - Vector* normalFlowList = m_root.normalFlowList(); - if (normalFlowList) { - m_index = normalFlowList->size() - 1; - return; - } - - m_remainingChildren &= ~NormalFlowChildren; +void RenderLayerStackingNodeReverseIterator::setIndexToLastItem() { + if (m_remainingChildren & NormalFlowChildren) { + Vector* normalFlowList = m_root.normalFlowList(); + if (normalFlowList) { + m_index = normalFlowList->size() - 1; + return; } - if (m_remainingChildren & PositiveZOrderChildren) { - Vector* zOrderList = m_root.zOrderList(); - if (zOrderList) { - m_index = zOrderList->size() - 1; - return; - } + m_remainingChildren &= ~NormalFlowChildren; + } - m_remainingChildren &= ~PositiveZOrderChildren; + if (m_remainingChildren & PositiveZOrderChildren) { + Vector* zOrderList = m_root.zOrderList(); + if (zOrderList) { + m_index = zOrderList->size() - 1; + return; } - // No more list to visit. - ASSERT(!m_remainingChildren); - m_index = -1; + m_remainingChildren &= ~PositiveZOrderChildren; + } + + // No more list to visit. + ASSERT(!m_remainingChildren); + m_index = -1; } -} // namespace blink +} // namespace blink diff --git a/sky/engine/core/rendering/RenderLayerStackingNodeIterator.h b/sky/engine/core/rendering/RenderLayerStackingNodeIterator.h index 5e81f83b1fa93..afe7603e34142 100644 --- a/sky/engine/core/rendering/RenderLayerStackingNodeIterator.h +++ b/sky/engine/core/rendering/RenderLayerStackingNodeIterator.h @@ -36,9 +36,9 @@ namespace blink { enum ChildrenIteration { - NormalFlowChildren = 1, - PositiveZOrderChildren = 1 << 1, - AllChildren = NormalFlowChildren | PositiveZOrderChildren + NormalFlowChildren = 1, + PositiveZOrderChildren = 1 << 1, + AllChildren = NormalFlowChildren | PositiveZOrderChildren }; class RenderLayerStackingNode; @@ -46,45 +46,43 @@ class RenderLayerStackingNode; // This iterator walks the RenderLayerStackingNode lists in the following order: // NormalFlowChildren -> PositiveZOrderChildren. class RenderLayerStackingNodeIterator { - WTF_MAKE_NONCOPYABLE(RenderLayerStackingNodeIterator); -public: - RenderLayerStackingNodeIterator(const RenderLayerStackingNode& root, unsigned whichChildren) - : m_root(root) - , m_remainingChildren(whichChildren) - , m_index(0) - { - } - - RenderLayerStackingNode* next(); - -private: - const RenderLayerStackingNode& m_root; - unsigned m_remainingChildren; - unsigned m_index; + WTF_MAKE_NONCOPYABLE(RenderLayerStackingNodeIterator); + + public: + RenderLayerStackingNodeIterator(const RenderLayerStackingNode& root, + unsigned whichChildren) + : m_root(root), m_remainingChildren(whichChildren), m_index(0) {} + + RenderLayerStackingNode* next(); + + private: + const RenderLayerStackingNode& m_root; + unsigned m_remainingChildren; + unsigned m_index; }; -// This iterator is similar to RenderLayerStackingNodeIterator but it walks the lists in reverse order -// (from the last item to the first one). +// This iterator is similar to RenderLayerStackingNodeIterator but it walks the +// lists in reverse order (from the last item to the first one). class RenderLayerStackingNodeReverseIterator { - WTF_MAKE_NONCOPYABLE(RenderLayerStackingNodeReverseIterator); -public: - RenderLayerStackingNodeReverseIterator(const RenderLayerStackingNode& root, unsigned whichChildren) - : m_root(root) - , m_remainingChildren(whichChildren) - { - setIndexToLastItem(); - } - - RenderLayerStackingNode* next(); - -private: - void setIndexToLastItem(); - - const RenderLayerStackingNode& m_root; - unsigned m_remainingChildren; - int m_index; + WTF_MAKE_NONCOPYABLE(RenderLayerStackingNodeReverseIterator); + + public: + RenderLayerStackingNodeReverseIterator(const RenderLayerStackingNode& root, + unsigned whichChildren) + : m_root(root), m_remainingChildren(whichChildren) { + setIndexToLastItem(); + } + + RenderLayerStackingNode* next(); + + private: + void setIndexToLastItem(); + + const RenderLayerStackingNode& m_root; + unsigned m_remainingChildren; + int m_index; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_RENDERLAYERSTACKINGNODEITERATOR_H_ diff --git a/sky/engine/core/rendering/RenderLineBoxList.cpp b/sky/engine/core/rendering/RenderLineBoxList.cpp index 8748592e149c0..36642da6c7599 100644 --- a/sky/engine/core/rendering/RenderLineBoxList.cpp +++ b/sky/engine/core/rendering/RenderLineBoxList.cpp @@ -38,304 +38,356 @@ namespace blink { #if ENABLE(ASSERT) -RenderLineBoxList::~RenderLineBoxList() -{ - ASSERT(!m_firstLineBox); - ASSERT(!m_lastLineBox); +RenderLineBoxList::~RenderLineBoxList() { + ASSERT(!m_firstLineBox); + ASSERT(!m_lastLineBox); } #endif -void RenderLineBoxList::appendLineBox(InlineFlowBox* box) -{ - checkConsistency(); +void RenderLineBoxList::appendLineBox(InlineFlowBox* box) { + checkConsistency(); - if (!m_firstLineBox) - m_firstLineBox = m_lastLineBox = box; - else { - m_lastLineBox->setNextLineBox(box); - box->setPreviousLineBox(m_lastLineBox); - m_lastLineBox = box; - } + if (!m_firstLineBox) + m_firstLineBox = m_lastLineBox = box; + else { + m_lastLineBox->setNextLineBox(box); + box->setPreviousLineBox(m_lastLineBox); + m_lastLineBox = box; + } - checkConsistency(); + checkConsistency(); } -void RenderLineBoxList::deleteLineBoxTree() -{ - InlineFlowBox* line = m_firstLineBox; - InlineFlowBox* nextLine; - while (line) { - nextLine = line->nextLineBox(); - line->deleteLine(); - line = nextLine; - } - m_firstLineBox = m_lastLineBox = 0; +void RenderLineBoxList::deleteLineBoxTree() { + InlineFlowBox* line = m_firstLineBox; + InlineFlowBox* nextLine; + while (line) { + nextLine = line->nextLineBox(); + line->deleteLine(); + line = nextLine; + } + m_firstLineBox = m_lastLineBox = 0; } -void RenderLineBoxList::extractLineBox(InlineFlowBox* box) -{ - checkConsistency(); +void RenderLineBoxList::extractLineBox(InlineFlowBox* box) { + checkConsistency(); - m_lastLineBox = box->prevLineBox(); - if (box == m_firstLineBox) - m_firstLineBox = 0; - if (box->prevLineBox()) - box->prevLineBox()->setNextLineBox(0); - box->setPreviousLineBox(0); - for (InlineFlowBox* curr = box; curr; curr = curr->nextLineBox()) - curr->setExtracted(); - - checkConsistency(); -} + m_lastLineBox = box->prevLineBox(); + if (box == m_firstLineBox) + m_firstLineBox = 0; + if (box->prevLineBox()) + box->prevLineBox()->setNextLineBox(0); + box->setPreviousLineBox(0); + for (InlineFlowBox* curr = box; curr; curr = curr->nextLineBox()) + curr->setExtracted(); -void RenderLineBoxList::attachLineBox(InlineFlowBox* box) -{ - checkConsistency(); - - if (m_lastLineBox) { - m_lastLineBox->setNextLineBox(box); - box->setPreviousLineBox(m_lastLineBox); - } else - m_firstLineBox = box; - InlineFlowBox* last = box; - for (InlineFlowBox* curr = box; curr; curr = curr->nextLineBox()) { - curr->setExtracted(false); - last = curr; - } - m_lastLineBox = last; + checkConsistency(); +} - checkConsistency(); +void RenderLineBoxList::attachLineBox(InlineFlowBox* box) { + checkConsistency(); + + if (m_lastLineBox) { + m_lastLineBox->setNextLineBox(box); + box->setPreviousLineBox(m_lastLineBox); + } else + m_firstLineBox = box; + InlineFlowBox* last = box; + for (InlineFlowBox* curr = box; curr; curr = curr->nextLineBox()) { + curr->setExtracted(false); + last = curr; + } + m_lastLineBox = last; + + checkConsistency(); } -void RenderLineBoxList::removeLineBox(InlineFlowBox* box) -{ - checkConsistency(); +void RenderLineBoxList::removeLineBox(InlineFlowBox* box) { + checkConsistency(); - if (box == m_firstLineBox) - m_firstLineBox = box->nextLineBox(); - if (box == m_lastLineBox) - m_lastLineBox = box->prevLineBox(); - if (box->nextLineBox()) - box->nextLineBox()->setPreviousLineBox(box->prevLineBox()); - if (box->prevLineBox()) - box->prevLineBox()->setNextLineBox(box->nextLineBox()); + if (box == m_firstLineBox) + m_firstLineBox = box->nextLineBox(); + if (box == m_lastLineBox) + m_lastLineBox = box->prevLineBox(); + if (box->nextLineBox()) + box->nextLineBox()->setPreviousLineBox(box->prevLineBox()); + if (box->prevLineBox()) + box->prevLineBox()->setNextLineBox(box->nextLineBox()); - checkConsistency(); + checkConsistency(); } -void RenderLineBoxList::deleteLineBoxes() -{ - if (m_firstLineBox) { - InlineFlowBox* next; - for (InlineFlowBox* curr = m_firstLineBox; curr; curr = next) { - next = curr->nextLineBox(); - curr->destroy(); - } - m_firstLineBox = 0; - m_lastLineBox = 0; +void RenderLineBoxList::deleteLineBoxes() { + if (m_firstLineBox) { + InlineFlowBox* next; + for (InlineFlowBox* curr = m_firstLineBox; curr; curr = next) { + next = curr->nextLineBox(); + curr->destroy(); } + m_firstLineBox = 0; + m_lastLineBox = 0; + } } -void RenderLineBoxList::dirtyLineBoxes() -{ - for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) - curr->dirtyLineBoxes(); +void RenderLineBoxList::dirtyLineBoxes() { + for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) + curr->dirtyLineBoxes(); } -bool RenderLineBoxList::rangeIntersectsRect(RenderBoxModelObject* renderer, LayoutUnit logicalTop, LayoutUnit logicalBottom, const LayoutRect& rect, const LayoutPoint& offset) const -{ - LayoutUnit physicalStart = logicalTop; - LayoutUnit physicalEnd = logicalBottom; - LayoutUnit physicalExtent = absoluteValue(physicalEnd - physicalStart); - physicalStart = std::min(physicalStart, physicalEnd); - - physicalStart += offset.y(); - if (physicalStart >= rect.maxY() || physicalStart + physicalExtent <= rect.y()) - return false; +bool RenderLineBoxList::rangeIntersectsRect(RenderBoxModelObject* renderer, + LayoutUnit logicalTop, + LayoutUnit logicalBottom, + const LayoutRect& rect, + const LayoutPoint& offset) const { + LayoutUnit physicalStart = logicalTop; + LayoutUnit physicalEnd = logicalBottom; + LayoutUnit physicalExtent = absoluteValue(physicalEnd - physicalStart); + physicalStart = std::min(physicalStart, physicalEnd); + + physicalStart += offset.y(); + if (physicalStart >= rect.maxY() || + physicalStart + physicalExtent <= rect.y()) + return false; - return true; + return true; } -bool RenderLineBoxList::anyLineIntersectsRect(RenderBoxModelObject* renderer, const LayoutRect& rect, const LayoutPoint& offset) const -{ - // We can check the first box and last box and avoid painting/hit testing if we don't - // intersect. This is a quick short-circuit that we can take to avoid walking any lines. - // FIXME: This check is flawed in the following extremely obscure way: - // if some line in the middle has a huge overflow, it might actually extend below the last line. - RootInlineBox& firstRootBox = firstLineBox()->root(); - RootInlineBox& lastRootBox = lastLineBox()->root(); - LayoutUnit firstLineTop = firstLineBox()->logicalTopVisualOverflow(firstRootBox.lineTop()); - LayoutUnit lastLineBottom = lastLineBox()->logicalBottomVisualOverflow(lastRootBox.lineBottom()); - - return rangeIntersectsRect(renderer, firstLineTop, lastLineBottom, rect, offset); +bool RenderLineBoxList::anyLineIntersectsRect(RenderBoxModelObject* renderer, + const LayoutRect& rect, + const LayoutPoint& offset) const { + // We can check the first box and last box and avoid painting/hit testing if + // we don't intersect. This is a quick short-circuit that we can take to + // avoid walking any lines. + // FIXME: This check is flawed in the following extremely obscure way: + // if some line in the middle has a huge overflow, it might actually extend + // below the last line. + RootInlineBox& firstRootBox = firstLineBox()->root(); + RootInlineBox& lastRootBox = lastLineBox()->root(); + LayoutUnit firstLineTop = + firstLineBox()->logicalTopVisualOverflow(firstRootBox.lineTop()); + LayoutUnit lastLineBottom = + lastLineBox()->logicalBottomVisualOverflow(lastRootBox.lineBottom()); + + return rangeIntersectsRect(renderer, firstLineTop, lastLineBottom, rect, + offset); } -bool RenderLineBoxList::lineIntersectsDirtyRect(RenderBoxModelObject* renderer, InlineFlowBox* box, const PaintInfo& paintInfo, const LayoutPoint& offset) const -{ - RootInlineBox& root = box->root(); - LayoutUnit logicalTop = std::min(box->logicalTopVisualOverflow(root.lineTop()), root.selectionTop()); - LayoutUnit logicalBottom = box->logicalBottomVisualOverflow(root.lineBottom()); - - return rangeIntersectsRect(renderer, logicalTop, logicalBottom, paintInfo.rect, offset); +bool RenderLineBoxList::lineIntersectsDirtyRect( + RenderBoxModelObject* renderer, + InlineFlowBox* box, + const PaintInfo& paintInfo, + const LayoutPoint& offset) const { + RootInlineBox& root = box->root(); + LayoutUnit logicalTop = std::min( + box->logicalTopVisualOverflow(root.lineTop()), root.selectionTop()); + LayoutUnit logicalBottom = + box->logicalBottomVisualOverflow(root.lineBottom()); + + return rangeIntersectsRect(renderer, logicalTop, logicalBottom, + paintInfo.rect, offset); } -void RenderLineBoxList::paint(RenderBoxModelObject* renderer, PaintInfo& paintInfo, const LayoutPoint& paintOffset, Vector& layers) const -{ - ASSERT(renderer->isRenderBlock() || (renderer->isRenderInline() && renderer->hasLayer())); // The only way an inline could paint like this is if it has a layer. - - // If we have no lines then we have no work to do. - if (!firstLineBox()) - return; - - if (!anyLineIntersectsRect(renderer, paintInfo.rect, paintOffset)) - return; - - PaintInfo info(paintInfo); - - // See if our root lines intersect with the dirty rect. If so, then we paint - // them. Note that boxes can easily overlap, so we can't make any assumptions - // based off positions of our first line box or our last line box. - for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) { - if (lineIntersectsDirtyRect(renderer, curr, info, paintOffset)) { - RootInlineBox& root = curr->root(); - curr->paint(info, paintOffset, root.lineTop(), root.lineBottom(), layers); - } +void RenderLineBoxList::paint(RenderBoxModelObject* renderer, + PaintInfo& paintInfo, + const LayoutPoint& paintOffset, + Vector& layers) const { + ASSERT(renderer->isRenderBlock() || + (renderer->isRenderInline() && renderer->hasLayer())); // The only way + // an inline + // could paint + // like this is + // if it has a + // layer. + + // If we have no lines then we have no work to do. + if (!firstLineBox()) + return; + + if (!anyLineIntersectsRect(renderer, paintInfo.rect, paintOffset)) + return; + + PaintInfo info(paintInfo); + + // See if our root lines intersect with the dirty rect. If so, then we paint + // them. Note that boxes can easily overlap, so we can't make any assumptions + // based off positions of our first line box or our last line box. + for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) { + if (lineIntersectsDirtyRect(renderer, curr, info, paintOffset)) { + RootInlineBox& root = curr->root(); + curr->paint(info, paintOffset, root.lineTop(), root.lineBottom(), layers); } + } } -bool RenderLineBoxList::hitTest(RenderBoxModelObject* renderer, const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset) const -{ - ASSERT(renderer->isRenderBlock() || (renderer->isRenderInline() && renderer->hasLayer())); // The only way an inline could hit test like this is if it has a layer. - - // If we have no lines then we have no work to do. - if (!firstLineBox()) - return false; - - LayoutPoint point = locationInContainer.point(); - LayoutRect rect = IntRect(point.x(), point.y() - locationInContainer.topPadding(), 1, locationInContainer.topPadding() + locationInContainer.bottomPadding() + 1); - - if (!anyLineIntersectsRect(renderer, rect, accumulatedOffset)) - return false; - - // See if our root lines contain the point. If so, then we hit test - // them further. Note that boxes can easily overlap, so we can't make any assumptions - // based off positions of our first line box or our last line box. - for (InlineFlowBox* curr = lastLineBox(); curr; curr = curr->prevLineBox()) { - RootInlineBox& root = curr->root(); - if (rangeIntersectsRect(renderer, curr->logicalTopVisualOverflow(root.lineTop()), curr->logicalBottomVisualOverflow(root.lineBottom()), rect, accumulatedOffset)) { - bool inside = curr->nodeAtPoint(request, result, locationInContainer, accumulatedOffset, root.lineTop(), root.lineBottom()); - if (inside) { - renderer->updateHitTestResult(result, locationInContainer.point() - toLayoutSize(accumulatedOffset)); - return true; - } - } - } +bool RenderLineBoxList::hitTest(RenderBoxModelObject* renderer, + const HitTestRequest& request, + HitTestResult& result, + const HitTestLocation& locationInContainer, + const LayoutPoint& accumulatedOffset) const { + ASSERT(renderer->isRenderBlock() || + (renderer->isRenderInline() && + renderer->hasLayer())); // The only way an inline could hit test like + // this is if it has a layer. + + // If we have no lines then we have no work to do. + if (!firstLineBox()) + return false; + + LayoutPoint point = locationInContainer.point(); + LayoutRect rect = + IntRect(point.x(), point.y() - locationInContainer.topPadding(), 1, + locationInContainer.topPadding() + + locationInContainer.bottomPadding() + 1); + if (!anyLineIntersectsRect(renderer, rect, accumulatedOffset)) return false; -} -void RenderLineBoxList::dirtyLinesFromChangedChild(RenderObject* container, RenderObject* child) -{ - if (!container->parent() || (container->isRenderBlock() && (container->selfNeedsLayout() || !container->isRenderParagraph()))) - return; - - RenderInline* inlineContainer = container->isRenderInline() ? toRenderInline(container) : 0; - InlineBox* firstBox = inlineContainer ? inlineContainer->firstLineBoxIncludingCulling() : firstLineBox(); - - // If we have no first line box, then just bail early. - if (!firstBox) { - // For an empty inline, go ahead and propagate the check up to our parent, unless the parent - // is already dirty. - if (container->isInline() && !container->ancestorLineBoxDirty()) { - container->parent()->dirtyLinesFromChangedChild(container); - container->setAncestorLineBoxDirty(); // Mark the container to avoid dirtying the same lines again across multiple destroy() calls of the same subtree. - } - return; + // See if our root lines contain the point. If so, then we hit test + // them further. Note that boxes can easily overlap, so we can't make any + // assumptions based off positions of our first line box or our last line box. + for (InlineFlowBox* curr = lastLineBox(); curr; curr = curr->prevLineBox()) { + RootInlineBox& root = curr->root(); + if (rangeIntersectsRect( + renderer, curr->logicalTopVisualOverflow(root.lineTop()), + curr->logicalBottomVisualOverflow(root.lineBottom()), rect, + accumulatedOffset)) { + bool inside = curr->nodeAtPoint(request, result, locationInContainer, + accumulatedOffset, root.lineTop(), + root.lineBottom()); + if (inside) { + renderer->updateHitTestResult( + result, + locationInContainer.point() - toLayoutSize(accumulatedOffset)); + return true; + } } + } + + return false; +} - // Try to figure out which line box we belong in. First try to find a previous - // line box by examining our siblings. If we didn't find a line box, then use our - // parent's first line box. - RootInlineBox* box = 0; - RenderObject* curr = 0; - ListHashSet potentialLineBreakObjects; - potentialLineBreakObjects.add(child); - for (curr = child->previousSibling(); curr; curr = curr->previousSibling()) { - potentialLineBreakObjects.add(curr); - - if (curr->isFloatingOrOutOfFlowPositioned()) - continue; - - if (curr->isReplaced()) { - InlineBox* wrapper = toRenderBox(curr)->inlineBoxWrapper(); - if (wrapper) - box = &wrapper->root(); - } else if (curr->isText()) { - InlineTextBox* textBox = toRenderText(curr)->lastTextBox(); - if (textBox) - box = &textBox->root(); - } else if (curr->isRenderInline()) { - InlineBox* lastSiblingBox = toRenderInline(curr)->lastLineBoxIncludingCulling(); - if (lastSiblingBox) - box = &lastSiblingBox->root(); - } - - if (box) - break; +void RenderLineBoxList::dirtyLinesFromChangedChild(RenderObject* container, + RenderObject* child) { + if (!container->parent() || + (container->isRenderBlock() && + (container->selfNeedsLayout() || !container->isRenderParagraph()))) + return; + + RenderInline* inlineContainer = + container->isRenderInline() ? toRenderInline(container) : 0; + InlineBox* firstBox = inlineContainer + ? inlineContainer->firstLineBoxIncludingCulling() + : firstLineBox(); + + // If we have no first line box, then just bail early. + if (!firstBox) { + // For an empty inline, go ahead and propagate the check up to our parent, + // unless the parent is already dirty. + if (container->isInline() && !container->ancestorLineBoxDirty()) { + container->parent()->dirtyLinesFromChangedChild(container); + container->setAncestorLineBoxDirty(); // Mark the container to avoid + // dirtying the same lines again + // across multiple destroy() calls + // of the same subtree. } - if (!box) { - if (inlineContainer && !inlineContainer->alwaysCreateLineBoxes()) { - // https://bugs.webkit.org/show_bug.cgi?id=60778 - // We may have just removed a
with no line box that was our first child. In this case - // we won't find a previous sibling, but firstBox can be pointing to a following sibling. - // This isn't good enough, since we won't locate the root line box that encloses the removed - //
. We have to just over-invalidate a bit and go up to our parent. - if (!inlineContainer->ancestorLineBoxDirty()) { - inlineContainer->parent()->dirtyLinesFromChangedChild(inlineContainer); - inlineContainer->setAncestorLineBoxDirty(); // Mark the container to avoid dirtying the same lines again across multiple destroy() calls of the same subtree. - } - return; - } - box = &firstBox->root(); + return; + } + + // Try to figure out which line box we belong in. First try to find a + // previous line box by examining our siblings. If we didn't find a line box, + // then use our parent's first line box. + RootInlineBox* box = 0; + RenderObject* curr = 0; + ListHashSet potentialLineBreakObjects; + potentialLineBreakObjects.add(child); + for (curr = child->previousSibling(); curr; curr = curr->previousSibling()) { + potentialLineBreakObjects.add(curr); + + if (curr->isFloatingOrOutOfFlowPositioned()) + continue; + + if (curr->isReplaced()) { + InlineBox* wrapper = toRenderBox(curr)->inlineBoxWrapper(); + if (wrapper) + box = &wrapper->root(); + } else if (curr->isText()) { + InlineTextBox* textBox = toRenderText(curr)->lastTextBox(); + if (textBox) + box = &textBox->root(); + } else if (curr->isRenderInline()) { + InlineBox* lastSiblingBox = + toRenderInline(curr)->lastLineBoxIncludingCulling(); + if (lastSiblingBox) + box = &lastSiblingBox->root(); } - // If we found a line box, then dirty it. - if (box) { - RootInlineBox* adjacentBox; - box->markDirty(); - - // dirty the adjacent lines that might be affected - // NOTE: we dirty the previous line because RootInlineBox objects cache - // the address of the first object on the next line after a BR, which we may be - // invalidating here. For more info, see how RenderParagraph::layoutChildren - // calls setLineBreakInfo with the result of findNextLineBreak. findNextLineBreak, - // despite the name, actually returns the first RenderObject after the BR. - // "Typing after pasting line does not appear until after window resize." - adjacentBox = box->prevRootBox(); - if (adjacentBox) - adjacentBox->markDirty(); - adjacentBox = box->nextRootBox(); - // If |child| or any of its immediately previous siblings with culled lineboxes is the object after a line-break in |box| or the linebox after it - // then that means |child| actually sits on the linebox after |box| (or is its line-break object) and so we need to dirty it as well. - if (adjacentBox && (potentialLineBreakObjects.contains(box->lineBreakObj()) || potentialLineBreakObjects.contains(adjacentBox->lineBreakObj()) || isIsolated(container->style()->unicodeBidi()))) - adjacentBox->markDirty(); + if (box) + break; + } + if (!box) { + if (inlineContainer && !inlineContainer->alwaysCreateLineBoxes()) { + // https://bugs.webkit.org/show_bug.cgi?id=60778 + // We may have just removed a
with no line box that was our first + // child. In this case we won't find a previous sibling, but firstBox can + // be pointing to a following sibling. This isn't good enough, since we + // won't locate the root line box that encloses the removed
. We have + // to just over-invalidate a bit and go up to our parent. + if (!inlineContainer->ancestorLineBoxDirty()) { + inlineContainer->parent()->dirtyLinesFromChangedChild(inlineContainer); + inlineContainer->setAncestorLineBoxDirty(); // Mark the container to + // avoid dirtying the same + // lines again across + // multiple destroy() calls + // of the same subtree. + } + return; } + box = &firstBox->root(); + } + + // If we found a line box, then dirty it. + if (box) { + RootInlineBox* adjacentBox; + box->markDirty(); + + // dirty the adjacent lines that might be affected + // NOTE: we dirty the previous line because RootInlineBox objects cache + // the address of the first object on the next line after a BR, which we may + // be invalidating here. For more info, see how + // RenderParagraph::layoutChildren calls setLineBreakInfo with the result of + // findNextLineBreak. findNextLineBreak, despite the name, actually returns + // the first RenderObject after the BR. "Typing + // after pasting line does not appear until after window resize." + adjacentBox = box->prevRootBox(); + if (adjacentBox) + adjacentBox->markDirty(); + adjacentBox = box->nextRootBox(); + // If |child| or any of its immediately previous siblings with culled + // lineboxes is the object after a line-break in |box| or the linebox after + // it then that means |child| actually sits on the linebox after |box| (or + // is its line-break object) and so we need to dirty it as well. + if (adjacentBox && + (potentialLineBreakObjects.contains(box->lineBreakObj()) || + potentialLineBreakObjects.contains(adjacentBox->lineBreakObj()) || + isIsolated(container->style()->unicodeBidi()))) + adjacentBox->markDirty(); + } } #if ENABLE(ASSERT) -void RenderLineBoxList::checkConsistency() const -{ +void RenderLineBoxList::checkConsistency() const { #ifdef CHECK_CONSISTENCY - const InlineFlowBox* prev = 0; - for (const InlineFlowBox* child = m_firstLineBox; child != 0; child = child->nextLineBox()) { - ASSERT(child->prevLineBox() == prev); - prev = child; - } - ASSERT(prev == m_lastLineBox); + const InlineFlowBox* prev = 0; + for (const InlineFlowBox* child = m_firstLineBox; child != 0; + child = child->nextLineBox()) { + ASSERT(child->prevLineBox() == prev); + prev = child; + } + ASSERT(prev == m_lastLineBox); #endif } #endif -} +} // namespace blink diff --git a/sky/engine/core/rendering/RenderLineBoxList.h b/sky/engine/core/rendering/RenderLineBoxList.h index 8df45e6bbb6f9..adcb39f716406 100644 --- a/sky/engine/core/rendering/RenderLineBoxList.h +++ b/sky/engine/core/rendering/RenderLineBoxList.h @@ -26,7 +26,6 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #ifndef SKY_ENGINE_CORE_RENDERING_RENDERLINEBOXLIST_H_ #define SKY_ENGINE_CORE_RENDERING_RENDERLINEBOXLIST_H_ @@ -35,56 +34,65 @@ namespace blink { class RenderLineBoxList { -public: - RenderLineBoxList() - : m_firstLineBox(0) - , m_lastLineBox(0) - { - } + public: + RenderLineBoxList() : m_firstLineBox(0), m_lastLineBox(0) {} #if ENABLE(ASSERT) - ~RenderLineBoxList(); + ~RenderLineBoxList(); #endif - InlineFlowBox* firstLineBox() const { return m_firstLineBox; } - InlineFlowBox* lastLineBox() const { return m_lastLineBox; } - - void checkConsistency() const; - - void appendLineBox(InlineFlowBox*); - - void deleteLineBoxTree(); - void deleteLineBoxes(); - - void extractLineBox(InlineFlowBox*); - void attachLineBox(InlineFlowBox*); - void removeLineBox(InlineFlowBox*); - - void dirtyLineBoxes(); - void dirtyLinesFromChangedChild(RenderObject* parent, RenderObject* child); - - void paint(RenderBoxModelObject*, PaintInfo&, const LayoutPoint&, Vector& layers) const; - bool hitTest(RenderBoxModelObject*, const HitTestRequest&, HitTestResult&, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset) const; - -private: - bool anyLineIntersectsRect(RenderBoxModelObject*, const LayoutRect&, const LayoutPoint&) const; - bool lineIntersectsDirtyRect(RenderBoxModelObject*, InlineFlowBox*, const PaintInfo&, const LayoutPoint&) const; - bool rangeIntersectsRect(RenderBoxModelObject*, LayoutUnit logicalTop, LayoutUnit logicalBottom, const LayoutRect&, const LayoutPoint&) const; - - // For block flows, each box represents the root inline box for a line in the - // paragraph. - // For inline flows, each box represents a portion of that inline. - InlineFlowBox* m_firstLineBox; - InlineFlowBox* m_lastLineBox; + InlineFlowBox* firstLineBox() const { return m_firstLineBox; } + InlineFlowBox* lastLineBox() const { return m_lastLineBox; } + + void checkConsistency() const; + + void appendLineBox(InlineFlowBox*); + + void deleteLineBoxTree(); + void deleteLineBoxes(); + + void extractLineBox(InlineFlowBox*); + void attachLineBox(InlineFlowBox*); + void removeLineBox(InlineFlowBox*); + + void dirtyLineBoxes(); + void dirtyLinesFromChangedChild(RenderObject* parent, RenderObject* child); + + void paint(RenderBoxModelObject*, + PaintInfo&, + const LayoutPoint&, + Vector& layers) const; + bool hitTest(RenderBoxModelObject*, + const HitTestRequest&, + HitTestResult&, + const HitTestLocation& locationInContainer, + const LayoutPoint& accumulatedOffset) const; + + private: + bool anyLineIntersectsRect(RenderBoxModelObject*, + const LayoutRect&, + const LayoutPoint&) const; + bool lineIntersectsDirtyRect(RenderBoxModelObject*, + InlineFlowBox*, + const PaintInfo&, + const LayoutPoint&) const; + bool rangeIntersectsRect(RenderBoxModelObject*, + LayoutUnit logicalTop, + LayoutUnit logicalBottom, + const LayoutRect&, + const LayoutPoint&) const; + + // For block flows, each box represents the root inline box for a line in the + // paragraph. + // For inline flows, each box represents a portion of that inline. + InlineFlowBox* m_firstLineBox; + InlineFlowBox* m_lastLineBox; }; - #if !ENABLE(ASSERT) -inline void RenderLineBoxList::checkConsistency() const -{ -} +inline void RenderLineBoxList::checkConsistency() const {} #endif -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_RENDERLINEBOXLIST_H_ diff --git a/sky/engine/core/rendering/RenderObject.cpp b/sky/engine/core/rendering/RenderObject.cpp index 7079353668922..fc0f32ea74769 100644 --- a/sky/engine/core/rendering/RenderObject.cpp +++ b/sky/engine/core/rendering/RenderObject.cpp @@ -3,9 +3,11 @@ * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2000 Dirk Mueller (mueller@kde.org) * (C) 2004 Allan Sandfeld Jensen (kde@carewolf.com) - * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2011 Apple Inc. All rights reserved. + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2011 Apple Inc. + * All rights reserved. * Copyright (C) 2009 Google Inc. All rights reserved. - * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) + * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. + * (http://www.torchmobile.com/) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -52,1534 +54,1650 @@ namespace blink { #if ENABLE(ASSERT) -RenderObject::SetLayoutNeededForbiddenScope::SetLayoutNeededForbiddenScope(RenderObject& renderObject) - : m_renderObject(renderObject) - , m_preexistingForbidden(m_renderObject.isSetNeedsLayoutForbidden()) -{ - m_renderObject.setNeedsLayoutIsForbidden(true); +RenderObject::SetLayoutNeededForbiddenScope::SetLayoutNeededForbiddenScope( + RenderObject& renderObject) + : m_renderObject(renderObject), + m_preexistingForbidden(m_renderObject.isSetNeedsLayoutForbidden()) { + m_renderObject.setNeedsLayoutIsForbidden(true); } -RenderObject::SetLayoutNeededForbiddenScope::~SetLayoutNeededForbiddenScope() -{ - m_renderObject.setNeedsLayoutIsForbidden(m_preexistingForbidden); +RenderObject::SetLayoutNeededForbiddenScope::~SetLayoutNeededForbiddenScope() { + m_renderObject.setNeedsLayoutIsForbidden(m_preexistingForbidden); } #endif struct SameSizeAsRenderObject { - virtual ~SameSizeAsRenderObject() { } // Allocate vtable pointer. - void* pointers[4]; + virtual ~SameSizeAsRenderObject() {} // Allocate vtable pointer. + void* pointers[4]; #if ENABLE(ASSERT) - unsigned m_debugBitfields : 2; + unsigned m_debugBitfields : 2; #if ENABLE(OILPAN) - unsigned m_oilpanBitfields : 1; + unsigned m_oilpanBitfields : 1; #endif #endif - unsigned m_bitfields; + unsigned m_bitfields; }; -COMPILE_ASSERT(sizeof(RenderObject) == sizeof(SameSizeAsRenderObject), RenderObject_should_stay_small); +COMPILE_ASSERT(sizeof(RenderObject) == sizeof(SameSizeAsRenderObject), + RenderObject_should_stay_small); bool RenderObject::s_affectsParentBlock = false; #if !ENABLE(OILPAN) -void* RenderObject::operator new(size_t sz) -{ - ASSERT(isMainThread()); - return partitionAlloc(Partitions::getRenderingPartition(), sz); +void* RenderObject::operator new(size_t sz) { + ASSERT(isMainThread()); + return partitionAlloc(Partitions::getRenderingPartition(), sz); } -void RenderObject::operator delete(void* ptr) -{ - ASSERT(isMainThread()); - partitionFree(ptr); +void RenderObject::operator delete(void* ptr) { + ASSERT(isMainThread()); + partitionFree(ptr); } #endif -DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, renderObjectCounter, ("RenderObject")); +DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, + renderObjectCounter, + ("RenderObject")); unsigned RenderObject::s_instanceCount = 0; RenderObject::RenderObject() - : m_style(nullptr) - , m_parent(nullptr) - , m_previous(nullptr) - , m_next(nullptr) + : m_style(nullptr), + m_parent(nullptr), + m_previous(nullptr), + m_next(nullptr) #if ENABLE(ASSERT) - , m_setNeedsLayoutForbidden(false) + , + m_setNeedsLayoutForbidden(false) #if ENABLE(OILPAN) - , m_didCallDestroy(false) + , + m_didCallDestroy(false) #endif #endif { #ifndef NDEBUG - renderObjectCounter.increment(); + renderObjectCounter.increment(); #endif - ++s_instanceCount; + ++s_instanceCount; } -RenderObject::~RenderObject() -{ +RenderObject::~RenderObject() { #if ENABLE(OILPAN) - ASSERT(m_didCallDestroy); + ASSERT(m_didCallDestroy); #endif #ifndef NDEBUG - renderObjectCounter.decrement(); + renderObjectCounter.decrement(); #endif - --s_instanceCount; + --s_instanceCount; } -String RenderObject::debugName() const -{ - return renderName(); +String RenderObject::debugName() const { + return renderName(); } -bool RenderObject::isDescendantOf(const RenderObject* obj) const -{ - for (const RenderObject* r = this; r; r = r->m_parent) { - if (r == obj) - return true; - } - return false; +bool RenderObject::isDescendantOf(const RenderObject* obj) const { + for (const RenderObject* r = this; r; r = r->m_parent) { + if (r == obj) + return true; + } + return false; } -void RenderObject::addChild(RenderObject* newChild, RenderObject* beforeChild) -{ - RenderObjectChildList* children = virtualChildren(); - ASSERT(children); - children->insertChildNode(this, newChild, beforeChild); +void RenderObject::addChild(RenderObject* newChild, RenderObject* beforeChild) { + RenderObjectChildList* children = virtualChildren(); + ASSERT(children); + children->insertChildNode(this, newChild, beforeChild); } -void RenderObject::removeChild(RenderObject* oldChild) -{ - RenderObjectChildList* children = virtualChildren(); - ASSERT(children); - if (!children) - return; +void RenderObject::removeChild(RenderObject* oldChild) { + RenderObjectChildList* children = virtualChildren(); + ASSERT(children); + if (!children) + return; - children->removeChildNode(this, oldChild); + children->removeChildNode(this, oldChild); } -RenderObject* RenderObject::nextInPreOrder() const -{ - if (RenderObject* o = slowFirstChild()) - return o; +RenderObject* RenderObject::nextInPreOrder() const { + if (RenderObject* o = slowFirstChild()) + return o; - return nextInPreOrderAfterChildren(); + return nextInPreOrderAfterChildren(); } -RenderObject* RenderObject::nextInPreOrderAfterChildren() const -{ - RenderObject* o = nextSibling(); - if (!o) { - o = parent(); - while (o && !o->nextSibling()) - o = o->parent(); - if (o) - o = o->nextSibling(); - } +RenderObject* RenderObject::nextInPreOrderAfterChildren() const { + RenderObject* o = nextSibling(); + if (!o) { + o = parent(); + while (o && !o->nextSibling()) + o = o->parent(); + if (o) + o = o->nextSibling(); + } - return o; + return o; } -RenderObject* RenderObject::nextInPreOrder(const RenderObject* stayWithin) const -{ - if (RenderObject* o = slowFirstChild()) - return o; - - return nextInPreOrderAfterChildren(stayWithin); -} +RenderObject* RenderObject::nextInPreOrder( + const RenderObject* stayWithin) const { + if (RenderObject* o = slowFirstChild()) + return o; -RenderObject* RenderObject::nextInPreOrderAfterChildren(const RenderObject* stayWithin) const -{ - if (this == stayWithin) - return 0; - - const RenderObject* current = this; - RenderObject* next = current->nextSibling(); - for (; !next; next = current->nextSibling()) { - current = current->parent(); - if (!current || current == stayWithin) - return 0; - } - return next; + return nextInPreOrderAfterChildren(stayWithin); } -RenderObject* RenderObject::previousInPreOrder() const -{ - if (RenderObject* o = previousSibling()) { - while (RenderObject* lastChild = o->slowLastChild()) - o = lastChild; - return o; - } +RenderObject* RenderObject::nextInPreOrderAfterChildren( + const RenderObject* stayWithin) const { + if (this == stayWithin) + return 0; - return parent(); + const RenderObject* current = this; + RenderObject* next = current->nextSibling(); + for (; !next; next = current->nextSibling()) { + current = current->parent(); + if (!current || current == stayWithin) + return 0; + } + return next; } -RenderObject* RenderObject::previousInPreOrder(const RenderObject* stayWithin) const -{ - if (this == stayWithin) - return 0; +RenderObject* RenderObject::previousInPreOrder() const { + if (RenderObject* o = previousSibling()) { + while (RenderObject* lastChild = o->slowLastChild()) + o = lastChild; + return o; + } - return previousInPreOrder(); + return parent(); } -RenderObject* RenderObject::childAt(unsigned index) const -{ - RenderObject* child = slowFirstChild(); - for (unsigned i = 0; child && i < index; i++) - child = child->nextSibling(); - return child; +RenderObject* RenderObject::previousInPreOrder( + const RenderObject* stayWithin) const { + if (this == stayWithin) + return 0; + + return previousInPreOrder(); } -RenderObject* RenderObject::lastLeafChild() const -{ - RenderObject* r = slowLastChild(); - while (r) { - RenderObject* n = 0; - n = r->slowLastChild(); - if (!n) - break; - r = n; - } - return r; +RenderObject* RenderObject::childAt(unsigned index) const { + RenderObject* child = slowFirstChild(); + for (unsigned i = 0; child && i < index; i++) + child = child->nextSibling(); + return child; } -static void addLayers(RenderObject* obj, RenderLayer* parentLayer, RenderObject*& newObject, - RenderLayer*& beforeChild) -{ - if (obj->hasLayer()) { - if (!beforeChild && newObject) { - // We need to figure out the layer that follows newObject. We only do - // this the first time we find a child layer, and then we update the - // pointer values for newObject and beforeChild used by everyone else. - beforeChild = newObject->parent()->findNextLayer(parentLayer, newObject); - newObject = 0; - } - parentLayer->addChild(toRenderBox(obj)->layer(), beforeChild); - return; +RenderObject* RenderObject::lastLeafChild() const { + RenderObject* r = slowLastChild(); + while (r) { + RenderObject* n = 0; + n = r->slowLastChild(); + if (!n) + break; + r = n; + } + return r; +} + +static void addLayers(RenderObject* obj, + RenderLayer* parentLayer, + RenderObject*& newObject, + RenderLayer*& beforeChild) { + if (obj->hasLayer()) { + if (!beforeChild && newObject) { + // We need to figure out the layer that follows newObject. We only do + // this the first time we find a child layer, and then we update the + // pointer values for newObject and beforeChild used by everyone else. + beforeChild = newObject->parent()->findNextLayer(parentLayer, newObject); + newObject = 0; } + parentLayer->addChild(toRenderBox(obj)->layer(), beforeChild); + return; + } - for (RenderObject* curr = obj->slowFirstChild(); curr; curr = curr->nextSibling()) - addLayers(curr, parentLayer, newObject, beforeChild); + for (RenderObject* curr = obj->slowFirstChild(); curr; + curr = curr->nextSibling()) + addLayers(curr, parentLayer, newObject, beforeChild); } -void RenderObject::addLayers(RenderLayer* parentLayer) -{ - if (!parentLayer) - return; +void RenderObject::addLayers(RenderLayer* parentLayer) { + if (!parentLayer) + return; - RenderObject* object = this; - RenderLayer* beforeChild = 0; - blink::addLayers(this, parentLayer, object, beforeChild); + RenderObject* object = this; + RenderLayer* beforeChild = 0; + blink::addLayers(this, parentLayer, object, beforeChild); } -void RenderObject::removeLayers(RenderLayer* parentLayer) -{ - if (!parentLayer) - return; +void RenderObject::removeLayers(RenderLayer* parentLayer) { + if (!parentLayer) + return; - if (hasLayer()) { - parentLayer->removeChild(toRenderBox(this)->layer()); - return; - } + if (hasLayer()) { + parentLayer->removeChild(toRenderBox(this)->layer()); + return; + } - for (RenderObject* curr = slowFirstChild(); curr; curr = curr->nextSibling()) - curr->removeLayers(parentLayer); + for (RenderObject* curr = slowFirstChild(); curr; curr = curr->nextSibling()) + curr->removeLayers(parentLayer); } -void RenderObject::moveLayers(RenderLayer* oldParent, RenderLayer* newParent) -{ - if (!newParent) - return; +void RenderObject::moveLayers(RenderLayer* oldParent, RenderLayer* newParent) { + if (!newParent) + return; - if (hasLayer()) { - RenderLayer* layer = toRenderBox(this)->layer(); - ASSERT(oldParent == layer->parent()); - if (oldParent) - oldParent->removeChild(layer); - newParent->addChild(layer); - return; - } + if (hasLayer()) { + RenderLayer* layer = toRenderBox(this)->layer(); + ASSERT(oldParent == layer->parent()); + if (oldParent) + oldParent->removeChild(layer); + newParent->addChild(layer); + return; + } - for (RenderObject* curr = slowFirstChild(); curr; curr = curr->nextSibling()) - curr->moveLayers(oldParent, newParent); + for (RenderObject* curr = slowFirstChild(); curr; curr = curr->nextSibling()) + curr->moveLayers(oldParent, newParent); } -RenderLayer* RenderObject::findNextLayer(RenderLayer* parentLayer, RenderObject* startPoint, - bool checkParent) -{ - // Error check the parent layer passed in. If it's null, we can't find anything. - if (!parentLayer) - return 0; - - // Step 1: If our layer is a child of the desired parent, then return our layer. - RenderLayer* ourLayer = hasLayer() ? toRenderBox(this)->layer() : 0; - if (ourLayer && ourLayer->parent() == parentLayer) - return ourLayer; - - // Step 2: If we don't have a layer, or our layer is the desired parent, then descend - // into our siblings trying to find the next layer whose parent is the desired parent. - if (!ourLayer || ourLayer == parentLayer) { - for (RenderObject* curr = startPoint ? startPoint->nextSibling() : slowFirstChild(); - curr; curr = curr->nextSibling()) { - RenderLayer* nextLayer = curr->findNextLayer(parentLayer, 0, false); - if (nextLayer) - return nextLayer; - } +RenderLayer* RenderObject::findNextLayer(RenderLayer* parentLayer, + RenderObject* startPoint, + bool checkParent) { + // Error check the parent layer passed in. If it's null, we can't find + // anything. + if (!parentLayer) + return 0; + + // Step 1: If our layer is a child of the desired parent, then return our + // layer. + RenderLayer* ourLayer = hasLayer() ? toRenderBox(this)->layer() : 0; + if (ourLayer && ourLayer->parent() == parentLayer) + return ourLayer; + + // Step 2: If we don't have a layer, or our layer is the desired parent, then + // descend into our siblings trying to find the next layer whose parent is the + // desired parent. + if (!ourLayer || ourLayer == parentLayer) { + for (RenderObject* curr = startPoint ? startPoint->nextSibling() + : slowFirstChild(); + curr; curr = curr->nextSibling()) { + RenderLayer* nextLayer = curr->findNextLayer(parentLayer, 0, false); + if (nextLayer) + return nextLayer; } + } - // Step 3: If our layer is the desired parent layer, then we're finished. We didn't - // find anything. - if (parentLayer == ourLayer) - return 0; + // Step 3: If our layer is the desired parent layer, then we're finished. We + // didn't find anything. + if (parentLayer == ourLayer) + return 0; - // Step 4: If |checkParent| is set, climb up to our parent and check its siblings that - // follow us to see if we can locate a layer. - if (checkParent && parent()) - return parent()->findNextLayer(parentLayer, this, true); + // Step 4: If |checkParent| is set, climb up to our parent and check its + // siblings that follow us to see if we can locate a layer. + if (checkParent && parent()) + return parent()->findNextLayer(parentLayer, this, true); - return 0; + return 0; } -RenderLayer* RenderObject::enclosingLayer() const -{ - for (const RenderObject* current = this; current; current = current->parent()) { - if (current->hasLayer()) - return toRenderBox(current)->layer(); - } - // FIXME: We should remove the one caller that triggers this case and make - // this function return a reference. - ASSERT(!m_parent && !isRenderView()); - return 0; +RenderLayer* RenderObject::enclosingLayer() const { + for (const RenderObject* current = this; current; + current = current->parent()) { + if (current->hasLayer()) + return toRenderBox(current)->layer(); + } + // FIXME: We should remove the one caller that triggers this case and make + // this function return a reference. + ASSERT(!m_parent && !isRenderView()); + return 0; } -RenderBox* RenderObject::enclosingBox() const -{ - RenderObject* curr = const_cast(this); - while (curr) { - if (curr->isBox()) - return toRenderBox(curr); - curr = curr->parent(); - } +RenderBox* RenderObject::enclosingBox() const { + RenderObject* curr = const_cast(this); + while (curr) { + if (curr->isBox()) + return toRenderBox(curr); + curr = curr->parent(); + } - ASSERT_NOT_REACHED(); - return 0; + ASSERT_NOT_REACHED(); + return 0; } -RenderBoxModelObject* RenderObject::enclosingBoxModelObject() const -{ - RenderObject* curr = const_cast(this); - while (curr) { - if (curr->isBoxModelObject()) - return toRenderBoxModelObject(curr); - curr = curr->parent(); - } +RenderBoxModelObject* RenderObject::enclosingBoxModelObject() const { + RenderObject* curr = const_cast(this); + while (curr) { + if (curr->isBoxModelObject()) + return toRenderBoxModelObject(curr); + curr = curr->parent(); + } - ASSERT_NOT_REACHED(); - return 0; + ASSERT_NOT_REACHED(); + return 0; } -bool RenderObject::skipInvalidationWhenLaidOutChildren() const -{ - if (!neededLayoutBecauseOfChildren()) - return false; +bool RenderObject::skipInvalidationWhenLaidOutChildren() const { + if (!neededLayoutBecauseOfChildren()) + return false; - // SVG renderers need to be invalidated when their children are laid out. - // RenderBlocks with line boxes are responsible to invalidate them so we can't ignore them. - if (isRenderParagraph() && toRenderParagraph(this)->firstLineBox()) - return false; + // SVG renderers need to be invalidated when their children are laid out. + // RenderBlocks with line boxes are responsible to invalidate them so we can't + // ignore them. + if (isRenderParagraph() && toRenderParagraph(this)->firstLineBox()) + return false; - return rendererHasNoBoxEffect(); + return rendererHasNoBoxEffect(); } -RenderBlock* RenderObject::firstLineBlock() const -{ - return 0; +RenderBlock* RenderObject::firstLineBlock() const { + return 0; } -static inline bool objectIsRelayoutBoundary(const RenderObject* object) -{ - if (!object->hasOverflowClip()) - return false; - - if (object->style()->width().isIntrinsicOrAuto() || object->style()->height().isIntrinsicOrAuto() || object->style()->height().isPercent()) - return false; +static inline bool objectIsRelayoutBoundary(const RenderObject* object) { + if (!object->hasOverflowClip()) + return false; - return true; -} + if (object->style()->width().isIntrinsicOrAuto() || + object->style()->height().isIntrinsicOrAuto() || + object->style()->height().isPercent()) + return false; -void RenderObject::markContainingBlocksForLayout(bool scheduleRelayout, RenderObject* newRoot, SubtreeLayoutScope* layouter) -{ - ASSERT(!scheduleRelayout || !newRoot); - ASSERT(!isSetNeedsLayoutForbidden()); - ASSERT(!layouter || this != layouter->root()); - - RenderObject* object = container(); - RenderObject* last = this; - - bool simplifiedNormalFlowLayout = needsSimplifiedNormalFlowLayout() && !selfNeedsLayout() && !normalChildNeedsLayout(); - - while (object) { - if (object->selfNeedsLayout()) - return; - - // Don't mark the outermost object of an unrooted subtree. That object will be - // marked when the subtree is added to the document. - RenderObject* container = object->container(); - if (!container && !object->isRenderView()) - return; - if (!last->isText() && last->style()->hasOutOfFlowPosition()) { - bool willSkipRelativelyPositionedInlines = !object->isRenderBlock(); - // Skip relatively positioned inlines and anonymous blocks to get to the enclosing RenderBlock. - while (object && !object->isRenderBlock()) - object = object->container(); - if (!object || object->posChildNeedsLayout()) - return; - if (willSkipRelativelyPositionedInlines) - container = object->container(); - object->setPosChildNeedsLayout(true); - simplifiedNormalFlowLayout = true; - ASSERT(!object->isSetNeedsLayoutForbidden()); - } else if (simplifiedNormalFlowLayout) { - if (object->needsSimplifiedNormalFlowLayout()) - return; - object->setNeedsSimplifiedNormalFlowLayout(true); - ASSERT(!object->isSetNeedsLayoutForbidden()); - } else { - if (object->normalChildNeedsLayout()) - return; - object->setNormalChildNeedsLayout(true); - ASSERT(!object->isSetNeedsLayoutForbidden()); - } + return true; +} + +void RenderObject::markContainingBlocksForLayout(bool scheduleRelayout, + RenderObject* newRoot, + SubtreeLayoutScope* layouter) { + ASSERT(!scheduleRelayout || !newRoot); + ASSERT(!isSetNeedsLayoutForbidden()); + ASSERT(!layouter || this != layouter->root()); + + RenderObject* object = container(); + RenderObject* last = this; + + bool simplifiedNormalFlowLayout = needsSimplifiedNormalFlowLayout() && + !selfNeedsLayout() && + !normalChildNeedsLayout(); + + while (object) { + if (object->selfNeedsLayout()) + return; + + // Don't mark the outermost object of an unrooted subtree. That object will + // be marked when the subtree is added to the document. + RenderObject* container = object->container(); + if (!container && !object->isRenderView()) + return; + if (!last->isText() && last->style()->hasOutOfFlowPosition()) { + bool willSkipRelativelyPositionedInlines = !object->isRenderBlock(); + // Skip relatively positioned inlines and anonymous blocks to get to the + // enclosing RenderBlock. + while (object && !object->isRenderBlock()) + object = object->container(); + if (!object || object->posChildNeedsLayout()) + return; + if (willSkipRelativelyPositionedInlines) + container = object->container(); + object->setPosChildNeedsLayout(true); + simplifiedNormalFlowLayout = true; + ASSERT(!object->isSetNeedsLayoutForbidden()); + } else if (simplifiedNormalFlowLayout) { + if (object->needsSimplifiedNormalFlowLayout()) + return; + object->setNeedsSimplifiedNormalFlowLayout(true); + ASSERT(!object->isSetNeedsLayoutForbidden()); + } else { + if (object->normalChildNeedsLayout()) + return; + object->setNormalChildNeedsLayout(true); + ASSERT(!object->isSetNeedsLayoutForbidden()); + } - if (layouter) { - layouter->addRendererToLayout(object); - if (object == layouter->root()) - return; - } + if (layouter) { + layouter->addRendererToLayout(object); + if (object == layouter->root()) + return; + } - if (object == newRoot) - return; + if (object == newRoot) + return; - last = object; - if (scheduleRelayout && objectIsRelayoutBoundary(last)) - break; - object = container; - } + last = object; + if (scheduleRelayout && objectIsRelayoutBoundary(last)) + break; + object = container; + } - if (scheduleRelayout) - last->scheduleRelayout(); + if (scheduleRelayout) + last->scheduleRelayout(); } #if ENABLE(ASSERT) -void RenderObject::checkBlockPositionedObjectsNeedLayout() -{ - ASSERT(!needsLayout()); +void RenderObject::checkBlockPositionedObjectsNeedLayout() { + ASSERT(!needsLayout()); - if (isRenderBlock()) - toRenderBlock(this)->checkPositionedObjectsNeedLayout(); + if (isRenderBlock()) + toRenderBlock(this)->checkPositionedObjectsNeedLayout(); } #endif -void RenderObject::setPreferredLogicalWidthsDirty(MarkingBehavior markParents) -{ - m_bitfields.setPreferredLogicalWidthsDirty(true); - if (markParents == MarkContainingBlockChain && (isText() || !style()->hasOutOfFlowPosition())) - invalidateContainerPreferredLogicalWidths(); -} - -void RenderObject::clearPreferredLogicalWidthsDirty() -{ - m_bitfields.setPreferredLogicalWidthsDirty(false); -} - -void RenderObject::invalidateContainerPreferredLogicalWidths() -{ - // In order to avoid pathological behavior when inlines are deeply nested, we do include them - // in the chain that we mark dirty (even though they're kind of irrelevant). - RenderObject* o = container(); - while (o && !o->preferredLogicalWidthsDirty()) { - // Don't invalidate the outermost object of an unrooted subtree. That object will be - // invalidated when the subtree is added to the document. - RenderObject* container = o->container(); - if (!container && !o->isRenderView()) - break; - - o->m_bitfields.setPreferredLogicalWidthsDirty(true); - if (o->style()->hasOutOfFlowPosition()) - // A positioned object has no effect on the min/max width of its containing block ever. - // We can optimize this case and not go up any further. - break; - o = container; - } -} - -RenderBlock* RenderObject::containingBlock() const -{ - RenderObject* o = parent(); - if (!isText() && m_style->position() == AbsolutePosition) { - while (o) { - // For relpositioned inlines, we return the nearest non-anonymous enclosing block. We don't try - // to return the inline itself. This allows us to avoid having a positioned objects - // list in all RenderInlines and lets us return a strongly-typed RenderBlock* result - // from this method. The container() method can actually be used to obtain the - // inline directly. - if (o->style()->position() != StaticPosition && (!o->isInline() || o->isReplaced())) - break; - - if (o->canContainAbsolutePositionObjects()) - break; - - if (o->style()->hasInFlowPosition() && o->isInline() && !o->isReplaced()) { - o = o->containingBlock(); - break; - } - - o = o->parent(); - } - - if (o && !o->isRenderBlock()) - o = o->containingBlock(); - } else { - while (o && ((o->isInline() && !o->isReplaced()) || !o->isRenderBlock())) - o = o->parent(); - } +void RenderObject::setPreferredLogicalWidthsDirty(MarkingBehavior markParents) { + m_bitfields.setPreferredLogicalWidthsDirty(true); + if (markParents == MarkContainingBlockChain && + (isText() || !style()->hasOutOfFlowPosition())) + invalidateContainerPreferredLogicalWidths(); +} + +void RenderObject::clearPreferredLogicalWidthsDirty() { + m_bitfields.setPreferredLogicalWidthsDirty(false); +} + +void RenderObject::invalidateContainerPreferredLogicalWidths() { + // In order to avoid pathological behavior when inlines are deeply nested, we + // do include them in the chain that we mark dirty (even though they're kind + // of irrelevant). + RenderObject* o = container(); + while (o && !o->preferredLogicalWidthsDirty()) { + // Don't invalidate the outermost object of an unrooted subtree. That object + // will be invalidated when the subtree is added to the document. + RenderObject* container = o->container(); + if (!container && !o->isRenderView()) + break; + + o->m_bitfields.setPreferredLogicalWidthsDirty(true); + if (o->style()->hasOutOfFlowPosition()) + // A positioned object has no effect on the min/max width of its + // containing block ever. We can optimize this case and not go up any + // further. + break; + o = container; + } +} + +RenderBlock* RenderObject::containingBlock() const { + RenderObject* o = parent(); + if (!isText() && m_style->position() == AbsolutePosition) { + while (o) { + // For relpositioned inlines, we return the nearest non-anonymous + // enclosing block. We don't try to return the inline itself. This allows + // us to avoid having a positioned objects list in all RenderInlines and + // lets us return a strongly-typed RenderBlock* result from this method. + // The container() method can actually be used to obtain the inline + // directly. + if (o->style()->position() != StaticPosition && + (!o->isInline() || o->isReplaced())) + break; - if (!o || !o->isRenderBlock()) - return 0; // This can still happen in case of an orphaned tree + if (o->canContainAbsolutePositionObjects()) + break; - return toRenderBlock(o); -} + if (o->style()->hasInFlowPosition() && o->isInline() && + !o->isReplaced()) { + o = o->containingBlock(); + break; + } -void RenderObject::drawLineForBoxSide(GraphicsContext* graphicsContext, int x1, int y1, int x2, int y2, - BoxSide side, Color color, EBorderStyle style, - int adjacentWidth1, int adjacentWidth2, bool antialias) -{ - int thickness; - int length; - if (side == BSTop || side == BSBottom) { - thickness = y2 - y1; - length = x2 - x1; - } else { - thickness = x2 - x1; - length = y2 - y1; + o = o->parent(); } - // FIXME: We really would like this check to be an ASSERT as we don't want to draw empty borders. However - // nothing guarantees that the following recursive calls to drawLineForBoxSide will have non-null dimensions. - if (!thickness || !length) - return; - - if (style == DOUBLE && thickness < 3) - style = SOLID; - - switch (style) { + if (o && !o->isRenderBlock()) + o = o->containingBlock(); + } else { + while (o && ((o->isInline() && !o->isReplaced()) || !o->isRenderBlock())) + o = o->parent(); + } + + if (!o || !o->isRenderBlock()) + return 0; // This can still happen in case of an orphaned tree + + return toRenderBlock(o); +} + +void RenderObject::drawLineForBoxSide(GraphicsContext* graphicsContext, + int x1, + int y1, + int x2, + int y2, + BoxSide side, + Color color, + EBorderStyle style, + int adjacentWidth1, + int adjacentWidth2, + bool antialias) { + int thickness; + int length; + if (side == BSTop || side == BSBottom) { + thickness = y2 - y1; + length = x2 - x1; + } else { + thickness = x2 - x1; + length = y2 - y1; + } + + // FIXME: We really would like this check to be an ASSERT as we don't want to + // draw empty borders. However nothing guarantees that the following recursive + // calls to drawLineForBoxSide will have non-null dimensions. + if (!thickness || !length) + return; + + if (style == DOUBLE && thickness < 3) + style = SOLID; + + switch (style) { case BNONE: case BHIDDEN: - return; + return; case DOTTED: case DASHED: - drawDashedOrDottedBoxSide(graphicsContext, x1, y1, x2, y2, side, - color, thickness, style, antialias); - break; + drawDashedOrDottedBoxSide(graphicsContext, x1, y1, x2, y2, side, color, + thickness, style, antialias); + break; case DOUBLE: - drawDoubleBoxSide(graphicsContext, x1, y1, x2, y2, length, side, color, - thickness, adjacentWidth1, adjacentWidth2, antialias); - break; + drawDoubleBoxSide(graphicsContext, x1, y1, x2, y2, length, side, color, + thickness, adjacentWidth1, adjacentWidth2, antialias); + break; case RIDGE: case GROOVE: - drawRidgeOrGrooveBoxSide(graphicsContext, x1, y1, x2, y2, side, color, - style, adjacentWidth1, adjacentWidth2, antialias); - break; + drawRidgeOrGrooveBoxSide(graphicsContext, x1, y1, x2, y2, side, color, + style, adjacentWidth1, adjacentWidth2, + antialias); + break; case INSET: - // FIXME: Maybe we should lighten the colors on one side like Firefox. - // https://bugs.webkit.org/show_bug.cgi?id=58608 - if (side == BSTop || side == BSLeft) - color = color.dark(); - // fall through + // FIXME: Maybe we should lighten the colors on one side like Firefox. + // https://bugs.webkit.org/show_bug.cgi?id=58608 + if (side == BSTop || side == BSLeft) + color = color.dark(); + // fall through case OUTSET: - if (style == OUTSET && (side == BSBottom || side == BSRight)) - color = color.dark(); - // fall through + if (style == OUTSET && (side == BSBottom || side == BSRight)) + color = color.dark(); + // fall through case SOLID: - drawSolidBoxSide(graphicsContext, x1, y1, x2, y2, side, color, adjacentWidth1, adjacentWidth2, antialias); - break; - } -} - -void RenderObject::drawDashedOrDottedBoxSide(GraphicsContext* graphicsContext, int x1, int y1, int x2, int y2, - BoxSide side, Color color, int thickness, EBorderStyle style, bool antialias) -{ - if (thickness <= 0) - return; + drawSolidBoxSide(graphicsContext, x1, y1, x2, y2, side, color, + adjacentWidth1, adjacentWidth2, antialias); + break; + } +} + +void RenderObject::drawDashedOrDottedBoxSide(GraphicsContext* graphicsContext, + int x1, + int y1, + int x2, + int y2, + BoxSide side, + Color color, + int thickness, + EBorderStyle style, + bool antialias) { + if (thickness <= 0) + return; + + bool wasAntialiased = graphicsContext->shouldAntialias(); + StrokeStyle oldStrokeStyle = graphicsContext->strokeStyle(); + graphicsContext->setShouldAntialias(antialias); + graphicsContext->setStrokeColor(color); + graphicsContext->setStrokeThickness(thickness); + graphicsContext->setStrokeStyle(style == DASHED ? DashedStroke + : DottedStroke); + + switch (side) { + case BSBottom: + case BSTop: + graphicsContext->drawLine(IntPoint(x1, (y1 + y2) / 2), + IntPoint(x2, (y1 + y2) / 2)); + break; + case BSRight: + case BSLeft: + graphicsContext->drawLine(IntPoint((x1 + x2) / 2, y1), + IntPoint((x1 + x2) / 2, y2)); + break; + } + graphicsContext->setShouldAntialias(wasAntialiased); + graphicsContext->setStrokeStyle(oldStrokeStyle); +} + +void RenderObject::drawDoubleBoxSide(GraphicsContext* graphicsContext, + int x1, + int y1, + int x2, + int y2, + int length, + BoxSide side, + Color color, + int thickness, + int adjacentWidth1, + int adjacentWidth2, + bool antialias) { + int thirdOfThickness = (thickness + 1) / 3; + ASSERT(thirdOfThickness); + + if (!adjacentWidth1 && !adjacentWidth2) { + StrokeStyle oldStrokeStyle = graphicsContext->strokeStyle(); + graphicsContext->setStrokeStyle(NoStroke); + graphicsContext->setFillColor(color); bool wasAntialiased = graphicsContext->shouldAntialias(); - StrokeStyle oldStrokeStyle = graphicsContext->strokeStyle(); graphicsContext->setShouldAntialias(antialias); - graphicsContext->setStrokeColor(color); - graphicsContext->setStrokeThickness(thickness); - graphicsContext->setStrokeStyle(style == DASHED ? DashedStroke : DottedStroke); switch (side) { - case BSBottom: - case BSTop: - graphicsContext->drawLine(IntPoint(x1, (y1 + y2) / 2), IntPoint(x2, (y1 + y2) / 2)); + case BSTop: + case BSBottom: + graphicsContext->drawRect(IntRect(x1, y1, length, thirdOfThickness)); + graphicsContext->drawRect( + IntRect(x1, y2 - thirdOfThickness, length, thirdOfThickness)); break; - case BSRight: - case BSLeft: - graphicsContext->drawLine(IntPoint((x1 + x2) / 2, y1), IntPoint((x1 + x2) / 2, y2)); + case BSLeft: + case BSRight: + // FIXME: Why do we offset the border by 1 in this case but not the + // other one? + if (length > 1) { + graphicsContext->drawRect( + IntRect(x1, y1 + 1, thirdOfThickness, length - 1)); + graphicsContext->drawRect(IntRect(x2 - thirdOfThickness, y1 + 1, + thirdOfThickness, length - 1)); + } break; } + graphicsContext->setShouldAntialias(wasAntialiased); graphicsContext->setStrokeStyle(oldStrokeStyle); -} - -void RenderObject::drawDoubleBoxSide(GraphicsContext* graphicsContext, int x1, int y1, int x2, int y2, - int length, BoxSide side, Color color, int thickness, int adjacentWidth1, int adjacentWidth2, bool antialias) -{ - int thirdOfThickness = (thickness + 1) / 3; - ASSERT(thirdOfThickness); - - if (!adjacentWidth1 && !adjacentWidth2) { - StrokeStyle oldStrokeStyle = graphicsContext->strokeStyle(); - graphicsContext->setStrokeStyle(NoStroke); - graphicsContext->setFillColor(color); - - bool wasAntialiased = graphicsContext->shouldAntialias(); - graphicsContext->setShouldAntialias(antialias); - - switch (side) { - case BSTop: - case BSBottom: - graphicsContext->drawRect(IntRect(x1, y1, length, thirdOfThickness)); - graphicsContext->drawRect(IntRect(x1, y2 - thirdOfThickness, length, thirdOfThickness)); - break; - case BSLeft: - case BSRight: - // FIXME: Why do we offset the border by 1 in this case but not the other one? - if (length > 1) { - graphicsContext->drawRect(IntRect(x1, y1 + 1, thirdOfThickness, length - 1)); - graphicsContext->drawRect(IntRect(x2 - thirdOfThickness, y1 + 1, thirdOfThickness, length - 1)); - } - break; - } + return; + } - graphicsContext->setShouldAntialias(wasAntialiased); - graphicsContext->setStrokeStyle(oldStrokeStyle); - return; - } - - int adjacent1BigThird = ((adjacentWidth1 > 0) ? adjacentWidth1 + 1 : adjacentWidth1 - 1) / 3; - int adjacent2BigThird = ((adjacentWidth2 > 0) ? adjacentWidth2 + 1 : adjacentWidth2 - 1) / 3; + int adjacent1BigThird = + ((adjacentWidth1 > 0) ? adjacentWidth1 + 1 : adjacentWidth1 - 1) / 3; + int adjacent2BigThird = + ((adjacentWidth2 > 0) ? adjacentWidth2 + 1 : adjacentWidth2 - 1) / 3; - switch (side) { + switch (side) { case BSTop: - drawLineForBoxSide(graphicsContext, x1 + std::max((-adjacentWidth1 * 2 + 1) / 3, 0), - y1, x2 - std::max((-adjacentWidth2 * 2 + 1) / 3, 0), y1 + thirdOfThickness, - side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias); - drawLineForBoxSide(graphicsContext, x1 + std::max((adjacentWidth1 * 2 + 1) / 3, 0), - y2 - thirdOfThickness, x2 - std::max((adjacentWidth2 * 2 + 1) / 3, 0), y2, - side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias); - break; + drawLineForBoxSide(graphicsContext, + x1 + std::max((-adjacentWidth1 * 2 + 1) / 3, 0), y1, + x2 - std::max((-adjacentWidth2 * 2 + 1) / 3, 0), + y1 + thirdOfThickness, side, color, SOLID, + adjacent1BigThird, adjacent2BigThird, antialias); + drawLineForBoxSide( + graphicsContext, x1 + std::max((adjacentWidth1 * 2 + 1) / 3, 0), + y2 - thirdOfThickness, x2 - std::max((adjacentWidth2 * 2 + 1) / 3, 0), + y2, side, color, SOLID, adjacent1BigThird, adjacent2BigThird, + antialias); + break; case BSLeft: - drawLineForBoxSide(graphicsContext, x1, y1 + std::max((-adjacentWidth1 * 2 + 1) / 3, 0), - x1 + thirdOfThickness, y2 - std::max((-adjacentWidth2 * 2 + 1) / 3, 0), - side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias); - drawLineForBoxSide(graphicsContext, x2 - thirdOfThickness, y1 + std::max((adjacentWidth1 * 2 + 1) / 3, 0), - x2, y2 - std::max((adjacentWidth2 * 2 + 1) / 3, 0), - side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias); - break; + drawLineForBoxSide( + graphicsContext, x1, y1 + std::max((-adjacentWidth1 * 2 + 1) / 3, 0), + x1 + thirdOfThickness, + y2 - std::max((-adjacentWidth2 * 2 + 1) / 3, 0), side, color, SOLID, + adjacent1BigThird, adjacent2BigThird, antialias); + drawLineForBoxSide(graphicsContext, x2 - thirdOfThickness, + y1 + std::max((adjacentWidth1 * 2 + 1) / 3, 0), x2, + y2 - std::max((adjacentWidth2 * 2 + 1) / 3, 0), side, + color, SOLID, adjacent1BigThird, adjacent2BigThird, + antialias); + break; case BSBottom: - drawLineForBoxSide(graphicsContext, x1 + std::max((adjacentWidth1 * 2 + 1) / 3, 0), - y1, x2 - std::max((adjacentWidth2 * 2 + 1) / 3, 0), y1 + thirdOfThickness, - side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias); - drawLineForBoxSide(graphicsContext, x1 + std::max((-adjacentWidth1 * 2 + 1) / 3, 0), - y2 - thirdOfThickness, x2 - std::max((-adjacentWidth2 * 2 + 1) / 3, 0), y2, - side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias); - break; + drawLineForBoxSide( + graphicsContext, x1 + std::max((adjacentWidth1 * 2 + 1) / 3, 0), y1, + x2 - std::max((adjacentWidth2 * 2 + 1) / 3, 0), y1 + thirdOfThickness, + side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias); + drawLineForBoxSide( + graphicsContext, x1 + std::max((-adjacentWidth1 * 2 + 1) / 3, 0), + y2 - thirdOfThickness, + x2 - std::max((-adjacentWidth2 * 2 + 1) / 3, 0), y2, side, color, + SOLID, adjacent1BigThird, adjacent2BigThird, antialias); + break; case BSRight: - drawLineForBoxSide(graphicsContext, x1, y1 + std::max((adjacentWidth1 * 2 + 1) / 3, 0), - x1 + thirdOfThickness, y2 - std::max((adjacentWidth2 * 2 + 1) / 3, 0), - side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias); - drawLineForBoxSide(graphicsContext, x2 - thirdOfThickness, y1 + std::max((-adjacentWidth1 * 2 + 1) / 3, 0), - x2, y2 - std::max((-adjacentWidth2 * 2 + 1) / 3, 0), - side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias); - break; + drawLineForBoxSide( + graphicsContext, x1, y1 + std::max((adjacentWidth1 * 2 + 1) / 3, 0), + x1 + thirdOfThickness, y2 - std::max((adjacentWidth2 * 2 + 1) / 3, 0), + side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias); + drawLineForBoxSide(graphicsContext, x2 - thirdOfThickness, + y1 + std::max((-adjacentWidth1 * 2 + 1) / 3, 0), x2, + y2 - std::max((-adjacentWidth2 * 2 + 1) / 3, 0), side, + color, SOLID, adjacent1BigThird, adjacent2BigThird, + antialias); + break; default: - break; - } -} - -void RenderObject::drawRidgeOrGrooveBoxSide(GraphicsContext* graphicsContext, int x1, int y1, int x2, int y2, - BoxSide side, Color color, EBorderStyle style, int adjacentWidth1, int adjacentWidth2, bool antialias) -{ - EBorderStyle s1; - EBorderStyle s2; - if (style == GROOVE) { - s1 = INSET; - s2 = OUTSET; - } else { - s1 = OUTSET; - s2 = INSET; - } - - int adjacent1BigHalf = ((adjacentWidth1 > 0) ? adjacentWidth1 + 1 : adjacentWidth1 - 1) / 2; - int adjacent2BigHalf = ((adjacentWidth2 > 0) ? adjacentWidth2 + 1 : adjacentWidth2 - 1) / 2; - - switch (side) { + break; + } +} + +void RenderObject::drawRidgeOrGrooveBoxSide(GraphicsContext* graphicsContext, + int x1, + int y1, + int x2, + int y2, + BoxSide side, + Color color, + EBorderStyle style, + int adjacentWidth1, + int adjacentWidth2, + bool antialias) { + EBorderStyle s1; + EBorderStyle s2; + if (style == GROOVE) { + s1 = INSET; + s2 = OUTSET; + } else { + s1 = OUTSET; + s2 = INSET; + } + + int adjacent1BigHalf = + ((adjacentWidth1 > 0) ? adjacentWidth1 + 1 : adjacentWidth1 - 1) / 2; + int adjacent2BigHalf = + ((adjacentWidth2 > 0) ? adjacentWidth2 + 1 : adjacentWidth2 - 1) / 2; + + switch (side) { case BSTop: - drawLineForBoxSide(graphicsContext, x1 + std::max(-adjacentWidth1, 0) / 2, y1, x2 - std::max(-adjacentWidth2, 0) / 2, (y1 + y2 + 1) / 2, - side, color, s1, adjacent1BigHalf, adjacent2BigHalf, antialias); - drawLineForBoxSide(graphicsContext, x1 + std::max(adjacentWidth1 + 1, 0) / 2, (y1 + y2 + 1) / 2, x2 - std::max(adjacentWidth2 + 1, 0) / 2, y2, - side, color, s2, adjacentWidth1 / 2, adjacentWidth2 / 2, antialias); - break; + drawLineForBoxSide(graphicsContext, x1 + std::max(-adjacentWidth1, 0) / 2, + y1, x2 - std::max(-adjacentWidth2, 0) / 2, + (y1 + y2 + 1) / 2, side, color, s1, adjacent1BigHalf, + adjacent2BigHalf, antialias); + drawLineForBoxSide( + graphicsContext, x1 + std::max(adjacentWidth1 + 1, 0) / 2, + (y1 + y2 + 1) / 2, x2 - std::max(adjacentWidth2 + 1, 0) / 2, y2, side, + color, s2, adjacentWidth1 / 2, adjacentWidth2 / 2, antialias); + break; case BSLeft: - drawLineForBoxSide(graphicsContext, x1, y1 + std::max(-adjacentWidth1, 0) / 2, (x1 + x2 + 1) / 2, y2 - std::max(-adjacentWidth2, 0) / 2, - side, color, s1, adjacent1BigHalf, adjacent2BigHalf, antialias); - drawLineForBoxSide(graphicsContext, (x1 + x2 + 1) / 2, y1 + std::max(adjacentWidth1 + 1, 0) / 2, x2, y2 - std::max(adjacentWidth2 + 1, 0) / 2, - side, color, s2, adjacentWidth1 / 2, adjacentWidth2 / 2, antialias); - break; + drawLineForBoxSide( + graphicsContext, x1, y1 + std::max(-adjacentWidth1, 0) / 2, + (x1 + x2 + 1) / 2, y2 - std::max(-adjacentWidth2, 0) / 2, side, color, + s1, adjacent1BigHalf, adjacent2BigHalf, antialias); + drawLineForBoxSide(graphicsContext, (x1 + x2 + 1) / 2, + y1 + std::max(adjacentWidth1 + 1, 0) / 2, x2, + y2 - std::max(adjacentWidth2 + 1, 0) / 2, side, color, + s2, adjacentWidth1 / 2, adjacentWidth2 / 2, antialias); + break; case BSBottom: - drawLineForBoxSide(graphicsContext, x1 + std::max(adjacentWidth1, 0) / 2, y1, x2 - std::max(adjacentWidth2, 0) / 2, (y1 + y2 + 1) / 2, - side, color, s2, adjacent1BigHalf, adjacent2BigHalf, antialias); - drawLineForBoxSide(graphicsContext, x1 + std::max(-adjacentWidth1 + 1, 0) / 2, (y1 + y2 + 1) / 2, x2 - std::max(-adjacentWidth2 + 1, 0) / 2, y2, - side, color, s1, adjacentWidth1 / 2, adjacentWidth2 / 2, antialias); - break; + drawLineForBoxSide(graphicsContext, x1 + std::max(adjacentWidth1, 0) / 2, + y1, x2 - std::max(adjacentWidth2, 0) / 2, + (y1 + y2 + 1) / 2, side, color, s2, adjacent1BigHalf, + adjacent2BigHalf, antialias); + drawLineForBoxSide( + graphicsContext, x1 + std::max(-adjacentWidth1 + 1, 0) / 2, + (y1 + y2 + 1) / 2, x2 - std::max(-adjacentWidth2 + 1, 0) / 2, y2, + side, color, s1, adjacentWidth1 / 2, adjacentWidth2 / 2, antialias); + break; case BSRight: - drawLineForBoxSide(graphicsContext, x1, y1 + std::max(adjacentWidth1, 0) / 2, (x1 + x2 + 1) / 2, y2 - std::max(adjacentWidth2, 0) / 2, - side, color, s2, adjacent1BigHalf, adjacent2BigHalf, antialias); - drawLineForBoxSide(graphicsContext, (x1 + x2 + 1) / 2, y1 + std::max(-adjacentWidth1 + 1, 0) / 2, x2, y2 - std::max(-adjacentWidth2 + 1, 0) / 2, - side, color, s1, adjacentWidth1 / 2, adjacentWidth2 / 2, antialias); - break; - } -} - -void RenderObject::drawSolidBoxSide(GraphicsContext* graphicsContext, int x1, int y1, int x2, int y2, - BoxSide side, Color color, int adjacentWidth1, int adjacentWidth2, bool antialias) -{ - StrokeStyle oldStrokeStyle = graphicsContext->strokeStyle(); - graphicsContext->setStrokeStyle(NoStroke); - graphicsContext->setFillColor(color); - ASSERT(x2 >= x1); - ASSERT(y2 >= y1); - if (!adjacentWidth1 && !adjacentWidth2) { - // Turn off antialiasing to match the behavior of drawConvexPolygon(); - // this matters for rects in transformed contexts. - bool wasAntialiased = graphicsContext->shouldAntialias(); - graphicsContext->setShouldAntialias(antialias); - graphicsContext->drawRect(IntRect(x1, y1, x2 - x1, y2 - y1)); - graphicsContext->setShouldAntialias(wasAntialiased); - graphicsContext->setStrokeStyle(oldStrokeStyle); - return; - } - FloatPoint quad[4]; - switch (side) { + drawLineForBoxSide( + graphicsContext, x1, y1 + std::max(adjacentWidth1, 0) / 2, + (x1 + x2 + 1) / 2, y2 - std::max(adjacentWidth2, 0) / 2, side, color, + s2, adjacent1BigHalf, adjacent2BigHalf, antialias); + drawLineForBoxSide(graphicsContext, (x1 + x2 + 1) / 2, + y1 + std::max(-adjacentWidth1 + 1, 0) / 2, x2, + y2 - std::max(-adjacentWidth2 + 1, 0) / 2, side, color, + s1, adjacentWidth1 / 2, adjacentWidth2 / 2, antialias); + break; + } +} + +void RenderObject::drawSolidBoxSide(GraphicsContext* graphicsContext, + int x1, + int y1, + int x2, + int y2, + BoxSide side, + Color color, + int adjacentWidth1, + int adjacentWidth2, + bool antialias) { + StrokeStyle oldStrokeStyle = graphicsContext->strokeStyle(); + graphicsContext->setStrokeStyle(NoStroke); + graphicsContext->setFillColor(color); + ASSERT(x2 >= x1); + ASSERT(y2 >= y1); + if (!adjacentWidth1 && !adjacentWidth2) { + // Turn off antialiasing to match the behavior of drawConvexPolygon(); + // this matters for rects in transformed contexts. + bool wasAntialiased = graphicsContext->shouldAntialias(); + graphicsContext->setShouldAntialias(antialias); + graphicsContext->drawRect(IntRect(x1, y1, x2 - x1, y2 - y1)); + graphicsContext->setShouldAntialias(wasAntialiased); + graphicsContext->setStrokeStyle(oldStrokeStyle); + return; + } + FloatPoint quad[4]; + switch (side) { case BSTop: - quad[0] = FloatPoint(x1 + std::max(-adjacentWidth1, 0), y1); - quad[1] = FloatPoint(x1 + std::max(adjacentWidth1, 0), y2); - quad[2] = FloatPoint(x2 - std::max(adjacentWidth2, 0), y2); - quad[3] = FloatPoint(x2 - std::max(-adjacentWidth2, 0), y1); - break; + quad[0] = FloatPoint(x1 + std::max(-adjacentWidth1, 0), y1); + quad[1] = FloatPoint(x1 + std::max(adjacentWidth1, 0), y2); + quad[2] = FloatPoint(x2 - std::max(adjacentWidth2, 0), y2); + quad[3] = FloatPoint(x2 - std::max(-adjacentWidth2, 0), y1); + break; case BSBottom: - quad[0] = FloatPoint(x1 + std::max(adjacentWidth1, 0), y1); - quad[1] = FloatPoint(x1 + std::max(-adjacentWidth1, 0), y2); - quad[2] = FloatPoint(x2 - std::max(-adjacentWidth2, 0), y2); - quad[3] = FloatPoint(x2 - std::max(adjacentWidth2, 0), y1); - break; + quad[0] = FloatPoint(x1 + std::max(adjacentWidth1, 0), y1); + quad[1] = FloatPoint(x1 + std::max(-adjacentWidth1, 0), y2); + quad[2] = FloatPoint(x2 - std::max(-adjacentWidth2, 0), y2); + quad[3] = FloatPoint(x2 - std::max(adjacentWidth2, 0), y1); + break; case BSLeft: - quad[0] = FloatPoint(x1, y1 + std::max(-adjacentWidth1, 0)); - quad[1] = FloatPoint(x1, y2 - std::max(-adjacentWidth2, 0)); - quad[2] = FloatPoint(x2, y2 - std::max(adjacentWidth2, 0)); - quad[3] = FloatPoint(x2, y1 + std::max(adjacentWidth1, 0)); - break; + quad[0] = FloatPoint(x1, y1 + std::max(-adjacentWidth1, 0)); + quad[1] = FloatPoint(x1, y2 - std::max(-adjacentWidth2, 0)); + quad[2] = FloatPoint(x2, y2 - std::max(adjacentWidth2, 0)); + quad[3] = FloatPoint(x2, y1 + std::max(adjacentWidth1, 0)); + break; case BSRight: - quad[0] = FloatPoint(x1, y1 + std::max(adjacentWidth1, 0)); - quad[1] = FloatPoint(x1, y2 - std::max(adjacentWidth2, 0)); - quad[2] = FloatPoint(x2, y2 - std::max(-adjacentWidth2, 0)); - quad[3] = FloatPoint(x2, y1 + std::max(-adjacentWidth1, 0)); - break; - } - - graphicsContext->drawConvexPolygon(4, quad, antialias); - graphicsContext->setStrokeStyle(oldStrokeStyle); -} - -void RenderObject::addChildFocusRingRects(Vector& rects, const LayoutPoint& additionalOffset, const RenderBox* paintContainer) const -{ - for (RenderObject* current = slowFirstChild(); current; current = current->nextSibling()) { - if (current->isText()) - continue; - - if (current->isBox()) { - RenderBox* box = toRenderBox(current); - if (box->hasLayer()) { - Vector layerFocusRingRects; - box->addFocusRingRects(layerFocusRingRects, LayoutPoint(), box); - for (size_t i = 0; i < layerFocusRingRects.size(); ++i) { - FloatQuad quadInBox = box->localToContainerQuad(FloatRect(layerFocusRingRects[i]), paintContainer); - FloatRect rect = quadInBox.boundingBox(); - // Floor the location instead of using pixelSnappedIntRect to match the !hasLayer() path. - // FIXME: roundedIntSize matches pixelSnappedIntRect in other places of addFocusRingRects - // because we always floor the offset. - // This assumption is fragile and should be replaced by better solution. - rects.append(IntRect(flooredIntPoint(rect.location()), roundedIntSize(rect.size()))); - } - } else { - FloatPoint pos(additionalOffset); - pos.move(box->locationOffset()); - box->addFocusRingRects(rects, flooredIntPoint(pos), paintContainer); - } - } else { - current->addFocusRingRects(rects, additionalOffset, paintContainer); + quad[0] = FloatPoint(x1, y1 + std::max(adjacentWidth1, 0)); + quad[1] = FloatPoint(x1, y2 - std::max(adjacentWidth2, 0)); + quad[2] = FloatPoint(x2, y2 - std::max(-adjacentWidth2, 0)); + quad[3] = FloatPoint(x2, y1 + std::max(-adjacentWidth1, 0)); + break; + } + + graphicsContext->drawConvexPolygon(4, quad, antialias); + graphicsContext->setStrokeStyle(oldStrokeStyle); +} + +void RenderObject::addChildFocusRingRects( + Vector& rects, + const LayoutPoint& additionalOffset, + const RenderBox* paintContainer) const { + for (RenderObject* current = slowFirstChild(); current; + current = current->nextSibling()) { + if (current->isText()) + continue; + + if (current->isBox()) { + RenderBox* box = toRenderBox(current); + if (box->hasLayer()) { + Vector layerFocusRingRects; + box->addFocusRingRects(layerFocusRingRects, LayoutPoint(), box); + for (size_t i = 0; i < layerFocusRingRects.size(); ++i) { + FloatQuad quadInBox = box->localToContainerQuad( + FloatRect(layerFocusRingRects[i]), paintContainer); + FloatRect rect = quadInBox.boundingBox(); + // Floor the location instead of using pixelSnappedIntRect to match + // the !hasLayer() path. + // FIXME: roundedIntSize matches pixelSnappedIntRect in other places + // of addFocusRingRects because we always floor the offset. This + // assumption is fragile and should be replaced by better solution. + rects.append(IntRect(flooredIntPoint(rect.location()), + roundedIntSize(rect.size()))); } + } else { + FloatPoint pos(additionalOffset); + pos.move(box->locationOffset()); + box->addFocusRingRects(rects, flooredIntPoint(pos), paintContainer); + } + } else { + current->addFocusRingRects(rects, additionalOffset, paintContainer); } + } } -IntRect RenderObject::absoluteBoundingBoxRect() const -{ - Vector quads; - absoluteQuads(quads); +IntRect RenderObject::absoluteBoundingBoxRect() const { + Vector quads; + absoluteQuads(quads); - size_t n = quads.size(); - if (!n) - return IntRect(); + size_t n = quads.size(); + if (!n) + return IntRect(); - IntRect result = quads[0].enclosingBoundingBox(); - for (size_t i = 1; i < n; ++i) - result.unite(quads[i].enclosingBoundingBox()); - return result; + IntRect result = quads[0].enclosingBoundingBox(); + for (size_t i = 1; i < n; ++i) + result.unite(quads[i].enclosingBoundingBox()); + return result; } -void RenderObject::addAbsoluteRectForLayer(LayoutRect& result) -{ - if (hasLayer()) - result.unite(absoluteBoundingBoxRect()); - for (RenderObject* current = slowFirstChild(); current; current = current->nextSibling()) - current->addAbsoluteRectForLayer(result); +void RenderObject::addAbsoluteRectForLayer(LayoutRect& result) { + if (hasLayer()) + result.unite(absoluteBoundingBoxRect()); + for (RenderObject* current = slowFirstChild(); current; + current = current->nextSibling()) + current->addAbsoluteRectForLayer(result); } -void RenderObject::paint(PaintInfo&, const LayoutPoint&, Vector& layers) -{ -} +void RenderObject::paint(PaintInfo&, + const LayoutPoint&, + Vector& layers) {} -void RenderObject::dirtyLinesFromChangedChild(RenderObject*) -{ -} +void RenderObject::dirtyLinesFromChangedChild(RenderObject*) {} #ifndef NDEBUG -void RenderObject::showTreeForThis() const -{ -} - -void RenderObject::showRenderTreeForThis() const -{ - showRenderTree(this, 0); -} +void RenderObject::showTreeForThis() const {} -void RenderObject::showLineTreeForThis() const -{ - if (containingBlock()) - containingBlock()->showLineTreeAndMark(0, 0, 0, 0, this); +void RenderObject::showRenderTreeForThis() const { + showRenderTree(this, 0); } -void RenderObject::showRenderObject() const -{ - showRenderObject(0); +void RenderObject::showLineTreeForThis() const { + if (containingBlock()) + containingBlock()->showLineTreeAndMark(0, 0, 0, 0, this); } -void RenderObject::showRenderObject(int printedCharacters) const -{ - printedCharacters += fprintf(stderr, "%s %p", renderName(), this); - fputc('\n', stderr); +void RenderObject::showRenderObject() const { + showRenderObject(0); } -void RenderObject::showRenderTreeAndMark(const RenderObject* markedObject1, const char* markedLabel1, const RenderObject* markedObject2, const char* markedLabel2, int depth) const -{ - int printedCharacters = 0; - if (markedObject1 == this && markedLabel1) - printedCharacters += fprintf(stderr, "%s", markedLabel1); - if (markedObject2 == this && markedLabel2) - printedCharacters += fprintf(stderr, "%s", markedLabel2); - for (; printedCharacters < depth * 2; printedCharacters++) - fputc(' ', stderr); - - showRenderObject(printedCharacters); - - for (const RenderObject* child = slowFirstChild(); child; child = child->nextSibling()) - child->showRenderTreeAndMark(markedObject1, markedLabel1, markedObject2, markedLabel2, depth + 1); +void RenderObject::showRenderObject(int printedCharacters) const { + printedCharacters += fprintf(stderr, "%s %p", renderName(), this); + fputc('\n', stderr); } -#endif // NDEBUG +void RenderObject::showRenderTreeAndMark(const RenderObject* markedObject1, + const char* markedLabel1, + const RenderObject* markedObject2, + const char* markedLabel2, + int depth) const { + int printedCharacters = 0; + if (markedObject1 == this && markedLabel1) + printedCharacters += fprintf(stderr, "%s", markedLabel1); + if (markedObject2 == this && markedLabel2) + printedCharacters += fprintf(stderr, "%s", markedLabel2); + for (; printedCharacters < depth * 2; printedCharacters++) + fputc(' ', stderr); -bool RenderObject::isSelectable() const -{ - return !(style()->userSelect() == SELECT_NONE && style()->userModify() == READ_ONLY); -} + showRenderObject(printedCharacters); -Color RenderObject::selectionBackgroundColor() const -{ - ASSERT_NOT_REACHED(); - // TODO(ianh): if we expose selection painting, we should expose a way to set the background colour - // TODO(ianh): need to be able to configure whether to consider the selection focused and active or not - if (!isSelectable()) - return Color::transparent; - bool isFocusedAndActive = true; - return isFocusedAndActive ? - RenderTheme::theme().activeSelectionBackgroundColor() : - RenderTheme::theme().inactiveSelectionBackgroundColor(); -} - -Color RenderObject::selectionColor() const -{ - ASSERT_NOT_REACHED(); - return style()->color(); + for (const RenderObject* child = slowFirstChild(); child; + child = child->nextSibling()) + child->showRenderTreeAndMark(markedObject1, markedLabel1, markedObject2, + markedLabel2, depth + 1); } -Color RenderObject::selectionForegroundColor() const -{ - return selectionColor(); -} +#endif // NDEBUG -Color RenderObject::selectionEmphasisMarkColor() const -{ - return selectionColor(); +bool RenderObject::isSelectable() const { + return !(style()->userSelect() == SELECT_NONE && + style()->userModify() == READ_ONLY); } -void RenderObject::selectionStartEnd(int& spos, int& epos) const -{ - spos = -1; - epos = -1; +Color RenderObject::selectionBackgroundColor() const { + ASSERT_NOT_REACHED(); + // TODO(ianh): if we expose selection painting, we should expose a way to set + // the background colour + // TODO(ianh): need to be able to configure whether to consider the selection + // focused and active or not + if (!isSelectable()) + return Color::transparent; + bool isFocusedAndActive = true; + return isFocusedAndActive + ? RenderTheme::theme().activeSelectionBackgroundColor() + : RenderTheme::theme().inactiveSelectionBackgroundColor(); } -StyleDifference RenderObject::adjustStyleDifference(StyleDifference diff) const -{ - // The answer to layerTypeRequired() for plugins, iframes, and canvas can change without the actual - // style changing, since it depends on whether we decide to composite these elements. When the - // layer status of one of these elements changes, we need to force a layout. - if (!diff.needsFullLayout() && style() && isBox()) { - bool requiresLayer = toRenderBox(this)->layerTypeRequired() != NoLayer; - if (hasLayer() != requiresLayer) - diff.setNeedsFullLayout(); - } - - return diff; +Color RenderObject::selectionColor() const { + ASSERT_NOT_REACHED(); + return style()->color(); } -inline bool RenderObject::hasImmediateNonWhitespaceTextChildOrPropertiesDependentOnColor() const -{ - if (style()->hasBorder() || style()->hasOutline()) - return true; - for (const RenderObject* r = slowFirstChild(); r; r = r->nextSibling()) { - if (r->isText() && !toRenderText(r)->isAllCollapsibleWhitespace()) - return true; - if (r->style()->hasOutline() || r->style()->hasBorder()) - return true; - } - return false; +Color RenderObject::selectionForegroundColor() const { + return selectionColor(); } -void RenderObject::markContainingBlocksForOverflowRecalc() -{ - for (RenderBlock* container = containingBlock(); container && !container->childNeedsOverflowRecalcAfterStyleChange(); container = container->containingBlock()) - container->setChildNeedsOverflowRecalcAfterStyleChange(true); +Color RenderObject::selectionEmphasisMarkColor() const { + return selectionColor(); } -void RenderObject::setNeedsOverflowRecalcAfterStyleChange() -{ - bool neededRecalc = needsOverflowRecalcAfterStyleChange(); - setSelfNeedsOverflowRecalcAfterStyleChange(true); - if (!neededRecalc) - markContainingBlocksForOverflowRecalc(); +void RenderObject::selectionStartEnd(int& spos, int& epos) const { + spos = -1; + epos = -1; } -void RenderObject::setStyle(PassRefPtr style) -{ - ASSERT(style); - StyleDifference diff; - if (m_style) - diff = m_style->visualInvalidationDiff(*style); - - diff = adjustStyleDifference(diff); - - styleWillChange(diff, *style); - - RefPtr oldStyle = m_style.release(); - setStyleInternal(style); - - updateFillImages(oldStyle ? &oldStyle->backgroundLayers() : 0, m_style->backgroundLayers()); - - bool doesNotNeedLayout = !m_parent || isText(); - - styleDidChange(diff, oldStyle.get()); - - // FIXME: |this| might be destroyed here. This can currently happen for a RenderTextFragment when - // its first-letter block gets an update in RenderTextFragment::styleDidChange. For RenderTextFragment(s), - // we will safely bail out with the doesNotNeedLayout flag. We might want to broaden this condition - // in the future as we move renderer changes out of layout and into style changes. - // FIXME(sky): Remove this. - if (doesNotNeedLayout) - return; - - // Now that the layer (if any) has been updated, we need to adjust the diff again, - // check whether we should layout now, and decide if we need to invalidate paints. - StyleDifference updatedDiff = adjustStyleDifference(diff); - - if (!diff.needsFullLayout()) { - if (updatedDiff.needsFullLayout()) - setNeedsLayoutAndPrefWidthsRecalc(); - else if (updatedDiff.needsPositionedMovementLayout()) - setNeedsPositionedMovementLayout(); - } - - if (diff.transformChanged() && !needsLayout()) { - if (RenderBlock* container = containingBlock()) - container->setNeedsOverflowRecalcAfterStyleChange(); - } -} +StyleDifference RenderObject::adjustStyleDifference( + StyleDifference diff) const { + // The answer to layerTypeRequired() for plugins, iframes, and canvas can + // change without the actual style changing, since it depends on whether we + // decide to composite these elements. When the layer status of one of these + // elements changes, we need to force a layout. + if (!diff.needsFullLayout() && style() && isBox()) { + bool requiresLayer = toRenderBox(this)->layerTypeRequired() != NoLayer; + if (hasLayer() != requiresLayer) + diff.setNeedsFullLayout(); + } -void RenderObject::styleWillChange(StyleDifference diff, const RenderStyle& newStyle) -{ - if (m_style) { - if (isOutOfFlowPositioned() && (m_style->position() != newStyle.position())) - // For changes in positioning styles, we need to conceivably remove ourselves - // from the positioned objects list. - toRenderBox(this)->removeFloatingOrPositionedChildFromBlockLists(); - - s_affectsParentBlock = isFloatingOrOutOfFlowPositioned() - && !newStyle.hasOutOfFlowPosition() - && parent() && (parent()->isRenderParagraph() || parent()->isRenderInline()); - - // Clearing these bits is required to avoid leaving stale renderers. - // FIXME: We shouldn't need that hack if our logic was totally correct. - if (diff.needsLayout()) { - clearPositionedState(); - } - } else { - s_affectsParentBlock = false; - } + return diff; } -void RenderObject::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) -{ - if (s_affectsParentBlock) { - // An object that was floating or positioned became a normal flow object again. We have to make sure the - // render tree updates as needed to accommodate the new normal flow object. - setInline(style()->isDisplayInlineType()); - ASSERT(isInline() == parent()->isRenderParagraph()); +inline bool +RenderObject::hasImmediateNonWhitespaceTextChildOrPropertiesDependentOnColor() + const { + if (style()->hasBorder() || style()->hasOutline()) + return true; + for (const RenderObject* r = slowFirstChild(); r; r = r->nextSibling()) { + if (r->isText() && !toRenderText(r)->isAllCollapsibleWhitespace()) + return true; + if (r->style()->hasOutline() || r->style()->hasBorder()) + return true; + } + return false; +} + +void RenderObject::markContainingBlocksForOverflowRecalc() { + for (RenderBlock* container = containingBlock(); + container && !container->childNeedsOverflowRecalcAfterStyleChange(); + container = container->containingBlock()) + container->setChildNeedsOverflowRecalcAfterStyleChange(true); +} + +void RenderObject::setNeedsOverflowRecalcAfterStyleChange() { + bool neededRecalc = needsOverflowRecalcAfterStyleChange(); + setSelfNeedsOverflowRecalcAfterStyleChange(true); + if (!neededRecalc) + markContainingBlocksForOverflowRecalc(); +} + +void RenderObject::setStyle(PassRefPtr style) { + ASSERT(style); + StyleDifference diff; + if (m_style) + diff = m_style->visualInvalidationDiff(*style); + + diff = adjustStyleDifference(diff); + + styleWillChange(diff, *style); + + RefPtr oldStyle = m_style.release(); + setStyleInternal(style); + + updateFillImages(oldStyle ? &oldStyle->backgroundLayers() : 0, + m_style->backgroundLayers()); + + bool doesNotNeedLayout = !m_parent || isText(); + + styleDidChange(diff, oldStyle.get()); + + // FIXME: |this| might be destroyed here. This can currently happen for a + // RenderTextFragment when its first-letter block gets an update in + // RenderTextFragment::styleDidChange. For RenderTextFragment(s), we will + // safely bail out with the doesNotNeedLayout flag. We might want to broaden + // this condition in the future as we move renderer changes out of layout and + // into style changes. + // FIXME(sky): Remove this. + if (doesNotNeedLayout) + return; + + // Now that the layer (if any) has been updated, we need to adjust the diff + // again, check whether we should layout now, and decide if we need to + // invalidate paints. + StyleDifference updatedDiff = adjustStyleDifference(diff); + + if (!diff.needsFullLayout()) { + if (updatedDiff.needsFullLayout()) + setNeedsLayoutAndPrefWidthsRecalc(); + else if (updatedDiff.needsPositionedMovementLayout()) + setNeedsPositionedMovementLayout(); + } + + if (diff.transformChanged() && !needsLayout()) { + if (RenderBlock* container = containingBlock()) + container->setNeedsOverflowRecalcAfterStyleChange(); + } +} + +void RenderObject::styleWillChange(StyleDifference diff, + const RenderStyle& newStyle) { + if (m_style) { + if (isOutOfFlowPositioned() && (m_style->position() != newStyle.position())) + // For changes in positioning styles, we need to conceivably remove + // ourselves from the positioned objects list. + toRenderBox(this)->removeFloatingOrPositionedChildFromBlockLists(); + + s_affectsParentBlock = + isFloatingOrOutOfFlowPositioned() && !newStyle.hasOutOfFlowPosition() && + parent() && + (parent()->isRenderParagraph() || parent()->isRenderInline()); + + // Clearing these bits is required to avoid leaving stale renderers. + // FIXME: We shouldn't need that hack if our logic was totally correct. + if (diff.needsLayout()) { + clearPositionedState(); } + } else { + s_affectsParentBlock = false; + } +} + +void RenderObject::styleDidChange(StyleDifference diff, + const RenderStyle* oldStyle) { + if (s_affectsParentBlock) { + // An object that was floating or positioned became a normal flow object + // again. We have to make sure the render tree updates as needed to + // accommodate the new normal flow object. + setInline(style()->isDisplayInlineType()); + ASSERT(isInline() == parent()->isRenderParagraph()); + } + + if (!m_parent) + return; + + if (diff.needsFullLayout()) { + // If the object already needs layout, then setNeedsLayout won't do + // any work. But if the containing block has changed, then we may need + // to mark the new containing blocks for layout. The change that can + // directly affect the containing block of this object is a change to + // the position style. + if (needsLayout() && oldStyle->position() != m_style->position()) + markContainingBlocksForLayout(); + + // Ditto. + if (needsOverflowRecalcAfterStyleChange() && + oldStyle->position() != m_style->position()) + markContainingBlocksForOverflowRecalc(); + + if (diff.needsFullLayout()) + setNeedsLayoutAndPrefWidthsRecalc(); + } else if (diff.needsPositionedMovementLayout()) + setNeedsPositionedMovementLayout(); + + // Don't check for paint invalidation here; we need to wait until the layer + // has been updated by subclasses before we know if we have to invalidate + // paints (in setStyle()). +} + +void RenderObject::updateFillImages(const FillLayer* oldLayers, + const FillLayer& newLayers) { + // Optimize the common case + if (oldLayers && !oldLayers->next() && !newLayers.next() && + (oldLayers->image() == newLayers.image())) + return; + + // Go through the new layers and addClients first, to avoid removing all + // clients of an image. + for (const FillLayer* currNew = &newLayers; currNew; + currNew = currNew->next()) { + if (currNew->image()) + currNew->image()->addClient(this); + } + + for (const FillLayer* currOld = oldLayers; currOld; + currOld = currOld->next()) { + if (currOld->image()) + currOld->image()->removeClient(this); + } +} + +void RenderObject::updateImage(StyleImage* oldImage, StyleImage* newImage) { + if (oldImage != newImage) { + if (oldImage) + oldImage->removeClient(this); + if (newImage) + newImage->addClient(this); + } +} + +FloatPoint RenderObject::localToAbsolute(const FloatPoint& localPoint, + MapCoordinatesFlags mode) const { + TransformState transformState(TransformState::ApplyTransformDirection, + localPoint); + mapLocalToContainer(0, transformState, mode | ApplyContainerFlip); + transformState.flatten(); + + return transformState.lastPlanarPoint(); +} + +FloatPoint RenderObject::absoluteToLocal(const FloatPoint& containerPoint, + MapCoordinatesFlags mode) const { + TransformState transformState( + TransformState::UnapplyInverseTransformDirection, containerPoint); + mapAbsoluteToLocalPoint(mode, transformState); + transformState.flatten(); + + return transformState.lastPlanarPoint(); +} + +FloatQuad RenderObject::absoluteToLocalQuad(const FloatQuad& quad, + MapCoordinatesFlags mode) const { + TransformState transformState( + TransformState::UnapplyInverseTransformDirection, + quad.boundingBox().center(), quad); + mapAbsoluteToLocalPoint(mode, transformState); + transformState.flatten(); + return transformState.lastPlanarQuad(); +} + +void RenderObject::mapLocalToContainer( + const RenderBox* paintInvalidationContainer, + TransformState& transformState, + MapCoordinatesFlags mode) const { + if (paintInvalidationContainer == this) + return; + + RenderObject* o = parent(); + if (!o) + return; + + // FIXME: this should call offsetFromContainer to share code, but I'm not sure + // it's ever called. + if (mode & ApplyContainerFlip && o->isBox()) + mode &= ~ApplyContainerFlip; + + o->mapLocalToContainer(paintInvalidationContainer, transformState, mode); +} + +const RenderObject* RenderObject::pushMappingToContainer( + const RenderBox* ancestorToStopAt, + RenderGeometryMap& geometryMap) const { + ASSERT_UNUSED(ancestorToStopAt, ancestorToStopAt != this); - if (!m_parent) - return; - - if (diff.needsFullLayout()) { - // If the object already needs layout, then setNeedsLayout won't do - // any work. But if the containing block has changed, then we may need - // to mark the new containing blocks for layout. The change that can - // directly affect the containing block of this object is a change to - // the position style. - if (needsLayout() && oldStyle->position() != m_style->position()) - markContainingBlocksForLayout(); - - // Ditto. - if (needsOverflowRecalcAfterStyleChange() && oldStyle->position() != m_style->position()) - markContainingBlocksForOverflowRecalc(); - - if (diff.needsFullLayout()) - setNeedsLayoutAndPrefWidthsRecalc(); - } else if (diff.needsPositionedMovementLayout()) - setNeedsPositionedMovementLayout(); + RenderObject* container = parent(); + if (!container) + return 0; + // FIXME(sky): Do we need to make this call? + geometryMap.push(this, LayoutSize(), false); + return container; +} + +void RenderObject::mapAbsoluteToLocalPoint( + MapCoordinatesFlags mode, + TransformState& transformState) const { + RenderObject* o = parent(); + if (o) + o->mapAbsoluteToLocalPoint(mode, transformState); +} + +bool RenderObject::shouldUseTransformFromContainer( + const RenderObject* containerObject) const { + // hasTransform() indicates whether the object has transform, transform-style + // or perspective. We just care about transform, so check the layer's + // transform directly. + return (isBox() && toRenderBox(this)->transform()) || + (containerObject && containerObject->style()->hasPerspective()); +} + +void RenderObject::getTransformFromContainer( + const RenderObject* containerObject, + const LayoutSize& offsetInContainer, + TransformationMatrix& transform) const { + transform.makeIdentity(); + transform.translate(offsetInContainer.width().toFloat(), + offsetInContainer.height().toFloat()); + TransformationMatrix* localTransform = + isBox() ? toRenderBox(this)->transform() : 0; + if (localTransform) + transform.multiply(*localTransform); + + if (containerObject && containerObject->hasLayer() && + containerObject->style()->hasPerspective()) { + // Perpsective on the container affects us, so we have to factor it in here. + ASSERT(containerObject->hasLayer()); + FloatPoint perspectiveOrigin = + toRenderBox(containerObject)->perspectiveOrigin(); + + TransformationMatrix perspectiveMatrix; + perspectiveMatrix.applyPerspective(containerObject->style()->perspective()); + + transform.translateRight3d(-perspectiveOrigin.x(), -perspectiveOrigin.y(), + 0); + transform = perspectiveMatrix * transform; + transform.translateRight3d(perspectiveOrigin.x(), perspectiveOrigin.y(), 0); + } +} + +FloatQuad RenderObject::localToContainerQuad( + const FloatQuad& localQuad, + const RenderBox* paintInvalidationContainer, + MapCoordinatesFlags mode) const { + // Track the point at the center of the quad's bounding box. As + // mapLocalToContainer() calls offsetFromContainer(), it will use that point + // as the reference point to decide which column's transform to apply in + // multiple-column blocks. + TransformState transformState(TransformState::ApplyTransformDirection, + localQuad.boundingBox().center(), localQuad); + mapLocalToContainer(paintInvalidationContainer, transformState, + mode | ApplyContainerFlip | UseTransforms); + transformState.flatten(); + + return transformState.lastPlanarQuad(); +} + +FloatPoint RenderObject::localToContainerPoint( + const FloatPoint& localPoint, + const RenderBox* paintInvalidationContainer, + MapCoordinatesFlags mode) const { + TransformState transformState(TransformState::ApplyTransformDirection, + localPoint); + mapLocalToContainer(paintInvalidationContainer, transformState, + mode | ApplyContainerFlip | UseTransforms); + transformState.flatten(); + + return transformState.lastPlanarPoint(); +} + +LayoutSize RenderObject::offsetFromContainer(const RenderObject* o, + const LayoutPoint& point, + bool* offsetDependsOnPoint) const { + ASSERT(o == container()); + LayoutSize offset; + if (offsetDependsOnPoint) + *offsetDependsOnPoint = false; + return offset; +} + +LayoutSize RenderObject::offsetFromAncestorContainer( + const RenderObject* container) const { + LayoutSize offset; + LayoutPoint referencePoint; + const RenderObject* currContainer = this; + do { + const RenderObject* nextContainer = currContainer->container(); + ASSERT(nextContainer); // This means we reached the top without finding + // container. + if (!nextContainer) + break; + ASSERT(!currContainer->hasTransform()); + LayoutSize currentOffset = + currContainer->offsetFromContainer(nextContainer, referencePoint); + offset += currentOffset; + referencePoint.move(currentOffset); + currContainer = nextContainer; + } while (currContainer != container); + + return offset; +} + +LayoutRect RenderObject::localCaretRect(InlineBox*, + int, + LayoutUnit* extraWidthToEndOfLine) { + if (extraWidthToEndOfLine) + *extraWidthToEndOfLine = 0; + + return LayoutRect(); +} + +bool RenderObject::isRooted() const { + const RenderObject* object = this; + while (object->parent() && !object->hasLayer()) + object = object->parent(); + if (object->hasLayer()) + return toRenderBox(object)->layer()->root()->isRootLayer(); + return false; +} + +RespectImageOrientationEnum RenderObject::shouldRespectImageOrientation() + const { + return DoNotRespectImageOrientation; +} + +bool RenderObject::hasEntirelyFixedBackground() const { + return m_style->hasEntirelyFixedBackground(); +} + +RenderObject* RenderObject::container( + const RenderBox* paintInvalidationContainer, + bool* paintInvalidationContainerSkipped) const { + if (paintInvalidationContainerSkipped) + *paintInvalidationContainerSkipped = false; + + // This method is extremely similar to containingBlock(), but with a few + // notable exceptions. (1) It can be used on orphaned subtrees, i.e., it can + // be called safely even when the object is not part of the primary document + // subtree yet. (2) For normal flow elements, it just returns the parent. (3) + // For absolute positioned elements, it will return a relative positioned + // inline. containingBlock() simply skips relpositioned inlines and lets an + // enclosing block handle the layout of the positioned object. This does mean + // that computePositionedLogicalWidth and computePositionedLogicalHeight have + // to use container(). + RenderObject* o = parent(); + + if (isText()) + return o; - // Don't check for paint invalidation here; we need to wait until the layer has been - // updated by subclasses before we know if we have to invalidate paints (in setStyle()). -} + EPosition pos = m_style->position(); + if (pos == AbsolutePosition) { + // We technically just want our containing block, but + // we may not have one if we're part of an uninstalled + // subtree. We'll climb as high as we can though. + while (o) { + if (o->style()->position() != StaticPosition) + break; -void RenderObject::updateFillImages(const FillLayer* oldLayers, const FillLayer& newLayers) -{ - // Optimize the common case - if (oldLayers && !oldLayers->next() && !newLayers.next() && (oldLayers->image() == newLayers.image())) - return; + if (o->canContainAbsolutePositionObjects()) + break; - // Go through the new layers and addClients first, to avoid removing all clients of an image. - for (const FillLayer* currNew = &newLayers; currNew; currNew = currNew->next()) { - if (currNew->image()) - currNew->image()->addClient(this); - } + if (paintInvalidationContainerSkipped && o == paintInvalidationContainer) + *paintInvalidationContainerSkipped = true; - for (const FillLayer* currOld = oldLayers; currOld; currOld = currOld->next()) { - if (currOld->image()) - currOld->image()->removeClient(this); + o = o->parent(); } -} + } -void RenderObject::updateImage(StyleImage* oldImage, StyleImage* newImage) -{ - if (oldImage != newImage) { - if (oldImage) - oldImage->removeClient(this); - if (newImage) - newImage->addClient(this); - } + return o; } -FloatPoint RenderObject::localToAbsolute(const FloatPoint& localPoint, MapCoordinatesFlags mode) const -{ - TransformState transformState(TransformState::ApplyTransformDirection, localPoint); - mapLocalToContainer(0, transformState, mode | ApplyContainerFlip); - transformState.flatten(); - - return transformState.lastPlanarPoint(); +bool RenderObject::isSelectionBorder() const { + SelectionState st = selectionState(); + return st == SelectionStart || st == SelectionEnd || st == SelectionBoth; } -FloatPoint RenderObject::absoluteToLocal(const FloatPoint& containerPoint, MapCoordinatesFlags mode) const -{ - TransformState transformState(TransformState::UnapplyInverseTransformDirection, containerPoint); - mapAbsoluteToLocalPoint(mode, transformState); - transformState.flatten(); +inline void RenderObject::clearLayoutRootIfNeeded() const {} - return transformState.lastPlanarPoint(); -} +void RenderObject::willBeDestroyed() { + // Destroy any leftover anonymous children. + RenderObjectChildList* children = virtualChildren(); + if (children) + children->destroyLeftoverChildren(); -FloatQuad RenderObject::absoluteToLocalQuad(const FloatQuad& quad, MapCoordinatesFlags mode) const -{ - TransformState transformState(TransformState::UnapplyInverseTransformDirection, quad.boundingBox().center(), quad); - mapAbsoluteToLocalPoint(mode, transformState); - transformState.flatten(); - return transformState.lastPlanarQuad(); + remove(); + setAncestorLineBoxDirty(false); + clearLayoutRootIfNeeded(); } -void RenderObject::mapLocalToContainer(const RenderBox* paintInvalidationContainer, TransformState& transformState, MapCoordinatesFlags mode) const -{ - if (paintInvalidationContainer == this) - return; +void RenderObject::insertedIntoTree() { + // FIXME: We should ASSERT(isRooted()) here but generated content makes some + // out-of-order insertion. - RenderObject* o = parent(); - if (!o) - return; + // Keep our layer hierarchy updated. Optimize for the common case where we + // don't have any children and don't have a layer attached to ourselves. + RenderLayer* layer = 0; + if (slowFirstChild() || hasLayer()) { + layer = parent()->enclosingLayer(); + addLayers(layer); + } - // FIXME: this should call offsetFromContainer to share code, but I'm not sure it's ever called. - if (mode & ApplyContainerFlip && o->isBox()) - mode &= ~ApplyContainerFlip; - - o->mapLocalToContainer(paintInvalidationContainer, transformState, mode); + if (parent()->isRenderParagraph()) + parent()->dirtyLinesFromChangedChild(this); } -const RenderObject* RenderObject::pushMappingToContainer(const RenderBox* ancestorToStopAt, RenderGeometryMap& geometryMap) const -{ - ASSERT_UNUSED(ancestorToStopAt, ancestorToStopAt != this); +void RenderObject::willBeRemovedFromTree() { + // FIXME: We should ASSERT(isRooted()) but we have some out-of-order removals + // which would need to be fixed first. - RenderObject* container = parent(); - if (!container) - return 0; - // FIXME(sky): Do we need to make this call? - geometryMap.push(this, LayoutSize(), false); - return container; -} + // Keep our layer hierarchy updated. + if (slowFirstChild() || hasLayer()) + removeLayers(parent()->enclosingLayer()); -void RenderObject::mapAbsoluteToLocalPoint(MapCoordinatesFlags mode, TransformState& transformState) const -{ - RenderObject* o = parent(); - if (o) - o->mapAbsoluteToLocalPoint(mode, transformState); + if (isOutOfFlowPositioned() && parent()->isRenderParagraph()) + parent()->dirtyLinesFromChangedChild(this); } -bool RenderObject::shouldUseTransformFromContainer(const RenderObject* containerObject) const -{ - // hasTransform() indicates whether the object has transform, transform-style or perspective. We just care about transform, - // so check the layer's transform directly. - return (isBox() && toRenderBox(this)->transform()) || (containerObject && containerObject->style()->hasPerspective()); +void RenderObject::destroy() { +#if ENABLE(ASSERT) && ENABLE(OILPAN) + ASSERT(!m_didCallDestroy); + m_didCallDestroy = true; +#endif + willBeDestroyed(); + postDestroy(); } -void RenderObject::getTransformFromContainer(const RenderObject* containerObject, const LayoutSize& offsetInContainer, TransformationMatrix& transform) const -{ - transform.makeIdentity(); - transform.translate(offsetInContainer.width().toFloat(), offsetInContainer.height().toFloat()); - TransformationMatrix* localTransform = isBox() ? toRenderBox(this)->transform() : 0; - if (localTransform) - transform.multiply(*localTransform); - - if (containerObject && containerObject->hasLayer() && containerObject->style()->hasPerspective()) { - // Perpsective on the container affects us, so we have to factor it in here. - ASSERT(containerObject->hasLayer()); - FloatPoint perspectiveOrigin = toRenderBox(containerObject)->perspectiveOrigin(); - - TransformationMatrix perspectiveMatrix; - perspectiveMatrix.applyPerspective(containerObject->style()->perspective()); - - transform.translateRight3d(-perspectiveOrigin.x(), -perspectiveOrigin.y(), 0); - transform = perspectiveMatrix * transform; - transform.translateRight3d(perspectiveOrigin.x(), perspectiveOrigin.y(), 0); +void RenderObject::postDestroy() { + // It seems ugly that this is not in willBeDestroyed(). + if (m_style) { + for (const FillLayer* bgLayer = &m_style->backgroundLayers(); bgLayer; + bgLayer = bgLayer->next()) { + if (StyleImage* backgroundImage = bgLayer->image()) + backgroundImage->removeClient(this); } -} - -FloatQuad RenderObject::localToContainerQuad(const FloatQuad& localQuad, const RenderBox* paintInvalidationContainer, MapCoordinatesFlags mode) const -{ - // Track the point at the center of the quad's bounding box. As mapLocalToContainer() calls offsetFromContainer(), - // it will use that point as the reference point to decide which column's transform to apply in multiple-column blocks. - TransformState transformState(TransformState::ApplyTransformDirection, localQuad.boundingBox().center(), localQuad); - mapLocalToContainer(paintInvalidationContainer, transformState, mode | ApplyContainerFlip | UseTransforms); - transformState.flatten(); - - return transformState.lastPlanarQuad(); -} - -FloatPoint RenderObject::localToContainerPoint(const FloatPoint& localPoint, const RenderBox* paintInvalidationContainer, MapCoordinatesFlags mode) const -{ - TransformState transformState(TransformState::ApplyTransformDirection, localPoint); - mapLocalToContainer(paintInvalidationContainer, transformState, mode | ApplyContainerFlip | UseTransforms); - transformState.flatten(); - - return transformState.lastPlanarPoint(); -} - -LayoutSize RenderObject::offsetFromContainer(const RenderObject* o, const LayoutPoint& point, bool* offsetDependsOnPoint) const -{ - ASSERT(o == container()); - LayoutSize offset; - if (offsetDependsOnPoint) - *offsetDependsOnPoint = false; - return offset; -} - -LayoutSize RenderObject::offsetFromAncestorContainer(const RenderObject* container) const -{ - LayoutSize offset; - LayoutPoint referencePoint; - const RenderObject* currContainer = this; - do { - const RenderObject* nextContainer = currContainer->container(); - ASSERT(nextContainer); // This means we reached the top without finding container. - if (!nextContainer) - break; - ASSERT(!currContainer->hasTransform()); - LayoutSize currentOffset = currContainer->offsetFromContainer(nextContainer, referencePoint); - offset += currentOffset; - referencePoint.move(currentOffset); - currContainer = nextContainer; - } while (currContainer != container); - - return offset; -} - -LayoutRect RenderObject::localCaretRect(InlineBox*, int, LayoutUnit* extraWidthToEndOfLine) -{ - if (extraWidthToEndOfLine) - *extraWidthToEndOfLine = 0; - - return LayoutRect(); -} - -bool RenderObject::isRooted() const -{ - const RenderObject* object = this; - while (object->parent() && !object->hasLayer()) - object = object->parent(); - if (object->hasLayer()) - return toRenderBox(object)->layer()->root()->isRootLayer(); - return false; -} - -RespectImageOrientationEnum RenderObject::shouldRespectImageOrientation() const -{ - return DoNotRespectImageOrientation; -} - -bool RenderObject::hasEntirelyFixedBackground() const -{ - return m_style->hasEntirelyFixedBackground(); -} - -RenderObject* RenderObject::container(const RenderBox* paintInvalidationContainer, bool* paintInvalidationContainerSkipped) const -{ - if (paintInvalidationContainerSkipped) - *paintInvalidationContainerSkipped = false; - - // This method is extremely similar to containingBlock(), but with a few notable - // exceptions. - // (1) It can be used on orphaned subtrees, i.e., it can be called safely even when - // the object is not part of the primary document subtree yet. - // (2) For normal flow elements, it just returns the parent. - // (3) For absolute positioned elements, it will return a relative positioned inline. - // containingBlock() simply skips relpositioned inlines and lets an enclosing block handle - // the layout of the positioned object. This does mean that computePositionedLogicalWidth and - // computePositionedLogicalHeight have to use container(). - RenderObject* o = parent(); - - if (isText()) - return o; - - EPosition pos = m_style->position(); - if (pos == AbsolutePosition) { - // We technically just want our containing block, but - // we may not have one if we're part of an uninstalled - // subtree. We'll climb as high as we can though. - while (o) { - if (o->style()->position() != StaticPosition) - break; - - if (o->canContainAbsolutePositionObjects()) - break; - - if (paintInvalidationContainerSkipped && o == paintInvalidationContainer) - *paintInvalidationContainerSkipped = true; - - o = o->parent(); - } + } + delete this; +} + +PositionWithAffinity RenderObject::positionForPoint(const LayoutPoint&) { + return createPositionWithAffinity(caretMinOffset(), DOWNSTREAM); +} + +// FIXME(sky): Change the callers to use nodeAtPoint direclty and remove this +// function. Or, rename nodeAtPoint to hitTest? +bool RenderObject::hitTest(const HitTestRequest& request, + HitTestResult& result, + const HitTestLocation& locationInContainer, + const LayoutPoint& accumulatedOffset) { + return nodeAtPoint(request, result, locationInContainer, accumulatedOffset); +} + +void RenderObject::updateHitTestResult(HitTestResult& result, + const LayoutPoint& point) {} + +bool RenderObject::nodeAtPoint(const HitTestRequest&, + HitTestResult&, + const HitTestLocation& /*locationInContainer*/, + const LayoutPoint& /*accumulatedOffset*/) { + return false; +} + +void RenderObject::scheduleRelayout() {} + +void RenderObject::forceLayout() { + setSelfNeedsLayout(true); + layout(); +} + +// FIXME: Does this do anything different than forceLayout given that we don't +// walk the containing block chain. If not, we should change all callers to use +// forceLayout. +void RenderObject::forceChildLayout() { + setNormalChildNeedsLayout(true); + layout(); +} + +void RenderObject::getTextDecorations(unsigned decorations, + AppliedTextDecoration& underline, + AppliedTextDecoration& overline, + AppliedTextDecoration& linethrough, + bool quirksMode, + bool firstlineStyle) { + RenderObject* curr = this; + RenderStyle* styleToUse = 0; + unsigned currDecs = TextDecorationNone; + Color resultColor; + TextDecorationStyle resultStyle; + do { + styleToUse = curr->style(firstlineStyle); + currDecs = styleToUse->textDecoration(); + currDecs &= decorations; + resultColor = styleToUse->decorationColor(); + resultStyle = styleToUse->textDecorationStyle(); + // Parameter 'decorations' is cast as an int to enable the bitwise + // operations below. + if (currDecs) { + if (currDecs & TextDecorationUnderline) { + decorations &= ~TextDecorationUnderline; + underline.color = resultColor; + underline.style = resultStyle; + } + if (currDecs & TextDecorationOverline) { + decorations &= ~TextDecorationOverline; + overline.color = resultColor; + overline.style = resultStyle; + } + if (currDecs & TextDecorationLineThrough) { + decorations &= ~TextDecorationLineThrough; + linethrough.color = resultColor; + linethrough.style = resultStyle; + } } - - return o; -} - -bool RenderObject::isSelectionBorder() const -{ - SelectionState st = selectionState(); - return st == SelectionStart || st == SelectionEnd || st == SelectionBoth; -} - -inline void RenderObject::clearLayoutRootIfNeeded() const -{ -} - -void RenderObject::willBeDestroyed() -{ - // Destroy any leftover anonymous children. - RenderObjectChildList* children = virtualChildren(); - if (children) - children->destroyLeftoverChildren(); - - remove(); - setAncestorLineBoxDirty(false); - clearLayoutRootIfNeeded(); -} - -void RenderObject::insertedIntoTree() -{ - // FIXME: We should ASSERT(isRooted()) here but generated content makes some out-of-order insertion. - - // Keep our layer hierarchy updated. Optimize for the common case where we don't have any children - // and don't have a layer attached to ourselves. - RenderLayer* layer = 0; - if (slowFirstChild() || hasLayer()) { - layer = parent()->enclosingLayer(); - addLayers(layer); + curr = curr->parent(); + } while (curr && decorations); + + // If we bailed out, use the element we bailed out at (typically a or + // element). + if (decorations && curr) { + styleToUse = curr->style(firstlineStyle); + resultColor = styleToUse->decorationColor(); + if (decorations & TextDecorationUnderline) { + underline.color = resultColor; + underline.style = resultStyle; } - - if (parent()->isRenderParagraph()) - parent()->dirtyLinesFromChangedChild(this); -} - -void RenderObject::willBeRemovedFromTree() -{ - // FIXME: We should ASSERT(isRooted()) but we have some out-of-order removals which would need to be fixed first. - - // Keep our layer hierarchy updated. - if (slowFirstChild() || hasLayer()) - removeLayers(parent()->enclosingLayer()); - - if (isOutOfFlowPositioned() && parent()->isRenderParagraph()) - parent()->dirtyLinesFromChangedChild(this); -} - -void RenderObject::destroy() -{ -#if ENABLE(ASSERT) && ENABLE(OILPAN) - ASSERT(!m_didCallDestroy); - m_didCallDestroy = true; -#endif - willBeDestroyed(); - postDestroy(); -} - -void RenderObject::postDestroy() -{ - // It seems ugly that this is not in willBeDestroyed(). - if (m_style) { - for (const FillLayer* bgLayer = &m_style->backgroundLayers(); bgLayer; bgLayer = bgLayer->next()) { - if (StyleImage* backgroundImage = bgLayer->image()) - backgroundImage->removeClient(this); - } + if (decorations & TextDecorationOverline) { + overline.color = resultColor; + overline.style = resultStyle; } - delete this; -} - -PositionWithAffinity RenderObject::positionForPoint(const LayoutPoint&) -{ - return createPositionWithAffinity(caretMinOffset(), DOWNSTREAM); -} - -// FIXME(sky): Change the callers to use nodeAtPoint direclty and remove this function. -// Or, rename nodeAtPoint to hitTest? -bool RenderObject::hitTest(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset) -{ - return nodeAtPoint(request, result, locationInContainer, accumulatedOffset); -} - -void RenderObject::updateHitTestResult(HitTestResult& result, const LayoutPoint& point) -{ -} - -bool RenderObject::nodeAtPoint(const HitTestRequest&, HitTestResult&, const HitTestLocation& /*locationInContainer*/, const LayoutPoint& /*accumulatedOffset*/) -{ - return false; -} - -void RenderObject::scheduleRelayout() -{ -} - -void RenderObject::forceLayout() -{ - setSelfNeedsLayout(true); - layout(); -} - -// FIXME: Does this do anything different than forceLayout given that we don't walk -// the containing block chain. If not, we should change all callers to use forceLayout. -void RenderObject::forceChildLayout() -{ - setNormalChildNeedsLayout(true); - layout(); -} - -void RenderObject::getTextDecorations(unsigned decorations, AppliedTextDecoration& underline, AppliedTextDecoration& overline, AppliedTextDecoration& linethrough, bool quirksMode, bool firstlineStyle) -{ - RenderObject* curr = this; - RenderStyle* styleToUse = 0; - unsigned currDecs = TextDecorationNone; - Color resultColor; - TextDecorationStyle resultStyle; - do { - styleToUse = curr->style(firstlineStyle); - currDecs = styleToUse->textDecoration(); - currDecs &= decorations; - resultColor = styleToUse->decorationColor(); - resultStyle = styleToUse->textDecorationStyle(); - // Parameter 'decorations' is cast as an int to enable the bitwise operations below. - if (currDecs) { - if (currDecs & TextDecorationUnderline) { - decorations &= ~TextDecorationUnderline; - underline.color = resultColor; - underline.style = resultStyle; - } - if (currDecs & TextDecorationOverline) { - decorations &= ~TextDecorationOverline; - overline.color = resultColor; - overline.style = resultStyle; - } - if (currDecs & TextDecorationLineThrough) { - decorations &= ~TextDecorationLineThrough; - linethrough.color = resultColor; - linethrough.style = resultStyle; - } - } - curr = curr->parent(); - } while (curr && decorations); - - // If we bailed out, use the element we bailed out at (typically a or element). - if (decorations && curr) { - styleToUse = curr->style(firstlineStyle); - resultColor = styleToUse->decorationColor(); - if (decorations & TextDecorationUnderline) { - underline.color = resultColor; - underline.style = resultStyle; - } - if (decorations & TextDecorationOverline) { - overline.color = resultColor; - overline.style = resultStyle; - } - if (decorations & TextDecorationLineThrough) { - linethrough.color = resultColor; - linethrough.style = resultStyle; - } + if (decorations & TextDecorationLineThrough) { + linethrough.color = resultColor; + linethrough.style = resultStyle; } + } } -int RenderObject::caretMinOffset() const -{ - return 0; +int RenderObject::caretMinOffset() const { + return 0; } -int RenderObject::caretMaxOffset() const -{ - if (isReplaced()) - return 1; - return 0; +int RenderObject::caretMaxOffset() const { + if (isReplaced()) + return 1; + return 0; } -int RenderObject::previousOffset(int current) const -{ - return current - 1; +int RenderObject::previousOffset(int current) const { + return current - 1; } -int RenderObject::previousOffsetForBackwardDeletion(int current) const -{ - return current - 1; +int RenderObject::previousOffsetForBackwardDeletion(int current) const { + return current - 1; } -int RenderObject::nextOffset(int current) const -{ - return current + 1; +int RenderObject::nextOffset(int current) const { + return current + 1; } // touch-action applies to all elements with both width AND height properties. -// According to the CSS Box Model Spec (http://dev.w3.org/csswg/css-box/#the-width-and-height-properties) -// width applies to all elements but non-replaced inline elements, table rows, and row groups and -// height applies to all elements but non-replaced inline elements, table columns, and column groups. -bool RenderObject::supportsTouchAction() const -{ - if (isInline() && !isReplaced()) - return false; - return true; +// According to the CSS Box Model Spec +// (http://dev.w3.org/csswg/css-box/#the-width-and-height-properties) width +// applies to all elements but non-replaced inline elements, table rows, and row +// groups and height applies to all elements but non-replaced inline elements, +// table columns, and column groups. +bool RenderObject::supportsTouchAction() const { + if (isInline() && !isReplaced()) + return false; + return true; } -PositionWithAffinity RenderObject::createPositionWithAffinity(int offset, EAffinity affinity) -{ - return PositionWithAffinity(this, offset, affinity); +PositionWithAffinity RenderObject::createPositionWithAffinity( + int offset, + EAffinity affinity) { + return PositionWithAffinity(this, offset, affinity); } -bool RenderObject::canUpdateSelectionOnRootLineBoxes() -{ - if (needsLayout()) - return false; +bool RenderObject::canUpdateSelectionOnRootLineBoxes() { + if (needsLayout()) + return false; - RenderBlock* containingBlock = this->containingBlock(); - return containingBlock ? !containingBlock->needsLayout() : false; + RenderBlock* containingBlock = this->containingBlock(); + return containingBlock ? !containingBlock->needsLayout() : false; } -bool RenderObject::nodeAtFloatPoint(const HitTestRequest&, HitTestResult&, const FloatPoint&) -{ - ASSERT_NOT_REACHED(); - return false; +bool RenderObject::nodeAtFloatPoint(const HitTestRequest&, + HitTestResult&, + const FloatPoint&) { + ASSERT_NOT_REACHED(); + return false; } -} // namespace blink +} // namespace blink #ifndef NDEBUG -void showTree(const blink::RenderObject* object) -{ - if (object) - object->showTreeForThis(); +void showTree(const blink::RenderObject* object) { + if (object) + object->showTreeForThis(); } -void showLineTree(const blink::RenderObject* object) -{ - if (object) - object->showLineTreeForThis(); +void showLineTree(const blink::RenderObject* object) { + if (object) + object->showLineTreeForThis(); } -void showRenderTree(const blink::RenderObject* object1) -{ - showRenderTree(object1, 0); +void showRenderTree(const blink::RenderObject* object1) { + showRenderTree(object1, 0); } -void showRenderTree(const blink::RenderObject* object1, const blink::RenderObject* object2) -{ - if (object1) { - const blink::RenderObject* root = object1; - while (root->parent()) - root = root->parent(); - root->showRenderTreeAndMark(object1, "*", object2, "-", 0); - } +void showRenderTree(const blink::RenderObject* object1, + const blink::RenderObject* object2) { + if (object1) { + const blink::RenderObject* root = object1; + while (root->parent()) + root = root->parent(); + root->showRenderTreeAndMark(object1, "*", object2, "-", 0); + } } #endif diff --git a/sky/engine/core/rendering/RenderObject.h b/sky/engine/core/rendering/RenderObject.h index 1ac81a2dbab66..f378e5e80b458 100644 --- a/sky/engine/core/rendering/RenderObject.h +++ b/sky/engine/core/rendering/RenderObject.h @@ -3,7 +3,8 @@ * (C) 2000 Antti Koivisto (koivisto@kde.org) * (C) 2000 Dirk Mueller (mueller@kde.org) * (C) 2004 Allan Sandfeld Jensen (kde@carewolf.com) - * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2012 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2012 Apple Inc. + * All rights reserved. * Copyright (C) 2009 Google Inc. All rights reserved. * * This library is free software; you can redistribute it and/or @@ -54,41 +55,36 @@ class TransformState; struct PaintInfo; -// Sides used when drawing borders and outlines. The values should run clockwise from top. -enum BoxSide { - BSTop, - BSRight, - BSBottom, - BSLeft -}; +// Sides used when drawing borders and outlines. The values should run clockwise +// from top. +enum BoxSide { BSTop, BSRight, BSBottom, BSLeft }; enum MarkingBehavior { - MarkOnlyThis, - MarkContainingBlockChain, + MarkOnlyThis, + MarkContainingBlockChain, }; enum MapCoordinatesMode { - UseTransforms = 1 << 0, - // FIXME(sky): What is this for? Do we need it? - ApplyContainerFlip = 1 << 1, - // FIXME(sky): Remove - TraverseDocumentBoundaries = 1 << 2, + UseTransforms = 1 << 0, + // FIXME(sky): What is this for? Do we need it? + ApplyContainerFlip = 1 << 1, + // FIXME(sky): Remove + TraverseDocumentBoundaries = 1 << 2, }; typedef unsigned MapCoordinatesFlags; const int caretWidth = 1; struct AnnotatedRegionValue { - bool operator==(const AnnotatedRegionValue& o) const - { - return draggable == o.draggable && bounds == o.bounds; - } + bool operator==(const AnnotatedRegionValue& o) const { + return draggable == o.draggable && bounds == o.bounds; + } - LayoutRect bounds; - bool draggable; + LayoutRect bounds; + bool draggable; }; -typedef WTF::HashMap > LayerHitTestRects; +typedef WTF::HashMap> LayerHitTestRects; #ifndef NDEBUG const int showTreeCharacterOffset = 39; @@ -96,704 +92,907 @@ const int showTreeCharacterOffset = 39; // Base class for all rendering tree objects. class RenderObject { - friend class RenderBlock; - friend class RenderObjectChildList; - WTF_MAKE_NONCOPYABLE(RenderObject); -public: - // TODO(ojan): Pass in a reference since the node can no longer be null. - explicit RenderObject(); - virtual ~RenderObject(); - - virtual const char* renderName() const = 0; - - String debugName() const; - - RenderObject* parent() const { return m_parent; } - bool isDescendantOf(const RenderObject*) const; - - RenderObject* previousSibling() const { return m_previous; } - RenderObject* nextSibling() const { return m_next; } - - RenderObject* slowFirstChild() const - { - if (const RenderObjectChildList* children = virtualChildren()) - return children->firstChild(); - return 0; - } - RenderObject* slowLastChild() const - { - if (const RenderObjectChildList* children = virtualChildren()) - return children->lastChild(); - return 0; - } - - virtual RenderObjectChildList* virtualChildren() { return 0; } - virtual const RenderObjectChildList* virtualChildren() const { return 0; } - - RenderObject* nextInPreOrder() const; - RenderObject* nextInPreOrder(const RenderObject* stayWithin) const; - RenderObject* nextInPreOrderAfterChildren() const; - RenderObject* nextInPreOrderAfterChildren(const RenderObject* stayWithin) const; - RenderObject* previousInPreOrder() const; - RenderObject* previousInPreOrder(const RenderObject* stayWithin) const; - RenderObject* childAt(unsigned) const; - - RenderObject* lastLeafChild() const; - - // The following six functions are used when the render tree hierarchy changes to make sure layers get - // properly added and removed. Since containership can be implemented by any subclass, and since a hierarchy - // can contain a mixture of boxes and other object types, these functions need to be in the base class. - RenderLayer* enclosingLayer() const; - void addLayers(RenderLayer* parentLayer); - void removeLayers(RenderLayer* parentLayer); - void moveLayers(RenderLayer* oldParent, RenderLayer* newParent); - RenderLayer* findNextLayer(RenderLayer* parentLayer, RenderObject* startPoint, bool checkParent = true); - - // Convenience function for getting to the nearest enclosing box of a RenderObject. - RenderBox* enclosingBox() const; - RenderBoxModelObject* enclosingBoxModelObject() const; + friend class RenderBlock; + friend class RenderObjectChildList; + WTF_MAKE_NONCOPYABLE(RenderObject); + + public: + // TODO(ojan): Pass in a reference since the node can no longer be null. + explicit RenderObject(); + virtual ~RenderObject(); + + virtual const char* renderName() const = 0; + + String debugName() const; + + RenderObject* parent() const { return m_parent; } + bool isDescendantOf(const RenderObject*) const; + + RenderObject* previousSibling() const { return m_previous; } + RenderObject* nextSibling() const { return m_next; } + + RenderObject* slowFirstChild() const { + if (const RenderObjectChildList* children = virtualChildren()) + return children->firstChild(); + return 0; + } + RenderObject* slowLastChild() const { + if (const RenderObjectChildList* children = virtualChildren()) + return children->lastChild(); + return 0; + } + + virtual RenderObjectChildList* virtualChildren() { return 0; } + virtual const RenderObjectChildList* virtualChildren() const { return 0; } + + RenderObject* nextInPreOrder() const; + RenderObject* nextInPreOrder(const RenderObject* stayWithin) const; + RenderObject* nextInPreOrderAfterChildren() const; + RenderObject* nextInPreOrderAfterChildren( + const RenderObject* stayWithin) const; + RenderObject* previousInPreOrder() const; + RenderObject* previousInPreOrder(const RenderObject* stayWithin) const; + RenderObject* childAt(unsigned) const; + + RenderObject* lastLeafChild() const; + + // The following six functions are used when the render tree hierarchy changes + // to make sure layers get properly added and removed. Since containership + // can be implemented by any subclass, and since a hierarchy can contain a + // mixture of boxes and other object types, these functions need to be in the + // base class. + RenderLayer* enclosingLayer() const; + void addLayers(RenderLayer* parentLayer); + void removeLayers(RenderLayer* parentLayer); + void moveLayers(RenderLayer* oldParent, RenderLayer* newParent); + RenderLayer* findNextLayer(RenderLayer* parentLayer, + RenderObject* startPoint, + bool checkParent = true); + + // Convenience function for getting to the nearest enclosing box of a + // RenderObject. + RenderBox* enclosingBox() const; + RenderBoxModelObject* enclosingBoxModelObject() const; #if ENABLE(ASSERT) - // Helper class forbidding calls to setNeedsLayout() during its lifetime. - class SetLayoutNeededForbiddenScope { - public: - explicit SetLayoutNeededForbiddenScope(RenderObject&); - ~SetLayoutNeededForbiddenScope(); - private: - RenderObject& m_renderObject; - bool m_preexistingForbidden; - }; - - void assertRendererLaidOut() const - { + // Helper class forbidding calls to setNeedsLayout() during its lifetime. + class SetLayoutNeededForbiddenScope { + public: + explicit SetLayoutNeededForbiddenScope(RenderObject&); + ~SetLayoutNeededForbiddenScope(); + + private: + RenderObject& m_renderObject; + bool m_preexistingForbidden; + }; + + void assertRendererLaidOut() const { #ifndef NDEBUG - if (needsLayout()) - showRenderTreeForThis(); + if (needsLayout()) + showRenderTreeForThis(); #endif - ASSERT_WITH_SECURITY_IMPLICATION(!needsLayout()); - } - - void assertSubtreeIsLaidOut() const - { - for (const RenderObject* renderer = this; renderer; renderer = renderer->nextInPreOrder()) - renderer->assertRendererLaidOut(); - } + ASSERT_WITH_SECURITY_IMPLICATION(!needsLayout()); + } + + void assertSubtreeIsLaidOut() const { + for (const RenderObject* renderer = this; renderer; + renderer = renderer->nextInPreOrder()) + renderer->assertRendererLaidOut(); + } #endif - bool skipInvalidationWhenLaidOutChildren() const; - - // FIXME: This could be used when changing the size of a renderer without children to skip some invalidations. - bool rendererHasNoBoxEffect() const - { - return !style()->hasVisualOverflowingEffect() && !style()->hasBorder() && !style()->hasBackground(); - } - - // Obtains the nearest enclosing block (including this block) that contributes a first-line style to our inline - // children. - virtual RenderBlock* firstLineBlock() const; - - // RenderObject tree manipulation - ////////////////////////////////////////// - virtual bool canHaveChildren() const { return virtualChildren(); } - virtual bool isChildAllowed(RenderObject*, RenderStyle*) const { return true; } - virtual void addChild(RenderObject* newChild, RenderObject* beforeChild = 0); - virtual void removeChild(RenderObject*); - ////////////////////////////////////////// - -protected: - ////////////////////////////////////////// - // Helper functions. Dangerous to use! - void setPreviousSibling(RenderObject* previous) { m_previous = previous; } - void setNextSibling(RenderObject* next) { m_next = next; } - void setParent(RenderObject* parent) { m_parent = parent; } - - ////////////////////////////////////////// -private: + bool skipInvalidationWhenLaidOutChildren() const; + + // FIXME: This could be used when changing the size of a renderer without + // children to skip some invalidations. + bool rendererHasNoBoxEffect() const { + return !style()->hasVisualOverflowingEffect() && !style()->hasBorder() && + !style()->hasBackground(); + } + + // Obtains the nearest enclosing block (including this block) that contributes + // a first-line style to our inline children. + virtual RenderBlock* firstLineBlock() const; + + // RenderObject tree manipulation + ////////////////////////////////////////// + virtual bool canHaveChildren() const { return virtualChildren(); } + virtual bool isChildAllowed(RenderObject*, RenderStyle*) const { + return true; + } + virtual void addChild(RenderObject* newChild, RenderObject* beforeChild = 0); + virtual void removeChild(RenderObject*); + ////////////////////////////////////////// + + protected: + ////////////////////////////////////////// + // Helper functions. Dangerous to use! + void setPreviousSibling(RenderObject* previous) { m_previous = previous; } + void setNextSibling(RenderObject* next) { m_next = next; } + void setParent(RenderObject* parent) { m_parent = parent; } + + ////////////////////////////////////////// + private: #if ENABLE(ASSERT) - bool isSetNeedsLayoutForbidden() const { return m_setNeedsLayoutForbidden; } - void setNeedsLayoutIsForbidden(bool flag) { m_setNeedsLayoutForbidden = flag; } + bool isSetNeedsLayoutForbidden() const { return m_setNeedsLayoutForbidden; } + void setNeedsLayoutIsForbidden(bool flag) { + m_setNeedsLayoutForbidden = flag; + } #endif - void addAbsoluteRectForLayer(LayoutRect& result); + void addAbsoluteRectForLayer(LayoutRect& result); -public: + public: #ifndef NDEBUG - void showTreeForThis() const; - void showRenderTreeForThis() const; - void showLineTreeForThis() const; - - void showRenderObject() const; - // We don't make printedCharacters an optional parameter so that - // showRenderObject can be called from gdb easily. - void showRenderObject(int printedCharacters) const; - void showRenderTreeAndMark(const RenderObject* markedObject1 = 0, const char* markedLabel1 = 0, const RenderObject* markedObject2 = 0, const char* markedLabel2 = 0, int depth = 0) const; + void showTreeForThis() const; + void showRenderTreeForThis() const; + void showLineTreeForThis() const; + + void showRenderObject() const; + // We don't make printedCharacters an optional parameter so that + // showRenderObject can be called from gdb easily. + void showRenderObject(int printedCharacters) const; + void showRenderTreeAndMark(const RenderObject* markedObject1 = 0, + const char* markedLabel1 = 0, + const RenderObject* markedObject2 = 0, + const char* markedLabel2 = 0, + int depth = 0) const; #endif - static unsigned instanceCount() { return s_instanceCount; } + static unsigned instanceCount() { return s_instanceCount; } #if !ENABLE(OILPAN) - // RenderObjects are allocated out of the rendering partition. - void* operator new(size_t); - void operator delete(void*); + // RenderObjects are allocated out of the rendering partition. + void* operator new(size_t); + void operator delete(void*); #endif -public: - virtual bool isBoxModelObject() const { return false; } - virtual bool isCanvas() const { return false; } - virtual bool isImage() const { return false; } - virtual bool isInlineBlock() const { return false; } - virtual bool isRenderBlock() const { return false; } - virtual bool isRenderParagraph() const { return false; } - virtual bool isRenderInline() const { return false; } - virtual bool isRenderView() const { return false; } - - bool everHadLayout() const { return m_bitfields.everHadLayout(); } - - bool alwaysCreateLineBoxesForRenderInline() const - { - ASSERT(isRenderInline()); - return m_bitfields.alwaysCreateLineBoxesForRenderInline(); - } - void setAlwaysCreateLineBoxesForRenderInline(bool alwaysCreateLineBoxes) - { - ASSERT(isRenderInline()); - m_bitfields.setAlwaysCreateLineBoxesForRenderInline(alwaysCreateLineBoxes); - } - - bool ancestorLineBoxDirty() const { return m_bitfields.ancestorLineBoxDirty(); } - void setAncestorLineBoxDirty(bool value = true) - { - m_bitfields.setAncestorLineBoxDirty(value); - if (value) - setNeedsLayout(); - } - - // SVG uses FloatPoint precise hit testing, and passes the point in parent - // coordinates instead of in paint invalidaiton container coordinates. Eventually the - // rest of the rendering tree will move to a similar model. - virtual bool nodeAtFloatPoint(const HitTestRequest&, HitTestResult&, const FloatPoint& pointInParent); - - bool canHaveWhitespaceChildren() const - { - return !isFlexibleBox(); - } - - bool isOutOfFlowPositioned() const { return m_bitfields.isOutOfFlowPositioned(); } // absolute or fixed positioning - bool isPositioned() const { return m_bitfields.isPositioned(); } - - bool isText() const { return m_bitfields.isText(); } - bool isBox() const { return m_bitfields.isBox(); } - bool isInline() const { return m_bitfields.isInline(); } // inline object - bool isDragging() const { return m_bitfields.isDragging(); } - bool isReplaced() const { return m_bitfields.isReplaced(); } // a "replaced" element (see CSS) - - bool hasLayer() const { return m_bitfields.hasLayer(); } - - bool hasBoxDecorationBackground() const { return m_bitfields.hasBoxDecorationBackground(); } - bool hasBackground() const { return style()->hasBackground(); } - bool hasEntirelyFixedBackground() const; - - bool needsLayoutBecauseOfChildren() const { return needsLayout() && !selfNeedsLayout() && !needsPositionedMovementLayout() && !needsSimplifiedNormalFlowLayout(); } - - bool needsLayout() const - { - return m_bitfields.selfNeedsLayout() || m_bitfields.normalChildNeedsLayout() || m_bitfields.posChildNeedsLayout() - || m_bitfields.needsSimplifiedNormalFlowLayout() || m_bitfields.needsPositionedMovementLayout(); - } - - bool selfNeedsLayout() const { return m_bitfields.selfNeedsLayout(); } - bool needsPositionedMovementLayout() const { return m_bitfields.needsPositionedMovementLayout(); } - bool needsPositionedMovementLayoutOnly() const - { - return m_bitfields.needsPositionedMovementLayout() && !m_bitfields.selfNeedsLayout() && !m_bitfields.normalChildNeedsLayout() - && !m_bitfields.posChildNeedsLayout() && !m_bitfields.needsSimplifiedNormalFlowLayout(); - } - - bool posChildNeedsLayout() const { return m_bitfields.posChildNeedsLayout(); } - bool needsSimplifiedNormalFlowLayout() const { return m_bitfields.needsSimplifiedNormalFlowLayout(); } - bool normalChildNeedsLayout() const { return m_bitfields.normalChildNeedsLayout(); } - - bool preferredLogicalWidthsDirty() const { return m_bitfields.preferredLogicalWidthsDirty(); } - - bool needsOverflowRecalcAfterStyleChange() const { return m_bitfields.selfNeedsOverflowRecalcAfterStyleChange() || m_bitfields.childNeedsOverflowRecalcAfterStyleChange(); } - bool selfNeedsOverflowRecalcAfterStyleChange() const { return m_bitfields.selfNeedsOverflowRecalcAfterStyleChange(); } - bool childNeedsOverflowRecalcAfterStyleChange() const { return m_bitfields.childNeedsOverflowRecalcAfterStyleChange(); } - - bool isSelectionBorder() const; - - bool hasClip() const { return isOutOfFlowPositioned() && !style()->hasAutoClip(); } - bool hasOverflowClip() const { return m_bitfields.hasOverflowClip(); } - bool hasClipOrOverflowClip() const { return hasClip() || hasOverflowClip(); } - - bool hasTransform() const { return m_bitfields.hasTransform(); } - bool hasClipPath() const { return style() && style()->clipPath(); } - - inline bool preservesNewline() const; - - bool isRooted() const; - - // Returns the object containing this one. Can be different from parent for positioned elements. - // If paintInvalidationContainer and paintInvalidationContainerSkipped are not null, on return *paintInvalidationContainerSkipped - // is true if the renderer returned is an ancestor of paintInvalidationContainer. - RenderObject* container(const RenderBox* paintInvalidationContainer = 0, bool* paintInvalidationContainerSkipped = 0) const; - - void markContainingBlocksForLayout(bool scheduleRelayout = true, RenderObject* newRoot = 0, SubtreeLayoutScope* = 0); - void setNeedsLayout(MarkingBehavior = MarkContainingBlockChain, SubtreeLayoutScope* = 0); - void clearNeedsLayout(); - void setChildNeedsLayout(MarkingBehavior = MarkContainingBlockChain, SubtreeLayoutScope* = 0); - void setNeedsPositionedMovementLayout(); - void setPreferredLogicalWidthsDirty(MarkingBehavior = MarkContainingBlockChain); - void clearPreferredLogicalWidthsDirty(); - void invalidateContainerPreferredLogicalWidths(); - - void setNeedsLayoutAndPrefWidthsRecalc() - { - setNeedsLayout(); - setPreferredLogicalWidthsDirty(); - } - - void setPositionState(EPosition position) - { - ASSERT(position != AbsolutePosition || isBox()); - m_bitfields.setPositionedState(position); - } - void clearPositionedState() { m_bitfields.clearPositionedState(); } - - void setInline(bool isInline) { m_bitfields.setIsInline(isInline); } - - void setIsText() { m_bitfields.setIsText(true); } - void setIsBox() { m_bitfields.setIsBox(true); } - void setReplaced(bool isReplaced) { m_bitfields.setIsReplaced(isReplaced); } - void setHasOverflowClip(bool hasOverflowClip) { m_bitfields.setHasOverflowClip(hasOverflowClip); } - void setHasLayer(bool hasLayer) { m_bitfields.setHasLayer(hasLayer); } - void setHasTransform(bool hasTransform) { m_bitfields.setHasTransform(hasTransform); } - void setHasBoxDecorationBackground(bool b) { m_bitfields.setHasBoxDecorationBackground(b); } - - void scheduleRelayout(); - - void updateFillImages(const FillLayer* oldLayers, const FillLayer& newLayers); - void updateImage(StyleImage*, StyleImage*); - - // paintOffset is the offset from the origin of the GraphicsContext at which to paint the current object. - virtual void paint(PaintInfo&, const LayoutPoint& paintOffset, Vector& layers); - - // Subclasses must reimplement this method to compute the size and position - // of this object and all its descendants. - virtual void layout() = 0; - - /* This function performs a layout only if one is needed. */ - void layoutIfNeeded() { if (needsLayout()) layout(); } - - void forceLayout(); - void forceChildLayout(); - - bool hitTest(const HitTestRequest&, HitTestResult&, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset); - virtual void updateHitTestResult(HitTestResult&, const LayoutPoint&); - virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset); - - virtual PositionWithAffinity positionForPoint(const LayoutPoint&); - PositionWithAffinity createPositionWithAffinity(int offset, EAffinity); - PositionWithAffinity createPositionWithAffinity(const Position&); - - virtual void dirtyLinesFromChangedChild(RenderObject*); - - // Set the style of the object and update the state of the object accordingly. - void setStyle(PassRefPtr); - - // Updates only the local style ptr of the object. Does not update the state of the object, - // and so only should be called when the style is known not to have changed (or from setStyle). - void setStyleInternal(PassRefPtr style) { m_style = style; } - - // returns the containing block level element for this element. - RenderBlock* containingBlock() const; - - // Convert the given local point to absolute coordinates - // FIXME: Temporary. If UseTransforms is true, take transforms into account. Eventually localToAbsolute() will always be transform-aware. - FloatPoint localToAbsolute(const FloatPoint& localPoint = FloatPoint(), MapCoordinatesFlags = 0) const; - FloatPoint absoluteToLocal(const FloatPoint&, MapCoordinatesFlags = 0) const; - - // Convert a local quad to absolute coordinates, taking transforms into account. - FloatQuad localToAbsoluteQuad(const FloatQuad& quad, MapCoordinatesFlags mode = 0) const - { - return localToContainerQuad(quad, 0, mode); - } - // Convert an absolute quad to local coordinates. - FloatQuad absoluteToLocalQuad(const FloatQuad&, MapCoordinatesFlags mode = 0) const; - - // Convert a local quad into the coordinate system of container, taking transforms into account. - FloatQuad localToContainerQuad(const FloatQuad&, const RenderBox* paintInvalidatinoContainer, MapCoordinatesFlags = 0) const; - FloatPoint localToContainerPoint(const FloatPoint&, const RenderBox* paintInvalidationContainer, MapCoordinatesFlags = 0) const; - - // Return the offset from the container() renderer (excluding transforms). In multi-column layout, - // different offsets apply at different points, so return the offset that applies to the given point. - virtual LayoutSize offsetFromContainer(const RenderObject*, const LayoutPoint&, bool* offsetDependsOnPoint = 0) const; - // Return the offset from an object up the container() chain. Asserts that none of the intermediate objects have transforms. - LayoutSize offsetFromAncestorContainer(const RenderObject*) const; - - IntRect absoluteBoundingBoxRect() const; - - // Build an array of quads in absolute coords for line boxes - virtual void absoluteQuads(Vector&) const { } - - virtual LayoutUnit minPreferredLogicalWidth() const { return 0; } - virtual LayoutUnit maxPreferredLogicalWidth() const { return 0; } - - RenderStyle* style() const { return m_style.get(); } - - /* The two following methods are inlined in RenderObjectInlines.h */ - RenderStyle* firstLineStyle() const; - RenderStyle* style(bool firstLine) const; - - struct AppliedTextDecoration { - Color color; - TextDecorationStyle style; - AppliedTextDecoration() : color(Color::transparent), style(TextDecorationStyleSolid) { } - }; - - void getTextDecorations(unsigned decorations, AppliedTextDecoration& underline, AppliedTextDecoration& overline, AppliedTextDecoration& linethrough, bool quirksMode = false, bool firstlineStyle = false); - - // Overriden by RenderText for character length, used in line layout code. - virtual unsigned length() const { return 1; } - - // FIXME(sky): Remove - bool isFloatingOrOutOfFlowPositioned() const { return isOutOfFlowPositioned(); } - - bool isTransparent() const { return style()->hasOpacity(); } - float opacity() const { return style()->opacity(); } - - enum SelectionState { - SelectionNone, // The object is not selected. - SelectionStart, // The object either contains the start of a selection run or is the start of a run - SelectionInside, // The object is fully encompassed by a selection run - SelectionEnd, // The object either contains the end of a selection run or is the end of a run - SelectionBoth // The object contains an entire run or is the sole selected object in that run - }; - - // The current selection state for an object. For blocks, the state refers to the state of the leaf - // descendants (as described above in the SelectionState enum declaration). - SelectionState selectionState() const { return m_bitfields.selectionState(); } - virtual void setSelectionState(SelectionState state) { m_bitfields.setSelectionState(state); } - inline void setSelectionStateIfNeeded(SelectionState); - bool canUpdateSelectionOnRootLineBoxes(); - - virtual bool canBeSelectionLeaf() const { return false; } - bool hasSelectedChildren() const { return selectionState() != SelectionNone; } - - bool isSelectable() const; - // Obtains the selection colors that should be used when painting a selection. - Color selectionBackgroundColor() const; - Color selectionForegroundColor() const; - Color selectionEmphasisMarkColor() const; - - // Whether or not a given block needs to paint selection gaps. - virtual bool shouldPaintSelectionGaps() const { return false; } - - /** - * Returns the local coordinates of the caret within this render object. - * @param caretOffset zero-based offset determining position within the render object. - * @param extraWidthToEndOfLine optional out arg to give extra width to end of line - - * useful for character range rect computations - */ - virtual LayoutRect localCaretRect(InlineBox*, int caretOffset, LayoutUnit* extraWidthToEndOfLine = 0); - - // When performing a global document tear-down, the renderer of the document is cleared. We use this - // as a hook to detect the case of document destruction and don't waste time doing unnecessary work. - bool documentBeingDestroyed() const; - - virtual void destroy(); - - // Virtual function helper for the new FlexibleBox Layout (display: -webkit-flex). - virtual bool isFlexibleBox() const { return false; } - - virtual int caretMinOffset() const; - virtual int caretMaxOffset() const; - - virtual int previousOffset(int current) const; - virtual int previousOffsetForBackwardDeletion(int current) const; - virtual int nextOffset(int current) const; - - void selectionStartEnd(int& spos, int& epos) const; - - void remove() { if (parent()) parent()->removeChild(this); } - - bool supportsTouchAction() const; - - bool visibleToHitTestRequest(const HitTestRequest& request) const { return (request.ignorePointerEventsNone() || style()->pointerEvents() != PE_NONE); } - - bool visibleToHitTesting() const { return style()->pointerEvents() != PE_NONE; } - - // Map points and quads through elements, potentially via 3d transforms. You should never need to call these directly; use - // localToAbsolute/absoluteToLocal methods instead. - virtual void mapLocalToContainer(const RenderBox* paintInvalidationContainer, TransformState&, MapCoordinatesFlags = ApplyContainerFlip) const; - virtual void mapAbsoluteToLocalPoint(MapCoordinatesFlags, TransformState&) const; - - // Pushes state onto RenderGeometryMap about how to map coordinates from this renderer to its container, or ancestorToStopAt (whichever is encountered first). - // Returns the renderer which was mapped to (container or ancestorToStopAt). - virtual const RenderObject* pushMappingToContainer(const RenderBox* ancestorToStopAt, RenderGeometryMap&) const; - - bool shouldUseTransformFromContainer(const RenderObject* container) const; - void getTransformFromContainer(const RenderObject* container, const LayoutSize& offsetInContainer, TransformationMatrix&) const; - - bool createsGroup() const { return isTransparent(); } - - virtual void addFocusRingRects(Vector&, const LayoutPoint& /* additionalOffset */, const RenderBox* /* paintContainer */ = 0) const { }; - - RespectImageOrientationEnum shouldRespectImageOrientation() const; - - bool onlyNeededPositionedMovementLayout() const { return m_bitfields.onlyNeededPositionedMovementLayout(); } - void setOnlyNeededPositionedMovementLayout(bool b) { m_bitfields.setOnlyNeededPositionedMovementLayout(b); } - - bool neededLayoutBecauseOfChildren() const { return m_bitfields.neededLayoutBecauseOfChildren(); } - void setNeededLayoutBecauseOfChildren(bool b) { m_bitfields.setNeededLayoutBecauseOfChildren(b); } - - void setNeedsOverflowRecalcAfterStyleChange(); - void markContainingBlocksForOverflowRecalc(); - -protected: - // Overrides should call the superclass at the end. m_style will be 0 the first time - // this function will be called. - virtual void styleWillChange(StyleDifference, const RenderStyle& newStyle); - // Overrides should call the superclass at the start. |oldStyle| will be 0 the first - // time this function is called. - virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); - - void drawLineForBoxSide(GraphicsContext*, int x1, int y1, int x2, int y2, BoxSide, - Color, EBorderStyle, int adjbw1, int adjbw2, bool antialias = false); - void drawDashedOrDottedBoxSide(GraphicsContext*, int x1, int y1, int x2, int y2, - BoxSide, Color, int thickness, EBorderStyle, bool antialias); - void drawDoubleBoxSide(GraphicsContext*, int x1, int y1, int x2, int y2, - int length, BoxSide, Color, int thickness, int adjacentWidth1, int adjacentWidth2, bool antialias); - void drawRidgeOrGrooveBoxSide(GraphicsContext*, int x1, int y1, int x2, int y2, - BoxSide, Color, EBorderStyle, int adjacentWidth1, int adjacentWidth2, bool antialias); - void drawSolidBoxSide(GraphicsContext*, int x1, int y1, int x2, int y2, - BoxSide, Color, int adjacentWidth1, int adjacentWidth2, bool antialias); - - void addChildFocusRingRects(Vector&, const LayoutPoint& additionalOffset, const RenderBox* paintContainer) const; - - void clearLayoutRootIfNeeded() const; - virtual void willBeDestroyed(); - void postDestroy(); - - void insertedIntoTree(); - void willBeRemovedFromTree(); - -private: - bool hasImmediateNonWhitespaceTextChildOrPropertiesDependentOnColor() const; - - StyleDifference adjustStyleDifference(StyleDifference) const; - - Color selectionColor() const; + public: + virtual bool isBoxModelObject() const { return false; } + virtual bool isCanvas() const { return false; } + virtual bool isImage() const { return false; } + virtual bool isInlineBlock() const { return false; } + virtual bool isRenderBlock() const { return false; } + virtual bool isRenderParagraph() const { return false; } + virtual bool isRenderInline() const { return false; } + virtual bool isRenderView() const { return false; } + + bool everHadLayout() const { return m_bitfields.everHadLayout(); } + + bool alwaysCreateLineBoxesForRenderInline() const { + ASSERT(isRenderInline()); + return m_bitfields.alwaysCreateLineBoxesForRenderInline(); + } + void setAlwaysCreateLineBoxesForRenderInline(bool alwaysCreateLineBoxes) { + ASSERT(isRenderInline()); + m_bitfields.setAlwaysCreateLineBoxesForRenderInline(alwaysCreateLineBoxes); + } + + bool ancestorLineBoxDirty() const { + return m_bitfields.ancestorLineBoxDirty(); + } + void setAncestorLineBoxDirty(bool value = true) { + m_bitfields.setAncestorLineBoxDirty(value); + if (value) + setNeedsLayout(); + } + + // SVG uses FloatPoint precise hit testing, and passes the point in parent + // coordinates instead of in paint invalidaiton container coordinates. + // Eventually the rest of the rendering tree will move to a similar model. + virtual bool nodeAtFloatPoint(const HitTestRequest&, + HitTestResult&, + const FloatPoint& pointInParent); + + bool canHaveWhitespaceChildren() const { return !isFlexibleBox(); } + + bool isOutOfFlowPositioned() const { + return m_bitfields.isOutOfFlowPositioned(); + } // absolute or fixed positioning + bool isPositioned() const { return m_bitfields.isPositioned(); } + + bool isText() const { return m_bitfields.isText(); } + bool isBox() const { return m_bitfields.isBox(); } + bool isInline() const { return m_bitfields.isInline(); } // inline object + bool isDragging() const { return m_bitfields.isDragging(); } + bool isReplaced() const { + return m_bitfields.isReplaced(); + } // a "replaced" element (see CSS) + + bool hasLayer() const { return m_bitfields.hasLayer(); } + + bool hasBoxDecorationBackground() const { + return m_bitfields.hasBoxDecorationBackground(); + } + bool hasBackground() const { return style()->hasBackground(); } + bool hasEntirelyFixedBackground() const; + + bool needsLayoutBecauseOfChildren() const { + return needsLayout() && !selfNeedsLayout() && + !needsPositionedMovementLayout() && + !needsSimplifiedNormalFlowLayout(); + } + + bool needsLayout() const { + return m_bitfields.selfNeedsLayout() || + m_bitfields.normalChildNeedsLayout() || + m_bitfields.posChildNeedsLayout() || + m_bitfields.needsSimplifiedNormalFlowLayout() || + m_bitfields.needsPositionedMovementLayout(); + } + + bool selfNeedsLayout() const { return m_bitfields.selfNeedsLayout(); } + bool needsPositionedMovementLayout() const { + return m_bitfields.needsPositionedMovementLayout(); + } + bool needsPositionedMovementLayoutOnly() const { + return m_bitfields.needsPositionedMovementLayout() && + !m_bitfields.selfNeedsLayout() && + !m_bitfields.normalChildNeedsLayout() && + !m_bitfields.posChildNeedsLayout() && + !m_bitfields.needsSimplifiedNormalFlowLayout(); + } + + bool posChildNeedsLayout() const { return m_bitfields.posChildNeedsLayout(); } + bool needsSimplifiedNormalFlowLayout() const { + return m_bitfields.needsSimplifiedNormalFlowLayout(); + } + bool normalChildNeedsLayout() const { + return m_bitfields.normalChildNeedsLayout(); + } + + bool preferredLogicalWidthsDirty() const { + return m_bitfields.preferredLogicalWidthsDirty(); + } + + bool needsOverflowRecalcAfterStyleChange() const { + return m_bitfields.selfNeedsOverflowRecalcAfterStyleChange() || + m_bitfields.childNeedsOverflowRecalcAfterStyleChange(); + } + bool selfNeedsOverflowRecalcAfterStyleChange() const { + return m_bitfields.selfNeedsOverflowRecalcAfterStyleChange(); + } + bool childNeedsOverflowRecalcAfterStyleChange() const { + return m_bitfields.childNeedsOverflowRecalcAfterStyleChange(); + } + + bool isSelectionBorder() const; + + bool hasClip() const { + return isOutOfFlowPositioned() && !style()->hasAutoClip(); + } + bool hasOverflowClip() const { return m_bitfields.hasOverflowClip(); } + bool hasClipOrOverflowClip() const { return hasClip() || hasOverflowClip(); } + + bool hasTransform() const { return m_bitfields.hasTransform(); } + bool hasClipPath() const { return style() && style()->clipPath(); } + + inline bool preservesNewline() const; + + bool isRooted() const; + + // Returns the object containing this one. Can be different from parent for + // positioned elements. If paintInvalidationContainer and + // paintInvalidationContainerSkipped are not null, on return + // *paintInvalidationContainerSkipped is true if the renderer returned is an + // ancestor of paintInvalidationContainer. + RenderObject* container(const RenderBox* paintInvalidationContainer = 0, + bool* paintInvalidationContainerSkipped = 0) const; + + void markContainingBlocksForLayout(bool scheduleRelayout = true, + RenderObject* newRoot = 0, + SubtreeLayoutScope* = 0); + void setNeedsLayout(MarkingBehavior = MarkContainingBlockChain, + SubtreeLayoutScope* = 0); + void clearNeedsLayout(); + void setChildNeedsLayout(MarkingBehavior = MarkContainingBlockChain, + SubtreeLayoutScope* = 0); + void setNeedsPositionedMovementLayout(); + void setPreferredLogicalWidthsDirty( + MarkingBehavior = MarkContainingBlockChain); + void clearPreferredLogicalWidthsDirty(); + void invalidateContainerPreferredLogicalWidths(); + + void setNeedsLayoutAndPrefWidthsRecalc() { + setNeedsLayout(); + setPreferredLogicalWidthsDirty(); + } + + void setPositionState(EPosition position) { + ASSERT(position != AbsolutePosition || isBox()); + m_bitfields.setPositionedState(position); + } + void clearPositionedState() { m_bitfields.clearPositionedState(); } + + void setInline(bool isInline) { m_bitfields.setIsInline(isInline); } + + void setIsText() { m_bitfields.setIsText(true); } + void setIsBox() { m_bitfields.setIsBox(true); } + void setReplaced(bool isReplaced) { m_bitfields.setIsReplaced(isReplaced); } + void setHasOverflowClip(bool hasOverflowClip) { + m_bitfields.setHasOverflowClip(hasOverflowClip); + } + void setHasLayer(bool hasLayer) { m_bitfields.setHasLayer(hasLayer); } + void setHasTransform(bool hasTransform) { + m_bitfields.setHasTransform(hasTransform); + } + void setHasBoxDecorationBackground(bool b) { + m_bitfields.setHasBoxDecorationBackground(b); + } + + void scheduleRelayout(); + + void updateFillImages(const FillLayer* oldLayers, const FillLayer& newLayers); + void updateImage(StyleImage*, StyleImage*); + + // paintOffset is the offset from the origin of the GraphicsContext at which + // to paint the current object. + virtual void paint(PaintInfo&, + const LayoutPoint& paintOffset, + Vector& layers); + + // Subclasses must reimplement this method to compute the size and position + // of this object and all its descendants. + virtual void layout() = 0; + + /* This function performs a layout only if one is needed. */ + void layoutIfNeeded() { + if (needsLayout()) + layout(); + } + + void forceLayout(); + void forceChildLayout(); + + bool hitTest(const HitTestRequest&, + HitTestResult&, + const HitTestLocation& locationInContainer, + const LayoutPoint& accumulatedOffset); + virtual void updateHitTestResult(HitTestResult&, const LayoutPoint&); + virtual bool nodeAtPoint(const HitTestRequest&, + HitTestResult&, + const HitTestLocation& locationInContainer, + const LayoutPoint& accumulatedOffset); + + virtual PositionWithAffinity positionForPoint(const LayoutPoint&); + PositionWithAffinity createPositionWithAffinity(int offset, EAffinity); + PositionWithAffinity createPositionWithAffinity(const Position&); + + virtual void dirtyLinesFromChangedChild(RenderObject*); + + // Set the style of the object and update the state of the object accordingly. + void setStyle(PassRefPtr); + + // Updates only the local style ptr of the object. Does not update the state + // of the object, and so only should be called when the style is known not to + // have changed (or from setStyle). + void setStyleInternal(PassRefPtr style) { m_style = style; } + + // returns the containing block level element for this element. + RenderBlock* containingBlock() const; + + // Convert the given local point to absolute coordinates + // FIXME: Temporary. If UseTransforms is true, take transforms into account. + // Eventually localToAbsolute() will always be transform-aware. + FloatPoint localToAbsolute(const FloatPoint& localPoint = FloatPoint(), + MapCoordinatesFlags = 0) const; + FloatPoint absoluteToLocal(const FloatPoint&, MapCoordinatesFlags = 0) const; + + // Convert a local quad to absolute coordinates, taking transforms into + // account. + FloatQuad localToAbsoluteQuad(const FloatQuad& quad, + MapCoordinatesFlags mode = 0) const { + return localToContainerQuad(quad, 0, mode); + } + // Convert an absolute quad to local coordinates. + FloatQuad absoluteToLocalQuad(const FloatQuad&, + MapCoordinatesFlags mode = 0) const; + + // Convert a local quad into the coordinate system of container, taking + // transforms into account. + FloatQuad localToContainerQuad(const FloatQuad&, + const RenderBox* paintInvalidatinoContainer, + MapCoordinatesFlags = 0) const; + FloatPoint localToContainerPoint(const FloatPoint&, + const RenderBox* paintInvalidationContainer, + MapCoordinatesFlags = 0) const; + + // Return the offset from the container() renderer (excluding transforms). In + // multi-column layout, different offsets apply at different points, so return + // the offset that applies to the given point. + virtual LayoutSize offsetFromContainer(const RenderObject*, + const LayoutPoint&, + bool* offsetDependsOnPoint = 0) const; + // Return the offset from an object up the container() chain. Asserts that + // none of the intermediate objects have transforms. + LayoutSize offsetFromAncestorContainer(const RenderObject*) const; + + IntRect absoluteBoundingBoxRect() const; + + // Build an array of quads in absolute coords for line boxes + virtual void absoluteQuads(Vector&) const {} + + virtual LayoutUnit minPreferredLogicalWidth() const { return 0; } + virtual LayoutUnit maxPreferredLogicalWidth() const { return 0; } + + RenderStyle* style() const { return m_style.get(); } + + /* The two following methods are inlined in RenderObjectInlines.h */ + RenderStyle* firstLineStyle() const; + RenderStyle* style(bool firstLine) const; + + struct AppliedTextDecoration { + Color color; + TextDecorationStyle style; + AppliedTextDecoration() + : color(Color::transparent), style(TextDecorationStyleSolid) {} + }; + + void getTextDecorations(unsigned decorations, + AppliedTextDecoration& underline, + AppliedTextDecoration& overline, + AppliedTextDecoration& linethrough, + bool quirksMode = false, + bool firstlineStyle = false); + + // Overriden by RenderText for character length, used in line layout code. + virtual unsigned length() const { return 1; } + + // FIXME(sky): Remove + bool isFloatingOrOutOfFlowPositioned() const { + return isOutOfFlowPositioned(); + } + + bool isTransparent() const { return style()->hasOpacity(); } + float opacity() const { return style()->opacity(); } + + enum SelectionState { + SelectionNone, // The object is not selected. + SelectionStart, // The object either contains the start of a selection run + // or is the start of a run + SelectionInside, // The object is fully encompassed by a selection run + SelectionEnd, // The object either contains the end of a selection run or + // is the end of a run + SelectionBoth // The object contains an entire run or is the sole selected + // object in that run + }; + + // The current selection state for an object. For blocks, the state refers to + // the state of the leaf descendants (as described above in the SelectionState + // enum declaration). + SelectionState selectionState() const { return m_bitfields.selectionState(); } + virtual void setSelectionState(SelectionState state) { + m_bitfields.setSelectionState(state); + } + inline void setSelectionStateIfNeeded(SelectionState); + bool canUpdateSelectionOnRootLineBoxes(); + + virtual bool canBeSelectionLeaf() const { return false; } + bool hasSelectedChildren() const { return selectionState() != SelectionNone; } + + bool isSelectable() const; + // Obtains the selection colors that should be used when painting a selection. + Color selectionBackgroundColor() const; + Color selectionForegroundColor() const; + Color selectionEmphasisMarkColor() const; + + // Whether or not a given block needs to paint selection gaps. + virtual bool shouldPaintSelectionGaps() const { return false; } + + /** + * Returns the local coordinates of the caret within this render object. + * @param caretOffset zero-based offset determining position within the render + * object. + * @param extraWidthToEndOfLine optional out arg to give extra width to end of + * line - useful for character range rect computations + */ + virtual LayoutRect localCaretRect(InlineBox*, + int caretOffset, + LayoutUnit* extraWidthToEndOfLine = 0); + + // When performing a global document tear-down, the renderer of the document + // is cleared. We use this as a hook to detect the case of document + // destruction and don't waste time doing unnecessary work. + bool documentBeingDestroyed() const; + + virtual void destroy(); + + // Virtual function helper for the new FlexibleBox Layout (display: + // -webkit-flex). + virtual bool isFlexibleBox() const { return false; } + + virtual int caretMinOffset() const; + virtual int caretMaxOffset() const; + + virtual int previousOffset(int current) const; + virtual int previousOffsetForBackwardDeletion(int current) const; + virtual int nextOffset(int current) const; + + void selectionStartEnd(int& spos, int& epos) const; + + void remove() { + if (parent()) + parent()->removeChild(this); + } + + bool supportsTouchAction() const; + + bool visibleToHitTestRequest(const HitTestRequest& request) const { + return (request.ignorePointerEventsNone() || + style()->pointerEvents() != PE_NONE); + } + + bool visibleToHitTesting() const { + return style()->pointerEvents() != PE_NONE; + } + + // Map points and quads through elements, potentially via 3d transforms. You + // should never need to call these directly; use + // localToAbsolute/absoluteToLocal methods instead. + virtual void mapLocalToContainer( + const RenderBox* paintInvalidationContainer, + TransformState&, + MapCoordinatesFlags = ApplyContainerFlip) const; + virtual void mapAbsoluteToLocalPoint(MapCoordinatesFlags, + TransformState&) const; + + // Pushes state onto RenderGeometryMap about how to map coordinates from this + // renderer to its container, or ancestorToStopAt (whichever is encountered + // first). Returns the renderer which was mapped to (container or + // ancestorToStopAt). + virtual const RenderObject* pushMappingToContainer( + const RenderBox* ancestorToStopAt, + RenderGeometryMap&) const; + + bool shouldUseTransformFromContainer(const RenderObject* container) const; + void getTransformFromContainer(const RenderObject* container, + const LayoutSize& offsetInContainer, + TransformationMatrix&) const; + + bool createsGroup() const { return isTransparent(); } + + virtual void addFocusRingRects( + Vector&, + const LayoutPoint& /* additionalOffset */, + const RenderBox* /* paintContainer */ = 0) const {}; + + RespectImageOrientationEnum shouldRespectImageOrientation() const; + + bool onlyNeededPositionedMovementLayout() const { + return m_bitfields.onlyNeededPositionedMovementLayout(); + } + void setOnlyNeededPositionedMovementLayout(bool b) { + m_bitfields.setOnlyNeededPositionedMovementLayout(b); + } + + bool neededLayoutBecauseOfChildren() const { + return m_bitfields.neededLayoutBecauseOfChildren(); + } + void setNeededLayoutBecauseOfChildren(bool b) { + m_bitfields.setNeededLayoutBecauseOfChildren(b); + } + + void setNeedsOverflowRecalcAfterStyleChange(); + void markContainingBlocksForOverflowRecalc(); + + protected: + // Overrides should call the superclass at the end. m_style will be 0 the + // first time this function will be called. + virtual void styleWillChange(StyleDifference, const RenderStyle& newStyle); + // Overrides should call the superclass at the start. |oldStyle| will be 0 the + // first time this function is called. + virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); + + void drawLineForBoxSide(GraphicsContext*, + int x1, + int y1, + int x2, + int y2, + BoxSide, + Color, + EBorderStyle, + int adjbw1, + int adjbw2, + bool antialias = false); + void drawDashedOrDottedBoxSide(GraphicsContext*, + int x1, + int y1, + int x2, + int y2, + BoxSide, + Color, + int thickness, + EBorderStyle, + bool antialias); + void drawDoubleBoxSide(GraphicsContext*, + int x1, + int y1, + int x2, + int y2, + int length, + BoxSide, + Color, + int thickness, + int adjacentWidth1, + int adjacentWidth2, + bool antialias); + void drawRidgeOrGrooveBoxSide(GraphicsContext*, + int x1, + int y1, + int x2, + int y2, + BoxSide, + Color, + EBorderStyle, + int adjacentWidth1, + int adjacentWidth2, + bool antialias); + void drawSolidBoxSide(GraphicsContext*, + int x1, + int y1, + int x2, + int y2, + BoxSide, + Color, + int adjacentWidth1, + int adjacentWidth2, + bool antialias); + + void addChildFocusRingRects(Vector&, + const LayoutPoint& additionalOffset, + const RenderBox* paintContainer) const; + + void clearLayoutRootIfNeeded() const; + virtual void willBeDestroyed(); + void postDestroy(); + + void insertedIntoTree(); + void willBeRemovedFromTree(); + + private: + bool hasImmediateNonWhitespaceTextChildOrPropertiesDependentOnColor() const; + + StyleDifference adjustStyleDifference(StyleDifference) const; + + Color selectionColor() const; #if ENABLE(ASSERT) - void checkBlockPositionedObjectsNeedLayout(); + void checkBlockPositionedObjectsNeedLayout(); #endif - // FIXME(sky): This method is just to avoid copy-paste. - // Merge container into containingBlock and then get rid of this method. - bool canContainAbsolutePositionObjects() const - { - return isRenderView() || (hasTransform() && isRenderBlock()); - } + // FIXME(sky): This method is just to avoid copy-paste. + // Merge container into containingBlock and then get rid of this method. + bool canContainAbsolutePositionObjects() const { + return isRenderView() || (hasTransform() && isRenderBlock()); + } - RefPtr m_style; + RefPtr m_style; - RawPtr m_parent; - RawPtr m_previous; - RawPtr m_next; + RawPtr m_parent; + RawPtr m_previous; + RawPtr m_next; #if ENABLE(ASSERT) - unsigned m_setNeedsLayoutForbidden : 1; + unsigned m_setNeedsLayoutForbidden : 1; #if ENABLE(OILPAN) -protected: - unsigned m_didCallDestroy : 1; -private: + protected: + unsigned m_didCallDestroy : 1; + + private: #endif #endif #define ADD_BOOLEAN_BITFIELD(name, Name) \ - private:\ - unsigned m_##name : 1;\ - public:\ - bool name() const { return m_##name; }\ - void set##Name(bool name) { m_##name = name; }\ - - class RenderObjectBitfields { - // FIXME(sky): Remove this enum and just use EPosition directly. - enum PositionedState { - IsStaticallyPositioned = 0, - IsOutOfFlowPositioned = 1, - }; - - public: - RenderObjectBitfields() - : m_selfNeedsLayout(false) - , m_onlyNeededPositionedMovementLayout(false) - , m_neededLayoutBecauseOfChildren(false) - , m_needsPositionedMovementLayout(false) - , m_normalChildNeedsLayout(false) - , m_posChildNeedsLayout(false) - , m_needsSimplifiedNormalFlowLayout(false) - , m_preferredLogicalWidthsDirty(false) - , m_selfNeedsOverflowRecalcAfterStyleChange(false) - , m_childNeedsOverflowRecalcAfterStyleChange(false) - , m_isText(false) - , m_isBox(false) - , m_isInline(true) - , m_isReplaced(false) - , m_isDragging(false) - , m_hasLayer(false) - , m_hasOverflowClip(false) - , m_hasTransform(false) - , m_hasBoxDecorationBackground(false) - , m_everHadLayout(false) - , m_ancestorLineBoxDirty(false) - , m_alwaysCreateLineBoxesForRenderInline(false) - , m_positionedState(IsStaticallyPositioned) - , m_selectionState(SelectionNone) - { - } - - // 32 bits have been used in the first word, and 11 in the second. - ADD_BOOLEAN_BITFIELD(selfNeedsLayout, SelfNeedsLayout); - ADD_BOOLEAN_BITFIELD(onlyNeededPositionedMovementLayout, OnlyNeededPositionedMovementLayout); - ADD_BOOLEAN_BITFIELD(neededLayoutBecauseOfChildren, NeededLayoutBecauseOfChildren); - ADD_BOOLEAN_BITFIELD(needsPositionedMovementLayout, NeedsPositionedMovementLayout); - ADD_BOOLEAN_BITFIELD(normalChildNeedsLayout, NormalChildNeedsLayout); - ADD_BOOLEAN_BITFIELD(posChildNeedsLayout, PosChildNeedsLayout); - ADD_BOOLEAN_BITFIELD(needsSimplifiedNormalFlowLayout, NeedsSimplifiedNormalFlowLayout); - ADD_BOOLEAN_BITFIELD(preferredLogicalWidthsDirty, PreferredLogicalWidthsDirty); - ADD_BOOLEAN_BITFIELD(selfNeedsOverflowRecalcAfterStyleChange, SelfNeedsOverflowRecalcAfterStyleChange); - ADD_BOOLEAN_BITFIELD(childNeedsOverflowRecalcAfterStyleChange, ChildNeedsOverflowRecalcAfterStyleChange); - - ADD_BOOLEAN_BITFIELD(isText, IsText); - ADD_BOOLEAN_BITFIELD(isBox, IsBox); - ADD_BOOLEAN_BITFIELD(isInline, IsInline); - ADD_BOOLEAN_BITFIELD(isReplaced, IsReplaced); - ADD_BOOLEAN_BITFIELD(isDragging, IsDragging); - - ADD_BOOLEAN_BITFIELD(hasLayer, HasLayer); - ADD_BOOLEAN_BITFIELD(hasOverflowClip, HasOverflowClip); // Set in the case of overflow:auto/scroll/hidden - ADD_BOOLEAN_BITFIELD(hasTransform, HasTransform); - ADD_BOOLEAN_BITFIELD(hasBoxDecorationBackground, HasBoxDecorationBackground); - - ADD_BOOLEAN_BITFIELD(everHadLayout, EverHadLayout); - ADD_BOOLEAN_BITFIELD(ancestorLineBoxDirty, AncestorLineBoxDirty); - - // from RenderInline - ADD_BOOLEAN_BITFIELD(alwaysCreateLineBoxesForRenderInline, AlwaysCreateLineBoxesForRenderInline); - - private: - unsigned m_positionedState : 1; // PositionedState - unsigned m_selectionState : 3; // SelectionState - - public: - bool isOutOfFlowPositioned() const { return m_positionedState == IsOutOfFlowPositioned; } - bool isPositioned() const { return m_positionedState != IsStaticallyPositioned; } - - void setPositionedState(int positionState) - { - m_positionedState = static_cast(positionState); - } - void clearPositionedState() { m_positionedState = StaticPosition; } - - ALWAYS_INLINE SelectionState selectionState() const { return static_cast(m_selectionState); } - ALWAYS_INLINE void setSelectionState(SelectionState selectionState) { m_selectionState = selectionState; } + private: \ + unsigned m_##name : 1; \ + \ + public: \ + bool name() const { return m_##name; } \ + void set##Name(bool name) { m_##name = name; } + + class RenderObjectBitfields { + // FIXME(sky): Remove this enum and just use EPosition directly. + enum PositionedState { + IsStaticallyPositioned = 0, + IsOutOfFlowPositioned = 1, }; -#undef ADD_BOOLEAN_BITFIELD + public: + RenderObjectBitfields() + : m_selfNeedsLayout(false), + m_onlyNeededPositionedMovementLayout(false), + m_neededLayoutBecauseOfChildren(false), + m_needsPositionedMovementLayout(false), + m_normalChildNeedsLayout(false), + m_posChildNeedsLayout(false), + m_needsSimplifiedNormalFlowLayout(false), + m_preferredLogicalWidthsDirty(false), + m_selfNeedsOverflowRecalcAfterStyleChange(false), + m_childNeedsOverflowRecalcAfterStyleChange(false), + m_isText(false), + m_isBox(false), + m_isInline(true), + m_isReplaced(false), + m_isDragging(false), + m_hasLayer(false), + m_hasOverflowClip(false), + m_hasTransform(false), + m_hasBoxDecorationBackground(false), + m_everHadLayout(false), + m_ancestorLineBoxDirty(false), + m_alwaysCreateLineBoxesForRenderInline(false), + m_positionedState(IsStaticallyPositioned), + m_selectionState(SelectionNone) {} + + // 32 bits have been used in the first word, and 11 in the second. + ADD_BOOLEAN_BITFIELD(selfNeedsLayout, SelfNeedsLayout); + ADD_BOOLEAN_BITFIELD(onlyNeededPositionedMovementLayout, + OnlyNeededPositionedMovementLayout); + ADD_BOOLEAN_BITFIELD(neededLayoutBecauseOfChildren, + NeededLayoutBecauseOfChildren); + ADD_BOOLEAN_BITFIELD(needsPositionedMovementLayout, + NeedsPositionedMovementLayout); + ADD_BOOLEAN_BITFIELD(normalChildNeedsLayout, NormalChildNeedsLayout); + ADD_BOOLEAN_BITFIELD(posChildNeedsLayout, PosChildNeedsLayout); + ADD_BOOLEAN_BITFIELD(needsSimplifiedNormalFlowLayout, + NeedsSimplifiedNormalFlowLayout); + ADD_BOOLEAN_BITFIELD(preferredLogicalWidthsDirty, + PreferredLogicalWidthsDirty); + ADD_BOOLEAN_BITFIELD(selfNeedsOverflowRecalcAfterStyleChange, + SelfNeedsOverflowRecalcAfterStyleChange); + ADD_BOOLEAN_BITFIELD(childNeedsOverflowRecalcAfterStyleChange, + ChildNeedsOverflowRecalcAfterStyleChange); + + ADD_BOOLEAN_BITFIELD(isText, IsText); + ADD_BOOLEAN_BITFIELD(isBox, IsBox); + ADD_BOOLEAN_BITFIELD(isInline, IsInline); + ADD_BOOLEAN_BITFIELD(isReplaced, IsReplaced); + ADD_BOOLEAN_BITFIELD(isDragging, IsDragging); + + ADD_BOOLEAN_BITFIELD(hasLayer, HasLayer); + ADD_BOOLEAN_BITFIELD( + hasOverflowClip, + HasOverflowClip); // Set in the case of overflow:auto/scroll/hidden + ADD_BOOLEAN_BITFIELD(hasTransform, HasTransform); + ADD_BOOLEAN_BITFIELD(hasBoxDecorationBackground, + HasBoxDecorationBackground); + + ADD_BOOLEAN_BITFIELD(everHadLayout, EverHadLayout); + ADD_BOOLEAN_BITFIELD(ancestorLineBoxDirty, AncestorLineBoxDirty); + + // from RenderInline + ADD_BOOLEAN_BITFIELD(alwaysCreateLineBoxesForRenderInline, + AlwaysCreateLineBoxesForRenderInline); + + private: + unsigned m_positionedState : 1; // PositionedState + unsigned m_selectionState : 3; // SelectionState + + public: + bool isOutOfFlowPositioned() const { + return m_positionedState == IsOutOfFlowPositioned; + } + bool isPositioned() const { + return m_positionedState != IsStaticallyPositioned; + } - RenderObjectBitfields m_bitfields; + void setPositionedState(int positionState) { + m_positionedState = static_cast(positionState); + } + void clearPositionedState() { m_positionedState = StaticPosition; } - void setSelfNeedsLayout(bool b) { m_bitfields.setSelfNeedsLayout(b); } - void setNeedsPositionedMovementLayout(bool b) { m_bitfields.setNeedsPositionedMovementLayout(b); } - void setNormalChildNeedsLayout(bool b) { m_bitfields.setNormalChildNeedsLayout(b); } - void setPosChildNeedsLayout(bool b) { m_bitfields.setPosChildNeedsLayout(b); } - void setNeedsSimplifiedNormalFlowLayout(bool b) { m_bitfields.setNeedsSimplifiedNormalFlowLayout(b); } - void setIsDragging(bool b) { m_bitfields.setIsDragging(b); } - void setEverHadLayout(bool b) { m_bitfields.setEverHadLayout(b); } - void setSelfNeedsOverflowRecalcAfterStyleChange(bool b) { m_bitfields.setSelfNeedsOverflowRecalcAfterStyleChange(b); } - void setChildNeedsOverflowRecalcAfterStyleChange(bool b) { m_bitfields.setChildNeedsOverflowRecalcAfterStyleChange(b); } + ALWAYS_INLINE SelectionState selectionState() const { + return static_cast(m_selectionState); + } + ALWAYS_INLINE void setSelectionState(SelectionState selectionState) { + m_selectionState = selectionState; + } + }; -private: - // Store state between styleWillChange and styleDidChange - static bool s_affectsParentBlock; +#undef ADD_BOOLEAN_BITFIELD - static unsigned s_instanceCount; + RenderObjectBitfields m_bitfields; + + void setSelfNeedsLayout(bool b) { m_bitfields.setSelfNeedsLayout(b); } + void setNeedsPositionedMovementLayout(bool b) { + m_bitfields.setNeedsPositionedMovementLayout(b); + } + void setNormalChildNeedsLayout(bool b) { + m_bitfields.setNormalChildNeedsLayout(b); + } + void setPosChildNeedsLayout(bool b) { m_bitfields.setPosChildNeedsLayout(b); } + void setNeedsSimplifiedNormalFlowLayout(bool b) { + m_bitfields.setNeedsSimplifiedNormalFlowLayout(b); + } + void setIsDragging(bool b) { m_bitfields.setIsDragging(b); } + void setEverHadLayout(bool b) { m_bitfields.setEverHadLayout(b); } + void setSelfNeedsOverflowRecalcAfterStyleChange(bool b) { + m_bitfields.setSelfNeedsOverflowRecalcAfterStyleChange(b); + } + void setChildNeedsOverflowRecalcAfterStyleChange(bool b) { + m_bitfields.setChildNeedsOverflowRecalcAfterStyleChange(b); + } + + private: + // Store state between styleWillChange and styleDidChange + static bool s_affectsParentBlock; + + static unsigned s_instanceCount; }; -// Allow equality comparisons of RenderObjects by reference or pointer, interchangeably. +// Allow equality comparisons of RenderObjects by reference or pointer, +// interchangeably. DEFINE_COMPARISON_OPERATORS_WITH_REFERENCES(RenderObject) -inline bool RenderObject::documentBeingDestroyed() const -{ - return false; +inline bool RenderObject::documentBeingDestroyed() const { + return false; } // setNeedsLayout() won't cause full paint invalidations as // setNeedsLayout() does. Otherwise the two methods are identical. -inline void RenderObject::setNeedsLayout(MarkingBehavior markParents, SubtreeLayoutScope* layouter) -{ - ASSERT(!isSetNeedsLayoutForbidden()); - bool alreadyNeededLayout = m_bitfields.selfNeedsLayout(); - setSelfNeedsLayout(true); - if (!alreadyNeededLayout) { - if (markParents == MarkContainingBlockChain && (!layouter || layouter->root() != this)) - markContainingBlocksForLayout(true, 0, layouter); - } +inline void RenderObject::setNeedsLayout(MarkingBehavior markParents, + SubtreeLayoutScope* layouter) { + ASSERT(!isSetNeedsLayoutForbidden()); + bool alreadyNeededLayout = m_bitfields.selfNeedsLayout(); + setSelfNeedsLayout(true); + if (!alreadyNeededLayout) { + if (markParents == MarkContainingBlockChain && + (!layouter || layouter->root() != this)) + markContainingBlocksForLayout(true, 0, layouter); + } } -inline void RenderObject::clearNeedsLayout() -{ - setOnlyNeededPositionedMovementLayout(needsPositionedMovementLayoutOnly()); - setNeededLayoutBecauseOfChildren(needsLayoutBecauseOfChildren()); - setSelfNeedsLayout(false); - setEverHadLayout(true); - setPosChildNeedsLayout(false); - setNeedsSimplifiedNormalFlowLayout(false); - setNormalChildNeedsLayout(false); - setNeedsPositionedMovementLayout(false); - setAncestorLineBoxDirty(false); +inline void RenderObject::clearNeedsLayout() { + setOnlyNeededPositionedMovementLayout(needsPositionedMovementLayoutOnly()); + setNeededLayoutBecauseOfChildren(needsLayoutBecauseOfChildren()); + setSelfNeedsLayout(false); + setEverHadLayout(true); + setPosChildNeedsLayout(false); + setNeedsSimplifiedNormalFlowLayout(false); + setNormalChildNeedsLayout(false); + setNeedsPositionedMovementLayout(false); + setAncestorLineBoxDirty(false); #if ENABLE(ASSERT) - checkBlockPositionedObjectsNeedLayout(); + checkBlockPositionedObjectsNeedLayout(); #endif } -inline void RenderObject::setChildNeedsLayout(MarkingBehavior markParents, SubtreeLayoutScope* layouter) -{ - ASSERT(!isSetNeedsLayoutForbidden()); - bool alreadyNeededLayout = normalChildNeedsLayout(); - setNormalChildNeedsLayout(true); - // FIXME: Replace MarkOnlyThis with the SubtreeLayoutScope code path and remove the MarkingBehavior argument entirely. - if (!alreadyNeededLayout && markParents == MarkContainingBlockChain && (!layouter || layouter->root() != this)) - markContainingBlocksForLayout(true, 0, layouter); +inline void RenderObject::setChildNeedsLayout(MarkingBehavior markParents, + SubtreeLayoutScope* layouter) { + ASSERT(!isSetNeedsLayoutForbidden()); + bool alreadyNeededLayout = normalChildNeedsLayout(); + setNormalChildNeedsLayout(true); + // FIXME: Replace MarkOnlyThis with the SubtreeLayoutScope code path and + // remove the MarkingBehavior argument entirely. + if (!alreadyNeededLayout && markParents == MarkContainingBlockChain && + (!layouter || layouter->root() != this)) + markContainingBlocksForLayout(true, 0, layouter); } -inline void RenderObject::setNeedsPositionedMovementLayout() -{ - bool alreadyNeededLayout = needsPositionedMovementLayout(); - setNeedsPositionedMovementLayout(true); - ASSERT(!isSetNeedsLayoutForbidden()); - if (!alreadyNeededLayout) - markContainingBlocksForLayout(); +inline void RenderObject::setNeedsPositionedMovementLayout() { + bool alreadyNeededLayout = needsPositionedMovementLayout(); + setNeedsPositionedMovementLayout(true); + ASSERT(!isSetNeedsLayoutForbidden()); + if (!alreadyNeededLayout) + markContainingBlocksForLayout(); } -inline bool RenderObject::preservesNewline() const -{ - return style()->preserveNewline(); +inline bool RenderObject::preservesNewline() const { + return style()->preserveNewline(); } -inline void RenderObject::setSelectionStateIfNeeded(SelectionState state) -{ - if (selectionState() == state) - return; +inline void RenderObject::setSelectionStateIfNeeded(SelectionState state) { + if (selectionState() == state) + return; - setSelectionState(state); + setSelectionState(state); } -#define DEFINE_RENDER_OBJECT_TYPE_CASTS(thisType, predicate) \ - DEFINE_TYPE_CASTS(thisType, RenderObject, object, object->predicate, object.predicate) +#define DEFINE_RENDER_OBJECT_TYPE_CASTS(thisType, predicate) \ + DEFINE_TYPE_CASTS(thisType, RenderObject, object, object->predicate, \ + object.predicate) -} // namespace blink +} // namespace blink #ifndef NDEBUG // Outside the WebCore namespace for ease of invocation from gdb. @@ -802,7 +1001,8 @@ void showLineTree(const blink::RenderObject*); void showRenderTree(const blink::RenderObject* object1); // We don't make object2 an optional parameter so that showRenderTree // can be called from gdb easily. -void showRenderTree(const blink::RenderObject* object1, const blink::RenderObject* object2); +void showRenderTree(const blink::RenderObject* object1, + const blink::RenderObject* object2); #endif diff --git a/sky/engine/core/rendering/RenderObjectChildList.cpp b/sky/engine/core/rendering/RenderObjectChildList.cpp index 39f05c72b08d3..79e549ca0c778 100644 --- a/sky/engine/core/rendering/RenderObjectChildList.cpp +++ b/sky/engine/core/rendering/RenderObjectChildList.cpp @@ -32,93 +32,99 @@ namespace blink { -void RenderObjectChildList::destroyLeftoverChildren() -{ - while (firstChild()) - firstChild()->destroy(); +void RenderObjectChildList::destroyLeftoverChildren() { + while (firstChild()) + firstChild()->destroy(); } -RenderObject* RenderObjectChildList::removeChildNode(RenderObject* owner, RenderObject* oldChild, bool notifyRenderer) -{ - ASSERT(oldChild->parent() == owner); - - if (oldChild->isFloatingOrOutOfFlowPositioned()) - toRenderBox(oldChild)->removeFloatingOrPositionedChildFromBlockLists(); - - // So that we'll get the appropriate dirty bit set (either that a normal flow child got yanked or - // that a positioned child got yanked). We also issue paint invalidations, so that the area exposed when the child - // disappears gets paint invalidated properly. - if (!owner->documentBeingDestroyed() && notifyRenderer && oldChild->everHadLayout()) - oldChild->setNeedsLayoutAndPrefWidthsRecalc(); - - // If we have a line box wrapper, delete it. - if (oldChild->isBox()) - toRenderBox(oldChild)->deleteLineBoxWrapper(); - - if (!owner->documentBeingDestroyed() && notifyRenderer) - oldChild->willBeRemovedFromTree(); - - // WARNING: There should be no code running between willBeRemovedFromTree and the actual removal below. - // This is needed to avoid race conditions where willBeRemovedFromTree would dirty the tree's structure - // and the code running here would force an untimely rebuilding, leaving |oldChild| dangling. - - if (oldChild->previousSibling()) - oldChild->previousSibling()->setNextSibling(oldChild->nextSibling()); - if (oldChild->nextSibling()) - oldChild->nextSibling()->setPreviousSibling(oldChild->previousSibling()); - - if (firstChild() == oldChild) - setFirstChild(oldChild->nextSibling()); - if (lastChild() == oldChild) - setLastChild(oldChild->previousSibling()); - - oldChild->setPreviousSibling(0); - oldChild->setNextSibling(0); - oldChild->setParent(0); - - return oldChild; +RenderObject* RenderObjectChildList::removeChildNode(RenderObject* owner, + RenderObject* oldChild, + bool notifyRenderer) { + ASSERT(oldChild->parent() == owner); + + if (oldChild->isFloatingOrOutOfFlowPositioned()) + toRenderBox(oldChild)->removeFloatingOrPositionedChildFromBlockLists(); + + // So that we'll get the appropriate dirty bit set (either that a normal flow + // child got yanked or that a positioned child got yanked). We also issue + // paint invalidations, so that the area exposed when the child disappears + // gets paint invalidated properly. + if (!owner->documentBeingDestroyed() && notifyRenderer && + oldChild->everHadLayout()) + oldChild->setNeedsLayoutAndPrefWidthsRecalc(); + + // If we have a line box wrapper, delete it. + if (oldChild->isBox()) + toRenderBox(oldChild)->deleteLineBoxWrapper(); + + if (!owner->documentBeingDestroyed() && notifyRenderer) + oldChild->willBeRemovedFromTree(); + + // WARNING: There should be no code running between willBeRemovedFromTree and + // the actual removal below. This is needed to avoid race conditions where + // willBeRemovedFromTree would dirty the tree's structure and the code running + // here would force an untimely rebuilding, leaving |oldChild| dangling. + + if (oldChild->previousSibling()) + oldChild->previousSibling()->setNextSibling(oldChild->nextSibling()); + if (oldChild->nextSibling()) + oldChild->nextSibling()->setPreviousSibling(oldChild->previousSibling()); + + if (firstChild() == oldChild) + setFirstChild(oldChild->nextSibling()); + if (lastChild() == oldChild) + setLastChild(oldChild->previousSibling()); + + oldChild->setPreviousSibling(0); + oldChild->setNextSibling(0); + oldChild->setParent(0); + + return oldChild; } -void RenderObjectChildList::insertChildNode(RenderObject* owner, RenderObject* newChild, RenderObject* beforeChild, bool notifyRenderer) -{ - ASSERT(!newChild->parent()); - - while (beforeChild && beforeChild->parent() && beforeChild->parent() != owner) - beforeChild = beforeChild->parent(); - - // This should never happen, but if it does prevent render tree corruption - // where child->parent() ends up being owner but child->nextSibling()->parent() - // is not owner. - if (beforeChild && beforeChild->parent() != owner) { - ASSERT_NOT_REACHED(); - return; - } - - newChild->setParent(owner); - - if (firstChild() == beforeChild) - setFirstChild(newChild); - - if (beforeChild) { - RenderObject* previousSibling = beforeChild->previousSibling(); - if (previousSibling) - previousSibling->setNextSibling(newChild); - newChild->setPreviousSibling(previousSibling); - newChild->setNextSibling(beforeChild); - beforeChild->setPreviousSibling(newChild); - } else { - if (lastChild()) - lastChild()->setNextSibling(newChild); - newChild->setPreviousSibling(lastChild()); - setLastChild(newChild); - } - - if (!owner->documentBeingDestroyed() && notifyRenderer) - newChild->insertedIntoTree(); - - newChild->setNeedsLayoutAndPrefWidthsRecalc(); - if (!owner->normalChildNeedsLayout()) - owner->setChildNeedsLayout(); // We may supply the static position for an absolute positioned child. +void RenderObjectChildList::insertChildNode(RenderObject* owner, + RenderObject* newChild, + RenderObject* beforeChild, + bool notifyRenderer) { + ASSERT(!newChild->parent()); + + while (beforeChild && beforeChild->parent() && beforeChild->parent() != owner) + beforeChild = beforeChild->parent(); + + // This should never happen, but if it does prevent render tree corruption + // where child->parent() ends up being owner but + // child->nextSibling()->parent() is not owner. + if (beforeChild && beforeChild->parent() != owner) { + ASSERT_NOT_REACHED(); + return; + } + + newChild->setParent(owner); + + if (firstChild() == beforeChild) + setFirstChild(newChild); + + if (beforeChild) { + RenderObject* previousSibling = beforeChild->previousSibling(); + if (previousSibling) + previousSibling->setNextSibling(newChild); + newChild->setPreviousSibling(previousSibling); + newChild->setNextSibling(beforeChild); + beforeChild->setPreviousSibling(newChild); + } else { + if (lastChild()) + lastChild()->setNextSibling(newChild); + newChild->setPreviousSibling(lastChild()); + setLastChild(newChild); + } + + if (!owner->documentBeingDestroyed() && notifyRenderer) + newChild->insertedIntoTree(); + + newChild->setNeedsLayoutAndPrefWidthsRecalc(); + if (!owner->normalChildNeedsLayout()) + owner->setChildNeedsLayout(); // We may supply the static position for an + // absolute positioned child. } -} // namespace blink +} // namespace blink diff --git a/sky/engine/core/rendering/RenderObjectChildList.h b/sky/engine/core/rendering/RenderObjectChildList.h index 271ed97f89f1c..9c7edb8f30abf 100644 --- a/sky/engine/core/rendering/RenderObjectChildList.h +++ b/sky/engine/core/rendering/RenderObjectChildList.h @@ -34,36 +34,40 @@ namespace blink { class RenderObject; class RenderObjectChildList { - DISALLOW_ALLOCATION(); -public: - RenderObjectChildList() - : m_firstChild(nullptr) - , m_lastChild(nullptr) - { - } + DISALLOW_ALLOCATION(); - RenderObject* firstChild() const { return m_firstChild; } - RenderObject* lastChild() const { return m_lastChild; } + public: + RenderObjectChildList() : m_firstChild(nullptr), m_lastChild(nullptr) {} - // FIXME: Temporary while RenderBox still exists. Eventually this will just happen during insert/append/remove methods on the child list, and nobody - // will need to manipulate firstChild or lastChild directly. - void setFirstChild(RenderObject* child) { m_firstChild = child; } - void setLastChild(RenderObject* child) { m_lastChild = child; } + RenderObject* firstChild() const { return m_firstChild; } + RenderObject* lastChild() const { return m_lastChild; } - void destroyLeftoverChildren(); + // FIXME: Temporary while RenderBox still exists. Eventually this will just + // happen during insert/append/remove methods on the child list, and nobody + // will need to manipulate firstChild or lastChild directly. + void setFirstChild(RenderObject* child) { m_firstChild = child; } + void setLastChild(RenderObject* child) { m_lastChild = child; } - RenderObject* removeChildNode(RenderObject* owner, RenderObject*, bool notifyRenderer = true); - void insertChildNode(RenderObject* owner, RenderObject* newChild, RenderObject* beforeChild, bool notifyRenderer = true); - void appendChildNode(RenderObject* owner, RenderObject* newChild, bool notifyRenderer = true) - { - insertChildNode(owner, newChild, 0, notifyRenderer); - } + void destroyLeftoverChildren(); -private: - RenderObject* m_firstChild; - RenderObject* m_lastChild; + RenderObject* removeChildNode(RenderObject* owner, + RenderObject*, + bool notifyRenderer = true); + void insertChildNode(RenderObject* owner, + RenderObject* newChild, + RenderObject* beforeChild, + bool notifyRenderer = true); + void appendChildNode(RenderObject* owner, + RenderObject* newChild, + bool notifyRenderer = true) { + insertChildNode(owner, newChild, 0, notifyRenderer); + } + + private: + RenderObject* m_firstChild; + RenderObject* m_lastChild; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_RENDEROBJECTCHILDLIST_H_ diff --git a/sky/engine/core/rendering/RenderObjectInlines.h b/sky/engine/core/rendering/RenderObjectInlines.h index 60b8e8e1cd772..b985d9325869c 100644 --- a/sky/engine/core/rendering/RenderObjectInlines.h +++ b/sky/engine/core/rendering/RenderObjectInlines.h @@ -14,16 +14,14 @@ namespace blink { // to StyleEngine.h for all users of RenderObject.h that don't use // these methods. -inline RenderStyle* RenderObject::firstLineStyle() const -{ - return style(); +inline RenderStyle* RenderObject::firstLineStyle() const { + return style(); } -inline RenderStyle* RenderObject::style(bool firstLine) const -{ - return style(); +inline RenderStyle* RenderObject::style(bool firstLine) const { + return style(); } -} +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_RENDEROBJECTINLINES_H_ diff --git a/sky/engine/core/rendering/RenderOverflow.h b/sky/engine/core/rendering/RenderOverflow.h index 85fac545c511e..f82d196476ee9 100644 --- a/sky/engine/core/rendering/RenderOverflow.h +++ b/sky/engine/core/rendering/RenderOverflow.h @@ -1,5 +1,6 @@ /* - * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. + * All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -23,93 +24,96 @@ #include "flutter/sky/engine/platform/geometry/LayoutRect.h" -namespace blink -{ -// RenderOverflow is a class for tracking content that spills out of a box. This class is used by RenderBox and -// InlineFlowBox. +namespace blink { +// RenderOverflow is a class for tracking content that spills out of a box. +// This class is used by RenderBox and InlineFlowBox. // -// There are two types of overflow: layout overflow (which is expected to be reachable via scrolling mechanisms) and -// visual overflow (which is not expected to be reachable via scrolling mechanisms). +// There are two types of overflow: layout overflow (which is expected to be +// reachable via scrolling mechanisms) and visual overflow (which is not +// expected to be reachable via scrolling mechanisms). // -// Layout overflow examples include other boxes that spill out of our box, For example, in the inline case a tall image -// could spill out of a line box. +// Layout overflow examples include other boxes that spill out of our box, For +// example, in the inline case a tall image could spill out of a line box. -// Examples of visual overflow are shadows, text stroke (and eventually outline and border-image). +// Examples of visual overflow are shadows, text stroke (and eventually outline +// and border-image). -// This object is allocated only when some of these fields have non-default values in the owning box. +// This object is allocated only when some of these fields have non-default +// values in the owning box. class RenderOverflow { - WTF_MAKE_NONCOPYABLE(RenderOverflow); WTF_MAKE_FAST_ALLOCATED; -public: - RenderOverflow(const LayoutRect& layoutRect, const LayoutRect& visualRect) - : m_layoutOverflow(layoutRect) - , m_visualOverflow(visualRect) - { - } + WTF_MAKE_NONCOPYABLE(RenderOverflow); + WTF_MAKE_FAST_ALLOCATED; - const LayoutRect layoutOverflowRect() const { return m_layoutOverflow; } - const LayoutRect visualOverflowRect() const { return m_visualOverflow; } - LayoutRect contentsVisualOverflowRect() const { return m_contentsVisualOverflow; } + public: + RenderOverflow(const LayoutRect& layoutRect, const LayoutRect& visualRect) + : m_layoutOverflow(layoutRect), m_visualOverflow(visualRect) {} - void move(LayoutUnit dx, LayoutUnit dy); + const LayoutRect layoutOverflowRect() const { return m_layoutOverflow; } + const LayoutRect visualOverflowRect() const { return m_visualOverflow; } + LayoutRect contentsVisualOverflowRect() const { + return m_contentsVisualOverflow; + } - void addLayoutOverflow(const LayoutRect&); - void addVisualOverflow(const LayoutRect&); - void addContentsVisualOverflow(const LayoutRect& rect) { m_contentsVisualOverflow.unite(rect); } + void move(LayoutUnit dx, LayoutUnit dy); - void setLayoutOverflow(const LayoutRect&); - void setVisualOverflow(const LayoutRect&); + void addLayoutOverflow(const LayoutRect&); + void addVisualOverflow(const LayoutRect&); + void addContentsVisualOverflow(const LayoutRect& rect) { + m_contentsVisualOverflow.unite(rect); + } - LayoutUnit layoutClientAfterEdge() const { return m_layoutClientAfterEdge; } - void setLayoutClientAfterEdge(LayoutUnit clientAfterEdge) { m_layoutClientAfterEdge = clientAfterEdge; } + void setLayoutOverflow(const LayoutRect&); + void setVisualOverflow(const LayoutRect&); -private: - LayoutRect m_layoutOverflow; - LayoutRect m_visualOverflow; - LayoutRect m_contentsVisualOverflow; + LayoutUnit layoutClientAfterEdge() const { return m_layoutClientAfterEdge; } + void setLayoutClientAfterEdge(LayoutUnit clientAfterEdge) { + m_layoutClientAfterEdge = clientAfterEdge; + } - LayoutUnit m_layoutClientAfterEdge; + private: + LayoutRect m_layoutOverflow; + LayoutRect m_visualOverflow; + LayoutRect m_contentsVisualOverflow; + + LayoutUnit m_layoutClientAfterEdge; }; -inline void RenderOverflow::move(LayoutUnit dx, LayoutUnit dy) -{ - m_layoutOverflow.move(dx, dy); - m_visualOverflow.move(dx, dy); - m_contentsVisualOverflow.move(dx, dy); +inline void RenderOverflow::move(LayoutUnit dx, LayoutUnit dy) { + m_layoutOverflow.move(dx, dy); + m_visualOverflow.move(dx, dy); + m_contentsVisualOverflow.move(dx, dy); } -inline void RenderOverflow::addLayoutOverflow(const LayoutRect& rect) -{ - LayoutUnit maxX = std::max(rect.maxX(), m_layoutOverflow.maxX()); - LayoutUnit maxY = std::max(rect.maxY(), m_layoutOverflow.maxY()); - LayoutUnit minX = std::min(rect.x(), m_layoutOverflow.x()); - LayoutUnit minY = std::min(rect.y(), m_layoutOverflow.y()); - // In case the width/height is larger than LayoutUnit can represent, fix the right/bottom edge and shift the top/left ones - m_layoutOverflow.setWidth(maxX - minX); - m_layoutOverflow.setHeight(maxY - minY); - m_layoutOverflow.setX(maxX - m_layoutOverflow.width()); - m_layoutOverflow.setY(maxY - m_layoutOverflow.height()); +inline void RenderOverflow::addLayoutOverflow(const LayoutRect& rect) { + LayoutUnit maxX = std::max(rect.maxX(), m_layoutOverflow.maxX()); + LayoutUnit maxY = std::max(rect.maxY(), m_layoutOverflow.maxY()); + LayoutUnit minX = std::min(rect.x(), m_layoutOverflow.x()); + LayoutUnit minY = std::min(rect.y(), m_layoutOverflow.y()); + // In case the width/height is larger than LayoutUnit can represent, fix the + // right/bottom edge and shift the top/left ones + m_layoutOverflow.setWidth(maxX - minX); + m_layoutOverflow.setHeight(maxY - minY); + m_layoutOverflow.setX(maxX - m_layoutOverflow.width()); + m_layoutOverflow.setY(maxY - m_layoutOverflow.height()); } -inline void RenderOverflow::addVisualOverflow(const LayoutRect& rect) -{ - LayoutUnit maxX = std::max(rect.maxX(), m_visualOverflow.maxX()); - LayoutUnit maxY = std::max(rect.maxY(), m_visualOverflow.maxY()); - m_visualOverflow.setX(std::min(rect.x(), m_visualOverflow.x())); - m_visualOverflow.setY(std::min(rect.y(), m_visualOverflow.y())); - m_visualOverflow.setWidth(maxX - m_visualOverflow.x()); - m_visualOverflow.setHeight(maxY - m_visualOverflow.y()); +inline void RenderOverflow::addVisualOverflow(const LayoutRect& rect) { + LayoutUnit maxX = std::max(rect.maxX(), m_visualOverflow.maxX()); + LayoutUnit maxY = std::max(rect.maxY(), m_visualOverflow.maxY()); + m_visualOverflow.setX(std::min(rect.x(), m_visualOverflow.x())); + m_visualOverflow.setY(std::min(rect.y(), m_visualOverflow.y())); + m_visualOverflow.setWidth(maxX - m_visualOverflow.x()); + m_visualOverflow.setHeight(maxY - m_visualOverflow.y()); } -inline void RenderOverflow::setLayoutOverflow(const LayoutRect& rect) -{ - m_layoutOverflow = rect; +inline void RenderOverflow::setLayoutOverflow(const LayoutRect& rect) { + m_layoutOverflow = rect; } -inline void RenderOverflow::setVisualOverflow(const LayoutRect& rect) -{ - m_visualOverflow = rect; +inline void RenderOverflow::setVisualOverflow(const LayoutRect& rect) { + m_visualOverflow = rect; } -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_RENDEROVERFLOW_H_ diff --git a/sky/engine/core/rendering/RenderOverflowTest.cpp b/sky/engine/core/rendering/RenderOverflowTest.cpp index 9da427a9e4d93..3ab844d149c4a 100644 --- a/sky/engine/core/rendering/RenderOverflowTest.cpp +++ b/sky/engine/core/rendering/RenderOverflowTest.cpp @@ -39,155 +39,136 @@ using namespace blink; namespace blink { // FIXME: Move this somewhere more generic. -void PrintTo(const LayoutRect& rect, std::ostream* os) -{ - *os << "LayoutRect(" - << rect.x().toFloat() << ", " - << rect.y().toFloat() << ", " - << rect.width().toFloat() << ", " - << rect.height().toFloat() << ")"; +void PrintTo(const LayoutRect& rect, std::ostream* os) { + *os << "LayoutRect(" << rect.x().toFloat() << ", " << rect.y().toFloat() + << ", " << rect.width().toFloat() << ", " << rect.height().toFloat() + << ")"; } -} // namespace blink +} // namespace blink namespace { -LayoutRect initialLayoutOverflow() -{ - return LayoutRect(10, 10, 80, 80); +LayoutRect initialLayoutOverflow() { + return LayoutRect(10, 10, 80, 80); } -LayoutRect initialVisualOverflow() -{ - return LayoutRect(0, 0, 100, 100); +LayoutRect initialVisualOverflow() { + return LayoutRect(0, 0, 100, 100); } class RenderOverflowTest : public testing::Test { -protected: - RenderOverflowTest() : m_overflow(initialLayoutOverflow(), initialVisualOverflow()) { } - RenderOverflow m_overflow; + protected: + RenderOverflowTest() + : m_overflow(initialLayoutOverflow(), initialVisualOverflow()) {} + RenderOverflow m_overflow; }; -TEST_F(RenderOverflowTest, InitialOverflowRects) -{ - EXPECT_EQ(initialLayoutOverflow(), m_overflow.layoutOverflowRect()); - EXPECT_EQ(initialVisualOverflow(), m_overflow.visualOverflowRect()); - EXPECT_TRUE(m_overflow.contentsVisualOverflowRect().isEmpty()); +TEST_F(RenderOverflowTest, InitialOverflowRects) { + EXPECT_EQ(initialLayoutOverflow(), m_overflow.layoutOverflowRect()); + EXPECT_EQ(initialVisualOverflow(), m_overflow.visualOverflowRect()); + EXPECT_TRUE(m_overflow.contentsVisualOverflowRect().isEmpty()); } -TEST_F(RenderOverflowTest, AddLayoutOverflowOutsideExpandsRect) -{ - m_overflow.addLayoutOverflow(LayoutRect(0, 10, 30, 10)); - EXPECT_EQ(LayoutRect(0, 10, 90, 80), m_overflow.layoutOverflowRect()); +TEST_F(RenderOverflowTest, AddLayoutOverflowOutsideExpandsRect) { + m_overflow.addLayoutOverflow(LayoutRect(0, 10, 30, 10)); + EXPECT_EQ(LayoutRect(0, 10, 90, 80), m_overflow.layoutOverflowRect()); } -TEST_F(RenderOverflowTest, AddLayoutOverflowInsideDoesNotAffectRect) -{ - m_overflow.addLayoutOverflow(LayoutRect(50, 50, 10, 20)); - EXPECT_EQ(initialLayoutOverflow(), m_overflow.layoutOverflowRect()); +TEST_F(RenderOverflowTest, AddLayoutOverflowInsideDoesNotAffectRect) { + m_overflow.addLayoutOverflow(LayoutRect(50, 50, 10, 20)); + EXPECT_EQ(initialLayoutOverflow(), m_overflow.layoutOverflowRect()); } -TEST_F(RenderOverflowTest, AddLayoutOverflowEmpty) -{ - // This test documents the existing behavior so that we are aware when/if - // it changes. It would also be reasonable for addLayoutOverflow to be - // a no-op in this situation. - m_overflow.addLayoutOverflow(LayoutRect(200, 200, 0, 0)); - EXPECT_EQ(LayoutRect(10, 10, 190, 190), m_overflow.layoutOverflowRect()); +TEST_F(RenderOverflowTest, AddLayoutOverflowEmpty) { + // This test documents the existing behavior so that we are aware when/if + // it changes. It would also be reasonable for addLayoutOverflow to be + // a no-op in this situation. + m_overflow.addLayoutOverflow(LayoutRect(200, 200, 0, 0)); + EXPECT_EQ(LayoutRect(10, 10, 190, 190), m_overflow.layoutOverflowRect()); } -TEST_F(RenderOverflowTest, AddLayoutOverflowDoesNotAffectVisualOverflow) -{ - m_overflow.addLayoutOverflow(LayoutRect(300, 300, 300, 300)); - EXPECT_EQ(initialVisualOverflow(), m_overflow.visualOverflowRect()); +TEST_F(RenderOverflowTest, AddLayoutOverflowDoesNotAffectVisualOverflow) { + m_overflow.addLayoutOverflow(LayoutRect(300, 300, 300, 300)); + EXPECT_EQ(initialVisualOverflow(), m_overflow.visualOverflowRect()); } -TEST_F(RenderOverflowTest, AddLayoutOverflowDoesNotAffectContentsVisualOverflow) -{ - m_overflow.addLayoutOverflow(LayoutRect(300, 300, 300, 300)); - EXPECT_TRUE(m_overflow.contentsVisualOverflowRect().isEmpty()); +TEST_F(RenderOverflowTest, + AddLayoutOverflowDoesNotAffectContentsVisualOverflow) { + m_overflow.addLayoutOverflow(LayoutRect(300, 300, 300, 300)); + EXPECT_TRUE(m_overflow.contentsVisualOverflowRect().isEmpty()); } -TEST_F(RenderOverflowTest, AddVisualOverflowOutsideExpandsRect) -{ - m_overflow.addVisualOverflow(LayoutRect(150, -50, 10, 10)); - EXPECT_EQ(LayoutRect(0, -50, 160, 150), m_overflow.visualOverflowRect()); +TEST_F(RenderOverflowTest, AddVisualOverflowOutsideExpandsRect) { + m_overflow.addVisualOverflow(LayoutRect(150, -50, 10, 10)); + EXPECT_EQ(LayoutRect(0, -50, 160, 150), m_overflow.visualOverflowRect()); } -TEST_F(RenderOverflowTest, AddVisualOverflowInsideDoesNotAffectRect) -{ - m_overflow.addVisualOverflow(LayoutRect(0, 10, 90, 90)); - EXPECT_EQ(initialVisualOverflow(), m_overflow.visualOverflowRect()); +TEST_F(RenderOverflowTest, AddVisualOverflowInsideDoesNotAffectRect) { + m_overflow.addVisualOverflow(LayoutRect(0, 10, 90, 90)); + EXPECT_EQ(initialVisualOverflow(), m_overflow.visualOverflowRect()); } -TEST_F(RenderOverflowTest, AddVisualOverflowEmpty) -{ - // This test documents the existing behavior so that we are aware when/if - // it changes. It would also be reasonable for addVisualOverflow to be - // a no-op in this situation. - m_overflow.addVisualOverflow(LayoutRect(200, 200, 0, 0)); - EXPECT_EQ(LayoutRect(0, 0, 200, 200), m_overflow.visualOverflowRect()); +TEST_F(RenderOverflowTest, AddVisualOverflowEmpty) { + // This test documents the existing behavior so that we are aware when/if + // it changes. It would also be reasonable for addVisualOverflow to be + // a no-op in this situation. + m_overflow.addVisualOverflow(LayoutRect(200, 200, 0, 0)); + EXPECT_EQ(LayoutRect(0, 0, 200, 200), m_overflow.visualOverflowRect()); } -TEST_F(RenderOverflowTest, AddVisualOverflowDoesNotAffectLayoutOverflow) -{ - m_overflow.addVisualOverflow(LayoutRect(300, 300, 300, 300)); - EXPECT_EQ(initialLayoutOverflow(), m_overflow.layoutOverflowRect()); +TEST_F(RenderOverflowTest, AddVisualOverflowDoesNotAffectLayoutOverflow) { + m_overflow.addVisualOverflow(LayoutRect(300, 300, 300, 300)); + EXPECT_EQ(initialLayoutOverflow(), m_overflow.layoutOverflowRect()); } -TEST_F(RenderOverflowTest, AddVisualOverflowDoesNotAffectContentsVisualOverflow) -{ - m_overflow.addVisualOverflow(LayoutRect(300, 300, 300, 300)); - EXPECT_TRUE(m_overflow.contentsVisualOverflowRect().isEmpty()); +TEST_F(RenderOverflowTest, + AddVisualOverflowDoesNotAffectContentsVisualOverflow) { + m_overflow.addVisualOverflow(LayoutRect(300, 300, 300, 300)); + EXPECT_TRUE(m_overflow.contentsVisualOverflowRect().isEmpty()); } -TEST_F(RenderOverflowTest, AddContentsVisualOverflowFirstCall) -{ - m_overflow.addContentsVisualOverflow(LayoutRect(0, 0, 10, 10)); - EXPECT_EQ(LayoutRect(0, 0, 10, 10), m_overflow.contentsVisualOverflowRect()); +TEST_F(RenderOverflowTest, AddContentsVisualOverflowFirstCall) { + m_overflow.addContentsVisualOverflow(LayoutRect(0, 0, 10, 10)); + EXPECT_EQ(LayoutRect(0, 0, 10, 10), m_overflow.contentsVisualOverflowRect()); } -TEST_F(RenderOverflowTest, AddContentsVisualOverflowUnitesRects) -{ - m_overflow.addContentsVisualOverflow(LayoutRect(0, 0, 10, 10)); - m_overflow.addContentsVisualOverflow(LayoutRect(80, 80, 10, 10)); - EXPECT_EQ(LayoutRect(0, 0, 90, 90), m_overflow.contentsVisualOverflowRect()); +TEST_F(RenderOverflowTest, AddContentsVisualOverflowUnitesRects) { + m_overflow.addContentsVisualOverflow(LayoutRect(0, 0, 10, 10)); + m_overflow.addContentsVisualOverflow(LayoutRect(80, 80, 10, 10)); + EXPECT_EQ(LayoutRect(0, 0, 90, 90), m_overflow.contentsVisualOverflowRect()); } -TEST_F(RenderOverflowTest, AddContentsVisualOverflowRectWithinRect) -{ - m_overflow.addContentsVisualOverflow(LayoutRect(0, 0, 10, 10)); - m_overflow.addContentsVisualOverflow(LayoutRect(2, 2, 5, 5)); - EXPECT_EQ(LayoutRect(0, 0, 10, 10), m_overflow.contentsVisualOverflowRect()); +TEST_F(RenderOverflowTest, AddContentsVisualOverflowRectWithinRect) { + m_overflow.addContentsVisualOverflow(LayoutRect(0, 0, 10, 10)); + m_overflow.addContentsVisualOverflow(LayoutRect(2, 2, 5, 5)); + EXPECT_EQ(LayoutRect(0, 0, 10, 10), m_overflow.contentsVisualOverflowRect()); } -TEST_F(RenderOverflowTest, AddContentsVisualOverflowEmpty) -{ - // This test documents the existing behavior so that we are aware when/if - // it changes. It would also be reasonable for addContentsVisualOverflow to - // expand in this situation. - m_overflow.addContentsVisualOverflow(LayoutRect(0, 0, 10, 10)); - m_overflow.addContentsVisualOverflow(LayoutRect(20, 20, 0, 0)); - EXPECT_EQ(LayoutRect(0, 0, 10, 10), m_overflow.contentsVisualOverflowRect()); +TEST_F(RenderOverflowTest, AddContentsVisualOverflowEmpty) { + // This test documents the existing behavior so that we are aware when/if + // it changes. It would also be reasonable for addContentsVisualOverflow to + // expand in this situation. + m_overflow.addContentsVisualOverflow(LayoutRect(0, 0, 10, 10)); + m_overflow.addContentsVisualOverflow(LayoutRect(20, 20, 0, 0)); + EXPECT_EQ(LayoutRect(0, 0, 10, 10), m_overflow.contentsVisualOverflowRect()); } -TEST_F(RenderOverflowTest, MoveAffectsLayoutOverflow) -{ - m_overflow.move(500, 100); - EXPECT_EQ(LayoutRect(510, 110, 80, 80), m_overflow.layoutOverflowRect()); +TEST_F(RenderOverflowTest, MoveAffectsLayoutOverflow) { + m_overflow.move(500, 100); + EXPECT_EQ(LayoutRect(510, 110, 80, 80), m_overflow.layoutOverflowRect()); } -TEST_F(RenderOverflowTest, MoveAffectsVisualOverflow) -{ - m_overflow.move(500, 100); - EXPECT_EQ(LayoutRect(500, 100, 100, 100), m_overflow.visualOverflowRect()); +TEST_F(RenderOverflowTest, MoveAffectsVisualOverflow) { + m_overflow.move(500, 100); + EXPECT_EQ(LayoutRect(500, 100, 100, 100), m_overflow.visualOverflowRect()); } -TEST_F(RenderOverflowTest, MoveAffectsContentsVisualOverflow) -{ - m_overflow.addContentsVisualOverflow(LayoutRect(0, 0, 10, 10)); - m_overflow.move(500, 100); - EXPECT_EQ(LayoutRect(500, 100, 10, 10), m_overflow.contentsVisualOverflowRect()); +TEST_F(RenderOverflowTest, MoveAffectsContentsVisualOverflow) { + m_overflow.addContentsVisualOverflow(LayoutRect(0, 0, 10, 10)); + m_overflow.move(500, 100); + EXPECT_EQ(LayoutRect(500, 100, 10, 10), + m_overflow.contentsVisualOverflowRect()); } -} // namespace +} // namespace diff --git a/sky/engine/core/rendering/RenderParagraph.cpp b/sky/engine/core/rendering/RenderParagraph.cpp index b3d65ad2c080b..722b9fe537817 100644 --- a/sky/engine/core/rendering/RenderParagraph.cpp +++ b/sky/engine/core/rendering/RenderParagraph.cpp @@ -27,1317 +27,1543 @@ namespace blink { using namespace WTF::Unicode; -RenderParagraph::RenderParagraph() - : m_didExceedMaxLines(false) -{ -} +RenderParagraph::RenderParagraph() : m_didExceedMaxLines(false) {} -RenderParagraph::~RenderParagraph() -{ -} +RenderParagraph::~RenderParagraph() {} -const char* RenderParagraph::renderName() const -{ - return "RenderParagraph"; +const char* RenderParagraph::renderName() const { + return "RenderParagraph"; } -LayoutUnit RenderParagraph::logicalLeftSelectionOffset(RenderBlock* rootBlock, LayoutUnit position) -{ - LayoutUnit logicalLeft = logicalLeftOffsetForLine(false); - if (logicalLeft == logicalLeftOffsetForContent()) - return RenderBlock::logicalLeftSelectionOffset(rootBlock, position); - - RenderBlock* cb = this; - while (cb != rootBlock) { - logicalLeft += cb->logicalLeft(); - cb = cb->containingBlock(); - } - return logicalLeft; +LayoutUnit RenderParagraph::logicalLeftSelectionOffset(RenderBlock* rootBlock, + LayoutUnit position) { + LayoutUnit logicalLeft = logicalLeftOffsetForLine(false); + if (logicalLeft == logicalLeftOffsetForContent()) + return RenderBlock::logicalLeftSelectionOffset(rootBlock, position); + + RenderBlock* cb = this; + while (cb != rootBlock) { + logicalLeft += cb->logicalLeft(); + cb = cb->containingBlock(); + } + return logicalLeft; } -LayoutUnit RenderParagraph::logicalRightSelectionOffset(RenderBlock* rootBlock, LayoutUnit position) -{ - LayoutUnit logicalRight = logicalRightOffsetForLine(false); - if (logicalRight == logicalRightOffsetForContent()) - return RenderBlock::logicalRightSelectionOffset(rootBlock, position); - - RenderBlock* cb = this; - while (cb != rootBlock) { - logicalRight += cb->logicalLeft(); - cb = cb->containingBlock(); - } - return logicalRight; +LayoutUnit RenderParagraph::logicalRightSelectionOffset(RenderBlock* rootBlock, + LayoutUnit position) { + LayoutUnit logicalRight = logicalRightOffsetForLine(false); + if (logicalRight == logicalRightOffsetForContent()) + return RenderBlock::logicalRightSelectionOffset(rootBlock, position); + + RenderBlock* cb = this; + while (cb != rootBlock) { + logicalRight += cb->logicalLeft(); + cb = cb->containingBlock(); + } + return logicalRight; } -RootInlineBox* RenderParagraph::lineAtIndex(int i) const -{ - ASSERT(i >= 0); +RootInlineBox* RenderParagraph::lineAtIndex(int i) const { + ASSERT(i >= 0); - for (RootInlineBox* box = firstRootBox(); box; box = box->nextRootBox()) { - if (!i--) - return box; - } + for (RootInlineBox* box = firstRootBox(); box; box = box->nextRootBox()) { + if (!i--) + return box; + } - return 0; + return 0; } -int RenderParagraph::lineCount(const RootInlineBox* stopRootInlineBox, bool* found) const -{ - int count = 0; - for (RootInlineBox* box = firstRootBox(); box; box = box->nextRootBox()) { - count++; - if (box == stopRootInlineBox) { - if (found) - *found = true; - break; - } +int RenderParagraph::lineCount(const RootInlineBox* stopRootInlineBox, + bool* found) const { + int count = 0; + for (RootInlineBox* box = firstRootBox(); box; box = box->nextRootBox()) { + count++; + if (box == stopRootInlineBox) { + if (found) + *found = true; + break; } + } - return count; + return count; } -void RenderParagraph::deleteLineBoxTree() -{ - m_lineBoxes.deleteLineBoxTree(); +void RenderParagraph::deleteLineBoxTree() { + m_lineBoxes.deleteLineBoxTree(); } -GapRects RenderParagraph::inlineSelectionGaps(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, - LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const PaintInfo* paintInfo) -{ - GapRects result; - - bool containsStart = selectionState() == SelectionStart || selectionState() == SelectionBoth; - - if (!firstLineBox()) { - if (containsStart) { - // Go ahead and update our lastLogicalTop to be the bottom of the block.
s or empty blocks with height can trip this - // case. - lastLogicalTop = rootBlock->blockDirectionOffset(offsetFromRootBlock) + logicalHeight(); - lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, logicalHeight()); - lastLogicalRight = logicalRightSelectionOffset(rootBlock, logicalHeight()); - } - return result; - } - - RootInlineBox* lastSelectedLine = 0; - RootInlineBox* curr; - for (curr = firstRootBox(); curr && !curr->hasSelectedChildren(); curr = curr->nextRootBox()) { } - - // Now paint the gaps for the lines. - for (; curr && curr->hasSelectedChildren(); curr = curr->nextRootBox()) { - LayoutUnit selTop = curr->selectionTopAdjustedForPrecedingBlock(); - LayoutUnit selHeight = curr->selectionHeightAdjustedForPrecedingBlock(); - - if (!containsStart && !lastSelectedLine && selectionState() != SelectionStart && selectionState() != SelectionBoth) { - result.uniteCenter(blockSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, - lastLogicalLeft, lastLogicalRight, selTop, paintInfo)); - } - - LayoutRect logicalRect(curr->logicalLeft(), selTop, curr->logicalWidth(), selTop + selHeight); - logicalRect.move(offsetFromRootBlock); - LayoutRect physicalRect = rootBlock->logicalRectToPhysicalRect(rootBlockPhysicalPosition, logicalRect); - if (!paintInfo || (physicalRect.y() < paintInfo->rect.maxY() && physicalRect.maxY() > paintInfo->rect.y())) - result.unite(curr->lineSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, selTop, selHeight, paintInfo)); - - lastSelectedLine = curr; +GapRects RenderParagraph::inlineSelectionGaps( + RenderBlock* rootBlock, + const LayoutPoint& rootBlockPhysicalPosition, + const LayoutSize& offsetFromRootBlock, + LayoutUnit& lastLogicalTop, + LayoutUnit& lastLogicalLeft, + LayoutUnit& lastLogicalRight, + const PaintInfo* paintInfo) { + GapRects result; + + bool containsStart = + selectionState() == SelectionStart || selectionState() == SelectionBoth; + + if (!firstLineBox()) { + if (containsStart) { + // Go ahead and update our lastLogicalTop to be the bottom of the block. + //
s or empty blocks with height can trip this case. + lastLogicalTop = rootBlock->blockDirectionOffset(offsetFromRootBlock) + + logicalHeight(); + lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, logicalHeight()); + lastLogicalRight = + logicalRightSelectionOffset(rootBlock, logicalHeight()); } - - if (containsStart && !lastSelectedLine) { - // VisibleSelection must start just after our last line. - lastSelectedLine = lastRootBox(); + return result; + } + + RootInlineBox* lastSelectedLine = 0; + RootInlineBox* curr; + for (curr = firstRootBox(); curr && !curr->hasSelectedChildren(); + curr = curr->nextRootBox()) { + } + + // Now paint the gaps for the lines. + for (; curr && curr->hasSelectedChildren(); curr = curr->nextRootBox()) { + LayoutUnit selTop = curr->selectionTopAdjustedForPrecedingBlock(); + LayoutUnit selHeight = curr->selectionHeightAdjustedForPrecedingBlock(); + + if (!containsStart && !lastSelectedLine && + selectionState() != SelectionStart && + selectionState() != SelectionBoth) { + result.uniteCenter(blockSelectionGap(rootBlock, rootBlockPhysicalPosition, + offsetFromRootBlock, lastLogicalTop, + lastLogicalLeft, lastLogicalRight, + selTop, paintInfo)); } - if (lastSelectedLine && selectionState() != SelectionEnd && selectionState() != SelectionBoth) { - // Go ahead and update our lastY to be the bottom of the last selected line. - lastLogicalTop = rootBlock->blockDirectionOffset(offsetFromRootBlock) + lastSelectedLine->selectionBottom(); - lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, lastSelectedLine->selectionBottom()); - lastLogicalRight = logicalRightSelectionOffset(rootBlock, lastSelectedLine->selectionBottom()); - } - return result; + LayoutRect logicalRect(curr->logicalLeft(), selTop, curr->logicalWidth(), + selTop + selHeight); + logicalRect.move(offsetFromRootBlock); + LayoutRect physicalRect = rootBlock->logicalRectToPhysicalRect( + rootBlockPhysicalPosition, logicalRect); + if (!paintInfo || (physicalRect.y() < paintInfo->rect.maxY() && + physicalRect.maxY() > paintInfo->rect.y())) + result.unite(curr->lineSelectionGap(rootBlock, rootBlockPhysicalPosition, + offsetFromRootBlock, selTop, + selHeight, paintInfo)); + + lastSelectedLine = curr; + } + + if (containsStart && !lastSelectedLine) { + // VisibleSelection must start just after our last line. + lastSelectedLine = lastRootBox(); + } + + if (lastSelectedLine && selectionState() != SelectionEnd && + selectionState() != SelectionBoth) { + // Go ahead and update our lastY to be the bottom of the last selected line. + lastLogicalTop = rootBlock->blockDirectionOffset(offsetFromRootBlock) + + lastSelectedLine->selectionBottom(); + lastLogicalLeft = logicalLeftSelectionOffset( + rootBlock, lastSelectedLine->selectionBottom()); + lastLogicalRight = logicalRightSelectionOffset( + rootBlock, lastSelectedLine->selectionBottom()); + } + return result; } -void RenderParagraph::addOverflowFromChildren() -{ - LayoutUnit endPadding = hasOverflowClip() ? paddingEnd() : LayoutUnit(); - for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) { - addLayoutOverflow(curr->paddedLayoutOverflowRect(endPadding)); - LayoutRect visualOverflow = curr->visualOverflowRect(curr->lineTop(), curr->lineBottom()); - addContentsVisualOverflow(visualOverflow); - } +void RenderParagraph::addOverflowFromChildren() { + LayoutUnit endPadding = hasOverflowClip() ? paddingEnd() : LayoutUnit(); + for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) { + addLayoutOverflow(curr->paddedLayoutOverflowRect(endPadding)); + LayoutRect visualOverflow = + curr->visualOverflowRect(curr->lineTop(), curr->lineBottom()); + addContentsVisualOverflow(visualOverflow); + } } -void RenderParagraph::simplifiedNormalFlowLayout() -{ - ListHashSet lineBoxes; - for (InlineWalker walker(this); !walker.atEnd(); walker.advance()) { - RenderObject* o = walker.current(); - if (!o->isOutOfFlowPositioned() && o->isReplaced()) { - o->layoutIfNeeded(); - if (toRenderBox(o)->inlineBoxWrapper()) { - RootInlineBox& box = toRenderBox(o)->inlineBoxWrapper()->root(); - lineBoxes.add(&box); - } - } else if (o->isText() || (o->isRenderInline() && !walker.atEndOfInline())) { - o->clearNeedsLayout(); - } - } - - // FIXME: Glyph overflow will get lost in this case, but not really a big deal. - GlyphOverflowAndFallbackFontsMap textBoxDataMap; - for (ListHashSet::const_iterator it = lineBoxes.begin(); it != lineBoxes.end(); ++it) { - RootInlineBox* box = *it; - box->computeOverflow(box->lineTop(), box->lineBottom(), textBoxDataMap); +void RenderParagraph::simplifiedNormalFlowLayout() { + ListHashSet lineBoxes; + for (InlineWalker walker(this); !walker.atEnd(); walker.advance()) { + RenderObject* o = walker.current(); + if (!o->isOutOfFlowPositioned() && o->isReplaced()) { + o->layoutIfNeeded(); + if (toRenderBox(o)->inlineBoxWrapper()) { + RootInlineBox& box = toRenderBox(o)->inlineBoxWrapper()->root(); + lineBoxes.add(&box); } + } else if (o->isText() || + (o->isRenderInline() && !walker.atEndOfInline())) { + o->clearNeedsLayout(); + } + } + + // FIXME: Glyph overflow will get lost in this case, but not really a big + // deal. + GlyphOverflowAndFallbackFontsMap textBoxDataMap; + for (ListHashSet::const_iterator it = lineBoxes.begin(); + it != lineBoxes.end(); ++it) { + RootInlineBox* box = *it; + box->computeOverflow(box->lineTop(), box->lineBottom(), textBoxDataMap); + } } -void RenderParagraph::paintChildren(PaintInfo& paintInfo, const LayoutPoint& paintOffset, Vector& layers) -{ - m_lineBoxes.paint(this, paintInfo, paintOffset, layers); - - for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { - // TODO(ojan): This is wrong at the moment. Inlines can have self painting - // layers as well. Either make inlines with self-painting layers work or - // don't allow inlines to be self painting. - if (child->isBox()) { - RenderBox* box = toRenderBox(child); - if (box->hasSelfPaintingLayer()) - layers.append(box); - } +void RenderParagraph::paintChildren(PaintInfo& paintInfo, + const LayoutPoint& paintOffset, + Vector& layers) { + m_lineBoxes.paint(this, paintInfo, paintOffset, layers); + + for (RenderObject* child = firstChild(); child; + child = child->nextSibling()) { + // TODO(ojan): This is wrong at the moment. Inlines can have self painting + // layers as well. Either make inlines with self-painting layers work or + // don't allow inlines to be self painting. + if (child->isBox()) { + RenderBox* box = toRenderBox(child); + if (box->hasSelfPaintingLayer()) + layers.append(box); } + } } -bool RenderParagraph::hitTestContents(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset) -{ - return m_lineBoxes.hitTest(this, request, result, locationInContainer, accumulatedOffset); +bool RenderParagraph::hitTestContents( + const HitTestRequest& request, + HitTestResult& result, + const HitTestLocation& locationInContainer, + const LayoutPoint& accumulatedOffset) { + return m_lineBoxes.hitTest(this, request, result, locationInContainer, + accumulatedOffset); } -void RenderParagraph::markLinesDirtyInBlockRange(LayoutUnit logicalTop, LayoutUnit logicalBottom, RootInlineBox* highest) -{ - if (logicalTop >= logicalBottom) - return; - - RootInlineBox* lowestDirtyLine = lastRootBox(); - RootInlineBox* afterLowest = lowestDirtyLine; - while (lowestDirtyLine && lowestDirtyLine->lineBottomWithLeading() >= logicalBottom && logicalBottom < LayoutUnit::max()) { - afterLowest = lowestDirtyLine; - lowestDirtyLine = lowestDirtyLine->prevRootBox(); - } - - while (afterLowest && afterLowest != highest && (afterLowest->lineBottomWithLeading() >= logicalTop || afterLowest->lineBottomWithLeading() < 0)) { - afterLowest->markDirty(); - afterLowest = afterLowest->prevRootBox(); - } +void RenderParagraph::markLinesDirtyInBlockRange(LayoutUnit logicalTop, + LayoutUnit logicalBottom, + RootInlineBox* highest) { + if (logicalTop >= logicalBottom) + return; + + RootInlineBox* lowestDirtyLine = lastRootBox(); + RootInlineBox* afterLowest = lowestDirtyLine; + while (lowestDirtyLine && + lowestDirtyLine->lineBottomWithLeading() >= logicalBottom && + logicalBottom < LayoutUnit::max()) { + afterLowest = lowestDirtyLine; + lowestDirtyLine = lowestDirtyLine->prevRootBox(); + } + + while (afterLowest && afterLowest != highest && + (afterLowest->lineBottomWithLeading() >= logicalTop || + afterLowest->lineBottomWithLeading() < 0)) { + afterLowest->markDirty(); + afterLowest = afterLowest->prevRootBox(); + } } -static void updateLogicalWidthForLeftAlignedBlock(bool isLeftToRightDirection, BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float availableLogicalWidth) -{ - // The direction of the block should determine what happens with wide lines. - // In particular with RTL blocks, wide lines should still spill out to the left. - if (isLeftToRightDirection) { - if (totalLogicalWidth > availableLogicalWidth && trailingSpaceRun) - trailingSpaceRun->m_box->setLogicalWidth(std::max(0, trailingSpaceRun->m_box->logicalWidth() - totalLogicalWidth + availableLogicalWidth)); - return; - } - - if (trailingSpaceRun) - trailingSpaceRun->m_box->setLogicalWidth(0); - else if (totalLogicalWidth > availableLogicalWidth) - logicalLeft -= (totalLogicalWidth - availableLogicalWidth); +static void updateLogicalWidthForLeftAlignedBlock(bool isLeftToRightDirection, + BidiRun* trailingSpaceRun, + float& logicalLeft, + float& totalLogicalWidth, + float availableLogicalWidth) { + // The direction of the block should determine what happens with wide lines. + // In particular with RTL blocks, wide lines should still spill out to the + // left. + if (isLeftToRightDirection) { + if (totalLogicalWidth > availableLogicalWidth && trailingSpaceRun) + trailingSpaceRun->m_box->setLogicalWidth( + std::max(0, trailingSpaceRun->m_box->logicalWidth() - + totalLogicalWidth + availableLogicalWidth)); + return; + } + + if (trailingSpaceRun) + trailingSpaceRun->m_box->setLogicalWidth(0); + else if (totalLogicalWidth > availableLogicalWidth) + logicalLeft -= (totalLogicalWidth - availableLogicalWidth); } -static void updateLogicalWidthForRightAlignedBlock(bool isLeftToRightDirection, BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float availableLogicalWidth) -{ - // Wide lines spill out of the block based off direction. - // So even if text-align is right, if direction is LTR, wide lines should overflow out of the right - // side of the block. - if (isLeftToRightDirection) { - if (trailingSpaceRun) { - totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth(); - trailingSpaceRun->m_box->setLogicalWidth(0); - } - if (totalLogicalWidth < availableLogicalWidth) - logicalLeft += availableLogicalWidth - totalLogicalWidth; - return; +static void updateLogicalWidthForRightAlignedBlock( + bool isLeftToRightDirection, + BidiRun* trailingSpaceRun, + float& logicalLeft, + float& totalLogicalWidth, + float availableLogicalWidth) { + // Wide lines spill out of the block based off direction. + // So even if text-align is right, if direction is LTR, wide lines should + // overflow out of the right side of the block. + if (isLeftToRightDirection) { + if (trailingSpaceRun) { + totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth(); + trailingSpaceRun->m_box->setLogicalWidth(0); } - - if (totalLogicalWidth > availableLogicalWidth && trailingSpaceRun) { - trailingSpaceRun->m_box->setLogicalWidth(std::max(0, trailingSpaceRun->m_box->logicalWidth() - totalLogicalWidth + availableLogicalWidth)); - totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth(); - } else - logicalLeft += availableLogicalWidth - totalLogicalWidth; + if (totalLogicalWidth < availableLogicalWidth) + logicalLeft += availableLogicalWidth - totalLogicalWidth; + return; + } + + if (totalLogicalWidth > availableLogicalWidth && trailingSpaceRun) { + trailingSpaceRun->m_box->setLogicalWidth( + std::max(0, trailingSpaceRun->m_box->logicalWidth() - + totalLogicalWidth + availableLogicalWidth)); + totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth(); + } else + logicalLeft += availableLogicalWidth - totalLogicalWidth; } -static void updateLogicalWidthForCenterAlignedBlock(bool isLeftToRightDirection, BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float availableLogicalWidth) -{ - float trailingSpaceWidth = 0; - if (trailingSpaceRun) { - totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth(); - trailingSpaceWidth = std::min(trailingSpaceRun->m_box->logicalWidth(), (availableLogicalWidth - totalLogicalWidth + 1) / 2); - trailingSpaceRun->m_box->setLogicalWidth(std::max(0, trailingSpaceWidth)); - } - if (isLeftToRightDirection) - logicalLeft += std::max((availableLogicalWidth - totalLogicalWidth) / 2, 0); - else - logicalLeft += totalLogicalWidth > availableLogicalWidth ? (availableLogicalWidth - totalLogicalWidth) : (availableLogicalWidth - totalLogicalWidth) / 2 - trailingSpaceWidth; +static void updateLogicalWidthForCenterAlignedBlock( + bool isLeftToRightDirection, + BidiRun* trailingSpaceRun, + float& logicalLeft, + float& totalLogicalWidth, + float availableLogicalWidth) { + float trailingSpaceWidth = 0; + if (trailingSpaceRun) { + totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth(); + trailingSpaceWidth = + std::min(trailingSpaceRun->m_box->logicalWidth(), + (availableLogicalWidth - totalLogicalWidth + 1) / 2); + trailingSpaceRun->m_box->setLogicalWidth( + std::max(0, trailingSpaceWidth)); + } + if (isLeftToRightDirection) + logicalLeft += + std::max((availableLogicalWidth - totalLogicalWidth) / 2, 0); + else + logicalLeft += totalLogicalWidth > availableLogicalWidth + ? (availableLogicalWidth - totalLogicalWidth) + : (availableLogicalWidth - totalLogicalWidth) / 2 - + trailingSpaceWidth; } -void RenderParagraph::updateLogicalWidthForAlignment(const ETextAlign& textAlign, const RootInlineBox* rootInlineBox, BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float& availableLogicalWidth, unsigned expansionOpportunityCount) -{ - TextDirection direction; - if (rootInlineBox && rootInlineBox->renderer().style()->unicodeBidi() == Plaintext) - direction = rootInlineBox->direction(); - else - direction = style()->direction(); +void RenderParagraph::updateLogicalWidthForAlignment( + const ETextAlign& textAlign, + const RootInlineBox* rootInlineBox, + BidiRun* trailingSpaceRun, + float& logicalLeft, + float& totalLogicalWidth, + float& availableLogicalWidth, + unsigned expansionOpportunityCount) { + TextDirection direction; + if (rootInlineBox && + rootInlineBox->renderer().style()->unicodeBidi() == Plaintext) + direction = rootInlineBox->direction(); + else + direction = style()->direction(); - // Armed with the total width of the line (without justification), - // we now examine our text-align property in order to determine where to position the - // objects horizontally. The total width of the line can be increased if we end up - // justifying text. - switch (textAlign) { + // Armed with the total width of the line (without justification), + // we now examine our text-align property in order to determine where to + // position the objects horizontally. The total width of the line can be + // increased if we end up justifying text. + switch (textAlign) { case LEFT: - updateLogicalWidthForLeftAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth); - break; + updateLogicalWidthForLeftAlignedBlock( + style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, + totalLogicalWidth, availableLogicalWidth); + break; case RIGHT: - updateLogicalWidthForRightAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth); - break; + updateLogicalWidthForRightAlignedBlock( + style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, + totalLogicalWidth, availableLogicalWidth); + break; case CENTER: - updateLogicalWidthForCenterAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth); - break; + updateLogicalWidthForCenterAlignedBlock( + style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, + totalLogicalWidth, availableLogicalWidth); + break; case JUSTIFY: - adjustInlineDirectionLineBounds(expansionOpportunityCount, logicalLeft, availableLogicalWidth); - if (expansionOpportunityCount) { - if (trailingSpaceRun) { - totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth(); - trailingSpaceRun->m_box->setLogicalWidth(0); - } - break; + adjustInlineDirectionLineBounds(expansionOpportunityCount, logicalLeft, + availableLogicalWidth); + if (expansionOpportunityCount) { + if (trailingSpaceRun) { + totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth(); + trailingSpaceRun->m_box->setLogicalWidth(0); } - // Fall through - case TASTART: - if (direction == LTR) - updateLogicalWidthForLeftAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth); - else - updateLogicalWidthForRightAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth); break; + } + // Fall through + case TASTART: + if (direction == LTR) + updateLogicalWidthForLeftAlignedBlock( + style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, + totalLogicalWidth, availableLogicalWidth); + else + updateLogicalWidthForRightAlignedBlock( + style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, + totalLogicalWidth, availableLogicalWidth); + break; case TAEND: - if (direction == LTR) - updateLogicalWidthForRightAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth); - else - updateLogicalWidthForLeftAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth); - break; - } + if (direction == LTR) + updateLogicalWidthForRightAlignedBlock( + style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, + totalLogicalWidth, availableLogicalWidth); + else + updateLogicalWidthForLeftAlignedBlock( + style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, + totalLogicalWidth, availableLogicalWidth); + break; + } } -RootInlineBox* RenderParagraph::createAndAppendRootInlineBox() -{ - RootInlineBox* rootBox = createRootInlineBox(); - m_lineBoxes.appendLineBox(rootBox); - return rootBox; +RootInlineBox* RenderParagraph::createAndAppendRootInlineBox() { + RootInlineBox* rootBox = createRootInlineBox(); + m_lineBoxes.appendLineBox(rootBox); + return rootBox; } -RootInlineBox* RenderParagraph::createRootInlineBox() -{ - return new RootInlineBox(*this); +RootInlineBox* RenderParagraph::createRootInlineBox() { + return new RootInlineBox(*this); } -InlineBox* RenderParagraph::createInlineBoxForRenderer(RenderObject* obj, bool isRootLineBox, bool isOnlyRun) -{ - if (isRootLineBox) - return toRenderParagraph(obj)->createAndAppendRootInlineBox(); +InlineBox* RenderParagraph::createInlineBoxForRenderer(RenderObject* obj, + bool isRootLineBox, + bool isOnlyRun) { + if (isRootLineBox) + return toRenderParagraph(obj)->createAndAppendRootInlineBox(); - if (obj->isText()) { - InlineTextBox* textBox = toRenderText(obj)->createInlineTextBox(); - // We only treat a box as text for a
if we are on a line by ourself or in strict mode - // (Note the use of strict mode. In "almost strict" mode, we don't treat the box for
as text.) - return textBox; - } + if (obj->isText()) { + InlineTextBox* textBox = toRenderText(obj)->createInlineTextBox(); + // We only treat a box as text for a
if we are on a line by ourself or + // in strict mode (Note the use of strict mode. In "almost strict" mode, we + // don't treat the box for
as text.) + return textBox; + } - if (obj->isBox()) - return toRenderBox(obj)->createInlineBox(); + if (obj->isBox()) + return toRenderBox(obj)->createInlineBox(); - return toRenderInline(obj)->createAndAppendInlineFlowBox(); + return toRenderInline(obj)->createAndAppendInlineFlowBox(); } -static inline void dirtyLineBoxesForRenderer(RenderObject* o, bool fullLayout) -{ - if (o->isText()) { - RenderText* renderText = toRenderText(o); - renderText->dirtyLineBoxes(fullLayout); - } else - toRenderInline(o)->dirtyLineBoxes(fullLayout); +static inline void dirtyLineBoxesForRenderer(RenderObject* o, bool fullLayout) { + if (o->isText()) { + RenderText* renderText = toRenderText(o); + renderText->dirtyLineBoxes(fullLayout); + } else + toRenderInline(o)->dirtyLineBoxes(fullLayout); } -static bool parentIsConstructedOrHaveNext(InlineFlowBox* parentBox) -{ - do { - if (parentBox->isConstructed() || parentBox->nextOnLine()) - return true; - parentBox = parentBox->parent(); - } while (parentBox); - return false; +static bool parentIsConstructedOrHaveNext(InlineFlowBox* parentBox) { + do { + if (parentBox->isConstructed() || parentBox->nextOnLine()) + return true; + parentBox = parentBox->parent(); + } while (parentBox); + return false; } -InlineFlowBox* RenderParagraph::createLineBoxes(RenderObject* obj, const LineInfo& lineInfo, InlineBox* childBox) -{ - // See if we have an unconstructed line box for this object that is also - // the last item on the line. - unsigned lineDepth = 1; - InlineFlowBox* parentBox = 0; - InlineFlowBox* result = 0; - bool hasDefaultLineBoxContain = style()->lineBoxContain() == RenderStyle::initialLineBoxContain(); - do { - ASSERT_WITH_SECURITY_IMPLICATION(obj->isRenderInline() || obj == this); - - RenderInline* inlineFlow = (obj != this) ? toRenderInline(obj) : 0; - - // Get the last box we made for this render object. - parentBox = inlineFlow ? inlineFlow->lastLineBox() : toRenderBlock(obj)->lastLineBox(); - - // If this box or its ancestor is constructed then it is from a previous line, and we need - // to make a new box for our line. If this box or its ancestor is unconstructed but it has - // something following it on the line, then we know we have to make a new box - // as well. In this situation our inline has actually been split in two on - // the same line (this can happen with very fancy language mixtures). - bool constructedNewBox = false; - bool allowedToConstructNewBox = !hasDefaultLineBoxContain || !inlineFlow || inlineFlow->alwaysCreateLineBoxes(); - bool canUseExistingParentBox = parentBox && !parentIsConstructedOrHaveNext(parentBox); - if (allowedToConstructNewBox && !canUseExistingParentBox) { - // We need to make a new box for this render object. Once - // made, we need to place it at the end of the current line. - InlineBox* newBox = createInlineBoxForRenderer(obj, obj == this); - ASSERT_WITH_SECURITY_IMPLICATION(newBox->isInlineFlowBox()); - parentBox = toInlineFlowBox(newBox); - parentBox->setFirstLineStyleBit(lineInfo.isFirstLine()); - if (!hasDefaultLineBoxContain) - parentBox->clearDescendantsHaveSameLineHeightAndBaseline(); - constructedNewBox = true; - } +InlineFlowBox* RenderParagraph::createLineBoxes(RenderObject* obj, + const LineInfo& lineInfo, + InlineBox* childBox) { + // See if we have an unconstructed line box for this object that is also + // the last item on the line. + unsigned lineDepth = 1; + InlineFlowBox* parentBox = 0; + InlineFlowBox* result = 0; + bool hasDefaultLineBoxContain = + style()->lineBoxContain() == RenderStyle::initialLineBoxContain(); + do { + ASSERT_WITH_SECURITY_IMPLICATION(obj->isRenderInline() || obj == this); + + RenderInline* inlineFlow = (obj != this) ? toRenderInline(obj) : 0; + + // Get the last box we made for this render object. + parentBox = inlineFlow ? inlineFlow->lastLineBox() + : toRenderBlock(obj)->lastLineBox(); + + // If this box or its ancestor is constructed then it is from a previous + // line, and we need to make a new box for our line. If this box or its + // ancestor is unconstructed but it has something following it on the line, + // then we know we have to make a new box as well. In this situation our + // inline has actually been split in two on the same line (this can happen + // with very fancy language mixtures). + bool constructedNewBox = false; + bool allowedToConstructNewBox = !hasDefaultLineBoxContain || !inlineFlow || + inlineFlow->alwaysCreateLineBoxes(); + bool canUseExistingParentBox = + parentBox && !parentIsConstructedOrHaveNext(parentBox); + if (allowedToConstructNewBox && !canUseExistingParentBox) { + // We need to make a new box for this render object. Once + // made, we need to place it at the end of the current line. + InlineBox* newBox = createInlineBoxForRenderer(obj, obj == this); + ASSERT_WITH_SECURITY_IMPLICATION(newBox->isInlineFlowBox()); + parentBox = toInlineFlowBox(newBox); + parentBox->setFirstLineStyleBit(lineInfo.isFirstLine()); + if (!hasDefaultLineBoxContain) + parentBox->clearDescendantsHaveSameLineHeightAndBaseline(); + constructedNewBox = true; + } - if (constructedNewBox || canUseExistingParentBox) { - if (!result) - result = parentBox; + if (constructedNewBox || canUseExistingParentBox) { + if (!result) + result = parentBox; - // If we have hit the block itself, then |box| represents the root - // inline box for the line, and it doesn't have to be appended to any parent - // inline. - if (childBox) - parentBox->addToLine(childBox); + // If we have hit the block itself, then |box| represents the root + // inline box for the line, and it doesn't have to be appended to any + // parent inline. + if (childBox) + parentBox->addToLine(childBox); - if (!constructedNewBox || obj == this) - break; + if (!constructedNewBox || obj == this) + break; - childBox = parentBox; - } + childBox = parentBox; + } - // If we've exceeded our line depth, then jump straight to the root and skip all the remaining - // intermediate inline flows. - obj = (++lineDepth >= cMaxLineDepth) ? this : obj->parent(); + // If we've exceeded our line depth, then jump straight to the root and skip + // all the remaining intermediate inline flows. + obj = (++lineDepth >= cMaxLineDepth) ? this : obj->parent(); - } while (true); + } while (true); - return result; + return result; } template -static inline bool endsWithASCIISpaces(const CharacterType* characters, unsigned pos, unsigned end) -{ - while (isASCIISpace(characters[pos])) { - pos++; - if (pos >= end) - return true; - } - return false; +static inline bool endsWithASCIISpaces(const CharacterType* characters, + unsigned pos, + unsigned end) { + while (isASCIISpace(characters[pos])) { + pos++; + if (pos >= end) + return true; + } + return false; } -static bool reachedEndOfTextRenderer(const BidiRunList& bidiRuns) -{ - BidiRun* run = bidiRuns.logicallyLastRun(); - if (!run) - return true; - unsigned pos = run->stop(); - RenderObject* r = run->m_object; - if (!r->isText()) - return false; - RenderText* renderText = toRenderText(r); - unsigned length = renderText->textLength(); - if (pos >= length) - return true; - - if (renderText->is8Bit()) - return endsWithASCIISpaces(renderText->characters8(), pos, length); - return endsWithASCIISpaces(renderText->characters16(), pos, length); -} +static bool reachedEndOfTextRenderer(const BidiRunList& bidiRuns) { + BidiRun* run = bidiRuns.logicallyLastRun(); + if (!run) + return true; + unsigned pos = run->stop(); + RenderObject* r = run->m_object; + if (!r->isText()) + return false; + RenderText* renderText = toRenderText(r); + unsigned length = renderText->textLength(); + if (pos >= length) + return true; -RootInlineBox* RenderParagraph::constructLine(BidiRunList& bidiRuns, const LineInfo& lineInfo) -{ - ASSERT(bidiRuns.firstRun()); - - bool rootHasSelectedChildren = false; - InlineFlowBox* parentBox = 0; - int runCount = bidiRuns.runCount() - lineInfo.runsFromLeadingWhitespace(); - for (BidiRun* r = bidiRuns.firstRun(); r; r = r->next()) { - // Create a box for our object. - bool isOnlyRun = (runCount == 1); - if (runCount == 2) - isOnlyRun = false; - - if (lineInfo.isEmpty()) - continue; - - InlineBox* box = createInlineBoxForRenderer(r->m_object, false, isOnlyRun); - r->m_box = box; - - ASSERT(box); - if (!box) - continue; - - if (!rootHasSelectedChildren && box->renderer().selectionState() != RenderObject::SelectionNone) - rootHasSelectedChildren = true; - - // If we have no parent box yet, or if the run is not simply a sibling, - // then we need to construct inline boxes as necessary to properly enclose the - // run's inline box. Segments can only be siblings at the root level, as - // they are positioned separately. - if (!parentBox || parentBox->renderer() != r->m_object->parent()) { - // Create new inline boxes all the way back to the appropriate insertion point. - parentBox = createLineBoxes(r->m_object->parent(), lineInfo, box); - } else { - // Append the inline box to this line. - parentBox->addToLine(box); - } + if (renderText->is8Bit()) + return endsWithASCIISpaces(renderText->characters8(), pos, length); + return endsWithASCIISpaces(renderText->characters16(), pos, length); +} - box->setBidiLevel(r->level()); - - if (box->isInlineTextBox()) { - InlineTextBox* text = toInlineTextBox(box); - text->setStart(r->m_start); - text->setLen(r->m_stop - r->m_start); - text->setDirOverride(r->dirOverride()); - if (r->m_hasHyphen) - text->setHasHyphen(true); - if (r->m_hasAddedEllipsis) - text->setHasAddedEllipsis(true); - } +RootInlineBox* RenderParagraph::constructLine(BidiRunList& bidiRuns, + const LineInfo& lineInfo) { + ASSERT(bidiRuns.firstRun()); + + bool rootHasSelectedChildren = false; + InlineFlowBox* parentBox = 0; + int runCount = bidiRuns.runCount() - lineInfo.runsFromLeadingWhitespace(); + for (BidiRun* r = bidiRuns.firstRun(); r; r = r->next()) { + // Create a box for our object. + bool isOnlyRun = (runCount == 1); + if (runCount == 2) + isOnlyRun = false; + + if (lineInfo.isEmpty()) + continue; + + InlineBox* box = createInlineBoxForRenderer(r->m_object, false, isOnlyRun); + r->m_box = box; + + ASSERT(box); + if (!box) + continue; + + if (!rootHasSelectedChildren && + box->renderer().selectionState() != RenderObject::SelectionNone) + rootHasSelectedChildren = true; + + // If we have no parent box yet, or if the run is not simply a sibling, + // then we need to construct inline boxes as necessary to properly enclose + // the run's inline box. Segments can only be siblings at the root level, as + // they are positioned separately. + if (!parentBox || parentBox->renderer() != r->m_object->parent()) { + // Create new inline boxes all the way back to the appropriate insertion + // point. + parentBox = createLineBoxes(r->m_object->parent(), lineInfo, box); + } else { + // Append the inline box to this line. + parentBox->addToLine(box); } - ASSERT(lastLineBox() && !lastLineBox()->isConstructed()); - - // Set the m_selectedChildren flag on the root inline box if one of the leaf inline box - // from the bidi runs walk above has a selection state. - if (rootHasSelectedChildren) - lastLineBox()->root().setHasSelectedChildren(true); - - // Set bits on our inline flow boxes that indicate which sides should - // paint borders/margins/padding. This knowledge will ultimately be used when - // we determine the horizontal positions and widths of all the inline boxes on - // the line. - bool isLogicallyLastRunWrapped = bidiRuns.logicallyLastRun()->m_object && bidiRuns.logicallyLastRun()->m_object->isText() ? !reachedEndOfTextRenderer(bidiRuns) : true; - lastLineBox()->determineSpacingForFlowBoxes(lineInfo.isLastLine(), isLogicallyLastRunWrapped, bidiRuns.logicallyLastRun()->m_object); - - // Now mark the line boxes as being constructed. - lastLineBox()->setConstructed(); - - // Return the last line. - return lastRootBox(); + box->setBidiLevel(r->level()); + + if (box->isInlineTextBox()) { + InlineTextBox* text = toInlineTextBox(box); + text->setStart(r->m_start); + text->setLen(r->m_stop - r->m_start); + text->setDirOverride(r->dirOverride()); + if (r->m_hasHyphen) + text->setHasHyphen(true); + if (r->m_hasAddedEllipsis) + text->setHasAddedEllipsis(true); + } + } + + ASSERT(lastLineBox() && !lastLineBox()->isConstructed()); + + // Set the m_selectedChildren flag on the root inline box if one of the leaf + // inline box from the bidi runs walk above has a selection state. + if (rootHasSelectedChildren) + lastLineBox()->root().setHasSelectedChildren(true); + + // Set bits on our inline flow boxes that indicate which sides should + // paint borders/margins/padding. This knowledge will ultimately be used when + // we determine the horizontal positions and widths of all the inline boxes on + // the line. + bool isLogicallyLastRunWrapped = + bidiRuns.logicallyLastRun()->m_object && + bidiRuns.logicallyLastRun()->m_object->isText() + ? !reachedEndOfTextRenderer(bidiRuns) + : true; + lastLineBox()->determineSpacingForFlowBoxes( + lineInfo.isLastLine(), isLogicallyLastRunWrapped, + bidiRuns.logicallyLastRun()->m_object); + + // Now mark the line boxes as being constructed. + lastLineBox()->setConstructed(); + + // Return the last line. + return lastRootBox(); } -ETextAlign RenderParagraph::textAlignmentForLine(bool endsWithSoftBreak) const -{ - ETextAlign alignment = style()->textAlign(); - if (endsWithSoftBreak) - return alignment; - return (alignment == JUSTIFY) ? TASTART : alignment; +ETextAlign RenderParagraph::textAlignmentForLine(bool endsWithSoftBreak) const { + ETextAlign alignment = style()->textAlign(); + if (endsWithSoftBreak) + return alignment; + return (alignment == JUSTIFY) ? TASTART : alignment; } -static inline void setLogicalWidthForTextRun(RootInlineBox* lineBox, BidiRun* run, RenderText* renderer, float xPos, const LineInfo& lineInfo, - GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache, WordMeasurements& wordMeasurements) -{ - HashSet fallbackFonts; - GlyphOverflow glyphOverflow; - +static inline void setLogicalWidthForTextRun( + RootInlineBox* lineBox, + BidiRun* run, + RenderText* renderer, + float xPos, + const LineInfo& lineInfo, + GlyphOverflowAndFallbackFontsMap& textBoxDataMap, + VerticalPositionCache& verticalPositionCache, + WordMeasurements& wordMeasurements) { + HashSet fallbackFonts; + GlyphOverflow glyphOverflow; + + const Font& font = renderer->style(lineInfo.isFirstLine())->font(); + // Always compute glyph overflow if the block's line-box-contain value is + // "glyphs". + if (lineBox->fitsToGlyphs()) { + // If we don't stick out of the root line's font box, then don't bother + // computing our glyph overflow. This optimization will keep us from + // computing glyph bounds in nearly all cases. + bool includeRootLine = lineBox->includesRootLineBoxFontOrLeading(); + int baselineShift = + lineBox->verticalPositionForBox(run->m_box, verticalPositionCache); + int rootDescent = includeRootLine ? font.fontMetrics().descent() : 0; + int rootAscent = includeRootLine ? font.fontMetrics().ascent() : 0; + int boxAscent = font.fontMetrics().ascent() - baselineShift; + int boxDescent = font.fontMetrics().descent() + baselineShift; + if (boxAscent > rootDescent || boxDescent > rootAscent) + glyphOverflow.computeBounds = true; + } + + LayoutUnit hyphenWidth = 0; + if (toInlineTextBox(run->m_box)->hasHyphen()) { const Font& font = renderer->style(lineInfo.isFirstLine())->font(); - // Always compute glyph overflow if the block's line-box-contain value is "glyphs". - if (lineBox->fitsToGlyphs()) { - // If we don't stick out of the root line's font box, then don't bother computing our glyph overflow. This optimization - // will keep us from computing glyph bounds in nearly all cases. - bool includeRootLine = lineBox->includesRootLineBoxFontOrLeading(); - int baselineShift = lineBox->verticalPositionForBox(run->m_box, verticalPositionCache); - int rootDescent = includeRootLine ? font.fontMetrics().descent() : 0; - int rootAscent = includeRootLine ? font.fontMetrics().ascent() : 0; - int boxAscent = font.fontMetrics().ascent() - baselineShift; - int boxDescent = font.fontMetrics().descent() + baselineShift; - if (boxAscent > rootDescent || boxDescent > rootAscent) - glyphOverflow.computeBounds = true; - } - - LayoutUnit hyphenWidth = 0; - if (toInlineTextBox(run->m_box)->hasHyphen()) { - const Font& font = renderer->style(lineInfo.isFirstLine())->font(); - hyphenWidth = measureHyphenWidth(renderer, font, run->direction()); - } - float measuredWidth = 0; - - bool kerningIsEnabled = font.fontDescription().typesettingFeatures() & Kerning; - - bool canUseSimpleFontCodePath = renderer->canUseSimpleFontCodePath(); - - // Since we don't cache glyph overflows, we need to re-measure the run if - // the style is linebox-contain: glyph. - - if (!lineBox->fitsToGlyphs() && canUseSimpleFontCodePath) { - int lastEndOffset = run->m_start; - for (size_t i = 0, size = wordMeasurements.size(); i < size && lastEndOffset < run->m_stop; ++i) { - const WordMeasurement& wordMeasurement = wordMeasurements[i]; - if (wordMeasurement.width <=0 || wordMeasurement.startOffset == wordMeasurement.endOffset) - continue; - if (wordMeasurement.renderer != renderer || wordMeasurement.startOffset != lastEndOffset || wordMeasurement.endOffset > run->m_stop) - continue; - - lastEndOffset = wordMeasurement.endOffset; - if (kerningIsEnabled && lastEndOffset == run->m_stop) { - int wordLength = lastEndOffset - wordMeasurement.startOffset; - measuredWidth += renderer->width(wordMeasurement.startOffset, wordLength, xPos, run->direction(), lineInfo.isFirstLine()); - if (i > 0 && wordLength == 1 && renderer->characterAt(wordMeasurement.startOffset) == ' ') - measuredWidth += renderer->style()->wordSpacing(); - } else - measuredWidth += wordMeasurement.width; - if (!wordMeasurement.fallbackFonts.isEmpty()) { - HashSet::const_iterator end = wordMeasurement.fallbackFonts.end(); - for (HashSet::const_iterator it = wordMeasurement.fallbackFonts.begin(); it != end; ++it) - fallbackFonts.add(*it); - } - } - if (measuredWidth && lastEndOffset != run->m_stop) { - // If we don't have enough cached data, we'll measure the run again. - measuredWidth = 0; - fallbackFonts.clear(); - } - } - - if (!measuredWidth) - measuredWidth = renderer->width(run->m_start, run->m_stop - run->m_start, xPos, run->direction(), lineInfo.isFirstLine(), &fallbackFonts, &glyphOverflow); - - run->m_box->setLogicalWidth(measuredWidth + hyphenWidth); - if (!fallbackFonts.isEmpty()) { - ASSERT(run->m_box->isText()); - GlyphOverflowAndFallbackFontsMap::ValueType* it = textBoxDataMap.add(toInlineTextBox(run->m_box), std::make_pair(Vector(), GlyphOverflow())).storedValue; - ASSERT(it->value.first.isEmpty()); - copyToVector(fallbackFonts, it->value.first); - run->m_box->parent()->clearDescendantsHaveSameLineHeightAndBaseline(); + hyphenWidth = measureHyphenWidth(renderer, font, run->direction()); + } + float measuredWidth = 0; + + bool kerningIsEnabled = + font.fontDescription().typesettingFeatures() & Kerning; + + bool canUseSimpleFontCodePath = renderer->canUseSimpleFontCodePath(); + + // Since we don't cache glyph overflows, we need to re-measure the run if + // the style is linebox-contain: glyph. + + if (!lineBox->fitsToGlyphs() && canUseSimpleFontCodePath) { + int lastEndOffset = run->m_start; + for (size_t i = 0, size = wordMeasurements.size(); + i < size && lastEndOffset < run->m_stop; ++i) { + const WordMeasurement& wordMeasurement = wordMeasurements[i]; + if (wordMeasurement.width <= 0 || + wordMeasurement.startOffset == wordMeasurement.endOffset) + continue; + if (wordMeasurement.renderer != renderer || + wordMeasurement.startOffset != lastEndOffset || + wordMeasurement.endOffset > run->m_stop) + continue; + + lastEndOffset = wordMeasurement.endOffset; + if (kerningIsEnabled && lastEndOffset == run->m_stop) { + int wordLength = lastEndOffset - wordMeasurement.startOffset; + measuredWidth += + renderer->width(wordMeasurement.startOffset, wordLength, xPos, + run->direction(), lineInfo.isFirstLine()); + if (i > 0 && wordLength == 1 && + renderer->characterAt(wordMeasurement.startOffset) == ' ') + measuredWidth += renderer->style()->wordSpacing(); + } else + measuredWidth += wordMeasurement.width; + if (!wordMeasurement.fallbackFonts.isEmpty()) { + HashSet::const_iterator end = + wordMeasurement.fallbackFonts.end(); + for (HashSet::const_iterator it = + wordMeasurement.fallbackFonts.begin(); + it != end; ++it) + fallbackFonts.add(*it); + } } - if (!glyphOverflow.isZero()) { - ASSERT(run->m_box->isText()); - GlyphOverflowAndFallbackFontsMap::ValueType* it = textBoxDataMap.add(toInlineTextBox(run->m_box), std::make_pair(Vector(), GlyphOverflow())).storedValue; - it->value.second = glyphOverflow; - run->m_box->clearKnownToHaveNoOverflow(); + if (measuredWidth && lastEndOffset != run->m_stop) { + // If we don't have enough cached data, we'll measure the run again. + measuredWidth = 0; + fallbackFonts.clear(); } + } + + if (!measuredWidth) + measuredWidth = renderer->width( + run->m_start, run->m_stop - run->m_start, xPos, run->direction(), + lineInfo.isFirstLine(), &fallbackFonts, &glyphOverflow); + + run->m_box->setLogicalWidth(measuredWidth + hyphenWidth); + if (!fallbackFonts.isEmpty()) { + ASSERT(run->m_box->isText()); + GlyphOverflowAndFallbackFontsMap::ValueType* it = + textBoxDataMap + .add(toInlineTextBox(run->m_box), + std::make_pair(Vector(), + GlyphOverflow())) + .storedValue; + ASSERT(it->value.first.isEmpty()); + copyToVector(fallbackFonts, it->value.first); + run->m_box->parent()->clearDescendantsHaveSameLineHeightAndBaseline(); + } + if (!glyphOverflow.isZero()) { + ASSERT(run->m_box->isText()); + GlyphOverflowAndFallbackFontsMap::ValueType* it = + textBoxDataMap + .add(toInlineTextBox(run->m_box), + std::make_pair(Vector(), + GlyphOverflow())) + .storedValue; + it->value.second = glyphOverflow; + run->m_box->clearKnownToHaveNoOverflow(); + } } -static inline void computeExpansionForJustifiedText(BidiRun* firstRun, BidiRun* trailingSpaceRun, Vector& expansionOpportunities, unsigned expansionOpportunityCount, float& totalLogicalWidth, float availableLogicalWidth) -{ - if (!expansionOpportunityCount || availableLogicalWidth <= totalLogicalWidth) - return; - - size_t i = 0; - for (BidiRun* r = firstRun; r; r = r->next()) { - if (!r->m_box || r == trailingSpaceRun) - continue; - - if (r->m_object->isText()) { - unsigned opportunitiesInRun = expansionOpportunities[i++]; - - ASSERT(opportunitiesInRun <= expansionOpportunityCount); - - // Don't justify for white-space: pre. - if (r->m_object->style()->whiteSpace() != PRE) { - InlineTextBox* textBox = toInlineTextBox(r->m_box); - int expansion = (availableLogicalWidth - totalLogicalWidth) * opportunitiesInRun / expansionOpportunityCount; - textBox->setExpansion(expansion); - totalLogicalWidth += expansion; - } - expansionOpportunityCount -= opportunitiesInRun; - if (!expansionOpportunityCount) - break; - } +static inline void computeExpansionForJustifiedText( + BidiRun* firstRun, + BidiRun* trailingSpaceRun, + Vector& expansionOpportunities, + unsigned expansionOpportunityCount, + float& totalLogicalWidth, + float availableLogicalWidth) { + if (!expansionOpportunityCount || availableLogicalWidth <= totalLogicalWidth) + return; + + size_t i = 0; + for (BidiRun* r = firstRun; r; r = r->next()) { + if (!r->m_box || r == trailingSpaceRun) + continue; + + if (r->m_object->isText()) { + unsigned opportunitiesInRun = expansionOpportunities[i++]; + + ASSERT(opportunitiesInRun <= expansionOpportunityCount); + + // Don't justify for white-space: pre. + if (r->m_object->style()->whiteSpace() != PRE) { + InlineTextBox* textBox = toInlineTextBox(r->m_box); + int expansion = (availableLogicalWidth - totalLogicalWidth) * + opportunitiesInRun / expansionOpportunityCount; + textBox->setExpansion(expansion); + totalLogicalWidth += expansion; + } + expansionOpportunityCount -= opportunitiesInRun; + if (!expansionOpportunityCount) + break; } + } } -static void updateLogicalInlinePositions(RenderParagraph* block, float& lineLogicalLeft, float& lineLogicalRight, float& availableLogicalWidth, IndentTextOrNot shouldIndentText) -{ - lineLogicalLeft = block->logicalLeftOffsetForLine(shouldIndentText == IndentText).toFloat(); - lineLogicalRight = block->logicalRightOffsetForLine(shouldIndentText == IndentText).toFloat(); - availableLogicalWidth = lineLogicalRight - lineLogicalLeft; +static void updateLogicalInlinePositions(RenderParagraph* block, + float& lineLogicalLeft, + float& lineLogicalRight, + float& availableLogicalWidth, + IndentTextOrNot shouldIndentText) { + lineLogicalLeft = + block->logicalLeftOffsetForLine(shouldIndentText == IndentText).toFloat(); + lineLogicalRight = + block->logicalRightOffsetForLine(shouldIndentText == IndentText) + .toFloat(); + availableLogicalWidth = lineLogicalRight - lineLogicalLeft; } -void RenderParagraph::computeInlineDirectionPositionsForLine(RootInlineBox* lineBox, const LineInfo& lineInfo, BidiRun* firstRun, BidiRun* trailingSpaceRun, bool reachedEnd, - GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache, WordMeasurements& wordMeasurements) -{ - ETextAlign textAlign = textAlignmentForLine(!reachedEnd && !lineBox->endsWithBreak()); - - // CSS 2.1: "'Text-indent' only affects a line if it is the first formatted line of an element. For example, the first line of an anonymous block - // box is only affected if it is the first child of its parent element." - // CSS3 "text-indent", "each-line" affects the first line of the block container as well as each line after a forced line break, - // but does not affect lines after a soft wrap break. - bool isFirstLine = lineInfo.isFirstLine(); - bool isAfterHardLineBreak = lineBox->prevRootBox() && lineBox->prevRootBox()->endsWithBreak(); - IndentTextOrNot shouldIndentText = requiresIndent(isFirstLine, isAfterHardLineBreak, style()); - float lineLogicalLeft; - float lineLogicalRight; - float availableLogicalWidth; - updateLogicalInlinePositions(this, lineLogicalLeft, lineLogicalRight, availableLogicalWidth, shouldIndentText); - bool needsWordSpacing; - - if (firstRun && firstRun->m_object->isReplaced()) - updateLogicalInlinePositions(this, lineLogicalLeft, lineLogicalRight, availableLogicalWidth, shouldIndentText); - - computeInlineDirectionPositionsForSegment(lineBox, lineInfo, textAlign, lineLogicalLeft, availableLogicalWidth, firstRun, trailingSpaceRun, textBoxDataMap, verticalPositionCache, wordMeasurements); - // The widths of all runs are now known. We can now place every inline box (and - // compute accurate widths for the inline flow boxes). - needsWordSpacing = false; - lineBox->placeBoxesInInlineDirection(lineLogicalLeft, needsWordSpacing); +void RenderParagraph::computeInlineDirectionPositionsForLine( + RootInlineBox* lineBox, + const LineInfo& lineInfo, + BidiRun* firstRun, + BidiRun* trailingSpaceRun, + bool reachedEnd, + GlyphOverflowAndFallbackFontsMap& textBoxDataMap, + VerticalPositionCache& verticalPositionCache, + WordMeasurements& wordMeasurements) { + ETextAlign textAlign = + textAlignmentForLine(!reachedEnd && !lineBox->endsWithBreak()); + + // CSS 2.1: "'Text-indent' only affects a line if it is the first formatted + // line of an element. For example, the first line of an anonymous block box + // is only affected if it is the first child of its parent element." CSS3 + // "text-indent", "each-line" affects the first line of the block container as + // well as each line after a forced line break, but does not affect lines + // after a soft wrap break. + bool isFirstLine = lineInfo.isFirstLine(); + bool isAfterHardLineBreak = + lineBox->prevRootBox() && lineBox->prevRootBox()->endsWithBreak(); + IndentTextOrNot shouldIndentText = + requiresIndent(isFirstLine, isAfterHardLineBreak, style()); + float lineLogicalLeft; + float lineLogicalRight; + float availableLogicalWidth; + updateLogicalInlinePositions(this, lineLogicalLeft, lineLogicalRight, + availableLogicalWidth, shouldIndentText); + bool needsWordSpacing; + + if (firstRun && firstRun->m_object->isReplaced()) + updateLogicalInlinePositions(this, lineLogicalLeft, lineLogicalRight, + availableLogicalWidth, shouldIndentText); + + computeInlineDirectionPositionsForSegment( + lineBox, lineInfo, textAlign, lineLogicalLeft, availableLogicalWidth, + firstRun, trailingSpaceRun, textBoxDataMap, verticalPositionCache, + wordMeasurements); + // The widths of all runs are now known. We can now place every inline box + // (and compute accurate widths for the inline flow boxes). + needsWordSpacing = false; + lineBox->placeBoxesInInlineDirection(lineLogicalLeft, needsWordSpacing); } -BidiRun* RenderParagraph::computeInlineDirectionPositionsForSegment(RootInlineBox* lineBox, const LineInfo& lineInfo, ETextAlign textAlign, float& logicalLeft, - float& availableLogicalWidth, BidiRun* firstRun, BidiRun* trailingSpaceRun, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache, - WordMeasurements& wordMeasurements) -{ - bool needsWordSpacing = true; - float totalLogicalWidth = lineBox->getFlowSpacingLogicalWidth().toFloat(); - unsigned expansionOpportunityCount = 0; - bool isAfterExpansion = true; - Vector expansionOpportunities; - TextJustify textJustify = style()->textJustify(); - - BidiRun* r = firstRun; - for (; r; r = r->next()) { - if (!r->m_box || r->m_object->isOutOfFlowPositioned() || r->m_box->isLineBreak()) - continue; // Positioned objects are only participating to figure out their - // correct static x position. They have no effect on the width. - // Similarly, line break boxes have no effect on the width. - if (r->m_object->isText()) { - RenderText* rt = toRenderText(r->m_object); - if (textAlign == JUSTIFY && r != trailingSpaceRun && textJustify != TextJustifyNone) { - if (!isAfterExpansion) - toInlineTextBox(r->m_box)->setCanHaveLeadingExpansion(true); - unsigned opportunitiesInRun; - if (rt->is8Bit()) - opportunitiesInRun = Character::expansionOpportunityCount(rt->characters8() + r->m_start, r->m_stop - r->m_start, r->m_box->direction(), isAfterExpansion); - else - opportunitiesInRun = Character::expansionOpportunityCount(rt->characters16() + r->m_start, r->m_stop - r->m_start, r->m_box->direction(), isAfterExpansion); - expansionOpportunities.append(opportunitiesInRun); - expansionOpportunityCount += opportunitiesInRun; - } - - if (rt->textLength()) { - if (!r->m_start && needsWordSpacing && isSpaceOrNewline(rt->characterAt(r->m_start))) - totalLogicalWidth += rt->style(lineInfo.isFirstLine())->font().fontDescription().wordSpacing(); - needsWordSpacing = !isSpaceOrNewline(rt->characterAt(r->m_stop - 1)); - } - - setLogicalWidthForTextRun(lineBox, r, rt, totalLogicalWidth, lineInfo, textBoxDataMap, verticalPositionCache, wordMeasurements); - } else { - isAfterExpansion = false; - if (!r->m_object->isRenderInline()) { - RenderBox* renderBox = toRenderBox(r->m_object); - r->m_box->setLogicalWidth(logicalWidthForChild(renderBox).toFloat()); - totalLogicalWidth += marginStartForChild(renderBox) + marginEndForChild(renderBox); - } - } +BidiRun* RenderParagraph::computeInlineDirectionPositionsForSegment( + RootInlineBox* lineBox, + const LineInfo& lineInfo, + ETextAlign textAlign, + float& logicalLeft, + float& availableLogicalWidth, + BidiRun* firstRun, + BidiRun* trailingSpaceRun, + GlyphOverflowAndFallbackFontsMap& textBoxDataMap, + VerticalPositionCache& verticalPositionCache, + WordMeasurements& wordMeasurements) { + bool needsWordSpacing = true; + float totalLogicalWidth = lineBox->getFlowSpacingLogicalWidth().toFloat(); + unsigned expansionOpportunityCount = 0; + bool isAfterExpansion = true; + Vector expansionOpportunities; + TextJustify textJustify = style()->textJustify(); + + BidiRun* r = firstRun; + for (; r; r = r->next()) { + if (!r->m_box || r->m_object->isOutOfFlowPositioned() || + r->m_box->isLineBreak()) + continue; // Positioned objects are only participating to figure out + // their correct static x position. They have no effect on the + // width. Similarly, line break boxes have no effect on the + // width. + if (r->m_object->isText()) { + RenderText* rt = toRenderText(r->m_object); + if (textAlign == JUSTIFY && r != trailingSpaceRun && + textJustify != TextJustifyNone) { + if (!isAfterExpansion) + toInlineTextBox(r->m_box)->setCanHaveLeadingExpansion(true); + unsigned opportunitiesInRun; + if (rt->is8Bit()) + opportunitiesInRun = Character::expansionOpportunityCount( + rt->characters8() + r->m_start, r->m_stop - r->m_start, + r->m_box->direction(), isAfterExpansion); + else + opportunitiesInRun = Character::expansionOpportunityCount( + rt->characters16() + r->m_start, r->m_stop - r->m_start, + r->m_box->direction(), isAfterExpansion); + expansionOpportunities.append(opportunitiesInRun); + expansionOpportunityCount += opportunitiesInRun; + } - totalLogicalWidth += r->m_box->logicalWidth(); - } + if (rt->textLength()) { + if (!r->m_start && needsWordSpacing && + isSpaceOrNewline(rt->characterAt(r->m_start))) + totalLogicalWidth += rt->style(lineInfo.isFirstLine()) + ->font() + .fontDescription() + .wordSpacing(); + needsWordSpacing = !isSpaceOrNewline(rt->characterAt(r->m_stop - 1)); + } - if (isAfterExpansion && !expansionOpportunities.isEmpty()) { - expansionOpportunities.last()--; - expansionOpportunityCount--; + setLogicalWidthForTextRun(lineBox, r, rt, totalLogicalWidth, lineInfo, + textBoxDataMap, verticalPositionCache, + wordMeasurements); + } else { + isAfterExpansion = false; + if (!r->m_object->isRenderInline()) { + RenderBox* renderBox = toRenderBox(r->m_object); + r->m_box->setLogicalWidth(logicalWidthForChild(renderBox).toFloat()); + totalLogicalWidth += + marginStartForChild(renderBox) + marginEndForChild(renderBox); + } } - updateLogicalWidthForAlignment(textAlign, lineBox, trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth, expansionOpportunityCount); + totalLogicalWidth += r->m_box->logicalWidth(); + } + + if (isAfterExpansion && !expansionOpportunities.isEmpty()) { + expansionOpportunities.last()--; + expansionOpportunityCount--; + } - computeExpansionForJustifiedText(firstRun, trailingSpaceRun, expansionOpportunities, expansionOpportunityCount, totalLogicalWidth, availableLogicalWidth); + updateLogicalWidthForAlignment( + textAlign, lineBox, trailingSpaceRun, logicalLeft, totalLogicalWidth, + availableLogicalWidth, expansionOpportunityCount); - return r; + computeExpansionForJustifiedText( + firstRun, trailingSpaceRun, expansionOpportunities, + expansionOpportunityCount, totalLogicalWidth, availableLogicalWidth); + + return r; } -void RenderParagraph::computeBlockDirectionPositionsForLine(RootInlineBox* lineBox, BidiRun* firstRun, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, - VerticalPositionCache& verticalPositionCache) -{ - setLogicalHeight(lineBox->alignBoxesInBlockDirection(logicalHeight(), textBoxDataMap, verticalPositionCache)); - - // Now make sure we place replaced render objects correctly. - for (BidiRun* r = firstRun; r; r = r->next()) { - ASSERT(r->m_box); - if (!r->m_box) - continue; // Skip runs with no line boxes. - - // Align positioned boxes with the top of the line box. This is - // a reasonable approximation of an appropriate y position. - if (r->m_object->isOutOfFlowPositioned()) - r->m_box->setLogicalTop(logicalHeight().toFloat()); - - // Position is used to properly position both replaced elements and - // to update the static normal flow x/y of positioned elements. - if (r->m_object->isText()) - toRenderText(r->m_object)->positionLineBox(r->m_box); - else if (r->m_object->isBox()) - toRenderBox(r->m_object)->positionLineBox(r->m_box); - } +void RenderParagraph::computeBlockDirectionPositionsForLine( + RootInlineBox* lineBox, + BidiRun* firstRun, + GlyphOverflowAndFallbackFontsMap& textBoxDataMap, + VerticalPositionCache& verticalPositionCache) { + setLogicalHeight(lineBox->alignBoxesInBlockDirection( + logicalHeight(), textBoxDataMap, verticalPositionCache)); + + // Now make sure we place replaced render objects correctly. + for (BidiRun* r = firstRun; r; r = r->next()) { + ASSERT(r->m_box); + if (!r->m_box) + continue; // Skip runs with no line boxes. + + // Align positioned boxes with the top of the line box. This is + // a reasonable approximation of an appropriate y position. + if (r->m_object->isOutOfFlowPositioned()) + r->m_box->setLogicalTop(logicalHeight().toFloat()); + + // Position is used to properly position both replaced elements and + // to update the static normal flow x/y of positioned elements. + if (r->m_object->isText()) + toRenderText(r->m_object)->positionLineBox(r->m_box); + else if (r->m_object->isBox()) + toRenderBox(r->m_object)->positionLineBox(r->m_box); + } } -// This function constructs line boxes for all of the text runs in the resolver and computes their position. -RootInlineBox* RenderParagraph::createLineBoxesFromBidiRuns(unsigned bidiLevel, BidiRunList& bidiRuns, const InlineIterator& end, LineInfo& lineInfo, VerticalPositionCache& verticalPositionCache, BidiRun* trailingSpaceRun, WordMeasurements& wordMeasurements) -{ - if (!bidiRuns.runCount()) - return 0; +// This function constructs line boxes for all of the text runs in the resolver +// and computes their position. +RootInlineBox* RenderParagraph::createLineBoxesFromBidiRuns( + unsigned bidiLevel, + BidiRunList& bidiRuns, + const InlineIterator& end, + LineInfo& lineInfo, + VerticalPositionCache& verticalPositionCache, + BidiRun* trailingSpaceRun, + WordMeasurements& wordMeasurements) { + if (!bidiRuns.runCount()) + return 0; - // FIXME: Why is this only done when we had runs? - lineInfo.setLastLine(!end.object()); + // FIXME: Why is this only done when we had runs? + lineInfo.setLastLine(!end.object()); - RootInlineBox* lineBox = constructLine(bidiRuns, lineInfo); - if (!lineBox) - return 0; + RootInlineBox* lineBox = constructLine(bidiRuns, lineInfo); + if (!lineBox) + return 0; - lineBox->setBidiLevel(bidiLevel); - lineBox->setEndsWithBreak(lineInfo.previousLineBrokeCleanly()); + lineBox->setBidiLevel(bidiLevel); + lineBox->setEndsWithBreak(lineInfo.previousLineBrokeCleanly()); - GlyphOverflowAndFallbackFontsMap textBoxDataMap; + GlyphOverflowAndFallbackFontsMap textBoxDataMap; - // Now we position all of our text runs horizontally. - computeInlineDirectionPositionsForLine(lineBox, lineInfo, bidiRuns.firstRun(), trailingSpaceRun, end.atEnd(), textBoxDataMap, verticalPositionCache, wordMeasurements); + // Now we position all of our text runs horizontally. + computeInlineDirectionPositionsForLine( + lineBox, lineInfo, bidiRuns.firstRun(), trailingSpaceRun, end.atEnd(), + textBoxDataMap, verticalPositionCache, wordMeasurements); - // Now position our text runs vertically. - computeBlockDirectionPositionsForLine(lineBox, bidiRuns.firstRun(), textBoxDataMap, verticalPositionCache); + // Now position our text runs vertically. + computeBlockDirectionPositionsForLine(lineBox, bidiRuns.firstRun(), + textBoxDataMap, verticalPositionCache); - // Compute our overflow now. - lineBox->computeOverflow(lineBox->lineTop(), lineBox->lineBottom(), textBoxDataMap); + // Compute our overflow now. + lineBox->computeOverflow(lineBox->lineTop(), lineBox->lineBottom(), + textBoxDataMap); - return lineBox; + return lineBox; } -static void deleteLineRange(LineLayoutState& layoutState, RootInlineBox* startLine, RootInlineBox* stopLine = 0) -{ - RootInlineBox* boxToDelete = startLine; - while (boxToDelete && boxToDelete != stopLine) { - // Note: deleteLineRange(firstRootBox()) is not identical to deleteLineBoxTree(). - // deleteLineBoxTree uses nextLineBox() instead of nextRootBox() when traversing. - RootInlineBox* next = boxToDelete->nextRootBox(); - boxToDelete->deleteLine(); - boxToDelete = next; - } +static void deleteLineRange(LineLayoutState& layoutState, + RootInlineBox* startLine, + RootInlineBox* stopLine = 0) { + RootInlineBox* boxToDelete = startLine; + while (boxToDelete && boxToDelete != stopLine) { + // Note: deleteLineRange(firstRootBox()) is not identical to + // deleteLineBoxTree(). deleteLineBoxTree uses nextLineBox() instead of + // nextRootBox() when traversing. + RootInlineBox* next = boxToDelete->nextRootBox(); + boxToDelete->deleteLine(); + boxToDelete = next; + } } -void RenderParagraph::layoutRunsAndFloats(LineLayoutState& layoutState) -{ - // We want to skip ahead to the first dirty line - InlineBidiResolver resolver; - RootInlineBox* startLine = determineStartPosition(layoutState, resolver); - - // We also find the first clean line and extract these lines. We will add them back - // if we determine that we're able to synchronize after handling all our dirty lines. - InlineIterator cleanLineStart; - BidiStatus cleanLineBidiStatus; - if (!layoutState.isFullLayout() && startLine) - determineEndPosition(layoutState, startLine, cleanLineStart, cleanLineBidiStatus); - - if (startLine) - deleteLineRange(layoutState, startLine); - - layoutRunsAndFloatsInRange(layoutState, resolver, cleanLineStart, cleanLineBidiStatus); - linkToEndLineIfNeeded(layoutState); +void RenderParagraph::layoutRunsAndFloats(LineLayoutState& layoutState) { + // We want to skip ahead to the first dirty line + InlineBidiResolver resolver; + RootInlineBox* startLine = determineStartPosition(layoutState, resolver); + + // We also find the first clean line and extract these lines. We will add + // them back if we determine that we're able to synchronize after handling all + // our dirty lines. + InlineIterator cleanLineStart; + BidiStatus cleanLineBidiStatus; + if (!layoutState.isFullLayout() && startLine) + determineEndPosition(layoutState, startLine, cleanLineStart, + cleanLineBidiStatus); + + if (startLine) + deleteLineRange(layoutState, startLine); + + layoutRunsAndFloatsInRange(layoutState, resolver, cleanLineStart, + cleanLineBidiStatus); + linkToEndLineIfNeeded(layoutState); } -void RenderParagraph::layoutRunsAndFloatsInRange(LineLayoutState& layoutState, - InlineBidiResolver& resolver, const InlineIterator& cleanLineStart, - const BidiStatus& cleanLineBidiStatus) -{ - RenderStyle* styleToUse = style(); - LineMidpointState& lineMidpointState = resolver.midpointState(); - InlineIterator endOfLine = resolver.position(); - bool checkForEndLineMatch = layoutState.endLine(); - RenderTextInfo renderTextInfo; - VerticalPositionCache verticalPositionCache; - LineBreaker lineBreaker(this); - - m_didExceedMaxLines = false; - - while (!endOfLine.atEnd()) { - // FIXME: Is this check necessary before the first iteration or can it be moved to the end? - if (checkForEndLineMatch) { - layoutState.setEndLineMatched(matchedEndLine(layoutState, resolver, cleanLineStart, cleanLineBidiStatus)); - if (layoutState.endLineMatched()) { - resolver.setPosition(InlineIterator(resolver.position().root(), 0, 0), 0); - break; - } - } +void RenderParagraph::layoutRunsAndFloatsInRange( + LineLayoutState& layoutState, + InlineBidiResolver& resolver, + const InlineIterator& cleanLineStart, + const BidiStatus& cleanLineBidiStatus) { + RenderStyle* styleToUse = style(); + LineMidpointState& lineMidpointState = resolver.midpointState(); + InlineIterator endOfLine = resolver.position(); + bool checkForEndLineMatch = layoutState.endLine(); + RenderTextInfo renderTextInfo; + VerticalPositionCache verticalPositionCache; + LineBreaker lineBreaker(this); + + m_didExceedMaxLines = false; + + while (!endOfLine.atEnd()) { + // FIXME: Is this check necessary before the first iteration or can it be + // moved to the end? + if (checkForEndLineMatch) { + layoutState.setEndLineMatched(matchedEndLine( + layoutState, resolver, cleanLineStart, cleanLineBidiStatus)); + if (layoutState.endLineMatched()) { + resolver.setPosition(InlineIterator(resolver.position().root(), 0, 0), + 0); + break; + } + } - lineMidpointState.reset(); - - layoutState.lineInfo().setEmpty(true); - layoutState.lineInfo().resetRunsFromLeadingWhitespace(); - - bool isNewUBAParagraph = layoutState.lineInfo().previousLineBrokeCleanly(); - FloatingObject* lastFloatFromPreviousLine = 0; - - WordMeasurements wordMeasurements; - endOfLine = lineBreaker.nextLineBreak(resolver, layoutState.lineInfo(), renderTextInfo, - lastFloatFromPreviousLine, wordMeasurements); - renderTextInfo.m_lineBreakIterator.resetPriorContext(); - m_didExceedMaxLines = layoutState.lineInfo().lineIndex() > styleToUse->maxLines() && !layoutState.lineInfo().isEmpty(); - if (resolver.position().atEnd() || m_didExceedMaxLines) { - // FIXME: We shouldn't be creating any runs in nextLineBreak to begin with! - // Once BidiRunList is separated from BidiResolver this will not be needed. - resolver.runs().deleteRuns(); - resolver.markCurrentRunEmpty(); // FIXME: This can probably be replaced by an ASSERT (or just removed). - resolver.setPosition(InlineIterator(resolver.position().root(), 0, 0), 0); - break; - } + lineMidpointState.reset(); + + layoutState.lineInfo().setEmpty(true); + layoutState.lineInfo().resetRunsFromLeadingWhitespace(); + + bool isNewUBAParagraph = layoutState.lineInfo().previousLineBrokeCleanly(); + FloatingObject* lastFloatFromPreviousLine = 0; + + WordMeasurements wordMeasurements; + endOfLine = lineBreaker.nextLineBreak( + resolver, layoutState.lineInfo(), renderTextInfo, + lastFloatFromPreviousLine, wordMeasurements); + renderTextInfo.m_lineBreakIterator.resetPriorContext(); + m_didExceedMaxLines = + layoutState.lineInfo().lineIndex() > styleToUse->maxLines() && + !layoutState.lineInfo().isEmpty(); + if (resolver.position().atEnd() || m_didExceedMaxLines) { + // FIXME: We shouldn't be creating any runs in nextLineBreak to begin + // with! Once BidiRunList is separated from BidiResolver this will not be + // needed. + resolver.runs().deleteRuns(); + resolver.markCurrentRunEmpty(); // FIXME: This can probably be replaced + // by an ASSERT (or just removed). + resolver.setPosition(InlineIterator(resolver.position().root(), 0, 0), 0); + break; + } - ASSERT(endOfLine != resolver.position()); + ASSERT(endOfLine != resolver.position()); - // This is a short-cut for empty lines. - if (layoutState.lineInfo().isEmpty()) { - if (lastRootBox()) - lastRootBox()->setLineBreakInfo(endOfLine.object(), endOfLine.offset(), resolver.status()); - } else { - VisualDirectionOverride override = (styleToUse->rtlOrdering() == VisualOrder ? (styleToUse->direction() == LTR ? VisualLeftToRightOverride : VisualRightToLeftOverride) : NoVisualOverride); - if (isNewUBAParagraph && styleToUse->unicodeBidi() == Plaintext && !resolver.context()->parent()) { - TextDirection direction = determinePlaintextDirectionality(resolver.position().root(), resolver.position().object(), resolver.position().offset()); - resolver.setStatus(BidiStatus(direction, isOverride(styleToUse->unicodeBidi()))); - } - // FIXME: This ownership is reversed. We should own the BidiRunList and pass it to createBidiRunsForLine. - BidiRunList& bidiRuns = resolver.runs(); - constructBidiRunsForLine(resolver, bidiRuns, endOfLine, override, layoutState.lineInfo().previousLineBrokeCleanly(), isNewUBAParagraph); - ASSERT(resolver.position() == endOfLine); - - BidiRun* trailingSpaceRun = resolver.trailingSpaceRun(); - - if (bidiRuns.runCount() && lineBreaker.lineWasHyphenated()) - bidiRuns.logicallyLastRun()->m_hasHyphen = true; - if (bidiRuns.runCount() && lineBreaker.lineWasEllipsized()) - bidiRuns.logicallyLastRun()->m_hasAddedEllipsis = true; - - // Now that the runs have been ordered, we create the line boxes. - // At the same time we figure out where border/padding/margin should be applied for - // inline flow boxes. - - RootInlineBox* lineBox = createLineBoxesFromBidiRuns(resolver.status().context->level(), bidiRuns, endOfLine, layoutState.lineInfo(), verticalPositionCache, trailingSpaceRun, wordMeasurements); - - bidiRuns.deleteRuns(); - resolver.markCurrentRunEmpty(); // FIXME: This can probably be replaced by an ASSERT (or just removed). - - if (lineBox) - lineBox->setLineBreakInfo(endOfLine.object(), endOfLine.offset(), resolver.status()); - } + // This is a short-cut for empty lines. + if (layoutState.lineInfo().isEmpty()) { + if (lastRootBox()) + lastRootBox()->setLineBreakInfo(endOfLine.object(), endOfLine.offset(), + resolver.status()); + } else { + VisualDirectionOverride override = + (styleToUse->rtlOrdering() == VisualOrder + ? (styleToUse->direction() == LTR ? VisualLeftToRightOverride + : VisualRightToLeftOverride) + : NoVisualOverride); + if (isNewUBAParagraph && styleToUse->unicodeBidi() == Plaintext && + !resolver.context()->parent()) { + TextDirection direction = determinePlaintextDirectionality( + resolver.position().root(), resolver.position().object(), + resolver.position().offset()); + resolver.setStatus( + BidiStatus(direction, isOverride(styleToUse->unicodeBidi()))); + } + // FIXME: This ownership is reversed. We should own the BidiRunList and + // pass it to createBidiRunsForLine. + BidiRunList& bidiRuns = resolver.runs(); + constructBidiRunsForLine( + resolver, bidiRuns, endOfLine, override, + layoutState.lineInfo().previousLineBrokeCleanly(), isNewUBAParagraph); + ASSERT(resolver.position() == endOfLine); + + BidiRun* trailingSpaceRun = resolver.trailingSpaceRun(); + + if (bidiRuns.runCount() && lineBreaker.lineWasHyphenated()) + bidiRuns.logicallyLastRun()->m_hasHyphen = true; + if (bidiRuns.runCount() && lineBreaker.lineWasEllipsized()) + bidiRuns.logicallyLastRun()->m_hasAddedEllipsis = true; + + // Now that the runs have been ordered, we create the line boxes. + // At the same time we figure out where border/padding/margin should be + // applied for inline flow boxes. + + RootInlineBox* lineBox = createLineBoxesFromBidiRuns( + resolver.status().context->level(), bidiRuns, endOfLine, + layoutState.lineInfo(), verticalPositionCache, trailingSpaceRun, + wordMeasurements); + + bidiRuns.deleteRuns(); + resolver.markCurrentRunEmpty(); // FIXME: This can probably be replaced + // by an ASSERT (or just removed). + + if (lineBox) + lineBox->setLineBreakInfo(endOfLine.object(), endOfLine.offset(), + resolver.status()); + } - if (!layoutState.lineInfo().isEmpty()) - layoutState.lineInfo().setFirstLine(false); + if (!layoutState.lineInfo().isEmpty()) + layoutState.lineInfo().setFirstLine(false); - lineMidpointState.reset(); - resolver.setPosition(endOfLine, numberOfIsolateAncestors(endOfLine)); + lineMidpointState.reset(); + resolver.setPosition(endOfLine, numberOfIsolateAncestors(endOfLine)); - // Limit ellipsized text to a single line. - if (lineBreaker.lineWasEllipsized()) { - m_didExceedMaxLines = true; - resolver.setPosition(InlineIterator(resolver.position().root(), 0, 0), 0); - break; - } + // Limit ellipsized text to a single line. + if (lineBreaker.lineWasEllipsized()) { + m_didExceedMaxLines = true; + resolver.setPosition(InlineIterator(resolver.position().root(), 0, 0), 0); + break; } + } } -void RenderParagraph::linkToEndLineIfNeeded(LineLayoutState& layoutState) -{ - if (layoutState.endLine()) { - if (layoutState.endLineMatched()) { - // Attach all the remaining lines, and then adjust their y-positions as needed. - LayoutUnit delta = logicalHeight() - layoutState.endLineLogicalTop(); - for (RootInlineBox* line = layoutState.endLine(); line; line = line->nextRootBox()) { - line->attachLine(); - if (delta) - line->adjustBlockDirectionPosition(delta.toFloat()); - } - setLogicalHeight(lastRootBox()->lineBottomWithLeading()); - } else { - // Delete all the remaining lines. - deleteLineRange(layoutState, layoutState.endLine()); - } +void RenderParagraph::linkToEndLineIfNeeded(LineLayoutState& layoutState) { + if (layoutState.endLine()) { + if (layoutState.endLineMatched()) { + // Attach all the remaining lines, and then adjust their y-positions as + // needed. + LayoutUnit delta = logicalHeight() - layoutState.endLineLogicalTop(); + for (RootInlineBox* line = layoutState.endLine(); line; + line = line->nextRootBox()) { + line->attachLine(); + if (delta) + line->adjustBlockDirectionPosition(delta.toFloat()); + } + setLogicalHeight(lastRootBox()->lineBottomWithLeading()); + } else { + // Delete all the remaining lines. + deleteLineRange(layoutState, layoutState.endLine()); } + } } struct InlineMinMaxIterator { -/* InlineMinMaxIterator is a class that will iterate over all render objects that contribute to - inline min/max width calculations. Note the following about the way it walks: - (1) Positioned content is skipped (since it does not contribute to min/max width of a block) - (2) We do not drill into the children of floats or replaced elements, since you can't break - in the middle of such an element. - (3) Inline flows (e.g.,
, , ) are walked twice, since each side can have - distinct borders/margin/padding that contribute to the min/max width. -*/ - RenderObject* parent; - RenderObject* current; - bool endOfInline; - - InlineMinMaxIterator(RenderObject* p) - : parent(p), current(p), endOfInline(false) - { - - } - - RenderObject* next(); + /* InlineMinMaxIterator is a class that will iterate over all render objects + that contribute to inline min/max width calculations. Note the following + about the way it walks: (1) Positioned content is skipped (since it does + not contribute to min/max width of a block) (2) We do not drill into the + children of floats or replaced elements, since you can't break in the + middle of such an element. (3) Inline flows (e.g., , , ) are + walked twice, since each side can have distinct borders/margin/padding that + contribute to the min/max width. + */ + RenderObject* parent; + RenderObject* current; + bool endOfInline; + + InlineMinMaxIterator(RenderObject* p) + : parent(p), current(p), endOfInline(false) {} + + RenderObject* next(); }; -RenderObject* InlineMinMaxIterator::next() -{ - RenderObject* result = 0; - bool oldEndOfInline = endOfInline; - endOfInline = false; - while (current || current == parent) { - if (!oldEndOfInline && (current == parent || (!current->isReplaced() && !current->isOutOfFlowPositioned()))) - result = current->slowFirstChild(); - - if (!result) { - // We hit the end of our inline. (It was empty, e.g., .) - if (!oldEndOfInline && current->isRenderInline()) { - result = current; - endOfInline = true; - break; - } - - while (current && current != parent) { - result = current->nextSibling(); - if (result) - break; - current = current->parent(); - if (current && current != parent && current->isRenderInline()) { - result = current; - endOfInline = true; - break; - } - } - } +RenderObject* InlineMinMaxIterator::next() { + RenderObject* result = 0; + bool oldEndOfInline = endOfInline; + endOfInline = false; + while (current || current == parent) { + if (!oldEndOfInline && + (current == parent || + (!current->isReplaced() && !current->isOutOfFlowPositioned()))) + result = current->slowFirstChild(); + + if (!result) { + // We hit the end of our inline. (It was empty, e.g., .) + if (!oldEndOfInline && current->isRenderInline()) { + result = current; + endOfInline = true; + break; + } - if (!result) - break; + while (current && current != parent) { + result = current->nextSibling(); + if (result) + break; + current = current->parent(); + if (current && current != parent && current->isRenderInline()) { + result = current; + endOfInline = true; + break; + } + } + } - if (!result->isOutOfFlowPositioned() && (result->isText() || result->isReplaced() || result->isRenderInline())) - break; + if (!result) + break; - current = result; - result = 0; - } + if (!result->isOutOfFlowPositioned() && + (result->isText() || result->isReplaced() || result->isRenderInline())) + break; - // Update our position. current = result; - return current; + result = 0; + } + + // Update our position. + current = result; + return current; } -static LayoutUnit getBPMWidth(LayoutUnit childValue, Length cssUnit) -{ - if (cssUnit.type() != Auto) - return (cssUnit.isFixed() ? static_cast(cssUnit.value()) : childValue); - return 0; +static LayoutUnit getBPMWidth(LayoutUnit childValue, Length cssUnit) { + if (cssUnit.type() != Auto) + return (cssUnit.isFixed() ? static_cast(cssUnit.value()) + : childValue); + return 0; } -static LayoutUnit getBorderPaddingMargin(RenderBoxModelObject* child, bool endOfInline) -{ - RenderStyle* childStyle = child->style(); - if (endOfInline) { - return getBPMWidth(child->marginEnd(), childStyle->marginEnd()) + - getBPMWidth(child->paddingEnd(), childStyle->paddingEnd()) + - child->borderEnd(); - } - return getBPMWidth(child->marginStart(), childStyle->marginStart()) + - getBPMWidth(child->paddingStart(), childStyle->paddingStart()) + - child->borderStart(); +static LayoutUnit getBorderPaddingMargin(RenderBoxModelObject* child, + bool endOfInline) { + RenderStyle* childStyle = child->style(); + if (endOfInline) { + return getBPMWidth(child->marginEnd(), childStyle->marginEnd()) + + getBPMWidth(child->paddingEnd(), childStyle->paddingEnd()) + + child->borderEnd(); + } + return getBPMWidth(child->marginStart(), childStyle->marginStart()) + + getBPMWidth(child->paddingStart(), childStyle->paddingStart()) + + child->borderStart(); } -static inline void stripTrailingSpace(float& inlineMax, float& inlineMin, RenderObject* trailingSpaceChild) -{ - if (trailingSpaceChild && trailingSpaceChild->isText()) { - // Collapse away the trailing space at the end of a block. - RenderText* t = toRenderText(trailingSpaceChild); - const UChar space = ' '; - const Font& font = t->style()->font(); // FIXME: This ignores first-line. - float spaceWidth = font.width(constructTextRun(t, font, &space, 1, t->style(), LTR)); - inlineMax -= spaceWidth + font.fontDescription().wordSpacing(); - if (inlineMin > inlineMax) - inlineMin = inlineMax; - } +static inline void stripTrailingSpace(float& inlineMax, + float& inlineMin, + RenderObject* trailingSpaceChild) { + if (trailingSpaceChild && trailingSpaceChild->isText()) { + // Collapse away the trailing space at the end of a block. + RenderText* t = toRenderText(trailingSpaceChild); + const UChar space = ' '; + const Font& font = t->style()->font(); // FIXME: This ignores first-line. + float spaceWidth = + font.width(constructTextRun(t, font, &space, 1, t->style(), LTR)); + inlineMax -= spaceWidth + font.fontDescription().wordSpacing(); + if (inlineMin > inlineMax) + inlineMin = inlineMax; + } } -static inline void updatePreferredWidth(LayoutUnit& preferredWidth, float& result) -{ - LayoutUnit snappedResult = LayoutUnit::fromFloatCeil(result); - preferredWidth = std::max(snappedResult, preferredWidth); +static inline void updatePreferredWidth(LayoutUnit& preferredWidth, + float& result) { + LayoutUnit snappedResult = LayoutUnit::fromFloatCeil(result); + preferredWidth = std::max(snappedResult, preferredWidth); } -// When converting between floating point and LayoutUnits we risk losing precision -// with each conversion. When this occurs while accumulating our preferred widths, -// we can wind up with a line width that's larger than our maxPreferredWidth due to -// pure float accumulation. -static inline LayoutUnit adjustFloatForSubPixelLayout(float value) -{ - return LayoutUnit::fromFloatCeil(value); +// When converting between floating point and LayoutUnits we risk losing +// precision with each conversion. When this occurs while accumulating our +// preferred widths, we can wind up with a line width that's larger than our +// maxPreferredWidth due to pure float accumulation. +static inline LayoutUnit adjustFloatForSubPixelLayout(float value) { + return LayoutUnit::fromFloatCeil(value); } // FIXME: This function should be broken into something less monolithic. -// FIXME: The main loop here is very similar to LineBreaker::nextSegmentBreak. They can probably reuse code. -void RenderParagraph::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const -{ - float inlineMax = 0; - float inlineMin = 0; - - RenderStyle* styleToUse = style(); - RenderBlock* containingBlock = this->containingBlock(); - LayoutUnit cw = containingBlock ? containingBlock->contentLogicalWidth() : LayoutUnit(); - - // If we are at the start of a line, we want to ignore all white-space. - // Also strip spaces if we previously had text that ended in a trailing space. - bool stripFrontSpaces = true; - RenderObject* trailingSpaceChild = 0; - - bool autoWrap, oldAutoWrap; - autoWrap = oldAutoWrap = styleToUse->autoWrap(); - - InlineMinMaxIterator childIterator(const_cast(this)); - - // Only gets added to the max preffered width once. - bool addedTextIndent = false; - // Signals the text indent was more negative than the min preferred width - bool hasRemainingNegativeTextIndent = false; - - LayoutUnit textIndent = minimumValueForLength(styleToUse->textIndent(), cw); - bool isPrevChildInlineFlow = false; - bool shouldBreakLineAfterText = false; - while (RenderObject* child = childIterator.next()) { - autoWrap = child->isReplaced() ? child->parent()->style()->autoWrap() : - child->style()->autoWrap(); - - // Step One: determine whether or not we need to go ahead and - // terminate our current line. Each discrete chunk can become - // the new min-width, if it is the widest chunk seen so far, and - // it can also become the max-width. - - // Children fall into three categories: - // (1) An inline flow object. These objects always have a min/max of 0, - // and are included in the iteration solely so that their margins can - // be added in. - // - // (2) An inline non-text non-flow object, e.g., an inline replaced element. - // These objects can always be on a line by themselves, so in this situation - // we need to go ahead and break the current line, and then add in our own - // margins and min/max width on its own line, and then terminate the line. - // - // (3) A text object. Text runs can have breakable characters at the start, - // the middle or the end. They may also lose whitespace off the front if - // we're already ignoring whitespace. In order to compute accurate min-width - // information, we need three pieces of information. - // (a) the min-width of the first non-breakable run. Should be 0 if the text string - // starts with whitespace. - // (b) the min-width of the last non-breakable run. Should be 0 if the text string - // ends with whitespace. - // (c) the min/max width of the string (trimmed for whitespace). - // - // If the text string starts with whitespace, then we need to go ahead and - // terminate our current line (unless we're already in a whitespace stripping - // mode. - // - // If the text string has a breakable character in the middle, but didn't start - // with whitespace, then we add the width of the first non-breakable run and - // then end the current line. We then need to use the intermediate min/max width - // values (if any of them are larger than our current min/max). We then look at - // the width of the last non-breakable run and use that to start a new line - // (unless we end in whitespace). - RenderStyle* childStyle = child->style(); - float childMin = 0; - float childMax = 0; - - if (!child->isText()) { - // Case (1) and (2). Inline replaced and inline flow elements. - if (child->isRenderInline()) { - // Add in padding/border/margin from the appropriate side of - // the element. - float bpm = getBorderPaddingMargin(toRenderInline(child), childIterator.endOfInline).toFloat(); - childMin += bpm; - childMax += bpm; - - inlineMin += childMin; - inlineMax += childMax; - - child->clearPreferredLogicalWidthsDirty(); - } else { - // Inline replaced elts add in their margins to their min/max values. - LayoutUnit margins = 0; - Length startMargin = childStyle->marginStart(); - Length endMargin = childStyle->marginEnd(); - if (startMargin.isFixed()) - margins += adjustFloatForSubPixelLayout(startMargin.value()); - if (endMargin.isFixed()) - margins += adjustFloatForSubPixelLayout(endMargin.value()); - childMin += margins.ceilToFloat(); - childMax += margins.ceilToFloat(); - } +// FIXME: The main loop here is very similar to LineBreaker::nextSegmentBreak. +// They can probably reuse code. +void RenderParagraph::computeIntrinsicLogicalWidths( + LayoutUnit& minLogicalWidth, + LayoutUnit& maxLogicalWidth) const { + float inlineMax = 0; + float inlineMin = 0; + + RenderStyle* styleToUse = style(); + RenderBlock* containingBlock = this->containingBlock(); + LayoutUnit cw = + containingBlock ? containingBlock->contentLogicalWidth() : LayoutUnit(); + + // If we are at the start of a line, we want to ignore all white-space. + // Also strip spaces if we previously had text that ended in a trailing space. + bool stripFrontSpaces = true; + RenderObject* trailingSpaceChild = 0; + + bool autoWrap, oldAutoWrap; + autoWrap = oldAutoWrap = styleToUse->autoWrap(); + + InlineMinMaxIterator childIterator(const_cast(this)); + + // Only gets added to the max preffered width once. + bool addedTextIndent = false; + // Signals the text indent was more negative than the min preferred width + bool hasRemainingNegativeTextIndent = false; + + LayoutUnit textIndent = minimumValueForLength(styleToUse->textIndent(), cw); + bool isPrevChildInlineFlow = false; + bool shouldBreakLineAfterText = false; + while (RenderObject* child = childIterator.next()) { + autoWrap = child->isReplaced() ? child->parent()->style()->autoWrap() + : child->style()->autoWrap(); + + // Step One: determine whether or not we need to go ahead and + // terminate our current line. Each discrete chunk can become + // the new min-width, if it is the widest chunk seen so far, and + // it can also become the max-width. + + // Children fall into three categories: + // (1) An inline flow object. These objects always have a min/max of 0, + // and are included in the iteration solely so that their margins can + // be added in. + // + // (2) An inline non-text non-flow object, e.g., an inline replaced element. + // These objects can always be on a line by themselves, so in this situation + // we need to go ahead and break the current line, and then add in our own + // margins and min/max width on its own line, and then terminate the line. + // + // (3) A text object. Text runs can have breakable characters at the start, + // the middle or the end. They may also lose whitespace off the front if + // we're already ignoring whitespace. In order to compute accurate min-width + // information, we need three pieces of information. + // (a) the min-width of the first non-breakable run. Should be 0 if the text + // string starts with whitespace. (b) the min-width of the last + // non-breakable run. Should be 0 if the text string ends with whitespace. + // (c) the min/max width of the string (trimmed for whitespace). + // + // If the text string starts with whitespace, then we need to go ahead and + // terminate our current line (unless we're already in a whitespace + // stripping mode. + // + // If the text string has a breakable character in the middle, but didn't + // start with whitespace, then we add the width of the first non-breakable + // run and then end the current line. We then need to use the intermediate + // min/max width values (if any of them are larger than our current + // min/max). We then look at the width of the last non-breakable run and use + // that to start a new line (unless we end in whitespace). + RenderStyle* childStyle = child->style(); + float childMin = 0; + float childMax = 0; + + if (!child->isText()) { + // Case (1) and (2). Inline replaced and inline flow elements. + if (child->isRenderInline()) { + // Add in padding/border/margin from the appropriate side of + // the element. + float bpm = getBorderPaddingMargin(toRenderInline(child), + childIterator.endOfInline) + .toFloat(); + childMin += bpm; + childMax += bpm; + + inlineMin += childMin; + inlineMax += childMax; + + child->clearPreferredLogicalWidthsDirty(); + } else { + // Inline replaced elts add in their margins to their min/max values. + LayoutUnit margins = 0; + Length startMargin = childStyle->marginStart(); + Length endMargin = childStyle->marginEnd(); + if (startMargin.isFixed()) + margins += adjustFloatForSubPixelLayout(startMargin.value()); + if (endMargin.isFixed()) + margins += adjustFloatForSubPixelLayout(endMargin.value()); + childMin += margins.ceilToFloat(); + childMax += margins.ceilToFloat(); + } + } + + if (!child->isRenderInline() && !child->isText()) { + // Case (2). Inline replaced elements and floats. + // Go ahead and terminate the current line as far as + // minwidth is concerned. + LayoutUnit childMinPreferredLogicalWidth = + child->minPreferredLogicalWidth(); + LayoutUnit childMaxPreferredLogicalWidth = + child->maxPreferredLogicalWidth(); + childMin += childMinPreferredLogicalWidth.ceilToFloat(); + childMax += childMaxPreferredLogicalWidth.ceilToFloat(); + + bool canBreakReplacedElement = true; + if ((canBreakReplacedElement && (autoWrap || oldAutoWrap) && + (!isPrevChildInlineFlow || shouldBreakLineAfterText))) { + updatePreferredWidth(minLogicalWidth, inlineMin); + inlineMin = 0; + } + + // Add in text-indent. This is added in only once. + if (!addedTextIndent) { + float ceiledTextIndent = textIndent.ceilToFloat(); + childMin += ceiledTextIndent; + childMax += ceiledTextIndent; + + if (childMin < 0) + textIndent = adjustFloatForSubPixelLayout(childMin); + else + addedTextIndent = true; + } + + // Add our width to the max. + inlineMax += std::max(0, childMax); + + if (!autoWrap || !canBreakReplacedElement || + (isPrevChildInlineFlow && !shouldBreakLineAfterText)) { + inlineMin += childMin; + } else { + // Now check our line. + updatePreferredWidth(minLogicalWidth, childMin); + + // Now start a new line. + inlineMin = 0; + } + + if (autoWrap && canBreakReplacedElement && isPrevChildInlineFlow) { + updatePreferredWidth(minLogicalWidth, inlineMin); + inlineMin = 0; + } + + // We are no longer stripping whitespace at the start of + // a line. + stripFrontSpaces = false; + trailingSpaceChild = 0; + } else if (child->isText()) { + // Case (3). Text. + RenderText* t = toRenderText(child); + + // Determine if we have a breakable character. Pass in + // whether or not we should ignore any spaces at the front + // of the string. If those are going to be stripped out, + // then they shouldn't be considered in the breakable char + // check. + bool hasBreakableChar, hasBreak; + float firstLineMinWidth, lastLineMinWidth; + bool hasBreakableStart, hasBreakableEnd; + float firstLineMaxWidth, lastLineMaxWidth; + t->trimmedPrefWidths(inlineMax, firstLineMinWidth, hasBreakableStart, + lastLineMinWidth, hasBreakableEnd, hasBreakableChar, + hasBreak, firstLineMaxWidth, lastLineMaxWidth, + childMin, childMax, stripFrontSpaces, + styleToUse->direction()); + + // This text object will not be rendered, but it may still provide a + // breaking opportunity. + if (!hasBreak && !childMax) { + if (autoWrap && (hasBreakableStart || hasBreakableEnd)) { + updatePreferredWidth(minLogicalWidth, inlineMin); + inlineMin = 0; } + continue; + } - if (!child->isRenderInline() && !child->isText()) { - // Case (2). Inline replaced elements and floats. - // Go ahead and terminate the current line as far as - // minwidth is concerned. - LayoutUnit childMinPreferredLogicalWidth = child->minPreferredLogicalWidth(); - LayoutUnit childMaxPreferredLogicalWidth = child->maxPreferredLogicalWidth(); - childMin += childMinPreferredLogicalWidth.ceilToFloat(); - childMax += childMaxPreferredLogicalWidth.ceilToFloat(); - - bool canBreakReplacedElement = true; - if ((canBreakReplacedElement && (autoWrap || oldAutoWrap) && (!isPrevChildInlineFlow || shouldBreakLineAfterText))) { - updatePreferredWidth(minLogicalWidth, inlineMin); - inlineMin = 0; - } - - // Add in text-indent. This is added in only once. - if (!addedTextIndent) { - float ceiledTextIndent = textIndent.ceilToFloat(); - childMin += ceiledTextIndent; - childMax += ceiledTextIndent; - - if (childMin < 0) - textIndent = adjustFloatForSubPixelLayout(childMin); - else - addedTextIndent = true; - } - - // Add our width to the max. - inlineMax += std::max(0, childMax); - - if (!autoWrap || !canBreakReplacedElement || (isPrevChildInlineFlow && !shouldBreakLineAfterText)) { - inlineMin += childMin; - } else { - // Now check our line. - updatePreferredWidth(minLogicalWidth, childMin); - - // Now start a new line. - inlineMin = 0; - } - - if (autoWrap && canBreakReplacedElement && isPrevChildInlineFlow) { - updatePreferredWidth(minLogicalWidth, inlineMin); - inlineMin = 0; - } - - // We are no longer stripping whitespace at the start of - // a line. - stripFrontSpaces = false; - trailingSpaceChild = 0; - } else if (child->isText()) { - // Case (3). Text. - RenderText* t = toRenderText(child); - - // Determine if we have a breakable character. Pass in - // whether or not we should ignore any spaces at the front - // of the string. If those are going to be stripped out, - // then they shouldn't be considered in the breakable char - // check. - bool hasBreakableChar, hasBreak; - float firstLineMinWidth, lastLineMinWidth; - bool hasBreakableStart, hasBreakableEnd; - float firstLineMaxWidth, lastLineMaxWidth; - t->trimmedPrefWidths(inlineMax, - firstLineMinWidth, hasBreakableStart, lastLineMinWidth, hasBreakableEnd, - hasBreakableChar, hasBreak, firstLineMaxWidth, lastLineMaxWidth, - childMin, childMax, stripFrontSpaces, styleToUse->direction()); - - // This text object will not be rendered, but it may still provide a breaking opportunity. - if (!hasBreak && !childMax) { - if (autoWrap && (hasBreakableStart || hasBreakableEnd)) { - updatePreferredWidth(minLogicalWidth, inlineMin); - inlineMin = 0; - } - continue; - } - - if (stripFrontSpaces) - trailingSpaceChild = child; - else - trailingSpaceChild = 0; - - // Add in text-indent. This is added in only once. - float ti = 0; - if (!addedTextIndent || hasRemainingNegativeTextIndent) { - ti = textIndent.ceilToFloat(); - childMin += ti; - firstLineMinWidth += ti; - - // It the text indent negative and larger than the child minimum, we re-use the remainder - // in future minimum calculations, but using the negative value again on the maximum - // will lead to under-counting the max pref width. - if (!addedTextIndent) { - childMax += ti; - firstLineMaxWidth += ti; - addedTextIndent = true; - } - - if (childMin < 0) { - textIndent = childMin; - hasRemainingNegativeTextIndent = true; - } - } - - // If we have no breakable characters at all, - // then this is the easy case. We add ourselves to the current - // min and max and continue. - if (!hasBreakableChar) { - inlineMin += childMin; - } else { - if (hasBreakableStart) { - updatePreferredWidth(minLogicalWidth, inlineMin); - } else { - inlineMin += firstLineMinWidth; - updatePreferredWidth(minLogicalWidth, inlineMin); - childMin -= ti; - } - - inlineMin = childMin; - - if (hasBreakableEnd) { - updatePreferredWidth(minLogicalWidth, inlineMin); - inlineMin = 0; - shouldBreakLineAfterText = false; - } else { - updatePreferredWidth(minLogicalWidth, inlineMin); - inlineMin = lastLineMinWidth; - shouldBreakLineAfterText = true; - } - } - - if (hasBreak) { - inlineMax += firstLineMaxWidth; - updatePreferredWidth(maxLogicalWidth, inlineMax); - updatePreferredWidth(maxLogicalWidth, childMax); - inlineMax = lastLineMaxWidth; - addedTextIndent = true; - } else { - inlineMax += std::max(0, childMax); - } + if (stripFrontSpaces) + trailingSpaceChild = child; + else + trailingSpaceChild = 0; + + // Add in text-indent. This is added in only once. + float ti = 0; + if (!addedTextIndent || hasRemainingNegativeTextIndent) { + ti = textIndent.ceilToFloat(); + childMin += ti; + firstLineMinWidth += ti; + + // It the text indent negative and larger than the child minimum, we + // re-use the remainder in future minimum calculations, but using the + // negative value again on the maximum will lead to under-counting the + // max pref width. + if (!addedTextIndent) { + childMax += ti; + firstLineMaxWidth += ti; + addedTextIndent = true; } - if (!child->isText() && child->isRenderInline()) - isPrevChildInlineFlow = true; - else - isPrevChildInlineFlow = false; + if (childMin < 0) { + textIndent = childMin; + hasRemainingNegativeTextIndent = true; + } + } + + // If we have no breakable characters at all, + // then this is the easy case. We add ourselves to the current + // min and max and continue. + if (!hasBreakableChar) { + inlineMin += childMin; + } else { + if (hasBreakableStart) { + updatePreferredWidth(minLogicalWidth, inlineMin); + } else { + inlineMin += firstLineMinWidth; + updatePreferredWidth(minLogicalWidth, inlineMin); + childMin -= ti; + } + + inlineMin = childMin; - oldAutoWrap = autoWrap; + if (hasBreakableEnd) { + updatePreferredWidth(minLogicalWidth, inlineMin); + inlineMin = 0; + shouldBreakLineAfterText = false; + } else { + updatePreferredWidth(minLogicalWidth, inlineMin); + inlineMin = lastLineMinWidth; + shouldBreakLineAfterText = true; + } + } + + if (hasBreak) { + inlineMax += firstLineMaxWidth; + updatePreferredWidth(maxLogicalWidth, inlineMax); + updatePreferredWidth(maxLogicalWidth, childMax); + inlineMax = lastLineMaxWidth; + addedTextIndent = true; + } else { + inlineMax += std::max(0, childMax); + } } - if (styleToUse->collapseWhiteSpace()) - stripTrailingSpace(inlineMax, inlineMin, trailingSpaceChild); + if (!child->isText() && child->isRenderInline()) + isPrevChildInlineFlow = true; + else + isPrevChildInlineFlow = false; + + oldAutoWrap = autoWrap; + } + + if (styleToUse->collapseWhiteSpace()) + stripTrailingSpace(inlineMax, inlineMin, trailingSpaceChild); - updatePreferredWidth(minLogicalWidth, inlineMin); - updatePreferredWidth(maxLogicalWidth, inlineMax); + updatePreferredWidth(minLogicalWidth, inlineMin); + updatePreferredWidth(maxLogicalWidth, inlineMax); - maxLogicalWidth = std::max(minLogicalWidth, maxLogicalWidth); + maxLogicalWidth = std::max(minLogicalWidth, maxLogicalWidth); } -int RenderParagraph::firstLineBoxBaseline(FontBaselineOrAuto baselineType) const -{ +int RenderParagraph::firstLineBoxBaseline( + FontBaselineOrAuto baselineType) const { if (!firstLineBox()) return -1; FontBaseline baseline; @@ -1345,257 +1571,292 @@ int RenderParagraph::firstLineBoxBaseline(FontBaselineOrAuto baselineType) const baseline = firstRootBox()->baselineType(); else baseline = baselineType.m_baseline; - return firstLineBox()->logicalTop() + style(true)->fontMetrics().ascent(baseline); + return firstLineBox()->logicalTop() + + style(true)->fontMetrics().ascent(baseline); } -int RenderParagraph::lastLineBoxBaseline(LineDirectionMode lineDirection) const -{ - if (!firstLineBox() && hasLineIfEmpty()) { - const FontMetrics& fontMetrics = firstLineStyle()->fontMetrics(); - return fontMetrics.ascent() - + (lineHeight(true, lineDirection, PositionOfInteriorLineBoxes) - fontMetrics.height()) / 2 - + (lineDirection == HorizontalLine ? borderTop() + paddingTop() : borderRight() + paddingRight()); - } - if (lastLineBox()) - return lastLineBox()->logicalTop() + style(lastLineBox() == firstLineBox())->fontMetrics().ascent(lastRootBox()->baselineType()); - return -1; +int RenderParagraph::lastLineBoxBaseline( + LineDirectionMode lineDirection) const { + if (!firstLineBox() && hasLineIfEmpty()) { + const FontMetrics& fontMetrics = firstLineStyle()->fontMetrics(); + return fontMetrics.ascent() + + (lineHeight(true, lineDirection, PositionOfInteriorLineBoxes) - + fontMetrics.height()) / + 2 + + (lineDirection == HorizontalLine ? borderTop() + paddingTop() + : borderRight() + paddingRight()); + } + if (lastLineBox()) + return lastLineBox()->logicalTop() + + style(lastLineBox() == firstLineBox()) + ->fontMetrics() + .ascent(lastRootBox()->baselineType()); + return -1; } -void RenderParagraph::layout() -{ - ASSERT(needsLayout()); - ASSERT(isInlineBlock() || !isInline()); +void RenderParagraph::layout() { + ASSERT(needsLayout()); + ASSERT(isInlineBlock() || !isInline()); - if (simplifiedLayout()) - return; + if (simplifiedLayout()) + return; - SubtreeLayoutScope layoutScope(*this); + SubtreeLayoutScope layoutScope(*this); - LayoutUnit oldLeft = logicalLeft(); - bool logicalWidthChanged = updateLogicalWidthAndColumnWidth(); - bool relayoutChildren = logicalWidthChanged; + LayoutUnit oldLeft = logicalLeft(); + bool logicalWidthChanged = updateLogicalWidthAndColumnWidth(); + bool relayoutChildren = logicalWidthChanged; - LayoutUnit beforeEdge = borderBefore() + paddingBefore(); - LayoutUnit afterEdge = borderAfter() + paddingAfter(); - LayoutUnit previousHeight = logicalHeight(); - setLogicalHeight(beforeEdge); + LayoutUnit beforeEdge = borderBefore() + paddingBefore(); + LayoutUnit afterEdge = borderAfter() + paddingAfter(); + LayoutUnit previousHeight = logicalHeight(); + setLogicalHeight(beforeEdge); - layoutChildren(relayoutChildren, layoutScope, beforeEdge, afterEdge); + layoutChildren(relayoutChildren, layoutScope, beforeEdge, afterEdge); - LayoutUnit oldClientAfterEdge = clientLogicalBottom(); + LayoutUnit oldClientAfterEdge = clientLogicalBottom(); - updateLogicalHeight(); + updateLogicalHeight(); - if (previousHeight != logicalHeight()) - relayoutChildren = true; + if (previousHeight != logicalHeight()) + relayoutChildren = true; - layoutPositionedObjects(relayoutChildren, oldLeft != logicalLeft() ? ForcedLayoutAfterContainingBlockMoved : DefaultLayout); + layoutPositionedObjects(relayoutChildren, + oldLeft != logicalLeft() + ? ForcedLayoutAfterContainingBlockMoved + : DefaultLayout); - // Add overflow from children (unless we're multi-column, since in that case all our child overflow is clipped anyway). - computeOverflow(oldClientAfterEdge); + // Add overflow from children (unless we're multi-column, since in that case + // all our child overflow is clipped anyway). + computeOverflow(oldClientAfterEdge); - updateLayerTransformAfterLayout(); + updateLayerTransformAfterLayout(); - clearNeedsLayout(); + clearNeedsLayout(); } -void RenderParagraph::layoutChildren(bool relayoutChildren, SubtreeLayoutScope& layoutScope, LayoutUnit beforeEdge, LayoutUnit afterEdge) -{ - // Figure out if we should clear out our line boxes. - // FIXME: Handle resize eventually! - bool isFullLayout = !firstLineBox() || selfNeedsLayout() || relayoutChildren; - LineLayoutState layoutState(isFullLayout); - - if (isFullLayout) - lineBoxes()->deleteLineBoxes(); - - if (firstChild()) { - // In full layout mode, clear the line boxes of children upfront. Otherwise, - // siblings can run into stale root lineboxes during layout. Then layout - // the replaced elements later. In partial layout mode, line boxes are not - // deleted and only dirtied. In that case, we can layout the replaced - // elements at the same time. - Vector replacedChildren; - for (InlineWalker walker(this); !walker.atEnd(); walker.advance()) { - RenderObject* o = walker.current(); - - if (!layoutState.hasInlineChild() && o->isInline()) - layoutState.setHasInlineChild(true); - - if (o->isReplaced() || o->isOutOfFlowPositioned()) { - RenderBox* box = toRenderBox(o); - - updateBlockChildDirtyBitsBeforeLayout(relayoutChildren, box); - - if (o->isOutOfFlowPositioned()) { - o->containingBlock()->insertPositionedObject(box); - } else if (isFullLayout || o->needsLayout()) { - // Replaced element. - box->dirtyLineBoxes(isFullLayout); - if (isFullLayout) - replacedChildren.append(box); - else - o->layoutIfNeeded(); - } - } else if (o->isText() || (o->isRenderInline() && !walker.atEndOfInline())) { - if (!o->isText()) - toRenderInline(o)->updateAlwaysCreateLineBoxes(layoutState.isFullLayout()); - if (layoutState.isFullLayout() || o->selfNeedsLayout()) - dirtyLineBoxesForRenderer(o, layoutState.isFullLayout()); - o->clearNeedsLayout(); - } +void RenderParagraph::layoutChildren(bool relayoutChildren, + SubtreeLayoutScope& layoutScope, + LayoutUnit beforeEdge, + LayoutUnit afterEdge) { + // Figure out if we should clear out our line boxes. + // FIXME: Handle resize eventually! + bool isFullLayout = !firstLineBox() || selfNeedsLayout() || relayoutChildren; + LineLayoutState layoutState(isFullLayout); + + if (isFullLayout) + lineBoxes()->deleteLineBoxes(); + + if (firstChild()) { + // In full layout mode, clear the line boxes of children upfront. Otherwise, + // siblings can run into stale root lineboxes during layout. Then layout + // the replaced elements later. In partial layout mode, line boxes are not + // deleted and only dirtied. In that case, we can layout the replaced + // elements at the same time. + Vector replacedChildren; + for (InlineWalker walker(this); !walker.atEnd(); walker.advance()) { + RenderObject* o = walker.current(); + + if (!layoutState.hasInlineChild() && o->isInline()) + layoutState.setHasInlineChild(true); + + if (o->isReplaced() || o->isOutOfFlowPositioned()) { + RenderBox* box = toRenderBox(o); + + updateBlockChildDirtyBitsBeforeLayout(relayoutChildren, box); + + if (o->isOutOfFlowPositioned()) { + o->containingBlock()->insertPositionedObject(box); + } else if (isFullLayout || o->needsLayout()) { + // Replaced element. + box->dirtyLineBoxes(isFullLayout); + if (isFullLayout) + replacedChildren.append(box); + else + o->layoutIfNeeded(); } - - for (size_t i = 0; i < replacedChildren.size(); i++) - replacedChildren[i]->layoutIfNeeded(); - - layoutRunsAndFloats(layoutState); + } else if (o->isText() || + (o->isRenderInline() && !walker.atEndOfInline())) { + if (!o->isText()) + toRenderInline(o)->updateAlwaysCreateLineBoxes( + layoutState.isFullLayout()); + if (layoutState.isFullLayout() || o->selfNeedsLayout()) + dirtyLineBoxesForRenderer(o, layoutState.isFullLayout()); + o->clearNeedsLayout(); + } } - // Expand the last line to accommodate Ruby and emphasis marks. - int lastLineAnnotationsAdjustment = 0; - if (lastRootBox()) { - LayoutUnit lowestAllowedPosition = std::max(lastRootBox()->lineBottom(), logicalHeight() + paddingAfter()); - lastLineAnnotationsAdjustment = lastRootBox()->computeUnderAnnotationAdjustment(lowestAllowedPosition); - } + for (size_t i = 0; i < replacedChildren.size(); i++) + replacedChildren[i]->layoutIfNeeded(); - // Now add in the bottom border/padding. - setLogicalHeight(logicalHeight() + lastLineAnnotationsAdjustment + afterEdge); + layoutRunsAndFloats(layoutState); + } - if (!firstLineBox() && hasLineIfEmpty()) - setLogicalHeight(logicalHeight() + lineHeight(true, HorizontalLine, PositionOfInteriorLineBoxes)); -} + // Expand the last line to accommodate Ruby and emphasis marks. + int lastLineAnnotationsAdjustment = 0; + if (lastRootBox()) { + LayoutUnit lowestAllowedPosition = + std::max(lastRootBox()->lineBottom(), logicalHeight() + paddingAfter()); + lastLineAnnotationsAdjustment = + lastRootBox()->computeUnderAnnotationAdjustment(lowestAllowedPosition); + } -RootInlineBox* RenderParagraph::determineStartPosition(LineLayoutState& layoutState, InlineBidiResolver& resolver) -{ - RootInlineBox* curr = 0; - RootInlineBox* last = 0; + // Now add in the bottom border/padding. + setLogicalHeight(logicalHeight() + lastLineAnnotationsAdjustment + afterEdge); - if (layoutState.isFullLayout()) { - // If we encountered a new float and have inline children, mark ourself to force us to issue paint invalidations. - if (layoutState.hasInlineChild() && !selfNeedsLayout()) { - setNeedsLayout(MarkOnlyThis); - } - - // FIXME: This should just call deleteLineBoxTree, but that causes - // crashes for fast/repaint tests. - curr = firstRootBox(); - while (curr) { - // Note: This uses nextRootBox() insted of nextLineBox() like deleteLineBoxTree does. - RootInlineBox* next = curr->nextRootBox(); - curr->deleteLine(); - curr = next; - } - ASSERT(!firstLineBox() && !lastLineBox()); - } else { - if (curr) { - // We have a dirty line. - if (RootInlineBox* prevRootBox = curr->prevRootBox()) { - // We have a previous line. - if (!prevRootBox->endsWithBreak() || !prevRootBox->lineBreakObj() || (prevRootBox->lineBreakObj()->isText() && prevRootBox->lineBreakPos() >= toRenderText(prevRootBox->lineBreakObj())->textLength())) - // The previous line didn't break cleanly or broke at a newline - // that has been deleted, so treat it as dirty too. - curr = prevRootBox; - } - } else { - // No dirty lines were found. - // If the last line didn't break cleanly, treat it as dirty. - if (lastRootBox() && !lastRootBox()->endsWithBreak()) - curr = lastRootBox(); - } + if (!firstLineBox() && hasLineIfEmpty()) + setLogicalHeight(logicalHeight() + lineHeight(true, HorizontalLine, + PositionOfInteriorLineBoxes)); +} - // If we have no dirty lines, then last is just the last root box. - last = curr ? curr->prevRootBox() : lastRootBox(); +RootInlineBox* RenderParagraph::determineStartPosition( + LineLayoutState& layoutState, + InlineBidiResolver& resolver) { + RootInlineBox* curr = 0; + RootInlineBox* last = 0; + + if (layoutState.isFullLayout()) { + // If we encountered a new float and have inline children, mark ourself to + // force us to issue paint invalidations. + if (layoutState.hasInlineChild() && !selfNeedsLayout()) { + setNeedsLayout(MarkOnlyThis); } - layoutState.lineInfo().setFirstLine(!last); - layoutState.lineInfo().setPreviousLineBrokeCleanly(!last || last->endsWithBreak()); - - if (last) { - setLogicalHeight(last->lineBottomWithLeading()); - InlineIterator iter = InlineIterator(this, last->lineBreakObj(), last->lineBreakPos()); - resolver.setPosition(iter, numberOfIsolateAncestors(iter)); - resolver.setStatus(last->lineBreakBidiStatus()); - } else { - TextDirection direction = style()->direction(); - if (style()->unicodeBidi() == Plaintext) - direction = determinePlaintextDirectionality(this); - resolver.setStatus(BidiStatus(direction, isOverride(style()->unicodeBidi()))); - InlineIterator iter = InlineIterator(this, bidiFirstSkippingEmptyInlines(this, resolver.runs(), &resolver), 0); - resolver.setPosition(iter, numberOfIsolateAncestors(iter)); + // FIXME: This should just call deleteLineBoxTree, but that causes + // crashes for fast/repaint tests. + curr = firstRootBox(); + while (curr) { + // Note: This uses nextRootBox() insted of nextLineBox() like + // deleteLineBoxTree does. + RootInlineBox* next = curr->nextRootBox(); + curr->deleteLine(); + curr = next; } - return curr; -} - -void RenderParagraph::determineEndPosition(LineLayoutState& layoutState, RootInlineBox* startLine, InlineIterator& cleanLineStart, BidiStatus& cleanLineBidiStatus) -{ - ASSERT(!layoutState.endLine()); - RootInlineBox* last = 0; - for (RootInlineBox* curr = startLine->nextRootBox(); curr; curr = curr->nextRootBox()) { - if (curr->isDirty()) - last = 0; - else if (!last) - last = curr; + ASSERT(!firstLineBox() && !lastLineBox()); + } else { + if (curr) { + // We have a dirty line. + if (RootInlineBox* prevRootBox = curr->prevRootBox()) { + // We have a previous line. + if (!prevRootBox->endsWithBreak() || !prevRootBox->lineBreakObj() || + (prevRootBox->lineBreakObj()->isText() && + prevRootBox->lineBreakPos() >= + toRenderText(prevRootBox->lineBreakObj())->textLength())) + // The previous line didn't break cleanly or broke at a newline + // that has been deleted, so treat it as dirty too. + curr = prevRootBox; + } + } else { + // No dirty lines were found. + // If the last line didn't break cleanly, treat it as dirty. + if (lastRootBox() && !lastRootBox()->endsWithBreak()) + curr = lastRootBox(); } - if (!last) - return; - - // At this point, |last| is the first line in a run of clean lines that ends with the last line - // in the block. - - RootInlineBox* prev = last->prevRootBox(); - cleanLineStart = InlineIterator(this, prev->lineBreakObj(), prev->lineBreakPos()); - cleanLineBidiStatus = prev->lineBreakBidiStatus(); - layoutState.setEndLineLogicalTop(prev->lineBottomWithLeading()); - - for (RootInlineBox* line = last; line; line = line->nextRootBox()) - line->extractLine(); // Disconnect all line boxes from their render objects while preserving - // their connections to one another. + // If we have no dirty lines, then last is just the last root box. + last = curr ? curr->prevRootBox() : lastRootBox(); + } + + layoutState.lineInfo().setFirstLine(!last); + layoutState.lineInfo().setPreviousLineBrokeCleanly(!last || + last->endsWithBreak()); + + if (last) { + setLogicalHeight(last->lineBottomWithLeading()); + InlineIterator iter = + InlineIterator(this, last->lineBreakObj(), last->lineBreakPos()); + resolver.setPosition(iter, numberOfIsolateAncestors(iter)); + resolver.setStatus(last->lineBreakBidiStatus()); + } else { + TextDirection direction = style()->direction(); + if (style()->unicodeBidi() == Plaintext) + direction = determinePlaintextDirectionality(this); + resolver.setStatus( + BidiStatus(direction, isOverride(style()->unicodeBidi()))); + InlineIterator iter = InlineIterator( + this, bidiFirstSkippingEmptyInlines(this, resolver.runs(), &resolver), + 0); + resolver.setPosition(iter, numberOfIsolateAncestors(iter)); + } + return curr; +} - layoutState.setEndLine(last); +void RenderParagraph::determineEndPosition(LineLayoutState& layoutState, + RootInlineBox* startLine, + InlineIterator& cleanLineStart, + BidiStatus& cleanLineBidiStatus) { + ASSERT(!layoutState.endLine()); + RootInlineBox* last = 0; + for (RootInlineBox* curr = startLine->nextRootBox(); curr; + curr = curr->nextRootBox()) { + if (curr->isDirty()) + last = 0; + else if (!last) + last = curr; + } + + if (!last) + return; + + // At this point, |last| is the first line in a run of clean lines that ends + // with the last line in the block. + + RootInlineBox* prev = last->prevRootBox(); + cleanLineStart = + InlineIterator(this, prev->lineBreakObj(), prev->lineBreakPos()); + cleanLineBidiStatus = prev->lineBreakBidiStatus(); + layoutState.setEndLineLogicalTop(prev->lineBottomWithLeading()); + + for (RootInlineBox* line = last; line; line = line->nextRootBox()) + line->extractLine(); // Disconnect all line boxes from their render objects + // while preserving their connections to one another. + + layoutState.setEndLine(last); } -bool RenderParagraph::checkPaginationAndFloatsAtEndLine(LineLayoutState& layoutState) -{ - // FIXME(sky): Remove this. - return true; +bool RenderParagraph::checkPaginationAndFloatsAtEndLine( + LineLayoutState& layoutState) { + // FIXME(sky): Remove this. + return true; } -bool RenderParagraph::matchedEndLine(LineLayoutState& layoutState, const InlineBidiResolver& resolver, const InlineIterator& endLineStart, const BidiStatus& endLineStatus) -{ - if (resolver.position() == endLineStart) { - if (resolver.status() != endLineStatus) - return false; - return checkPaginationAndFloatsAtEndLine(layoutState); - } +bool RenderParagraph::matchedEndLine(LineLayoutState& layoutState, + const InlineBidiResolver& resolver, + const InlineIterator& endLineStart, + const BidiStatus& endLineStatus) { + if (resolver.position() == endLineStart) { + if (resolver.status() != endLineStatus) + return false; + return checkPaginationAndFloatsAtEndLine(layoutState); + } + + // The first clean line doesn't match, but we can check a handful of following + // lines to try to match back up. + static int numLines = 8; // The # of lines we're willing to match against. + RootInlineBox* originalEndLine = layoutState.endLine(); + RootInlineBox* line = originalEndLine; + for (int i = 0; i < numLines && line; i++, line = line->nextRootBox()) { + if (line->lineBreakObj() == resolver.position().object() && + line->lineBreakPos() == resolver.position().offset()) { + // We have a match. + if (line->lineBreakBidiStatus() != resolver.status()) + return false; // ...but the bidi state doesn't match. + + bool matched = false; + RootInlineBox* result = line->nextRootBox(); + layoutState.setEndLine(result); + if (result) { + layoutState.setEndLineLogicalTop(line->lineBottomWithLeading()); + matched = checkPaginationAndFloatsAtEndLine(layoutState); + } - // The first clean line doesn't match, but we can check a handful of following lines to try - // to match back up. - static int numLines = 8; // The # of lines we're willing to match against. - RootInlineBox* originalEndLine = layoutState.endLine(); - RootInlineBox* line = originalEndLine; - for (int i = 0; i < numLines && line; i++, line = line->nextRootBox()) { - if (line->lineBreakObj() == resolver.position().object() && line->lineBreakPos() == resolver.position().offset()) { - // We have a match. - if (line->lineBreakBidiStatus() != resolver.status()) - return false; // ...but the bidi state doesn't match. - - bool matched = false; - RootInlineBox* result = line->nextRootBox(); - layoutState.setEndLine(result); - if (result) { - layoutState.setEndLineLogicalTop(line->lineBottomWithLeading()); - matched = checkPaginationAndFloatsAtEndLine(layoutState); - } - - // Now delete the lines that we failed to sync. - deleteLineRange(layoutState, originalEndLine, result); - return matched; - } + // Now delete the lines that we failed to sync. + deleteLineRange(layoutState, originalEndLine, result); + return matched; } + } - return false; + return false; } -} // namespace blink +} // namespace blink diff --git a/sky/engine/core/rendering/RenderParagraph.h b/sky/engine/core/rendering/RenderParagraph.h index 0e7b11331da1d..fb21a2c51f828 100644 --- a/sky/engine/core/rendering/RenderParagraph.h +++ b/sky/engine/core/rendering/RenderParagraph.h @@ -15,102 +15,162 @@ struct BidiRun; class InlineIterator; class RenderParagraph final : public RenderBlock { -public: - explicit RenderParagraph(); - virtual ~RenderParagraph(); - - bool isRenderParagraph() const final { return true; } - - void layout() final; - - LayoutUnit logicalRightOffsetForLine(bool shouldIndentText) const - { - LayoutUnit right = logicalRightOffsetForContent(); - if (shouldIndentText && !style()->isLeftToRightDirection()) - right -= textIndentOffset(); - return right; - } - LayoutUnit logicalLeftOffsetForLine(bool shouldIndentText) const - { - LayoutUnit left = logicalLeftOffsetForContent(); - if (shouldIndentText && style()->isLeftToRightDirection()) - left += textIndentOffset(); - return left; - } - - LayoutUnit logicalLeftSelectionOffset(RenderBlock* rootBlock, LayoutUnit position) final; - LayoutUnit logicalRightSelectionOffset(RenderBlock* rootBlock, LayoutUnit position) final; - - virtual RootInlineBox* lineAtIndex(int) const; - virtual int lineCount(const RootInlineBox* = 0, bool* = 0) const; - - void deleteLineBoxTree() final; - - GapRects inlineSelectionGaps(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, - LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const PaintInfo*); - - static bool shouldSkipCreatingRunsForObject(RenderObject* obj) - { - return obj->isOutOfFlowPositioned() && !obj->style()->isOriginalDisplayInlineType() && !obj->container()->isRenderInline(); - } - - bool didExceedMaxLines() const { return m_didExceedMaxLines; } - - // TODO(ojan): Remove the need for these. - using RenderBlock::lineBoxes; - using RenderBlock::firstLineBox; - using RenderBlock::lastRootBox; - -protected: - void addOverflowFromChildren() final; - - void simplifiedNormalFlowLayout() final; - - void paintChildren(PaintInfo&, const LayoutPoint&, Vector& layers) final; - - bool hitTestContents(const HitTestRequest&, HitTestResult&, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset) final; - - virtual ETextAlign textAlignmentForLine(bool endsWithSoftBreak) const; - - void computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const final; - - int firstLineBoxBaseline(FontBaselineOrAuto baselineType) const final; - int lastLineBoxBaseline(LineDirectionMode) const final; - -private: - virtual const char* renderName() const override; - - void layoutChildren(bool relayoutChildren, SubtreeLayoutScope&, LayoutUnit beforeEdge, LayoutUnit afterEdge); - - void markLinesDirtyInBlockRange(LayoutUnit logicalTop, LayoutUnit logicalBottom, RootInlineBox* highest = 0); - - void updateLogicalWidthForAlignment(const ETextAlign&, const RootInlineBox*, BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float& availableLogicalWidth, unsigned expansionOpportunityCount); - - RootInlineBox* createAndAppendRootInlineBox(); - RootInlineBox* createRootInlineBox(); - InlineFlowBox* createLineBoxes(RenderObject*, const LineInfo&, InlineBox* childBox); - InlineBox* createInlineBoxForRenderer(RenderObject*, bool isRootLineBox, bool isOnlyRun = false); - RootInlineBox* constructLine(BidiRunList&, const LineInfo&); - void computeInlineDirectionPositionsForLine(RootInlineBox*, const LineInfo&, BidiRun* firstRun, BidiRun* trailingSpaceRun, bool reachedEnd, GlyphOverflowAndFallbackFontsMap&, VerticalPositionCache&, WordMeasurements&); - BidiRun* computeInlineDirectionPositionsForSegment(RootInlineBox*, const LineInfo&, ETextAlign, float& logicalLeft, - float& availableLogicalWidth, BidiRun* firstRun, BidiRun* trailingSpaceRun, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache&, WordMeasurements&); - void computeBlockDirectionPositionsForLine(RootInlineBox*, BidiRun*, GlyphOverflowAndFallbackFontsMap&, VerticalPositionCache&); - // Helper function for layoutChildren() - RootInlineBox* createLineBoxesFromBidiRuns(unsigned bidiLevel, BidiRunList&, const InlineIterator& end, LineInfo&, VerticalPositionCache&, BidiRun* trailingSpaceRun, WordMeasurements&); - void layoutRunsAndFloats(LineLayoutState&); - void layoutRunsAndFloatsInRange(LineLayoutState&, InlineBidiResolver&, - const InlineIterator& cleanLineStart, const BidiStatus& cleanLineBidiStatus); - void linkToEndLineIfNeeded(LineLayoutState&); - RootInlineBox* determineStartPosition(LineLayoutState&, InlineBidiResolver&); - void determineEndPosition(LineLayoutState&, RootInlineBox* startBox, InlineIterator& cleanLineStart, BidiStatus& cleanLineBidiStatus); - bool checkPaginationAndFloatsAtEndLine(LineLayoutState&); - bool matchedEndLine(LineLayoutState&, const InlineBidiResolver&, const InlineIterator& endLineStart, const BidiStatus& endLineStatus); - - bool m_didExceedMaxLines; + public: + explicit RenderParagraph(); + virtual ~RenderParagraph(); + + bool isRenderParagraph() const final { return true; } + + void layout() final; + + LayoutUnit logicalRightOffsetForLine(bool shouldIndentText) const { + LayoutUnit right = logicalRightOffsetForContent(); + if (shouldIndentText && !style()->isLeftToRightDirection()) + right -= textIndentOffset(); + return right; + } + LayoutUnit logicalLeftOffsetForLine(bool shouldIndentText) const { + LayoutUnit left = logicalLeftOffsetForContent(); + if (shouldIndentText && style()->isLeftToRightDirection()) + left += textIndentOffset(); + return left; + } + + LayoutUnit logicalLeftSelectionOffset(RenderBlock* rootBlock, + LayoutUnit position) final; + LayoutUnit logicalRightSelectionOffset(RenderBlock* rootBlock, + LayoutUnit position) final; + + virtual RootInlineBox* lineAtIndex(int) const; + virtual int lineCount(const RootInlineBox* = 0, bool* = 0) const; + + void deleteLineBoxTree() final; + + GapRects inlineSelectionGaps(RenderBlock* rootBlock, + const LayoutPoint& rootBlockPhysicalPosition, + const LayoutSize& offsetFromRootBlock, + LayoutUnit& lastLogicalTop, + LayoutUnit& lastLogicalLeft, + LayoutUnit& lastLogicalRight, + const PaintInfo*); + + static bool shouldSkipCreatingRunsForObject(RenderObject* obj) { + return obj->isOutOfFlowPositioned() && + !obj->style()->isOriginalDisplayInlineType() && + !obj->container()->isRenderInline(); + } + + bool didExceedMaxLines() const { return m_didExceedMaxLines; } + + // TODO(ojan): Remove the need for these. + using RenderBlock::firstLineBox; + using RenderBlock::lastRootBox; + using RenderBlock::lineBoxes; + + protected: + void addOverflowFromChildren() final; + + void simplifiedNormalFlowLayout() final; + + void paintChildren(PaintInfo&, + const LayoutPoint&, + Vector& layers) final; + + bool hitTestContents(const HitTestRequest&, + HitTestResult&, + const HitTestLocation& locationInContainer, + const LayoutPoint& accumulatedOffset) final; + + virtual ETextAlign textAlignmentForLine(bool endsWithSoftBreak) const; + + void computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, + LayoutUnit& maxLogicalWidth) const final; + + int firstLineBoxBaseline(FontBaselineOrAuto baselineType) const final; + int lastLineBoxBaseline(LineDirectionMode) const final; + + private: + virtual const char* renderName() const override; + + void layoutChildren(bool relayoutChildren, + SubtreeLayoutScope&, + LayoutUnit beforeEdge, + LayoutUnit afterEdge); + + void markLinesDirtyInBlockRange(LayoutUnit logicalTop, + LayoutUnit logicalBottom, + RootInlineBox* highest = 0); + + void updateLogicalWidthForAlignment(const ETextAlign&, + const RootInlineBox*, + BidiRun* trailingSpaceRun, + float& logicalLeft, + float& totalLogicalWidth, + float& availableLogicalWidth, + unsigned expansionOpportunityCount); + + RootInlineBox* createAndAppendRootInlineBox(); + RootInlineBox* createRootInlineBox(); + InlineFlowBox* createLineBoxes(RenderObject*, + const LineInfo&, + InlineBox* childBox); + InlineBox* createInlineBoxForRenderer(RenderObject*, + bool isRootLineBox, + bool isOnlyRun = false); + RootInlineBox* constructLine(BidiRunList&, const LineInfo&); + void computeInlineDirectionPositionsForLine(RootInlineBox*, + const LineInfo&, + BidiRun* firstRun, + BidiRun* trailingSpaceRun, + bool reachedEnd, + GlyphOverflowAndFallbackFontsMap&, + VerticalPositionCache&, + WordMeasurements&); + BidiRun* computeInlineDirectionPositionsForSegment( + RootInlineBox*, + const LineInfo&, + ETextAlign, + float& logicalLeft, + float& availableLogicalWidth, + BidiRun* firstRun, + BidiRun* trailingSpaceRun, + GlyphOverflowAndFallbackFontsMap& textBoxDataMap, + VerticalPositionCache&, + WordMeasurements&); + void computeBlockDirectionPositionsForLine(RootInlineBox*, + BidiRun*, + GlyphOverflowAndFallbackFontsMap&, + VerticalPositionCache&); + // Helper function for layoutChildren() + RootInlineBox* createLineBoxesFromBidiRuns(unsigned bidiLevel, + BidiRunList&, + const InlineIterator& end, + LineInfo&, + VerticalPositionCache&, + BidiRun* trailingSpaceRun, + WordMeasurements&); + void layoutRunsAndFloats(LineLayoutState&); + void layoutRunsAndFloatsInRange(LineLayoutState&, + InlineBidiResolver&, + const InlineIterator& cleanLineStart, + const BidiStatus& cleanLineBidiStatus); + void linkToEndLineIfNeeded(LineLayoutState&); + RootInlineBox* determineStartPosition(LineLayoutState&, InlineBidiResolver&); + void determineEndPosition(LineLayoutState&, + RootInlineBox* startBox, + InlineIterator& cleanLineStart, + BidiStatus& cleanLineBidiStatus); + bool checkPaginationAndFloatsAtEndLine(LineLayoutState&); + bool matchedEndLine(LineLayoutState&, + const InlineBidiResolver&, + const InlineIterator& endLineStart, + const BidiStatus& endLineStatus); + + bool m_didExceedMaxLines; }; DEFINE_RENDER_OBJECT_TYPE_CASTS(RenderParagraph, isRenderParagraph()); -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_RENDERPARAGRAPH_H_ diff --git a/sky/engine/core/rendering/RenderReplaced.cpp b/sky/engine/core/rendering/RenderReplaced.cpp index 5f9b778f52a2d..6774866d5946f 100644 --- a/sky/engine/core/rendering/RenderReplaced.cpp +++ b/sky/engine/core/rendering/RenderReplaced.cpp @@ -35,394 +35,473 @@ const int RenderReplaced::defaultWidth = 300; const int RenderReplaced::defaultHeight = 150; RenderReplaced::RenderReplaced() - : m_intrinsicSize(defaultWidth, defaultHeight) -{ - setReplaced(true); + : m_intrinsicSize(defaultWidth, defaultHeight) { + setReplaced(true); } RenderReplaced::RenderReplaced(const LayoutSize& intrinsicSize) - : m_intrinsicSize(intrinsicSize) -{ - setReplaced(true); + : m_intrinsicSize(intrinsicSize) { + setReplaced(true); } -RenderReplaced::~RenderReplaced() -{ -} +RenderReplaced::~RenderReplaced() {} -void RenderReplaced::willBeDestroyed() -{ - if (!documentBeingDestroyed() && parent()) - parent()->dirtyLinesFromChangedChild(this); +void RenderReplaced::willBeDestroyed() { + if (!documentBeingDestroyed() && parent()) + parent()->dirtyLinesFromChangedChild(this); - RenderBox::willBeDestroyed(); + RenderBox::willBeDestroyed(); } -void RenderReplaced::layout() -{ - ASSERT(needsLayout()); +void RenderReplaced::layout() { + ASSERT(needsLayout()); - setHeight(minimumReplacedHeight()); + setHeight(minimumReplacedHeight()); - updateLogicalWidth(); - updateLogicalHeight(); + updateLogicalWidth(); + updateLogicalHeight(); - m_overflow.clear(); - addVisualEffectOverflow(); - updateLayerTransformAfterLayout(); + m_overflow.clear(); + addVisualEffectOverflow(); + updateLayerTransformAfterLayout(); - clearNeedsLayout(); + clearNeedsLayout(); } -void RenderReplaced::intrinsicSizeChanged() -{ - m_intrinsicSize = IntSize(defaultWidth, defaultHeight); - setNeedsLayoutAndPrefWidthsRecalc(); +void RenderReplaced::intrinsicSizeChanged() { + m_intrinsicSize = IntSize(defaultWidth, defaultHeight); + setNeedsLayoutAndPrefWidthsRecalc(); } -void RenderReplaced::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, Vector& layers) -{ - if (!shouldPaint(paintInfo, paintOffset)) - return; - - LayoutPoint adjustedPaintOffset = paintOffset + location(); - - if (hasBoxDecorationBackground()) - paintBoxDecorationBackground(paintInfo, adjustedPaintOffset); - - LayoutRect paintRect = LayoutRect(adjustedPaintOffset, size()); - - bool completelyClippedOut = false; - if (style()->hasBorderRadius()) { - LayoutRect borderRect = LayoutRect(adjustedPaintOffset, size()); - - if (borderRect.isEmpty()) - completelyClippedOut = true; - else { - // Push a clip if we have a border radius, since we want to round the foreground content that gets painted. - paintInfo.context->save(); - RoundedRect roundedInnerRect = style()->getRoundedInnerBorderFor(paintRect, - paddingTop() + borderTop(), paddingBottom() + borderBottom(), paddingLeft() + borderLeft(), paddingRight() + borderRight(), true, true); - clipRoundedInnerRect(paintInfo.context, paintRect, roundedInnerRect); - } - } - - if (!completelyClippedOut) { - paintReplaced(paintInfo, adjustedPaintOffset); - if (style()->hasBorderRadius()) - paintInfo.context->restore(); - } - - // The selection tint never gets clipped by border-radius rounding, since we want it to run right up to the edges of - // surrounding content. - if (selectionState() != SelectionNone) { - LayoutRect selectionPaintingRect = localSelectionRect(); - selectionPaintingRect.moveBy(adjustedPaintOffset); - paintInfo.context->fillRect(pixelSnappedIntRect(selectionPaintingRect), selectionBackgroundColor()); +void RenderReplaced::paint(PaintInfo& paintInfo, + const LayoutPoint& paintOffset, + Vector& layers) { + if (!shouldPaint(paintInfo, paintOffset)) + return; + + LayoutPoint adjustedPaintOffset = paintOffset + location(); + + if (hasBoxDecorationBackground()) + paintBoxDecorationBackground(paintInfo, adjustedPaintOffset); + + LayoutRect paintRect = LayoutRect(adjustedPaintOffset, size()); + + bool completelyClippedOut = false; + if (style()->hasBorderRadius()) { + LayoutRect borderRect = LayoutRect(adjustedPaintOffset, size()); + + if (borderRect.isEmpty()) + completelyClippedOut = true; + else { + // Push a clip if we have a border radius, since we want to round the + // foreground content that gets painted. + paintInfo.context->save(); + RoundedRect roundedInnerRect = style()->getRoundedInnerBorderFor( + paintRect, paddingTop() + borderTop(), + paddingBottom() + borderBottom(), paddingLeft() + borderLeft(), + paddingRight() + borderRight(), true, true); + clipRoundedInnerRect(paintInfo.context, paintRect, roundedInnerRect); } + } + + if (!completelyClippedOut) { + paintReplaced(paintInfo, adjustedPaintOffset); + if (style()->hasBorderRadius()) + paintInfo.context->restore(); + } + + // The selection tint never gets clipped by border-radius rounding, since we + // want it to run right up to the edges of surrounding content. + if (selectionState() != SelectionNone) { + LayoutRect selectionPaintingRect = localSelectionRect(); + selectionPaintingRect.moveBy(adjustedPaintOffset); + paintInfo.context->fillRect(pixelSnappedIntRect(selectionPaintingRect), + selectionBackgroundColor()); + } } -bool RenderReplaced::shouldPaint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) -{ - LayoutPoint adjustedPaintOffset = paintOffset + location(); - - // Early exit if the element touches the edges. - LayoutUnit top = adjustedPaintOffset.y() + visualOverflowRect().y(); - LayoutUnit bottom = adjustedPaintOffset.y() + visualOverflowRect().maxY(); - if (isSelected() && inlineBoxWrapper()) { - LayoutUnit selTop = paintOffset.y() + inlineBoxWrapper()->root().selectionTop(); - LayoutUnit selBottom = paintOffset.y() + selTop + inlineBoxWrapper()->root().selectionHeight(); - top = std::min(selTop, top); - bottom = std::max(selBottom, bottom); - } - - if (adjustedPaintOffset.x() + visualOverflowRect().x() >= paintInfo.rect.maxX() || adjustedPaintOffset.x() + visualOverflowRect().maxX() <= paintInfo.rect.x()) - return false; +bool RenderReplaced::shouldPaint(PaintInfo& paintInfo, + const LayoutPoint& paintOffset) { + LayoutPoint adjustedPaintOffset = paintOffset + location(); + + // Early exit if the element touches the edges. + LayoutUnit top = adjustedPaintOffset.y() + visualOverflowRect().y(); + LayoutUnit bottom = adjustedPaintOffset.y() + visualOverflowRect().maxY(); + if (isSelected() && inlineBoxWrapper()) { + LayoutUnit selTop = + paintOffset.y() + inlineBoxWrapper()->root().selectionTop(); + LayoutUnit selBottom = + paintOffset.y() + selTop + inlineBoxWrapper()->root().selectionHeight(); + top = std::min(selTop, top); + bottom = std::max(selBottom, bottom); + } + + if (adjustedPaintOffset.x() + visualOverflowRect().x() >= + paintInfo.rect.maxX() || + adjustedPaintOffset.x() + visualOverflowRect().maxX() <= + paintInfo.rect.x()) + return false; - if (top >= paintInfo.rect.maxY() || bottom <= paintInfo.rect.y()) - return false; + if (top >= paintInfo.rect.maxY() || bottom <= paintInfo.rect.y()) + return false; - return true; + return true; } -bool RenderReplaced::hasReplacedLogicalHeight() const -{ - if (style()->logicalHeight().isAuto()) - return false; +bool RenderReplaced::hasReplacedLogicalHeight() const { + if (style()->logicalHeight().isAuto()) + return false; - if (style()->logicalHeight().isSpecified()) { - if (hasAutoHeightOrContainingBlockWithAutoHeight()) - return false; - return true; - } + if (style()->logicalHeight().isSpecified()) { + if (hasAutoHeightOrContainingBlockWithAutoHeight()) + return false; + return true; + } - if (style()->logicalHeight().isIntrinsic()) - return true; + if (style()->logicalHeight().isIntrinsic()) + return true; - return false; + return false; } -bool RenderReplaced::needsPreferredWidthsRecalculation() const -{ - // If the height is a percentage and the width is auto, then the containingBlocks's height changing can cause - // this node to change it's preferred width because it maintains aspect ratio. - return hasRelativeLogicalHeight() && style()->logicalWidth().isAuto() && !hasAutoHeightOrContainingBlockWithAutoHeight(); +bool RenderReplaced::needsPreferredWidthsRecalculation() const { + // If the height is a percentage and the width is auto, then the + // containingBlocks's height changing can cause this node to change it's + // preferred width because it maintains aspect ratio. + return hasRelativeLogicalHeight() && style()->logicalWidth().isAuto() && + !hasAutoHeightOrContainingBlockWithAutoHeight(); } -static inline bool rendererHasAspectRatio(const RenderObject* renderer) -{ - ASSERT(renderer); - return renderer->isImage() || renderer->isCanvas(); +static inline bool rendererHasAspectRatio(const RenderObject* renderer) { + ASSERT(renderer); + return renderer->isImage() || renderer->isCanvas(); } -void RenderReplaced::computeAspectRatioInformationForRenderBox(FloatSize& constrainedSize, double& intrinsicRatio) const -{ - FloatSize intrinsicSize; - computeIntrinsicRatioInformation(intrinsicSize, intrinsicRatio); - if (intrinsicRatio && !intrinsicSize.isEmpty()) - m_intrinsicSize = LayoutSize(intrinsicSize); - - // Now constrain the intrinsic size along each axis according to minimum and maximum width/heights along the - // opposite axis. So for example a maximum width that shrinks our width will result in the height we compute here - // having to shrink in order to preserve the aspect ratio. Because we compute these values independently along - // each axis, the final returned size may in fact not preserve the aspect ratio. - // FIXME: In the long term, it might be better to just return this code more to the way it used to be before this - // function was added, since all it has done is make the code more unclear. - constrainedSize = intrinsicSize; - if (intrinsicRatio && !intrinsicSize.isEmpty() && style()->logicalWidth().isAuto() && style()->logicalHeight().isAuto()) { - // We can't multiply or divide by 'intrinsicRatio' here, it breaks tests, like fast/images/zoomed-img-size.html, which - // can only be fixed once subpixel precision is available for things like intrinsicWidth/Height. - constrainedSize.setWidth(RenderBox::computeReplacedLogicalHeight() * intrinsicSize.width() / intrinsicSize.height()); - constrainedSize.setHeight(RenderBox::computeReplacedLogicalWidth() * intrinsicSize.height() / intrinsicSize.width()); - } +void RenderReplaced::computeAspectRatioInformationForRenderBox( + FloatSize& constrainedSize, + double& intrinsicRatio) const { + FloatSize intrinsicSize; + computeIntrinsicRatioInformation(intrinsicSize, intrinsicRatio); + if (intrinsicRatio && !intrinsicSize.isEmpty()) + m_intrinsicSize = LayoutSize(intrinsicSize); + + // Now constrain the intrinsic size along each axis according to minimum and + // maximum width/heights along the opposite axis. So for example a maximum + // width that shrinks our width will result in the height we compute here + // having to shrink in order to preserve the aspect ratio. Because we compute + // these values independently along each axis, the final returned size may in + // fact not preserve the aspect ratio. + // FIXME: In the long term, it might be better to just return this code more + // to the way it used to be before this function was added, since all it has + // done is make the code more unclear. + constrainedSize = intrinsicSize; + if (intrinsicRatio && !intrinsicSize.isEmpty() && + style()->logicalWidth().isAuto() && style()->logicalHeight().isAuto()) { + // We can't multiply or divide by 'intrinsicRatio' here, it breaks tests, + // like fast/images/zoomed-img-size.html, which can only be fixed once + // subpixel precision is available for things like intrinsicWidth/Height. + constrainedSize.setWidth(RenderBox::computeReplacedLogicalHeight() * + intrinsicSize.width() / intrinsicSize.height()); + constrainedSize.setHeight(RenderBox::computeReplacedLogicalWidth() * + intrinsicSize.height() / intrinsicSize.width()); + } } -LayoutRect RenderReplaced::replacedContentRect(const LayoutSize* overriddenIntrinsicSize) const -{ - LayoutRect contentRect = contentBoxRect(); - ObjectFit objectFit = style()->objectFit(); +LayoutRect RenderReplaced::replacedContentRect( + const LayoutSize* overriddenIntrinsicSize) const { + LayoutRect contentRect = contentBoxRect(); + ObjectFit objectFit = style()->objectFit(); - if (objectFit == ObjectFitFill && style()->objectPosition() == RenderStyle::initialObjectPosition()) - objectFit = ObjectFitContain; + if (objectFit == ObjectFitFill && + style()->objectPosition() == RenderStyle::initialObjectPosition()) + objectFit = ObjectFitContain; - LayoutSize intrinsicSize = overriddenIntrinsicSize ? *overriddenIntrinsicSize : this->intrinsicSize(); - if (!intrinsicSize.width() || !intrinsicSize.height()) - return contentRect; + LayoutSize intrinsicSize = overriddenIntrinsicSize ? *overriddenIntrinsicSize + : this->intrinsicSize(); + if (!intrinsicSize.width() || !intrinsicSize.height()) + return contentRect; - LayoutRect finalRect = contentRect; - switch (objectFit) { + LayoutRect finalRect = contentRect; + switch (objectFit) { case ObjectFitContain: case ObjectFitScaleDown: case ObjectFitCover: - finalRect.setSize(finalRect.size().fitToAspectRatio(intrinsicSize, objectFit == ObjectFitCover ? AspectRatioFitGrow : AspectRatioFitShrink)); - if (objectFit != ObjectFitScaleDown || finalRect.width() <= intrinsicSize.width()) - break; - // fall through - case ObjectFitNone: - finalRect.setSize(intrinsicSize); + finalRect.setSize(finalRect.size().fitToAspectRatio( + intrinsicSize, objectFit == ObjectFitCover ? AspectRatioFitGrow + : AspectRatioFitShrink)); + if (objectFit != ObjectFitScaleDown || + finalRect.width() <= intrinsicSize.width()) break; + // fall through + case ObjectFitNone: + finalRect.setSize(intrinsicSize); + break; case ObjectFitFill: - break; + break; default: - ASSERT_NOT_REACHED(); - } + ASSERT_NOT_REACHED(); + } - LayoutUnit xOffset = minimumValueForLength(style()->objectPosition().x(), contentRect.width() - finalRect.width()); - LayoutUnit yOffset = minimumValueForLength(style()->objectPosition().y(), contentRect.height() - finalRect.height()); - finalRect.move(xOffset, yOffset); + LayoutUnit xOffset = minimumValueForLength( + style()->objectPosition().x(), contentRect.width() - finalRect.width()); + LayoutUnit yOffset = minimumValueForLength( + style()->objectPosition().y(), contentRect.height() - finalRect.height()); + finalRect.move(xOffset, yOffset); - return finalRect; + return finalRect; } -void RenderReplaced::computeIntrinsicRatioInformation(FloatSize& intrinsicSize, double& intrinsicRatio) const -{ - intrinsicSize = FloatSize(intrinsicLogicalWidth().toFloat(), intrinsicLogicalHeight().toFloat()); +void RenderReplaced::computeIntrinsicRatioInformation( + FloatSize& intrinsicSize, + double& intrinsicRatio) const { + intrinsicSize = FloatSize(intrinsicLogicalWidth().toFloat(), + intrinsicLogicalHeight().toFloat()); - // Figure out if we need to compute an intrinsic ratio. - if (intrinsicSize.isEmpty() || !rendererHasAspectRatio(this)) - return; + // Figure out if we need to compute an intrinsic ratio. + if (intrinsicSize.isEmpty() || !rendererHasAspectRatio(this)) + return; - intrinsicRatio = intrinsicSize.width() / intrinsicSize.height(); + intrinsicRatio = intrinsicSize.width() / intrinsicSize.height(); } -LayoutUnit RenderReplaced::computeReplacedLogicalWidth(ShouldComputePreferred shouldComputePreferred) const -{ - if (style()->logicalWidth().isSpecified() || style()->logicalWidth().isIntrinsic()) - return computeReplacedLogicalWidthRespectingMinMaxWidth(computeReplacedLogicalWidthUsing(style()->logicalWidth()), shouldComputePreferred); - - // 10.3.2 Inline, replaced elements: http://www.w3.org/TR/CSS21/visudet.html#inline-replaced-width - double intrinsicRatio = 0; - FloatSize constrainedSize; - computeAspectRatioInformationForRenderBox(constrainedSize, intrinsicRatio); - - if (style()->logicalWidth().isAuto()) { - bool computedHeightIsAuto = hasAutoHeightOrContainingBlockWithAutoHeight(); - bool hasIntrinsicWidth = constrainedSize.width() > 0; - - // If 'height' and 'width' both have computed values of 'auto' and the element also has an intrinsic width, then that intrinsic width is the used value of 'width'. - if (computedHeightIsAuto && hasIntrinsicWidth) - return computeReplacedLogicalWidthRespectingMinMaxWidth(constrainedSize.width(), shouldComputePreferred); - - bool hasIntrinsicHeight = constrainedSize.height() > 0; - if (intrinsicRatio) { - // If 'height' and 'width' both have computed values of 'auto' and the element has no intrinsic width, but does have an intrinsic height and intrinsic ratio; - // or if 'width' has a computed value of 'auto', 'height' has some other computed value, and the element does have an intrinsic ratio; then the used value - // of 'width' is: (used height) * (intrinsic ratio) - if (intrinsicRatio && ((computedHeightIsAuto && !hasIntrinsicWidth && hasIntrinsicHeight) || !computedHeightIsAuto)) { - LayoutUnit logicalHeight = computeReplacedLogicalHeight(); - return computeReplacedLogicalWidthRespectingMinMaxWidth(roundToInt(round(logicalHeight * intrinsicRatio)), shouldComputePreferred); - } - - // If 'height' and 'width' both have computed values of 'auto' and the element has an intrinsic ratio but no intrinsic height or width, then the used value of - // 'width' is undefined in CSS 2.1. However, it is suggested that, if the containing block's width does not itself depend on the replaced element's width, then - // the used value of 'width' is calculated from the constraint equation used for block-level, non-replaced elements in normal flow. - if (computedHeightIsAuto && !hasIntrinsicWidth && !hasIntrinsicHeight) { - if (shouldComputePreferred == ComputePreferred) - return 0; - // The aforementioned 'constraint equation' used for block-level, non-replaced elements in normal flow: - // 'margin-left' + 'border-left-width' + 'padding-left' + 'width' + 'padding-right' + 'border-right-width' + 'margin-right' = width of containing block - LayoutUnit logicalWidth = containingBlock()->availableLogicalWidth(); - - // This solves above equation for 'width' (== logicalWidth). - LayoutUnit marginStart = minimumValueForLength(style()->marginStart(), logicalWidth); - LayoutUnit marginEnd = minimumValueForLength(style()->marginEnd(), logicalWidth); - logicalWidth = std::max(0, logicalWidth - (marginStart + marginEnd + (width() - clientWidth()))); - return computeReplacedLogicalWidthRespectingMinMaxWidth(logicalWidth, shouldComputePreferred); - } - } - - // Otherwise, if 'width' has a computed value of 'auto', and the element has an intrinsic width, then that intrinsic width is the used value of 'width'. - if (hasIntrinsicWidth) - return computeReplacedLogicalWidthRespectingMinMaxWidth(constrainedSize.width(), shouldComputePreferred); - - // Otherwise, if 'width' has a computed value of 'auto', but none of the conditions above are met, then the used value of 'width' becomes 300px. If 300px is too - // wide to fit the device, UAs should use the width of the largest rectangle that has a 2:1 ratio and fits the device instead. - // Note: We fall through and instead return intrinsicLogicalWidth() here - to preserve existing WebKit behavior, which might or might not be correct, or desired. - // Changing this to return cDefaultWidth, will affect lots of test results. Eg. some tests assume that a blank tag (which implies width/height=auto) - // has no intrinsic size, which is wrong per CSS 2.1, but matches our behavior since a long time. - } - - return computeReplacedLogicalWidthRespectingMinMaxWidth(intrinsicLogicalWidth(), shouldComputePreferred); -} +LayoutUnit RenderReplaced::computeReplacedLogicalWidth( + ShouldComputePreferred shouldComputePreferred) const { + if (style()->logicalWidth().isSpecified() || + style()->logicalWidth().isIntrinsic()) + return computeReplacedLogicalWidthRespectingMinMaxWidth( + computeReplacedLogicalWidthUsing(style()->logicalWidth()), + shouldComputePreferred); + + // 10.3.2 Inline, replaced elements: + // http://www.w3.org/TR/CSS21/visudet.html#inline-replaced-width + double intrinsicRatio = 0; + FloatSize constrainedSize; + computeAspectRatioInformationForRenderBox(constrainedSize, intrinsicRatio); + + if (style()->logicalWidth().isAuto()) { + bool computedHeightIsAuto = hasAutoHeightOrContainingBlockWithAutoHeight(); + bool hasIntrinsicWidth = constrainedSize.width() > 0; + + // If 'height' and 'width' both have computed values of 'auto' and the + // element also has an intrinsic width, then that intrinsic width is the + // used value of 'width'. + if (computedHeightIsAuto && hasIntrinsicWidth) + return computeReplacedLogicalWidthRespectingMinMaxWidth( + constrainedSize.width(), shouldComputePreferred); -LayoutUnit RenderReplaced::computeReplacedLogicalHeight() const -{ - // 10.5 Content height: the 'height' property: http://www.w3.org/TR/CSS21/visudet.html#propdef-height - if (hasReplacedLogicalHeight()) - return computeReplacedLogicalHeightRespectingMinMaxHeight(computeReplacedLogicalHeightUsing(style()->logicalHeight())); - - // 10.6.2 Inline, replaced elements: http://www.w3.org/TR/CSS21/visudet.html#inline-replaced-height - double intrinsicRatio = 0; - FloatSize constrainedSize; - computeAspectRatioInformationForRenderBox(constrainedSize, intrinsicRatio); - - bool widthIsAuto = style()->logicalWidth().isAuto(); bool hasIntrinsicHeight = constrainedSize.height() > 0; + if (intrinsicRatio) { + // If 'height' and 'width' both have computed values of 'auto' and the + // element has no intrinsic width, but does have an intrinsic height and + // intrinsic ratio; or if 'width' has a computed value of 'auto', 'height' + // has some other computed value, and the element does have an intrinsic + // ratio; then the used value of 'width' is: (used height) * (intrinsic + // ratio) + if (intrinsicRatio && + ((computedHeightIsAuto && !hasIntrinsicWidth && hasIntrinsicHeight) || + !computedHeightIsAuto)) { + LayoutUnit logicalHeight = computeReplacedLogicalHeight(); + return computeReplacedLogicalWidthRespectingMinMaxWidth( + roundToInt(round(logicalHeight * intrinsicRatio)), + shouldComputePreferred); + } + + // If 'height' and 'width' both have computed values of 'auto' and the + // element has an intrinsic ratio but no intrinsic height or width, then + // the used value of 'width' is undefined in CSS 2.1. However, it is + // suggested that, if the containing block's width does not itself depend + // on the replaced element's width, then the used value of 'width' is + // calculated from the constraint equation used for block-level, + // non-replaced elements in normal flow. + if (computedHeightIsAuto && !hasIntrinsicWidth && !hasIntrinsicHeight) { + if (shouldComputePreferred == ComputePreferred) + return 0; + // The aforementioned 'constraint equation' used for block-level, + // non-replaced elements in normal flow: 'margin-left' + + // 'border-left-width' + 'padding-left' + 'width' + 'padding-right' + + // 'border-right-width' + 'margin-right' = width of containing block + LayoutUnit logicalWidth = containingBlock()->availableLogicalWidth(); + + // This solves above equation for 'width' (== logicalWidth). + LayoutUnit marginStart = + minimumValueForLength(style()->marginStart(), logicalWidth); + LayoutUnit marginEnd = + minimumValueForLength(style()->marginEnd(), logicalWidth); + logicalWidth = std::max( + 0, logicalWidth - + (marginStart + marginEnd + (width() - clientWidth()))); + return computeReplacedLogicalWidthRespectingMinMaxWidth( + logicalWidth, shouldComputePreferred); + } + } - // If 'height' and 'width' both have computed values of 'auto' and the element also has an intrinsic height, then that intrinsic height is the used value of 'height'. - if (widthIsAuto && hasIntrinsicHeight) - return computeReplacedLogicalHeightRespectingMinMaxHeight(constrainedSize.height()); - - // Otherwise, if 'height' has a computed value of 'auto', and the element has an intrinsic ratio then the used value of 'height' is: - // (used width) / (intrinsic ratio) - if (intrinsicRatio) - return computeReplacedLogicalHeightRespectingMinMaxHeight(roundToInt(round(availableLogicalWidth() / intrinsicRatio))); - - // Otherwise, if 'height' has a computed value of 'auto', and the element has an intrinsic height, then that intrinsic height is the used value of 'height'. - if (hasIntrinsicHeight) - return computeReplacedLogicalHeightRespectingMinMaxHeight(constrainedSize.height()); - - // Otherwise, if 'height' has a computed value of 'auto', but none of the conditions above are met, then the used value of 'height' must be set to the height - // of the largest rectangle that has a 2:1 ratio, has a height not greater than 150px, and has a width not greater than the device width. - return computeReplacedLogicalHeightRespectingMinMaxHeight(intrinsicLogicalHeight()); + // Otherwise, if 'width' has a computed value of 'auto', and the element has + // an intrinsic width, then that intrinsic width is the used value of + // 'width'. + if (hasIntrinsicWidth) + return computeReplacedLogicalWidthRespectingMinMaxWidth( + constrainedSize.width(), shouldComputePreferred); + + // Otherwise, if 'width' has a computed value of 'auto', but none of the + // conditions above are met, then the used value of 'width' becomes 300px. + // If 300px is too wide to fit the device, UAs should use the width of the + // largest rectangle that has a 2:1 ratio and fits the device instead. Note: + // We fall through and instead return intrinsicLogicalWidth() here - to + // preserve existing WebKit behavior, which might or might not be correct, + // or desired. Changing this to return cDefaultWidth, will affect lots of + // test results. Eg. some tests assume that a blank tag (which implies + // width/height=auto) has no intrinsic size, which is wrong per CSS 2.1, but + // matches our behavior since a long time. + } + + return computeReplacedLogicalWidthRespectingMinMaxWidth( + intrinsicLogicalWidth(), shouldComputePreferred); } -void RenderReplaced::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const -{ - minLogicalWidth = maxLogicalWidth = intrinsicLogicalWidth(); +LayoutUnit RenderReplaced::computeReplacedLogicalHeight() const { + // 10.5 Content height: the 'height' property: + // http://www.w3.org/TR/CSS21/visudet.html#propdef-height + if (hasReplacedLogicalHeight()) + return computeReplacedLogicalHeightRespectingMinMaxHeight( + computeReplacedLogicalHeightUsing(style()->logicalHeight())); + + // 10.6.2 Inline, replaced elements: + // http://www.w3.org/TR/CSS21/visudet.html#inline-replaced-height + double intrinsicRatio = 0; + FloatSize constrainedSize; + computeAspectRatioInformationForRenderBox(constrainedSize, intrinsicRatio); + + bool widthIsAuto = style()->logicalWidth().isAuto(); + bool hasIntrinsicHeight = constrainedSize.height() > 0; + + // If 'height' and 'width' both have computed values of 'auto' and the element + // also has an intrinsic height, then that intrinsic height is the used value + // of 'height'. + if (widthIsAuto && hasIntrinsicHeight) + return computeReplacedLogicalHeightRespectingMinMaxHeight( + constrainedSize.height()); + + // Otherwise, if 'height' has a computed value of 'auto', and the element has + // an intrinsic ratio then the used value of 'height' is: (used width) / + // (intrinsic ratio) + if (intrinsicRatio) + return computeReplacedLogicalHeightRespectingMinMaxHeight( + roundToInt(round(availableLogicalWidth() / intrinsicRatio))); + + // Otherwise, if 'height' has a computed value of 'auto', and the element has + // an intrinsic height, then that intrinsic height is the used value of + // 'height'. + if (hasIntrinsicHeight) + return computeReplacedLogicalHeightRespectingMinMaxHeight( + constrainedSize.height()); + + // Otherwise, if 'height' has a computed value of 'auto', but none of the + // conditions above are met, then the used value of 'height' must be set to + // the height of the largest rectangle that has a 2:1 ratio, has a height not + // greater than 150px, and has a width not greater than the device width. + return computeReplacedLogicalHeightRespectingMinMaxHeight( + intrinsicLogicalHeight()); } -void RenderReplaced::computePreferredLogicalWidths() -{ - ASSERT(preferredLogicalWidthsDirty()); - - // We cannot resolve any percent logical width here as the available logical - // width may not be set on our containing block. - if (style()->logicalWidth().isPercent()) - computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth); - else - m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = computeReplacedLogicalWidth(ComputePreferred); - - RenderStyle* styleToUse = style(); - if (styleToUse->logicalWidth().isPercent() || styleToUse->logicalMaxWidth().isPercent()) - m_minPreferredLogicalWidth = 0; - - if (styleToUse->logicalMinWidth().isFixed() && styleToUse->logicalMinWidth().value() > 0) { - m_maxPreferredLogicalWidth = std::max(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMinWidth().value())); - m_minPreferredLogicalWidth = std::max(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMinWidth().value())); - } - - if (styleToUse->logicalMaxWidth().isFixed()) { - m_maxPreferredLogicalWidth = std::min(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMaxWidth().value())); - m_minPreferredLogicalWidth = std::min(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMaxWidth().value())); - } - - LayoutUnit borderAndPadding = borderAndPaddingLogicalWidth(); - m_minPreferredLogicalWidth += borderAndPadding; - m_maxPreferredLogicalWidth += borderAndPadding; +void RenderReplaced::computeIntrinsicLogicalWidths( + LayoutUnit& minLogicalWidth, + LayoutUnit& maxLogicalWidth) const { + minLogicalWidth = maxLogicalWidth = intrinsicLogicalWidth(); +} - clearPreferredLogicalWidthsDirty(); +void RenderReplaced::computePreferredLogicalWidths() { + ASSERT(preferredLogicalWidthsDirty()); + + // We cannot resolve any percent logical width here as the available logical + // width may not be set on our containing block. + if (style()->logicalWidth().isPercent()) + computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, + m_maxPreferredLogicalWidth); + else + m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = + computeReplacedLogicalWidth(ComputePreferred); + + RenderStyle* styleToUse = style(); + if (styleToUse->logicalWidth().isPercent() || + styleToUse->logicalMaxWidth().isPercent()) + m_minPreferredLogicalWidth = 0; + + if (styleToUse->logicalMinWidth().isFixed() && + styleToUse->logicalMinWidth().value() > 0) { + m_maxPreferredLogicalWidth = std::max( + m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing( + styleToUse->logicalMinWidth().value())); + m_minPreferredLogicalWidth = std::max( + m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing( + styleToUse->logicalMinWidth().value())); + } + + if (styleToUse->logicalMaxWidth().isFixed()) { + m_maxPreferredLogicalWidth = std::min( + m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing( + styleToUse->logicalMaxWidth().value())); + m_minPreferredLogicalWidth = std::min( + m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing( + styleToUse->logicalMaxWidth().value())); + } + + LayoutUnit borderAndPadding = borderAndPaddingLogicalWidth(); + m_minPreferredLogicalWidth += borderAndPadding; + m_maxPreferredLogicalWidth += borderAndPadding; + + clearPreferredLogicalWidthsDirty(); } -PositionWithAffinity RenderReplaced::positionForPoint(const LayoutPoint& point) -{ - // FIXME: This code is buggy if the replaced element is relative positioned. - InlineBox* box = inlineBoxWrapper(); - RootInlineBox* rootBox = box ? &box->root() : 0; +PositionWithAffinity RenderReplaced::positionForPoint( + const LayoutPoint& point) { + // FIXME: This code is buggy if the replaced element is relative positioned. + InlineBox* box = inlineBoxWrapper(); + RootInlineBox* rootBox = box ? &box->root() : 0; - LayoutUnit top = rootBox ? rootBox->selectionTop() : logicalTop(); - LayoutUnit bottom = rootBox ? rootBox->selectionBottom() : logicalBottom(); + LayoutUnit top = rootBox ? rootBox->selectionTop() : logicalTop(); + LayoutUnit bottom = rootBox ? rootBox->selectionBottom() : logicalBottom(); - LayoutUnit blockDirectionPosition = point.y() + y(); + LayoutUnit blockDirectionPosition = point.y() + y(); - if (blockDirectionPosition < top) - return createPositionWithAffinity(caretMinOffset(), DOWNSTREAM); // coordinates are above + if (blockDirectionPosition < top) + return createPositionWithAffinity(caretMinOffset(), + DOWNSTREAM); // coordinates are above - if (blockDirectionPosition >= bottom) - return createPositionWithAffinity(caretMaxOffset(), DOWNSTREAM); // coordinates are below + if (blockDirectionPosition >= bottom) + return createPositionWithAffinity(caretMaxOffset(), + DOWNSTREAM); // coordinates are below - return RenderBox::positionForPoint(point); + return RenderBox::positionForPoint(point); } -LayoutRect RenderReplaced::localSelectionRect(bool checkWhetherSelected) const -{ - if (checkWhetherSelected && !isSelected()) - return LayoutRect(); +LayoutRect RenderReplaced::localSelectionRect(bool checkWhetherSelected) const { + if (checkWhetherSelected && !isSelected()) + return LayoutRect(); - if (!inlineBoxWrapper()) - // We're a block-level replaced element. Just return our own dimensions. - return LayoutRect(LayoutPoint(), size()); + if (!inlineBoxWrapper()) + // We're a block-level replaced element. Just return our own dimensions. + return LayoutRect(LayoutPoint(), size()); - RootInlineBox& root = inlineBoxWrapper()->root(); - LayoutUnit newLogicalTop = root.selectionTop() - inlineBoxWrapper()->logicalTop(); - return LayoutRect(0, newLogicalTop, width(), root.selectionHeight()); + RootInlineBox& root = inlineBoxWrapper()->root(); + LayoutUnit newLogicalTop = + root.selectionTop() - inlineBoxWrapper()->logicalTop(); + return LayoutRect(0, newLogicalTop, width(), root.selectionHeight()); } -void RenderReplaced::setSelectionState(SelectionState state) -{ - // The selection state for our containing block hierarchy is updated by the base class call. - RenderBox::setSelectionState(state); +void RenderReplaced::setSelectionState(SelectionState state) { + // The selection state for our containing block hierarchy is updated by the + // base class call. + RenderBox::setSelectionState(state); - if (!inlineBoxWrapper()) - return; + if (!inlineBoxWrapper()) + return; - if (canUpdateSelectionOnRootLineBoxes()) - inlineBoxWrapper()->root().setHasSelectedChildren(isSelected()); + if (canUpdateSelectionOnRootLineBoxes()) + inlineBoxWrapper()->root().setHasSelectedChildren(isSelected()); } -bool RenderReplaced::isSelected() const -{ - return false; +bool RenderReplaced::isSelected() const { + return false; } -} +} // namespace blink diff --git a/sky/engine/core/rendering/RenderReplaced.h b/sky/engine/core/rendering/RenderReplaced.h index 2c17bfaabb541..41ac2960d597b 100644 --- a/sky/engine/core/rendering/RenderReplaced.h +++ b/sky/engine/core/rendering/RenderReplaced.h @@ -27,66 +27,84 @@ namespace blink { class RenderReplaced : public RenderBox { -public: - RenderReplaced(); - RenderReplaced(const LayoutSize& intrinsicSize); - virtual ~RenderReplaced(); + public: + RenderReplaced(); + RenderReplaced(const LayoutSize& intrinsicSize); + virtual ~RenderReplaced(); - virtual LayoutUnit computeReplacedLogicalWidth(ShouldComputePreferred = ComputeActual) const override; - virtual LayoutUnit computeReplacedLogicalHeight() const override; + virtual LayoutUnit computeReplacedLogicalWidth( + ShouldComputePreferred = ComputeActual) const override; + virtual LayoutUnit computeReplacedLogicalHeight() const override; - bool hasReplacedLogicalHeight() const; - LayoutRect replacedContentRect(const LayoutSize* overriddenIntrinsicSize = 0) const; + bool hasReplacedLogicalHeight() const; + LayoutRect replacedContentRect( + const LayoutSize* overriddenIntrinsicSize = 0) const; - virtual bool needsPreferredWidthsRecalculation() const override; + virtual bool needsPreferredWidthsRecalculation() const override; - // These values are specified to be 300 and 150 pixels in the CSS 2.1 spec. - // http://www.w3.org/TR/CSS2/visudet.html#inline-replaced-width - static const int defaultWidth; - static const int defaultHeight; + // These values are specified to be 300 and 150 pixels in the CSS 2.1 spec. + // http://www.w3.org/TR/CSS2/visudet.html#inline-replaced-width + static const int defaultWidth; + static const int defaultHeight; -protected: - virtual void willBeDestroyed() override; + protected: + virtual void willBeDestroyed() override; - virtual void layout() override; + virtual void layout() override; - virtual LayoutSize intrinsicSize() const override final { return m_intrinsicSize; } - virtual void computeIntrinsicRatioInformation(FloatSize& intrinsicSize, double& intrinsicRatio) const override; + virtual LayoutSize intrinsicSize() const override final { + return m_intrinsicSize; + } + virtual void computeIntrinsicRatioInformation( + FloatSize& intrinsicSize, + double& intrinsicRatio) const override; - virtual void computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const override final; + virtual void computeIntrinsicLogicalWidths( + LayoutUnit& minLogicalWidth, + LayoutUnit& maxLogicalWidth) const override final; - virtual LayoutUnit intrinsicContentLogicalHeight() const { return intrinsicLogicalHeight(); } + virtual LayoutUnit intrinsicContentLogicalHeight() const { + return intrinsicLogicalHeight(); + } - virtual LayoutUnit minimumReplacedHeight() const { return LayoutUnit(); } + virtual LayoutUnit minimumReplacedHeight() const { return LayoutUnit(); } - virtual void setSelectionState(SelectionState) override final; + virtual void setSelectionState(SelectionState) override final; - bool isSelected() const; + bool isSelected() const; - void setIntrinsicSize(const LayoutSize& intrinsicSize) { m_intrinsicSize = intrinsicSize; } - virtual void intrinsicSizeChanged(); + void setIntrinsicSize(const LayoutSize& intrinsicSize) { + m_intrinsicSize = intrinsicSize; + } + virtual void intrinsicSizeChanged(); - virtual void paint(PaintInfo&, const LayoutPoint&, Vector& layers) final; - bool shouldPaint(PaintInfo&, const LayoutPoint&); - LayoutRect localSelectionRect(bool checkWhetherSelected = true) const; // This is in local coordinates, but it's a physical rect (so the top left corner is physical top left). + virtual void paint(PaintInfo&, + const LayoutPoint&, + Vector& layers) final; + bool shouldPaint(PaintInfo&, const LayoutPoint&); + LayoutRect localSelectionRect(bool checkWhetherSelected = true) + const; // This is in local coordinates, but it's a physical rect (so the + // top left corner is physical top left). -private: - virtual const char* renderName() const override { return "RenderReplaced"; } + private: + virtual const char* renderName() const override { return "RenderReplaced"; } - virtual bool canHaveChildren() const override { return false; } + virtual bool canHaveChildren() const override { return false; } - virtual void computePreferredLogicalWidths() override final; - virtual void paintReplaced(PaintInfo&, const LayoutPoint&) { } + virtual void computePreferredLogicalWidths() override final; + virtual void paintReplaced(PaintInfo&, const LayoutPoint&) {} - virtual PositionWithAffinity positionForPoint(const LayoutPoint&) override final; + virtual PositionWithAffinity positionForPoint( + const LayoutPoint&) override final; - virtual bool canBeSelectionLeaf() const override { return true; } + virtual bool canBeSelectionLeaf() const override { return true; } - void computeAspectRatioInformationForRenderBox(FloatSize& constrainedSize, double& intrinsicRatio) const; + void computeAspectRatioInformationForRenderBox(FloatSize& constrainedSize, + double& intrinsicRatio) const; - mutable LayoutSize m_intrinsicSize; + mutable LayoutSize m_intrinsicSize; }; -} +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_RENDERREPLACED_H_ diff --git a/sky/engine/core/rendering/RenderText.cpp b/sky/engine/core/rendering/RenderText.cpp index 021d0569277fb..61073e14e1773 100644 --- a/sky/engine/core/rendering/RenderText.cpp +++ b/sky/engine/core/rendering/RenderText.cpp @@ -24,17 +24,17 @@ #include "flutter/sky/engine/core/rendering/RenderText.h" -#include "flutter/sky/engine/core/rendering/break_lines.h" #include "flutter/sky/engine/core/rendering/InlineTextBox.h" #include "flutter/sky/engine/core/rendering/RenderBlock.h" #include "flutter/sky/engine/core/rendering/RenderLayer.h" #include "flutter/sky/engine/core/rendering/RenderView.h" #include "flutter/sky/engine/core/rendering/TextRunConstructor.h" -#include "flutter/sky/engine/platform/text/TextBox.h" +#include "flutter/sky/engine/core/rendering/break_lines.h" #include "flutter/sky/engine/platform/fonts/Character.h" #include "flutter/sky/engine/platform/fonts/FontCache.h" #include "flutter/sky/engine/platform/geometry/FloatQuad.h" #include "flutter/sky/engine/platform/text/BidiResolver.h" +#include "flutter/sky/engine/platform/text/TextBox.h" #include "flutter/sky/engine/platform/text/TextBreakIterator.h" #include "flutter/sky/engine/platform/text/TextRunIterator.h" #include "flutter/sky/engine/wtf/text/StringBuffer.h" @@ -487,8 +487,9 @@ PositionWithAffinity RenderText::positionForPoint(const LayoutPoint& point) { lineDirectionPointFitsInBox(pointLineDirection, lastBox, shouldAffinityBeDownstream); return createPositionWithAffinityForBoxAfterAdjustingOffsetForBiDi( - lastBox, lastBox->offsetForPosition(pointLineDirection.toFloat()) + - lastBox->start(), + lastBox, + lastBox->offsetForPosition(pointLineDirection.toFloat()) + + lastBox->start(), shouldAffinityBeDownstream); } return createPositionWithAffinity(0, DOWNSTREAM); diff --git a/sky/engine/core/rendering/RenderText.h b/sky/engine/core/rendering/RenderText.h index 54f7844200c8e..7b7b3e8dae892 100644 --- a/sky/engine/core/rendering/RenderText.h +++ b/sky/engine/core/rendering/RenderText.h @@ -36,182 +36,242 @@ class InlineTextBox; class TextBox; class RenderText : public RenderObject { -public: - // FIXME: If the node argument is not a Text node or the string argument is - // not the content of the Text node, updating text-transform property - // doesn't re-transform the string. - RenderText(PassRefPtr); + public: + // FIXME: If the node argument is not a Text node or the string argument is + // not the content of the Text node, updating text-transform property + // doesn't re-transform the string. + RenderText(PassRefPtr); #if ENABLE(ASSERT) - virtual ~RenderText(); + virtual ~RenderText(); #endif - virtual const char* renderName() const override; - - void extractTextBox(InlineTextBox*); - void attachTextBox(InlineTextBox*); - void removeTextBox(InlineTextBox*); - - const String& text() const { return m_text; } - virtual unsigned textStartOffset() const { return 0; } - - InlineTextBox* createInlineTextBox(); - void dirtyLineBoxes(bool fullLayout); - - void appendAbsoluteTextBoxesForRange(std::vector&, unsigned startOffset = 0, unsigned endOffset = INT_MAX); - - virtual void absoluteQuads(Vector&) const override final; - void absoluteQuadsForRange(Vector&, unsigned startOffset = 0, unsigned endOffset = INT_MAX, bool useSelectionHeight = false); - - enum ClippingOption { NoClipping, ClipToEllipsis }; - void absoluteQuads(Vector&, ClippingOption = NoClipping) const; - - virtual PositionWithAffinity positionForPoint(const LayoutPoint&) override; - - bool is8Bit() const { return m_text.is8Bit(); } - const LChar* characters8() const { return m_text.impl()->characters8(); } - const UChar* characters16() const { return m_text.impl()->characters16(); } - bool hasEmptyText() const { return m_text.isEmpty(); } - String substring(unsigned position, unsigned length) const { return m_text.substring(position, length); } - UChar characterAt(unsigned) const; - UChar uncheckedCharacterAt(unsigned) const; - UChar operator[](unsigned i) const { return uncheckedCharacterAt(i); } - unsigned textLength() const { return m_text.length(); } // non virtual implementation of length() - void positionLineBox(InlineBox*); - - virtual float width(unsigned from, unsigned len, const Font&, float xPos, TextDirection, HashSet* fallbackFonts = 0, GlyphOverflow* = 0) const; - virtual float width(unsigned from, unsigned len, float xPos, TextDirection, bool firstLine = false, HashSet* fallbackFonts = 0, GlyphOverflow* = 0) const; - - float minLogicalWidth() const; - float maxLogicalWidth() const; - - void trimmedPrefWidths(float leadWidth, - float& firstLineMinWidth, bool& hasBreakableStart, - float& lastLineMinWidth, bool& hasBreakableEnd, - bool& hasBreakableChar, bool& hasBreak, - float& firstLineMaxWidth, float& lastLineMaxWidth, - float& minWidth, float& maxWidth, bool& stripFrontSpaces, - TextDirection); - - virtual IntRect linesBoundingBox() const; - LayoutRect linesVisualOverflowBoundingBox() const; - - FloatPoint firstRunOrigin() const; - float firstRunX() const; - float firstRunY() const; - - virtual void setText(PassRefPtr, bool force = false); - void setTextWithOffset(PassRefPtr, unsigned offset, unsigned len, bool force = false); - - virtual bool canBeSelectionLeaf() const override { return true; } - virtual void setSelectionState(SelectionState s) override final; - virtual LayoutRect localCaretRect(InlineBox*, int caretOffset, LayoutUnit* extraWidthToEndOfLine = 0) override; - - LayoutUnit marginLeft() const { return minimumValueForLength(style()->marginLeft(), 0); } - LayoutUnit marginRight() const { return minimumValueForLength(style()->marginRight(), 0); } - - InlineTextBox* firstTextBox() const { return m_firstTextBox; } - InlineTextBox* lastTextBox() const { return m_lastTextBox; } - - virtual int caretMinOffset() const override; - virtual int caretMaxOffset() const override; - unsigned renderedTextLength() const; - - virtual int previousOffset(int current) const override final; - virtual int previousOffsetForBackwardDeletion(int current) const override final; - virtual int nextOffset(int current) const override final; - - bool containsReversedText() const { return m_containsReversedText; } - - void checkConsistency() const; - - bool isAllCollapsibleWhitespace() const; - - bool canUseSimpleFontCodePath() const { return m_canUseSimpleFontCodePath; } - - void removeAndDestroyTextBoxes(); - -protected: - virtual void willBeDestroyed() override; - - virtual void styleWillChange(StyleDifference, const RenderStyle&) override final { } - virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle) override; - - virtual void setTextInternal(PassRefPtr); - virtual UChar previousCharacter() const; - - InlineTextBox* createTextBox(); - -private: - void computePreferredLogicalWidths(float leadWidth); - void computePreferredLogicalWidths(float leadWidth, HashSet& fallbackFonts, GlyphOverflow&); - - bool computeCanUseSimpleFontCodePath() const; - - // Make length() private so that callers that have a RenderText* - // will use the more efficient textLength() instead, while - // callers with a RenderObject* can continue to use length(). - virtual unsigned length() const override final { return textLength(); } - - virtual void paint(PaintInfo&, const LayoutPoint&, Vector& layers) override final { ASSERT_NOT_REACHED(); } - virtual void layout() override final { ASSERT_NOT_REACHED(); } - virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, const HitTestLocation&, const LayoutPoint&) override final { ASSERT_NOT_REACHED(); return false; } - - void deleteTextBoxes(); - bool containsOnlyWhitespace(unsigned from, unsigned len) const; - float widthFromCache(const Font&, int start, int len, float xPos, TextDirection, HashSet* fallbackFonts, GlyphOverflow*) const; - bool isAllASCII() const { return m_isAllASCII; } - - bool isText() const = delete; // This will catch anyone doing an unnecessary check. - - // We put the bitfield first to minimize padding on 64-bit. - bool m_hasBreakableChar : 1; // Whether or not we can be broken into multiple lines. - bool m_hasBreak : 1; // Whether or not we have a hard break (e.g.,
 with '\n').
-    bool m_hasTab : 1; // Whether or not we have a variable width tab character (e.g., 
 with '\t').
-    bool m_hasBreakableStart : 1;
-    bool m_hasBreakableEnd : 1;
-    bool m_hasEndWhiteSpace : 1;
-    bool m_linesDirty : 1; // This bit indicates that the text run has already dirtied specific
-                           // line boxes, and this hint will enable RenderParagraph::layoutChildren to avoid
-                           // just dirtying everything when character data is modified (e.g., appended/inserted
-                           // or removed).
-    bool m_containsReversedText : 1;
-    bool m_isAllASCII : 1;
-    bool m_canUseSimpleFontCodePath : 1;
-    mutable bool m_knownToHaveNoOverflowAndNoFallbackFonts : 1;
-
-    float m_minWidth;
-    float m_maxWidth;
-    float m_firstLineMinWidth;
-    float m_lastLineLineMinWidth;
-
-    String m_text;
-
-    InlineTextBox* m_firstTextBox;
-    InlineTextBox* m_lastTextBox;
+  virtual const char* renderName() const override;
+
+  void extractTextBox(InlineTextBox*);
+  void attachTextBox(InlineTextBox*);
+  void removeTextBox(InlineTextBox*);
+
+  const String& text() const { return m_text; }
+  virtual unsigned textStartOffset() const { return 0; }
+
+  InlineTextBox* createInlineTextBox();
+  void dirtyLineBoxes(bool fullLayout);
+
+  void appendAbsoluteTextBoxesForRange(std::vector&,
+                                       unsigned startOffset = 0,
+                                       unsigned endOffset = INT_MAX);
+
+  virtual void absoluteQuads(Vector&) const override final;
+  void absoluteQuadsForRange(Vector&,
+                             unsigned startOffset = 0,
+                             unsigned endOffset = INT_MAX,
+                             bool useSelectionHeight = false);
+
+  enum ClippingOption { NoClipping, ClipToEllipsis };
+  void absoluteQuads(Vector&, ClippingOption = NoClipping) const;
+
+  virtual PositionWithAffinity positionForPoint(const LayoutPoint&) override;
+
+  bool is8Bit() const { return m_text.is8Bit(); }
+  const LChar* characters8() const { return m_text.impl()->characters8(); }
+  const UChar* characters16() const { return m_text.impl()->characters16(); }
+  bool hasEmptyText() const { return m_text.isEmpty(); }
+  String substring(unsigned position, unsigned length) const {
+    return m_text.substring(position, length);
+  }
+  UChar characterAt(unsigned) const;
+  UChar uncheckedCharacterAt(unsigned) const;
+  UChar operator[](unsigned i) const { return uncheckedCharacterAt(i); }
+  unsigned textLength() const {
+    return m_text.length();
+  }  // non virtual implementation of length()
+  void positionLineBox(InlineBox*);
+
+  virtual float width(unsigned from,
+                      unsigned len,
+                      const Font&,
+                      float xPos,
+                      TextDirection,
+                      HashSet* fallbackFonts = 0,
+                      GlyphOverflow* = 0) const;
+  virtual float width(unsigned from,
+                      unsigned len,
+                      float xPos,
+                      TextDirection,
+                      bool firstLine = false,
+                      HashSet* fallbackFonts = 0,
+                      GlyphOverflow* = 0) const;
+
+  float minLogicalWidth() const;
+  float maxLogicalWidth() const;
+
+  void trimmedPrefWidths(float leadWidth,
+                         float& firstLineMinWidth,
+                         bool& hasBreakableStart,
+                         float& lastLineMinWidth,
+                         bool& hasBreakableEnd,
+                         bool& hasBreakableChar,
+                         bool& hasBreak,
+                         float& firstLineMaxWidth,
+                         float& lastLineMaxWidth,
+                         float& minWidth,
+                         float& maxWidth,
+                         bool& stripFrontSpaces,
+                         TextDirection);
+
+  virtual IntRect linesBoundingBox() const;
+  LayoutRect linesVisualOverflowBoundingBox() const;
+
+  FloatPoint firstRunOrigin() const;
+  float firstRunX() const;
+  float firstRunY() const;
+
+  virtual void setText(PassRefPtr, bool force = false);
+  void setTextWithOffset(PassRefPtr,
+                         unsigned offset,
+                         unsigned len,
+                         bool force = false);
+
+  virtual bool canBeSelectionLeaf() const override { return true; }
+  virtual void setSelectionState(SelectionState s) override final;
+  virtual LayoutRect localCaretRect(
+      InlineBox*,
+      int caretOffset,
+      LayoutUnit* extraWidthToEndOfLine = 0) override;
+
+  LayoutUnit marginLeft() const {
+    return minimumValueForLength(style()->marginLeft(), 0);
+  }
+  LayoutUnit marginRight() const {
+    return minimumValueForLength(style()->marginRight(), 0);
+  }
+
+  InlineTextBox* firstTextBox() const { return m_firstTextBox; }
+  InlineTextBox* lastTextBox() const { return m_lastTextBox; }
+
+  virtual int caretMinOffset() const override;
+  virtual int caretMaxOffset() const override;
+  unsigned renderedTextLength() const;
+
+  virtual int previousOffset(int current) const override final;
+  virtual int previousOffsetForBackwardDeletion(
+      int current) const override final;
+  virtual int nextOffset(int current) const override final;
+
+  bool containsReversedText() const { return m_containsReversedText; }
+
+  void checkConsistency() const;
+
+  bool isAllCollapsibleWhitespace() const;
+
+  bool canUseSimpleFontCodePath() const { return m_canUseSimpleFontCodePath; }
+
+  void removeAndDestroyTextBoxes();
+
+ protected:
+  virtual void willBeDestroyed() override;
+
+  virtual void styleWillChange(StyleDifference,
+                               const RenderStyle&) override final {}
+  virtual void styleDidChange(StyleDifference,
+                              const RenderStyle* oldStyle) override;
+
+  virtual void setTextInternal(PassRefPtr);
+  virtual UChar previousCharacter() const;
+
+  InlineTextBox* createTextBox();
+
+ private:
+  void computePreferredLogicalWidths(float leadWidth);
+  void computePreferredLogicalWidths(
+      float leadWidth,
+      HashSet& fallbackFonts,
+      GlyphOverflow&);
+
+  bool computeCanUseSimpleFontCodePath() const;
+
+  // Make length() private so that callers that have a RenderText*
+  // will use the more efficient textLength() instead, while
+  // callers with a RenderObject* can continue to use length().
+  virtual unsigned length() const override final { return textLength(); }
+
+  virtual void paint(PaintInfo&,
+                     const LayoutPoint&,
+                     Vector& layers) override final {
+    ASSERT_NOT_REACHED();
+  }
+  virtual void layout() override final { ASSERT_NOT_REACHED(); }
+  virtual bool nodeAtPoint(const HitTestRequest&,
+                           HitTestResult&,
+                           const HitTestLocation&,
+                           const LayoutPoint&) override final {
+    ASSERT_NOT_REACHED();
+    return false;
+  }
+
+  void deleteTextBoxes();
+  bool containsOnlyWhitespace(unsigned from, unsigned len) const;
+  float widthFromCache(const Font&,
+                       int start,
+                       int len,
+                       float xPos,
+                       TextDirection,
+                       HashSet* fallbackFonts,
+                       GlyphOverflow*) const;
+  bool isAllASCII() const { return m_isAllASCII; }
+
+  bool isText() const =
+      delete;  // This will catch anyone doing an unnecessary check.
+
+  // We put the bitfield first to minimize padding on 64-bit.
+  bool m_hasBreakableChar : 1;  // Whether or not we can be broken into multiple
+                                // lines.
+  bool m_hasBreak : 1;  // Whether or not we have a hard break (e.g., 
 with
+                        // '\n').
+  bool m_hasTab : 1;    // Whether or not we have a variable width tab character
+                        // (e.g., 
 with '\t').
+  bool m_hasBreakableStart : 1;
+  bool m_hasBreakableEnd : 1;
+  bool m_hasEndWhiteSpace : 1;
+  bool m_linesDirty : 1;  // This bit indicates that the text run has already
+                          // dirtied specific line boxes, and this hint will
+                          // enable RenderParagraph::layoutChildren to avoid
+                          // just dirtying everything when character data is
+                          // modified (e.g., appended/inserted or removed).
+  bool m_containsReversedText : 1;
+  bool m_isAllASCII : 1;
+  bool m_canUseSimpleFontCodePath : 1;
+  mutable bool m_knownToHaveNoOverflowAndNoFallbackFonts : 1;
+
+  float m_minWidth;
+  float m_maxWidth;
+  float m_firstLineMinWidth;
+  float m_lastLineLineMinWidth;
+
+  String m_text;
+
+  InlineTextBox* m_firstTextBox;
+  InlineTextBox* m_lastTextBox;
 };
 
-inline UChar RenderText::uncheckedCharacterAt(unsigned i) const
-{
-    ASSERT_WITH_SECURITY_IMPLICATION(i < textLength());
-    return is8Bit() ? characters8()[i] : characters16()[i];
+inline UChar RenderText::uncheckedCharacterAt(unsigned i) const {
+  ASSERT_WITH_SECURITY_IMPLICATION(i < textLength());
+  return is8Bit() ? characters8()[i] : characters16()[i];
 }
 
-inline UChar RenderText::characterAt(unsigned i) const
-{
-    if (i >= textLength())
-        return 0;
+inline UChar RenderText::characterAt(unsigned i) const {
+  if (i >= textLength())
+    return 0;
 
-    return uncheckedCharacterAt(i);
+  return uncheckedCharacterAt(i);
 }
 
 DEFINE_RENDER_OBJECT_TYPE_CASTS(RenderText, isText());
 
 #if !ENABLE(ASSERT)
-inline void RenderText::checkConsistency() const
-{
-}
+inline void RenderText::checkConsistency() const {}
 #endif
 
-} // namespace blink
+}  // namespace blink
 
 #endif  // SKY_ENGINE_CORE_RENDERING_RENDERTEXT_H_
diff --git a/sky/engine/core/rendering/RenderTheme.cpp b/sky/engine/core/rendering/RenderTheme.cpp
index bdde723520201..fd8d122c1ad4c 100644
--- a/sky/engine/core/rendering/RenderTheme.cpp
+++ b/sky/engine/core/rendering/RenderTheme.cpp
@@ -34,76 +34,62 @@
 
 namespace blink {
 
-RenderTheme::RenderTheme()
-    : m_hasCustomFocusRingColor(false)
-{
-}
+RenderTheme::RenderTheme() : m_hasCustomFocusRingColor(false) {}
 
-Color RenderTheme::activeSelectionBackgroundColor() const
-{
-    return platformActiveSelectionBackgroundColor().blendWithWhite();
+Color RenderTheme::activeSelectionBackgroundColor() const {
+  return platformActiveSelectionBackgroundColor().blendWithWhite();
 }
 
-Color RenderTheme::inactiveSelectionBackgroundColor() const
-{
-    return platformInactiveSelectionBackgroundColor().blendWithWhite();
+Color RenderTheme::inactiveSelectionBackgroundColor() const {
+  return platformInactiveSelectionBackgroundColor().blendWithWhite();
 }
 
-Color RenderTheme::activeSelectionForegroundColor() const
-{
-    return platformActiveSelectionForegroundColor();
+Color RenderTheme::activeSelectionForegroundColor() const {
+  return platformActiveSelectionForegroundColor();
 }
 
-Color RenderTheme::inactiveSelectionForegroundColor() const
-{
-    return platformInactiveSelectionForegroundColor();
+Color RenderTheme::inactiveSelectionForegroundColor() const {
+  return platformInactiveSelectionForegroundColor();
 }
 
-Color RenderTheme::platformActiveSelectionBackgroundColor() const
-{
-    // Use a blue color by default if the platform theme doesn't define anything.
-    return Color(0, 0, 255);
+Color RenderTheme::platformActiveSelectionBackgroundColor() const {
+  // Use a blue color by default if the platform theme doesn't define anything.
+  return Color(0, 0, 255);
 }
 
-Color RenderTheme::platformActiveSelectionForegroundColor() const
-{
-    // Use a white color by default if the platform theme doesn't define anything.
-    return Color::white;
+Color RenderTheme::platformActiveSelectionForegroundColor() const {
+  // Use a white color by default if the platform theme doesn't define anything.
+  return Color::white;
 }
 
-Color RenderTheme::platformInactiveSelectionBackgroundColor() const
-{
-    // Use a grey color by default if the platform theme doesn't define anything.
-    // This color matches Firefox's inactive color.
-    return Color(176, 176, 176);
+Color RenderTheme::platformInactiveSelectionBackgroundColor() const {
+  // Use a grey color by default if the platform theme doesn't define anything.
+  // This color matches Firefox's inactive color.
+  return Color(176, 176, 176);
 }
 
-Color RenderTheme::platformInactiveSelectionForegroundColor() const
-{
-    // Use a black color by default.
-    return Color::black;
+Color RenderTheme::platformInactiveSelectionForegroundColor() const {
+  // Use a black color by default.
+  return Color::black;
 }
 
-void RenderTheme::setCustomFocusRingColor(const Color& c)
-{
-    m_customFocusRingColor = c;
-    m_hasCustomFocusRingColor = true;
+void RenderTheme::setCustomFocusRingColor(const Color& c) {
+  m_customFocusRingColor = c;
+  m_hasCustomFocusRingColor = true;
 }
 
-Color RenderTheme::focusRingColor() const
-{
-    return m_hasCustomFocusRingColor ? m_customFocusRingColor : theme().platformFocusRingColor();
+Color RenderTheme::focusRingColor() const {
+  return m_hasCustomFocusRingColor ? m_customFocusRingColor
+                                   : theme().platformFocusRingColor();
 }
 
-Color RenderTheme::tapHighlightColor()
-{
-    return theme().platformTapHighlightColor();
+Color RenderTheme::tapHighlightColor() {
+  return theme().platformTapHighlightColor();
 }
 
-RenderTheme& RenderTheme::theme()
-{
-    DEFINE_STATIC_LOCAL(RenderTheme, renderTheme, ());
-    return renderTheme;
+RenderTheme& RenderTheme::theme() {
+  DEFINE_STATIC_LOCAL(RenderTheme, renderTheme, ());
+  return renderTheme;
 }
 
-} // namespace blink
+}  // namespace blink
diff --git a/sky/engine/core/rendering/RenderTheme.h b/sky/engine/core/rendering/RenderTheme.h
index 9729e8c562513..f9a82654b1795 100644
--- a/sky/engine/core/rendering/RenderTheme.h
+++ b/sky/engine/core/rendering/RenderTheme.h
@@ -31,47 +31,51 @@
 namespace blink {
 
 class RenderTheme : public RefCounted {
-protected:
-    RenderTheme();
+ protected:
+  RenderTheme();
 
-public:
-    // This function is to be implemented in your platform-specific theme implementation to hand back the
-    // appropriate platform theme.
-    static RenderTheme& theme();
+ public:
+  // This function is to be implemented in your platform-specific theme
+  // implementation to hand back the appropriate platform theme.
+  static RenderTheme& theme();
 
-    Color focusRingColor() const;
-    virtual double caretBlinkInterval() const { return 0.5; }
+  Color focusRingColor() const;
+  virtual double caretBlinkInterval() const { return 0.5; }
 
-    // Text selection colors.
-    Color activeSelectionBackgroundColor() const;
-    Color inactiveSelectionBackgroundColor() const;
-    Color activeSelectionForegroundColor() const;
-    Color inactiveSelectionForegroundColor() const;
+  // Text selection colors.
+  Color activeSelectionBackgroundColor() const;
+  Color inactiveSelectionBackgroundColor() const;
+  Color activeSelectionForegroundColor() const;
+  Color inactiveSelectionForegroundColor() const;
 
-    virtual bool supportsSelectionForegroundColors() const { return true; }
-    virtual Color platformDefaultCompositionBackgroundColor() const { return defaultCompositionBackgroundColor; }
-    void setCustomFocusRingColor(const Color&);
+  virtual bool supportsSelectionForegroundColors() const { return true; }
+  virtual Color platformDefaultCompositionBackgroundColor() const {
+    return defaultCompositionBackgroundColor;
+  }
+  void setCustomFocusRingColor(const Color&);
 
-    static Color tapHighlightColor();
-    virtual Color platformTapHighlightColor() const { return RenderTheme::defaultTapHighlightColor; }
+  static Color tapHighlightColor();
+  virtual Color platformTapHighlightColor() const {
+    return RenderTheme::defaultTapHighlightColor;
+  }
 
-protected:
-    virtual Color platformActiveSelectionBackgroundColor() const;
-    virtual Color platformInactiveSelectionBackgroundColor() const;
-    virtual Color platformActiveSelectionForegroundColor() const;
-    virtual Color platformInactiveSelectionForegroundColor() const;
-    virtual Color platformFocusRingColor() const { return Color(0, 0, 0); }
+ protected:
+  virtual Color platformActiveSelectionBackgroundColor() const;
+  virtual Color platformInactiveSelectionBackgroundColor() const;
+  virtual Color platformActiveSelectionForegroundColor() const;
+  virtual Color platformInactiveSelectionForegroundColor() const;
+  virtual Color platformFocusRingColor() const { return Color(0, 0, 0); }
 
-private:
-    Color m_customFocusRingColor;
-    bool m_hasCustomFocusRingColor;
+ private:
+  Color m_customFocusRingColor;
+  bool m_hasCustomFocusRingColor;
 
-    // This color is expected to be drawn on a semi-transparent overlay,
-    // making it more transparent than its alpha value indicates.
-    static const RGBA32 defaultTapHighlightColor = 0x66000000;
-    static const RGBA32 defaultCompositionBackgroundColor = 0xFFFFDD55;
+  // This color is expected to be drawn on a semi-transparent overlay,
+  // making it more transparent than its alpha value indicates.
+  static const RGBA32 defaultTapHighlightColor = 0x66000000;
+  static const RGBA32 defaultCompositionBackgroundColor = 0xFFFFDD55;
 };
 
-} // namespace blink
+}  // namespace blink
 
 #endif  // SKY_ENGINE_CORE_RENDERING_RENDERTHEME_H_
diff --git a/sky/engine/core/rendering/RenderTreeAsText.cpp b/sky/engine/core/rendering/RenderTreeAsText.cpp
index c79a28af91613..41677be03a113 100644
--- a/sky/engine/core/rendering/RenderTreeAsText.cpp
+++ b/sky/engine/core/rendering/RenderTreeAsText.cpp
@@ -35,156 +35,184 @@
 
 namespace blink {
 
-String quoteAndEscapeNonPrintables(const String& s)
-{
-    StringBuilder result;
-    result.append('"');
-    for (unsigned i = 0; i != s.length(); ++i) {
-        UChar c = s[i];
-        if (c == '\\') {
-            result.append('\\');
-            result.append('\\');
-        } else if (c == '"') {
-            result.append('\\');
-            result.append('"');
-        } else if (c == '\n' || c == noBreakSpace)
-            result.append(' ');
-        else {
-            if (c >= 0x20 && c < 0x7F)
-                result.append(c);
-            else {
-                result.append('\\');
-                result.append('x');
-                result.append('{');
-                appendUnsignedAsHex(c, result);
-                result.append('}');
-            }
-        }
+String quoteAndEscapeNonPrintables(const String& s) {
+  StringBuilder result;
+  result.append('"');
+  for (unsigned i = 0; i != s.length(); ++i) {
+    UChar c = s[i];
+    if (c == '\\') {
+      result.append('\\');
+      result.append('\\');
+    } else if (c == '"') {
+      result.append('\\');
+      result.append('"');
+    } else if (c == '\n' || c == noBreakSpace)
+      result.append(' ');
+    else {
+      if (c >= 0x20 && c < 0x7F)
+        result.append(c);
+      else {
+        result.append('\\');
+        result.append('x');
+        result.append('{');
+        appendUnsignedAsHex(c, result);
+        result.append('}');
+      }
     }
-    result.append('"');
-    return result.toString();
+  }
+  result.append('"');
+  return result.toString();
 }
 
-void RenderTreeAsText::writeRenderObject(TextStream& ts, const RenderObject& o, RenderAsTextBehavior behavior)
-{
+void RenderTreeAsText::writeRenderObject(TextStream& ts,
+                                         const RenderObject& o,
+                                         RenderAsTextBehavior behavior) {}
+
+static void writeTextRun(TextStream& ts,
+                         const RenderText& o,
+                         const InlineTextBox& run) {
+  // FIXME: For now use an "enclosingIntRect" model for x, y and logicalWidth,
+  // although this makes it harder to detect any changes caused by the
+  // conversion to floating point. :(
+  int x = run.x();
+  int y = run.y();
+  int logicalWidth = ceilf(run.left() + run.logicalWidth()) - x;
+
+  ts << "text run at (" << x << "," << y << ") width " << logicalWidth;
+  if (!run.isLeftToRightDirection() || run.dirOverride()) {
+    ts << (!run.isLeftToRightDirection() ? " RTL" : " LTR");
+    if (run.dirOverride())
+      ts << " override";
+  }
+  ts << ": "
+     << quoteAndEscapeNonPrintables(
+            String(o.text()).substring(run.start(), run.len()));
+  if (run.hasHyphen())
+    ts << " + hyphen string "
+       << quoteAndEscapeNonPrintables(o.style()->hyphenString());
+  ts << "\n";
 }
 
-static void writeTextRun(TextStream& ts, const RenderText& o, const InlineTextBox& run)
-{
-    // FIXME: For now use an "enclosingIntRect" model for x, y and logicalWidth, although this makes it harder
-    // to detect any changes caused by the conversion to floating point. :(
-    int x = run.x();
-    int y = run.y();
-    int logicalWidth = ceilf(run.left() + run.logicalWidth()) - x;
-
-    ts << "text run at (" << x << "," << y << ") width " << logicalWidth;
-    if (!run.isLeftToRightDirection() || run.dirOverride()) {
-        ts << (!run.isLeftToRightDirection() ? " RTL" : " LTR");
-        if (run.dirOverride())
-            ts << " override";
+void write(TextStream& ts,
+           const RenderObject& o,
+           int indent,
+           RenderAsTextBehavior behavior) {
+  writeIndent(ts, indent);
+
+  RenderTreeAsText::writeRenderObject(ts, o, behavior);
+  ts << "\n";
+
+  if (o.isText()) {
+    const RenderText& text = toRenderText(o);
+    for (InlineTextBox* box = text.firstTextBox(); box;
+         box = box->nextTextBox()) {
+      writeIndent(ts, indent + 1);
+      writeTextRun(ts, text, *box);
     }
-    ts << ": "
-        << quoteAndEscapeNonPrintables(String(o.text()).substring(run.start(), run.len()));
-    if (run.hasHyphen())
-        ts << " + hyphen string " << quoteAndEscapeNonPrintables(o.style()->hyphenString());
-    ts << "\n";
+  }
+
+  for (RenderObject* child = o.slowFirstChild(); child;
+       child = child->nextSibling()) {
+    if (child->hasLayer())
+      continue;
+    write(ts, *child, indent + 1, behavior);
+  }
 }
 
-void write(TextStream& ts, const RenderObject& o, int indent, RenderAsTextBehavior behavior)
-{
-    writeIndent(ts, indent);
+static void write(TextStream& ts,
+                  RenderLayer& l,
+                  const LayoutRect& layerBounds,
+                  const LayoutRect& backgroundClipRect,
+                  int indent = 0,
+                  RenderAsTextBehavior behavior = RenderAsTextBehaviorNormal) {
+  IntRect adjustedLayoutBounds = pixelSnappedIntRect(layerBounds);
+  IntRect adjustedBackgroundClipRect = pixelSnappedIntRect(backgroundClipRect);
 
-    RenderTreeAsText::writeRenderObject(ts, o, behavior);
-    ts << "\n";
+  writeIndent(ts, indent);
 
-    if (o.isText()) {
-        const RenderText& text = toRenderText(o);
-        for (InlineTextBox* box = text.firstTextBox(); box; box = box->nextTextBox()) {
-            writeIndent(ts, indent + 1);
-            writeTextRun(ts, text, *box);
-        }
-    }
-
-    for (RenderObject* child = o.slowFirstChild(); child; child = child->nextSibling()) {
-        if (child->hasLayer())
-            continue;
-        write(ts, *child, indent + 1, behavior);
-    }
-}
+  ts << "layer ";
 
-static void write(TextStream& ts, RenderLayer& l,
-                  const LayoutRect& layerBounds, const LayoutRect& backgroundClipRect,
-                  int indent = 0, RenderAsTextBehavior behavior = RenderAsTextBehaviorNormal)
-{
-    IntRect adjustedLayoutBounds = pixelSnappedIntRect(layerBounds);
-    IntRect adjustedBackgroundClipRect = pixelSnappedIntRect(backgroundClipRect);
+  if (behavior & RenderAsTextShowAddresses)
+    ts << static_cast(&l) << " ";
 
-    writeIndent(ts, indent);
+  ts << adjustedLayoutBounds;
 
-    ts << "layer ";
-
-    if (behavior & RenderAsTextShowAddresses)
-        ts << static_cast(&l) << " ";
-
-    ts << adjustedLayoutBounds;
-
-    if (!adjustedLayoutBounds.isEmpty()) {
-        if (!adjustedBackgroundClipRect.contains(adjustedLayoutBounds))
-            ts << " backgroundClip " << adjustedBackgroundClipRect;
-    }
+  if (!adjustedLayoutBounds.isEmpty()) {
+    if (!adjustedBackgroundClipRect.contains(adjustedLayoutBounds))
+      ts << " backgroundClip " << adjustedBackgroundClipRect;
+  }
 
-    ts << "\n";
-    write(ts, *l.renderer(), indent + 1, behavior);
+  ts << "\n";
+  write(ts, *l.renderer(), indent + 1, behavior);
 }
 
-void RenderTreeAsText::writeLayers(TextStream& ts, const RenderLayer* rootLayer, RenderLayer* layer,
-    const LayoutRect& paintRect, int indent, RenderAsTextBehavior behavior)
-{
-    // FIXME: Apply overflow to the root layer to not break every test. Complete hack. Sigh.
-    LayoutRect paintDirtyRect(paintRect);
-    if (rootLayer == layer) {
-        paintDirtyRect.setWidth(max(paintDirtyRect.width(), rootLayer->renderer()->layoutOverflowRect().maxX()));
-        paintDirtyRect.setHeight(max(paintDirtyRect.height(), rootLayer->renderer()->layoutOverflowRect().maxY()));
+void RenderTreeAsText::writeLayers(TextStream& ts,
+                                   const RenderLayer* rootLayer,
+                                   RenderLayer* layer,
+                                   const LayoutRect& paintRect,
+                                   int indent,
+                                   RenderAsTextBehavior behavior) {
+  // FIXME: Apply overflow to the root layer to not break every test. Complete
+  // hack. Sigh.
+  LayoutRect paintDirtyRect(paintRect);
+  if (rootLayer == layer) {
+    paintDirtyRect.setWidth(
+        max(paintDirtyRect.width(),
+                        rootLayer->renderer()->layoutOverflowRect().maxX()));
+    paintDirtyRect.setHeight(
+        max(paintDirtyRect.height(),
+                        rootLayer->renderer()->layoutOverflowRect().maxY()));
+  }
+
+  // Calculate the clip rects we should use.
+  LayoutRect layerBounds;
+  ClipRect damageRect;
+  layer->clipper().calculateRects(
+      ClipRectsContext(rootLayer, UncachedClipRects), paintDirtyRect,
+      layerBounds, damageRect);
+
+  // FIXME: Apply overflow to the root layer to not break every test. Complete
+  // hack. Sigh.
+  if (rootLayer == layer)
+    layerBounds.setSize(layer->size().expandedTo(pixelSnappedIntSize(
+        layer->renderer()->maxLayoutOverflow(), LayoutPoint(0, 0))));
+
+  // Ensure our lists are up-to-date.
+  layer->stackingNode()->updateLayerListsIfNeeded();
+
+  bool shouldPaint = (behavior & RenderAsTextShowAllLayers)
+                         ? true
+                         : layer->intersectsDamageRect(
+                               layerBounds, damageRect.rect(), rootLayer);
+
+  if (shouldPaint)
+    write(ts, *layer, layerBounds, damageRect.rect(), indent, behavior);
+
+  if (Vector* normalFlowList =
+          layer->stackingNode()->normalFlowList()) {
+    int currIndent = indent;
+    if (behavior & RenderAsTextShowLayerNesting) {
+      writeIndent(ts, indent);
+      ts << " normal flow list(" << normalFlowList->size() << ")\n";
+      ++currIndent;
     }
-
-    // Calculate the clip rects we should use.
-    LayoutRect layerBounds;
-    ClipRect damageRect;
-    layer->clipper().calculateRects(ClipRectsContext(rootLayer, UncachedClipRects), paintDirtyRect, layerBounds, damageRect);
-
-    // FIXME: Apply overflow to the root layer to not break every test. Complete hack. Sigh.
-    if (rootLayer == layer)
-        layerBounds.setSize(layer->size().expandedTo(pixelSnappedIntSize(layer->renderer()->maxLayoutOverflow(), LayoutPoint(0, 0))));
-
-    // Ensure our lists are up-to-date.
-    layer->stackingNode()->updateLayerListsIfNeeded();
-
-    bool shouldPaint = (behavior & RenderAsTextShowAllLayers) ? true : layer->intersectsDamageRect(layerBounds, damageRect.rect(), rootLayer);
-
-    if (shouldPaint)
-        write(ts, *layer, layerBounds, damageRect.rect(), indent, behavior);
-
-    if (Vector* normalFlowList = layer->stackingNode()->normalFlowList()) {
-        int currIndent = indent;
-        if (behavior & RenderAsTextShowLayerNesting) {
-            writeIndent(ts, indent);
-            ts << " normal flow list(" << normalFlowList->size() << ")\n";
-            ++currIndent;
-        }
-        for (unsigned i = 0; i != normalFlowList->size(); ++i)
-            writeLayers(ts, rootLayer, normalFlowList->at(i)->layer(), paintDirtyRect, currIndent, behavior);
-    }
-
-    if (Vector* posList = layer->stackingNode()->zOrderList()) {
-        int currIndent = indent;
-        if (behavior & RenderAsTextShowLayerNesting) {
-            writeIndent(ts, indent);
-            ts << " positive z-order list(" << posList->size() << ")\n";
-            ++currIndent;
-        }
-        for (unsigned i = 0; i != posList->size(); ++i)
-            writeLayers(ts, rootLayer, posList->at(i)->layer(), paintDirtyRect, currIndent, behavior);
+    for (unsigned i = 0; i != normalFlowList->size(); ++i)
+      writeLayers(ts, rootLayer, normalFlowList->at(i)->layer(), paintDirtyRect,
+                  currIndent, behavior);
+  }
+
+  if (Vector* posList =
+          layer->stackingNode()->zOrderList()) {
+    int currIndent = indent;
+    if (behavior & RenderAsTextShowLayerNesting) {
+      writeIndent(ts, indent);
+      ts << " positive z-order list(" << posList->size() << ")\n";
+      ++currIndent;
     }
+    for (unsigned i = 0; i != posList->size(); ++i)
+      writeLayers(ts, rootLayer, posList->at(i)->layer(), paintDirtyRect,
+                  currIndent, behavior);
+  }
 }
 
-} // namespace blink
+}  // namespace blink
diff --git a/sky/engine/core/rendering/RenderTreeAsText.h b/sky/engine/core/rendering/RenderTreeAsText.h
index 075a4ddef94d7..e9cd7fd8e81a4 100644
--- a/sky/engine/core/rendering/RenderTreeAsText.h
+++ b/sky/engine/core/rendering/RenderTreeAsText.h
@@ -38,32 +38,49 @@ class RenderObject;
 class TextStream;
 
 enum RenderAsTextBehaviorFlags {
-    RenderAsTextBehaviorNormal = 0,
-    RenderAsTextShowAllLayers = 1 << 0, // Dump all layers, not just those that would paint.
-    RenderAsTextShowLayerNesting = 1 << 1, // Annotate the layer lists.
-    RenderAsTextShowCompositedLayers = 1 << 2, // Show which layers are composited.
-    RenderAsTextShowAddresses = 1 << 3, // Show layer and renderer addresses.
-    RenderAsTextShowIDAndClass = 1 << 4, // Show id and class attributes
-    RenderAsTextDontUpdateLayout = 1 << 6, // Don't update layout, to make it safe to call showLayerTree() from the debugger inside layout or painting code.
-    RenderAsTextShowLayoutState = 1 << 7 // Print the various 'needs layout' bits on renderers.
+  RenderAsTextBehaviorNormal = 0,
+  RenderAsTextShowAllLayers =
+      1 << 0,  // Dump all layers, not just those that would paint.
+  RenderAsTextShowLayerNesting = 1 << 1,  // Annotate the layer lists.
+  RenderAsTextShowCompositedLayers = 1
+                                     << 2,  // Show which layers are composited.
+  RenderAsTextShowAddresses = 1 << 3,   // Show layer and renderer addresses.
+  RenderAsTextShowIDAndClass = 1 << 4,  // Show id and class attributes
+  RenderAsTextDontUpdateLayout =
+      1 << 6,  // Don't update layout, to make it safe to call showLayerTree()
+               // from the debugger inside layout or painting code.
+  RenderAsTextShowLayoutState =
+      1 << 7  // Print the various 'needs layout' bits on renderers.
 };
 typedef unsigned RenderAsTextBehavior;
 
-// You don't need pageWidthInPixels if you don't specify RenderAsTextInPrintingMode.
-void write(TextStream&, const RenderObject&, int indent = 0, RenderAsTextBehavior = RenderAsTextBehaviorNormal);
+// You don't need pageWidthInPixels if you don't specify
+// RenderAsTextInPrintingMode.
+void write(TextStream&,
+           const RenderObject&,
+           int indent = 0,
+           RenderAsTextBehavior = RenderAsTextBehaviorNormal);
 
 class RenderTreeAsText {
-// FIXME: This is a cheesy hack to allow easy access to RenderStyle colors.  It won't be needed if we convert
-// it to use colorIncludingFallback instead. (This just involves rebaselining many results though, so for now it's
-// not being done).
-public:
-static void writeRenderObject(TextStream& ts, const RenderObject& o, RenderAsTextBehavior behavior);
-static void writeLayers(TextStream&, const RenderLayer* rootLayer, RenderLayer*, const LayoutRect& paintDirtyRect, int indent = 0, RenderAsTextBehavior = RenderAsTextBehaviorNormal);
+  // FIXME: This is a cheesy hack to allow easy access to RenderStyle colors.
+  // It won't be needed if we convert it to use colorIncludingFallback instead.
+  // (This just involves rebaselining many results though, so for now it's not
+  // being done).
+ public:
+  static void writeRenderObject(TextStream& ts,
+                                const RenderObject& o,
+                                RenderAsTextBehavior behavior);
+  static void writeLayers(TextStream&,
+                          const RenderLayer* rootLayer,
+                          RenderLayer*,
+                          const LayoutRect& paintDirtyRect,
+                          int indent = 0,
+                          RenderAsTextBehavior = RenderAsTextBehaviorNormal);
 };
 
 // Helper function shared with SVGRenderTreeAsText
 String quoteAndEscapeNonPrintables(const String&);
 
-} // namespace blink
+}  // namespace blink
 
 #endif  // SKY_ENGINE_CORE_RENDERING_RENDERTREEASTEXT_H_
diff --git a/sky/engine/core/rendering/RenderView.cpp b/sky/engine/core/rendering/RenderView.cpp
index b1b9dd4e99d5e..6118bbbd92f06 100644
--- a/sky/engine/core/rendering/RenderView.cpp
+++ b/sky/engine/core/rendering/RenderView.cpp
@@ -1,6 +1,7 @@
 /*
  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
- * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc.
+ * All rights reserved.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -30,303 +31,312 @@
 namespace blink {
 
 RenderView::RenderView()
-    : m_selectionStart(nullptr)
-    , m_selectionEnd(nullptr)
-    , m_selectionStartPos(-1)
-    , m_selectionEndPos(-1)
-    , m_renderCounterCount(0)
-    , m_hitTestCount(0)
-{
-    // init RenderObject attributes
-    setInline(false);
-
-    m_minPreferredLogicalWidth = 0;
-    m_maxPreferredLogicalWidth = 0;
-
-    setPreferredLogicalWidthsDirty(MarkOnlyThis);
-
-    setPositionState(AbsolutePosition); // to 0,0 :)
-}
+    : m_selectionStart(nullptr),
+      m_selectionEnd(nullptr),
+      m_selectionStartPos(-1),
+      m_selectionEndPos(-1),
+      m_renderCounterCount(0),
+      m_hitTestCount(0) {
+  // init RenderObject attributes
+  setInline(false);
+
+  m_minPreferredLogicalWidth = 0;
+  m_maxPreferredLogicalWidth = 0;
 
-RenderView::~RenderView()
-{
+  setPreferredLogicalWidthsDirty(MarkOnlyThis);
+
+  setPositionState(AbsolutePosition);  // to 0,0 :)
 }
 
-bool RenderView::hitTest(const HitTestRequest& request, HitTestResult& result)
-{
-    return hitTest(request, result.hitTestLocation(), result);
+RenderView::~RenderView() {}
+
+bool RenderView::hitTest(const HitTestRequest& request, HitTestResult& result) {
+  return hitTest(request, result.hitTestLocation(), result);
 }
 
-bool RenderView::hitTest(const HitTestRequest& request, const HitTestLocation& location, HitTestResult& result)
-{
-    m_hitTestCount++;
-
-    // TODO(ojan): Does any of this intersection stuff make sense for Sky?
-    LayoutRect hitTestArea;
-    hitTestArea.setSize(m_frameViewSize);
-
-    bool insideLayer = hitTestLayer(layer(), 0, request, result, hitTestArea, location);
-    if (!insideLayer) {
-        // TODO(ojan): Is this code needed for Sky?
-
-        // We didn't hit any layer. If we are the root layer and the mouse is -- or just was -- down,
-        // return ourselves. We do this so mouse events continue getting delivered after a drag has
-        // exited the WebView, and so hit testing over a scrollbar hits the content document.
-        if (request.active() || request.release()) {
-            updateHitTestResult(result, location.point());
-            insideLayer = true;
-        }
+bool RenderView::hitTest(const HitTestRequest& request,
+                         const HitTestLocation& location,
+                         HitTestResult& result) {
+  m_hitTestCount++;
+
+  // TODO(ojan): Does any of this intersection stuff make sense for Sky?
+  LayoutRect hitTestArea;
+  hitTestArea.setSize(m_frameViewSize);
+
+  bool insideLayer =
+      hitTestLayer(layer(), 0, request, result, hitTestArea, location);
+  if (!insideLayer) {
+    // TODO(ojan): Is this code needed for Sky?
+
+    // We didn't hit any layer. If we are the root layer and the mouse is -- or
+    // just was -- down, return ourselves. We do this so mouse events continue
+    // getting delivered after a drag has exited the WebView, and so hit testing
+    // over a scrollbar hits the content document.
+    if (request.active() || request.release()) {
+      updateHitTestResult(result, location.point());
+      insideLayer = true;
     }
-    return insideLayer;
+  }
+  return insideLayer;
 }
 
-void RenderView::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit, LogicalExtentComputedValues& computedValues) const
-{
-    computedValues.m_extent = logicalHeight;
+void RenderView::computeLogicalHeight(
+    LayoutUnit logicalHeight,
+    LayoutUnit,
+    LogicalExtentComputedValues& computedValues) const {
+  computedValues.m_extent = logicalHeight;
 }
 
-void RenderView::updateLogicalWidth()
-{
-    setLogicalWidth(viewLogicalWidth());
+void RenderView::updateLogicalWidth() {
+  setLogicalWidth(viewLogicalWidth());
 }
 
-bool RenderView::isChildAllowed(RenderObject* child, RenderStyle*) const
-{
-    return child->isBox();
+bool RenderView::isChildAllowed(RenderObject* child, RenderStyle*) const {
+  return child->isBox();
 }
 
-void RenderView::layout()
-{
-    SubtreeLayoutScope layoutScope(*this);
-
-    bool relayoutChildren = width() != viewWidth() || height() != viewHeight();
-    if (relayoutChildren) {
-        layoutScope.setChildNeedsLayout(this);
-        for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
-            if ((child->isBox() && toRenderBox(child)->hasRelativeLogicalHeight())
-                    || child->style()->logicalHeight().isPercent()
-                    || child->style()->logicalMinHeight().isPercent()
-                    || child->style()->logicalMaxHeight().isPercent())
-                layoutScope.setChildNeedsLayout(child);
-        }
+void RenderView::layout() {
+  SubtreeLayoutScope layoutScope(*this);
+
+  bool relayoutChildren = width() != viewWidth() || height() != viewHeight();
+  if (relayoutChildren) {
+    layoutScope.setChildNeedsLayout(this);
+    for (RenderObject* child = firstChild(); child;
+         child = child->nextSibling()) {
+      if ((child->isBox() && toRenderBox(child)->hasRelativeLogicalHeight()) ||
+          child->style()->logicalHeight().isPercent() ||
+          child->style()->logicalMinHeight().isPercent() ||
+          child->style()->logicalMaxHeight().isPercent())
+        layoutScope.setChildNeedsLayout(child);
     }
+  }
 
-    if (!needsLayout())
-        return;
+  if (!needsLayout())
+    return;
 
-    RenderFlexibleBox::layout();
-    clearNeedsLayout();
+  RenderFlexibleBox::layout();
+  clearNeedsLayout();
 }
 
-void RenderView::mapLocalToContainer(const RenderBox* paintInvalidationContainer, TransformState& transformState, MapCoordinatesFlags mode) const
-{
-    if (!paintInvalidationContainer && mode & UseTransforms && shouldUseTransformFromContainer(0)) {
-        TransformationMatrix t;
-        getTransformFromContainer(0, LayoutSize(), t);
-        transformState.applyTransform(t);
-    }
+void RenderView::mapLocalToContainer(
+    const RenderBox* paintInvalidationContainer,
+    TransformState& transformState,
+    MapCoordinatesFlags mode) const {
+  if (!paintInvalidationContainer && mode & UseTransforms &&
+      shouldUseTransformFromContainer(0)) {
+    TransformationMatrix t;
+    getTransformFromContainer(0, LayoutSize(), t);
+    transformState.applyTransform(t);
+  }
 }
 
-const RenderObject* RenderView::pushMappingToContainer(const RenderBox* ancestorToStopAt, RenderGeometryMap& geometryMap) const
-{
-    LayoutSize offset;
-    RenderObject* container = 0;
-
-    // If a container was specified, and was not 0 or the RenderView, then we
-    // should have found it by now unless we're traversing to a parent document.
-    ASSERT_ARG(ancestorToStopAt, !ancestorToStopAt || ancestorToStopAt == this || container);
-
-    if ((!ancestorToStopAt || container) && shouldUseTransformFromContainer(container)) {
-        TransformationMatrix t;
-        getTransformFromContainer(container, LayoutSize(), t);
-        geometryMap.push(this, t, false, false, true);
-    } else {
-        geometryMap.push(this, offset, false, false, false);
-    }
-
-    return container;
+const RenderObject* RenderView::pushMappingToContainer(
+    const RenderBox* ancestorToStopAt,
+    RenderGeometryMap& geometryMap) const {
+  LayoutSize offset;
+  RenderObject* container = 0;
+
+  // If a container was specified, and was not 0 or the RenderView, then we
+  // should have found it by now unless we're traversing to a parent document.
+  ASSERT_ARG(ancestorToStopAt,
+             !ancestorToStopAt || ancestorToStopAt == this || container);
+
+  if ((!ancestorToStopAt || container) &&
+      shouldUseTransformFromContainer(container)) {
+    TransformationMatrix t;
+    getTransformFromContainer(container, LayoutSize(), t);
+    geometryMap.push(this, t, false, false, true);
+  } else {
+    geometryMap.push(this, offset, false, false, false);
+  }
+
+  return container;
 }
 
-void RenderView::mapAbsoluteToLocalPoint(MapCoordinatesFlags mode, TransformState& transformState) const
-{
-    if (mode & UseTransforms && shouldUseTransformFromContainer(0)) {
-        TransformationMatrix t;
-        getTransformFromContainer(0, LayoutSize(), t);
-        transformState.applyTransform(t);
-    }
+void RenderView::mapAbsoluteToLocalPoint(MapCoordinatesFlags mode,
+                                         TransformState& transformState) const {
+  if (mode & UseTransforms && shouldUseTransformFromContainer(0)) {
+    TransformationMatrix t;
+    getTransformFromContainer(0, LayoutSize(), t);
+    transformState.applyTransform(t);
+  }
 }
 
-void RenderView::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, Vector& layers)
-{
-    // If we ever require layout but receive a paint anyway, something has gone horribly wrong.
-    ASSERT(!needsLayout());
-    // RenderViews should never be called to paint with an offset not on device pixels.
-    ASSERT(LayoutPoint(IntPoint(paintOffset.x(), paintOffset.y())) == paintOffset);
-
-    paintObject(paintInfo, paintOffset, layers);
+void RenderView::paint(PaintInfo& paintInfo,
+                       const LayoutPoint& paintOffset,
+                       Vector& layers) {
+  // If we ever require layout but receive a paint anyway, something has gone
+  // horribly wrong.
+  ASSERT(!needsLayout());
+  // RenderViews should never be called to paint with an offset not on device
+  // pixels.
+  ASSERT(LayoutPoint(IntPoint(paintOffset.x(), paintOffset.y())) ==
+         paintOffset);
+
+  paintObject(paintInfo, paintOffset, layers);
 }
 
-void RenderView::paintBoxDecorationBackground(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
-{
-}
+void RenderView::paintBoxDecorationBackground(PaintInfo& paintInfo,
+                                              const LayoutPoint& paintOffset) {}
 
-void RenderView::absoluteQuads(Vector& quads) const
-{
-    quads.append(FloatRect(FloatPoint(), layer()->size()));
+void RenderView::absoluteQuads(Vector& quads) const {
+  quads.append(FloatRect(FloatPoint(), layer()->size()));
 }
 
-static RenderObject* rendererAfterPosition(RenderObject* object, unsigned offset)
-{
-    if (!object)
-        return 0;
+static RenderObject* rendererAfterPosition(RenderObject* object,
+                                           unsigned offset) {
+  if (!object)
+    return 0;
 
-    RenderObject* child = object->childAt(offset);
-    return child ? child : object->nextInPreOrderAfterChildren();
+  RenderObject* child = object->childAt(offset);
+  return child ? child : object->nextInPreOrderAfterChildren();
 }
 
-// When exploring the RenderTree looking for the nodes involved in the Selection, sometimes it's
-// required to change the traversing direction because the "start" position is below the "end" one.
-static inline RenderObject* getNextOrPrevRenderObjectBasedOnDirection(const RenderObject* o, const RenderObject* stop, bool& continueExploring, bool& exploringBackwards)
-{
-    RenderObject* next;
+// When exploring the RenderTree looking for the nodes involved in the
+// Selection, sometimes it's required to change the traversing direction because
+// the "start" position is below the "end" one.
+static inline RenderObject* getNextOrPrevRenderObjectBasedOnDirection(
+    const RenderObject* o,
+    const RenderObject* stop,
+    bool& continueExploring,
+    bool& exploringBackwards) {
+  RenderObject* next;
+  if (exploringBackwards) {
+    next = o->previousInPreOrder();
+    continueExploring = next && !(next)->isRenderView();
+  } else {
+    next = o->nextInPreOrder();
+    continueExploring = next && next != stop;
+    exploringBackwards = !next && (next != stop);
     if (exploringBackwards) {
-        next = o->previousInPreOrder();
-        continueExploring = next && !(next)->isRenderView();
-    } else {
-        next = o->nextInPreOrder();
-        continueExploring = next && next != stop;
-        exploringBackwards = !next && (next != stop);
-        if (exploringBackwards) {
-            next = stop->previousInPreOrder();
-            continueExploring = next && !next->isRenderView();
-        }
+      next = stop->previousInPreOrder();
+      continueExploring = next && !next->isRenderView();
     }
+  }
 
-    return next;
+  return next;
 }
 
-void RenderView::setSelection(RenderObject* start, int startPos, RenderObject* end, int endPos)
-{
-    // This code makes no assumptions as to if the rendering tree is up to date or not
-    // and will not try to update it. Currently clearSelection calls this
-    // (intentionally) without updating the rendering tree as it doesn't care.
-    // Other callers may want to force recalc style before calling this.
-
-    // Make sure both our start and end objects are defined.
-    // Check www.msnbc.com and try clicking around to find the case where this happened.
-    if ((start && !end) || (end && !start))
-        return;
-
-    // Just return if the selection hasn't changed.
-    if (m_selectionStart == start && m_selectionStartPos == startPos &&
-        m_selectionEnd == end && m_selectionEndPos == endPos)
-        return;
-
-    RenderObject* os = m_selectionStart;
-    RenderObject* stop = rendererAfterPosition(m_selectionEnd, m_selectionEndPos);
-    bool exploringBackwards = false;
-    bool continueExploring = os && (os != stop);
-    while (continueExploring) {
-        if ((os->canBeSelectionLeaf() || os == m_selectionStart || os == m_selectionEnd) && os->selectionState() != SelectionNone) {
-            os->setSelectionStateIfNeeded(SelectionNone);
-        }
-
-        os = getNextOrPrevRenderObjectBasedOnDirection(os, stop, continueExploring, exploringBackwards);
+void RenderView::setSelection(RenderObject* start,
+                              int startPos,
+                              RenderObject* end,
+                              int endPos) {
+  // This code makes no assumptions as to if the rendering tree is up to date or
+  // not and will not try to update it. Currently clearSelection calls this
+  // (intentionally) without updating the rendering tree as it doesn't care.
+  // Other callers may want to force recalc style before calling this.
+
+  // Make sure both our start and end objects are defined.
+  // Check www.msnbc.com and try clicking around to find the case where this
+  // happened.
+  if ((start && !end) || (end && !start))
+    return;
+
+  // Just return if the selection hasn't changed.
+  if (m_selectionStart == start && m_selectionStartPos == startPos &&
+      m_selectionEnd == end && m_selectionEndPos == endPos)
+    return;
+
+  RenderObject* os = m_selectionStart;
+  RenderObject* stop = rendererAfterPosition(m_selectionEnd, m_selectionEndPos);
+  bool exploringBackwards = false;
+  bool continueExploring = os && (os != stop);
+  while (continueExploring) {
+    if ((os->canBeSelectionLeaf() || os == m_selectionStart ||
+         os == m_selectionEnd) &&
+        os->selectionState() != SelectionNone) {
+      os->setSelectionStateIfNeeded(SelectionNone);
     }
 
-    // set selection start and end
-    m_selectionStart = start;
-    m_selectionStartPos = startPos;
-    m_selectionEnd = end;
-    m_selectionEndPos = endPos;
-
-    // Update the selection status of all objects between m_selectionStart and m_selectionEnd
-    if (start && start == end) {
-        start->setSelectionStateIfNeeded(SelectionBoth);
-    } else {
-        if (start)
-            start->setSelectionStateIfNeeded(SelectionStart);
-        if (end)
-            end->setSelectionStateIfNeeded(SelectionEnd);
-    }
-
-    RenderObject* o = start;
-    stop = rendererAfterPosition(end, endPos);
-
-    while (o && o != stop) {
-        if (o != start && o != end && o->canBeSelectionLeaf())
-            o->setSelectionStateIfNeeded(SelectionInside);
-        o = o->nextInPreOrder();
-    }
+    os = getNextOrPrevRenderObjectBasedOnDirection(os, stop, continueExploring,
+                                                   exploringBackwards);
+  }
+
+  // set selection start and end
+  m_selectionStart = start;
+  m_selectionStartPos = startPos;
+  m_selectionEnd = end;
+  m_selectionEndPos = endPos;
+
+  // Update the selection status of all objects between m_selectionStart and
+  // m_selectionEnd
+  if (start && start == end) {
+    start->setSelectionStateIfNeeded(SelectionBoth);
+  } else {
+    if (start)
+      start->setSelectionStateIfNeeded(SelectionStart);
+    if (end)
+      end->setSelectionStateIfNeeded(SelectionEnd);
+  }
+
+  RenderObject* o = start;
+  stop = rendererAfterPosition(end, endPos);
+
+  while (o && o != stop) {
+    if (o != start && o != end && o->canBeSelectionLeaf())
+      o->setSelectionStateIfNeeded(SelectionInside);
+    o = o->nextInPreOrder();
+  }
 }
 
-void RenderView::getSelection(RenderObject*& startRenderer, int& startOffset, RenderObject*& endRenderer, int& endOffset) const
-{
-    startRenderer = m_selectionStart;
-    startOffset = m_selectionStartPos;
-    endRenderer = m_selectionEnd;
-    endOffset = m_selectionEndPos;
+void RenderView::getSelection(RenderObject*& startRenderer,
+                              int& startOffset,
+                              RenderObject*& endRenderer,
+                              int& endOffset) const {
+  startRenderer = m_selectionStart;
+  startOffset = m_selectionStartPos;
+  endRenderer = m_selectionEnd;
+  endOffset = m_selectionEndPos;
 }
 
-void RenderView::clearSelection()
-{
-    setSelection(0, -1, 0, -1);
+void RenderView::clearSelection() {
+  setSelection(0, -1, 0, -1);
 }
 
-void RenderView::selectionStartEnd(int& startPos, int& endPos) const
-{
-    startPos = m_selectionStartPos;
-    endPos = m_selectionEndPos;
+void RenderView::selectionStartEnd(int& startPos, int& endPos) const {
+  startPos = m_selectionStartPos;
+  endPos = m_selectionEndPos;
 }
 
-IntRect RenderView::unscaledDocumentRect() const
-{
-    return pixelSnappedIntRect(layoutOverflowRect());
+IntRect RenderView::unscaledDocumentRect() const {
+  return pixelSnappedIntRect(layoutOverflowRect());
 }
 
-LayoutRect RenderView::backgroundRect(RenderBox* backgroundRenderer) const
-{
-    return unscaledDocumentRect();
+LayoutRect RenderView::backgroundRect(RenderBox* backgroundRenderer) const {
+  return unscaledDocumentRect();
 }
 
-IntRect RenderView::documentRect() const
-{
-    FloatRect overflowRect(unscaledDocumentRect());
-    if (hasTransform())
-        overflowRect = transform()->mapRect(overflowRect);
-    return IntRect(overflowRect);
+IntRect RenderView::documentRect() const {
+  FloatRect overflowRect(unscaledDocumentRect());
+  if (hasTransform())
+    overflowRect = transform()->mapRect(overflowRect);
+  return IntRect(overflowRect);
 }
 
-int RenderView::viewHeight() const
-{
-    return m_frameViewSize.height();
+int RenderView::viewHeight() const {
+  return m_frameViewSize.height();
 }
 
-int RenderView::viewWidth() const
-{
-    return m_frameViewSize.width();
+int RenderView::viewWidth() const {
+  return m_frameViewSize.width();
 }
 
-int RenderView::viewLogicalHeight() const
-{
-    return viewHeight();
+int RenderView::viewLogicalHeight() const {
+  return viewHeight();
 }
 
-LayoutUnit RenderView::viewLogicalHeightForPercentages() const
-{
-    return viewLogicalHeight();
+LayoutUnit RenderView::viewLogicalHeightForPercentages() const {
+  return viewLogicalHeight();
 }
 
 // FIXME(sky): remove
-double RenderView::layoutViewportWidth() const
-{
-    return viewWidth();
+double RenderView::layoutViewportWidth() const {
+  return viewWidth();
 }
 
 // FIXME(sky): remove
-double RenderView::layoutViewportHeight() const
-{
-    return viewHeight();
+double RenderView::layoutViewportHeight() const {
+  return viewHeight();
 }
 
-} // namespace blink
+}  // namespace blink
diff --git a/sky/engine/core/rendering/RenderView.h b/sky/engine/core/rendering/RenderView.h
index 6383c2dec949e..fd166aaca863e 100644
--- a/sky/engine/core/rendering/RenderView.h
+++ b/sky/engine/core/rendering/RenderView.h
@@ -27,85 +27,107 @@
 
 namespace blink {
 
-// The root of the render tree, corresponding to the CSS initial containing block.
-// It's dimensions match that of the logical viewport (which may be different from
-// the visible viewport in fixed-layout mode), and it is always at position (0,0)
-// relative to the document (and so isn't necessarily in view).
+// The root of the render tree, corresponding to the CSS initial containing
+// block. It's dimensions match that of the logical viewport (which may be
+// different from the visible viewport in fixed-layout mode), and it is always
+// at position (0,0) relative to the document (and so isn't necessarily in
+// view).
 class RenderView final : public RenderFlexibleBox {
-public:
-    explicit RenderView();
-    virtual ~RenderView();
+ public:
+  explicit RenderView();
+  virtual ~RenderView();
 
-    bool hitTest(const HitTestRequest&, HitTestResult&);
-    bool hitTest(const HitTestRequest&, const HitTestLocation&, HitTestResult&);
+  bool hitTest(const HitTestRequest&, HitTestResult&);
+  bool hitTest(const HitTestRequest&, const HitTestLocation&, HitTestResult&);
 
-    // Returns the total count of calls to HitTest, for testing.
-    unsigned hitTestCount() const { return m_hitTestCount; }
+  // Returns the total count of calls to HitTest, for testing.
+  unsigned hitTestCount() const { return m_hitTestCount; }
 
-    virtual const char* renderName() const override { return "RenderView"; }
+  virtual const char* renderName() const override { return "RenderView"; }
 
-    virtual bool isRenderView() const override { return true; }
+  virtual bool isRenderView() const override { return true; }
 
-    virtual LayerType layerTypeRequired() const override { return NormalLayer; }
+  virtual LayerType layerTypeRequired() const override { return NormalLayer; }
 
-    virtual bool isChildAllowed(RenderObject*, RenderStyle*) const override;
+  virtual bool isChildAllowed(RenderObject*, RenderStyle*) const override;
 
-    virtual void layout() override;
-    virtual void updateLogicalWidth() override;
-    virtual void computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues&) const override;
+  virtual void layout() override;
+  virtual void updateLogicalWidth() override;
+  virtual void computeLogicalHeight(
+      LayoutUnit logicalHeight,
+      LayoutUnit logicalTop,
+      LogicalExtentComputedValues&) const override;
 
-    // The same as the FrameView's layoutHeight/layoutWidth but with null check guards.
-    int viewHeight() const;
-    int viewWidth() const;
-    int viewLogicalWidth() const { return viewWidth(); }
-    int viewLogicalHeight() const;
-    LayoutUnit viewLogicalHeightForPercentages() const;
+  // The same as the FrameView's layoutHeight/layoutWidth but with null check
+  // guards.
+  int viewHeight() const;
+  int viewWidth() const;
+  int viewLogicalWidth() const { return viewWidth(); }
+  int viewLogicalHeight() const;
+  LayoutUnit viewLogicalHeightForPercentages() const;
 
-    virtual void paint(PaintInfo&, const LayoutPoint&, Vector& layers) override;
-    virtual void paintBoxDecorationBackground(PaintInfo&, const LayoutPoint&) override;
+  virtual void paint(PaintInfo&,
+                     const LayoutPoint&,
+                     Vector& layers) override;
+  virtual void paintBoxDecorationBackground(PaintInfo&,
+                                            const LayoutPoint&) override;
 
-    void setSelection(RenderObject* start, int startPos, RenderObject*, int endPos);
-    void getSelection(RenderObject*& startRenderer, int& startOffset, RenderObject*& endRenderer, int& endOffset) const;
-    void clearSelection();
-    RenderObject* selectionStart() const { return m_selectionStart; }
-    RenderObject* selectionEnd() const { return m_selectionEnd; }
-    void selectionStartEnd(int& startPos, int& endPos) const;
+  void setSelection(RenderObject* start,
+                    int startPos,
+                    RenderObject*,
+                    int endPos);
+  void getSelection(RenderObject*& startRenderer,
+                    int& startOffset,
+                    RenderObject*& endRenderer,
+                    int& endOffset) const;
+  void clearSelection();
+  RenderObject* selectionStart() const { return m_selectionStart; }
+  RenderObject* selectionEnd() const { return m_selectionEnd; }
+  void selectionStartEnd(int& startPos, int& endPos) const;
 
-    virtual void absoluteQuads(Vector&) const override;
+  virtual void absoluteQuads(Vector&) const override;
 
-    void setFrameViewSize(const IntSize& frameViewSize) { m_frameViewSize = frameViewSize; }
+  void setFrameViewSize(const IntSize& frameViewSize) {
+    m_frameViewSize = frameViewSize;
+  }
 
-    IntRect unscaledDocumentRect() const;
-    LayoutRect backgroundRect(RenderBox* backgroundRenderer) const;
+  IntRect unscaledDocumentRect() const;
+  LayoutRect backgroundRect(RenderBox* backgroundRenderer) const;
 
-    IntRect documentRect() const;
+  IntRect documentRect() const;
 
-    double layoutViewportWidth() const;
-    double layoutViewportHeight() const;
+  double layoutViewportWidth() const;
+  double layoutViewportHeight() const;
 
-private:
-    virtual void mapLocalToContainer(const RenderBox* paintInvalidationContainer, TransformState&, MapCoordinatesFlags = ApplyContainerFlip) const override;
-    virtual const RenderObject* pushMappingToContainer(const RenderBox* ancestorToStopAt, RenderGeometryMap&) const override;
-    virtual void mapAbsoluteToLocalPoint(MapCoordinatesFlags, TransformState&) const override;
+ private:
+  virtual void mapLocalToContainer(
+      const RenderBox* paintInvalidationContainer,
+      TransformState&,
+      MapCoordinatesFlags = ApplyContainerFlip) const override;
+  virtual const RenderObject* pushMappingToContainer(
+      const RenderBox* ancestorToStopAt,
+      RenderGeometryMap&) const override;
+  virtual void mapAbsoluteToLocalPoint(MapCoordinatesFlags,
+                                       TransformState&) const override;
 
-    void positionDialog(RenderBox*);
-    void positionDialogs();
+  void positionDialog(RenderBox*);
+  void positionDialogs();
 
-    IntSize m_frameViewSize;
+  IntSize m_frameViewSize;
 
-    RawPtr m_selectionStart;
-    RawPtr m_selectionEnd;
+  RawPtr m_selectionStart;
+  RawPtr m_selectionEnd;
 
-    int m_selectionStartPos;
-    int m_selectionEndPos;
+  int m_selectionStartPos;
+  int m_selectionEndPos;
 
-    unsigned m_renderCounterCount;
+  unsigned m_renderCounterCount;
 
-    unsigned m_hitTestCount;
+  unsigned m_hitTestCount;
 };
 
 DEFINE_RENDER_OBJECT_TYPE_CASTS(RenderView, isRenderView());
 
-} // namespace blink
+}  // namespace blink
 
 #endif  // SKY_ENGINE_CORE_RENDERING_RENDERVIEW_H_
diff --git a/sky/engine/core/rendering/RootInlineBox.cpp b/sky/engine/core/rendering/RootInlineBox.cpp
index 24d7526337aab..ca08a6d7b6379 100644
--- a/sky/engine/core/rendering/RootInlineBox.cpp
+++ b/sky/engine/core/rendering/RootInlineBox.cpp
@@ -22,8 +22,8 @@
 #include "flutter/sky/engine/core/rendering/HitTestResult.h"
 #include "flutter/sky/engine/core/rendering/InlineTextBox.h"
 #include "flutter/sky/engine/core/rendering/PaintInfo.h"
-#include "flutter/sky/engine/core/rendering/RenderParagraph.h"
 #include "flutter/sky/engine/core/rendering/RenderInline.h"
+#include "flutter/sky/engine/core/rendering/RenderParagraph.h"
 #include "flutter/sky/engine/core/rendering/RenderView.h"
 #include "flutter/sky/engine/core/rendering/VerticalPositionCache.h"
 #include "flutter/sky/engine/platform/text/BidiResolver.h"
@@ -32,658 +32,773 @@
 namespace blink {
 
 struct SameSizeAsRootInlineBox : public InlineFlowBox {
-    unsigned unsignedVariable;
-    void* pointers[2];
-    LayoutUnit layoutVariables[5];
+  unsigned unsignedVariable;
+  void* pointers[2];
+  LayoutUnit layoutVariables[5];
 };
 
-COMPILE_ASSERT(sizeof(RootInlineBox) == sizeof(SameSizeAsRootInlineBox), RootInlineBox_should_stay_small);
+COMPILE_ASSERT(sizeof(RootInlineBox) == sizeof(SameSizeAsRootInlineBox),
+               RootInlineBox_should_stay_small);
 
 RootInlineBox::RootInlineBox(RenderParagraph& block)
-    : InlineFlowBox(block)
-    , m_lineBreakPos(0)
-    , m_lineBreakObj(0)
-    , m_lineTop(0)
-    , m_lineBottom(0)
-    , m_lineTopWithLeading(0)
-    , m_lineBottomWithLeading(0)
-    , m_selectionBottom(0)
-{
-}
-
-
-void RootInlineBox::destroy()
-{
-    InlineFlowBox::destroy();
-}
+    : InlineFlowBox(block),
+      m_lineBreakPos(0),
+      m_lineBreakObj(0),
+      m_lineTop(0),
+      m_lineBottom(0),
+      m_lineTopWithLeading(0),
+      m_lineBottomWithLeading(0),
+      m_selectionBottom(0) {}
 
-RenderLineBoxList* RootInlineBox::rendererLineBoxes() const
-{
-    return block().lineBoxes();
+void RootInlineBox::destroy() {
+  InlineFlowBox::destroy();
 }
 
-void RootInlineBox::clearTruncation()
-{
+RenderLineBoxList* RootInlineBox::rendererLineBoxes() const {
+  return block().lineBoxes();
 }
 
-int RootInlineBox::baselinePosition(FontBaseline baselineType) const
-{
-    return boxModelObject()->baselinePosition(baselineType, isFirstLineStyle(), HorizontalLine, PositionOfInteriorLineBoxes);
-}
+void RootInlineBox::clearTruncation() {}
 
-LayoutUnit RootInlineBox::lineHeight() const
-{
-    return boxModelObject()->lineHeight(isFirstLineStyle(), HorizontalLine, PositionOfInteriorLineBoxes);
+int RootInlineBox::baselinePosition(FontBaseline baselineType) const {
+  return boxModelObject()->baselinePosition(baselineType, isFirstLineStyle(),
+                                            HorizontalLine,
+                                            PositionOfInteriorLineBoxes);
 }
 
-void RootInlineBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit lineTop, LayoutUnit lineBottom, Vector& layers)
-{
-    InlineFlowBox::paint(paintInfo, paintOffset, lineTop, lineBottom, layers);
+LayoutUnit RootInlineBox::lineHeight() const {
+  return boxModelObject()->lineHeight(isFirstLineStyle(), HorizontalLine,
+                                      PositionOfInteriorLineBoxes);
 }
 
-bool RootInlineBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, LayoutUnit lineTop, LayoutUnit lineBottom)
-{
-    return InlineFlowBox::nodeAtPoint(request, result, locationInContainer, accumulatedOffset, lineTop, lineBottom);
+void RootInlineBox::paint(PaintInfo& paintInfo,
+                          const LayoutPoint& paintOffset,
+                          LayoutUnit lineTop,
+                          LayoutUnit lineBottom,
+                          Vector& layers) {
+  InlineFlowBox::paint(paintInfo, paintOffset, lineTop, lineBottom, layers);
 }
 
-void RootInlineBox::adjustPosition(float dx, float dy)
-{
-    InlineFlowBox::adjustPosition(dx, dy);
-    LayoutUnit blockDirectionDelta = dy; // The block direction delta is a LayoutUnit.
-    m_lineTop += blockDirectionDelta;
-    m_lineBottom += blockDirectionDelta;
-    m_lineTopWithLeading += blockDirectionDelta;
-    m_lineBottomWithLeading += blockDirectionDelta;
-    m_selectionBottom += blockDirectionDelta;
+bool RootInlineBox::nodeAtPoint(const HitTestRequest& request,
+                                HitTestResult& result,
+                                const HitTestLocation& locationInContainer,
+                                const LayoutPoint& accumulatedOffset,
+                                LayoutUnit lineTop,
+                                LayoutUnit lineBottom) {
+  return InlineFlowBox::nodeAtPoint(request, result, locationInContainer,
+                                    accumulatedOffset, lineTop, lineBottom);
 }
 
-void RootInlineBox::childRemoved(InlineBox* box)
-{
-    if (&box->renderer() == m_lineBreakObj)
-        setLineBreakInfo(0, 0, BidiStatus());
-
-    for (RootInlineBox* prev = prevRootBox(); prev && prev->lineBreakObj() == &box->renderer(); prev = prev->prevRootBox()) {
-        prev->setLineBreakInfo(0, 0, BidiStatus());
-        prev->markDirty();
-    }
+void RootInlineBox::adjustPosition(float dx, float dy) {
+  InlineFlowBox::adjustPosition(dx, dy);
+  LayoutUnit blockDirectionDelta =
+      dy;  // The block direction delta is a LayoutUnit.
+  m_lineTop += blockDirectionDelta;
+  m_lineBottom += blockDirectionDelta;
+  m_lineTopWithLeading += blockDirectionDelta;
+  m_lineBottomWithLeading += blockDirectionDelta;
+  m_selectionBottom += blockDirectionDelta;
 }
 
-LayoutUnit RootInlineBox::alignBoxesInBlockDirection(LayoutUnit heightOfBlock, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache)
-{
-    // SVG will handle vertical alignment on its own.
-    if (isSVGRootInlineBox())
-        return 0;
-
-    LayoutUnit maxPositionTop = 0;
-    LayoutUnit maxPositionBottom = 0;
-    int maxAscent = 0;
-    int maxDescent = 0;
-    bool setMaxAscent = false;
-    bool setMaxDescent = false;
-
-    m_baselineType = AlphabeticBaseline;
-
-    computeLogicalBoxHeights(this, maxPositionTop, maxPositionBottom, maxAscent, maxDescent, setMaxAscent, setMaxDescent, true,
-                             textBoxDataMap, baselineType(), verticalPositionCache);
-
-    if (maxAscent + maxDescent < std::max(maxPositionTop, maxPositionBottom))
-        adjustMaxAscentAndDescent(maxAscent, maxDescent, maxPositionTop, maxPositionBottom);
-
-    LayoutUnit maxHeight = maxAscent + maxDescent;
-    LayoutUnit lineTop = heightOfBlock;
-    LayoutUnit lineBottom = heightOfBlock;
-    LayoutUnit lineTopIncludingMargins = heightOfBlock;
-    LayoutUnit lineBottomIncludingMargins = heightOfBlock;
-    LayoutUnit selectionBottom = heightOfBlock;
-    bool setLineTop = false;
-    bool hasAnnotationsBefore = false;
-    bool hasAnnotationsAfter = false;
-    placeBoxesInBlockDirection(heightOfBlock, maxHeight, maxAscent, true, lineTop, lineBottom, selectionBottom, setLineTop,
-                               lineTopIncludingMargins, lineBottomIncludingMargins, hasAnnotationsBefore, hasAnnotationsAfter, baselineType());
-    m_hasAnnotationsBefore = hasAnnotationsBefore;
-    m_hasAnnotationsAfter = hasAnnotationsAfter;
-
-    maxHeight = std::max(0, maxHeight); // FIXME: Is this really necessary?
-
-    setLineTopBottomPositions(lineTop, lineBottom, heightOfBlock, heightOfBlock + maxHeight, selectionBottom);
-
-    LayoutUnit annotationsAdjustment = beforeAnnotationsAdjustment();
-    if (annotationsAdjustment) {
-        // FIXME: Need to handle pagination here. We might have to move to the next page/column as a result of the
-        // ruby expansion.
-        adjustBlockDirectionPosition(annotationsAdjustment.toFloat());
-        heightOfBlock += annotationsAdjustment;
-    }
+void RootInlineBox::childRemoved(InlineBox* box) {
+  if (&box->renderer() == m_lineBreakObj)
+    setLineBreakInfo(0, 0, BidiStatus());
 
-    return heightOfBlock + maxHeight;
+  for (RootInlineBox* prev = prevRootBox();
+       prev && prev->lineBreakObj() == &box->renderer();
+       prev = prev->prevRootBox()) {
+    prev->setLineBreakInfo(0, 0, BidiStatus());
+    prev->markDirty();
+  }
 }
 
-float RootInlineBox::maxLogicalTop() const
-{
-    float maxLogicalTop = 0;
-    computeMaxLogicalTop(maxLogicalTop);
-    return maxLogicalTop;
-}
-
-LayoutUnit RootInlineBox::beforeAnnotationsAdjustment() const
-{
-    LayoutUnit result = 0;
-
-    // Annotations under the previous line may push us down.
-    if (prevRootBox() && prevRootBox()->hasAnnotationsAfter())
-        result = prevRootBox()->computeUnderAnnotationAdjustment(lineTop());
-
-    if (!hasAnnotationsBefore())
-        return result;
-
-    // Annotations over this line may push us further down.
-    LayoutUnit highestAllowedPosition = prevRootBox() ? std::min(prevRootBox()->lineBottom(), lineTop()) + result : static_cast(block().borderBefore());
-    result = computeOverAnnotationAdjustment(highestAllowedPosition);
+LayoutUnit RootInlineBox::alignBoxesInBlockDirection(
+    LayoutUnit heightOfBlock,
+    GlyphOverflowAndFallbackFontsMap& textBoxDataMap,
+    VerticalPositionCache& verticalPositionCache) {
+  // SVG will handle vertical alignment on its own.
+  if (isSVGRootInlineBox())
+    return 0;
 
+  LayoutUnit maxPositionTop = 0;
+  LayoutUnit maxPositionBottom = 0;
+  int maxAscent = 0;
+  int maxDescent = 0;
+  bool setMaxAscent = false;
+  bool setMaxDescent = false;
+
+  m_baselineType = AlphabeticBaseline;
+
+  computeLogicalBoxHeights(this, maxPositionTop, maxPositionBottom, maxAscent,
+                           maxDescent, setMaxAscent, setMaxDescent, true,
+                           textBoxDataMap, baselineType(),
+                           verticalPositionCache);
+
+  if (maxAscent + maxDescent < std::max(maxPositionTop, maxPositionBottom))
+    adjustMaxAscentAndDescent(maxAscent, maxDescent, maxPositionTop,
+                              maxPositionBottom);
+
+  LayoutUnit maxHeight = maxAscent + maxDescent;
+  LayoutUnit lineTop = heightOfBlock;
+  LayoutUnit lineBottom = heightOfBlock;
+  LayoutUnit lineTopIncludingMargins = heightOfBlock;
+  LayoutUnit lineBottomIncludingMargins = heightOfBlock;
+  LayoutUnit selectionBottom = heightOfBlock;
+  bool setLineTop = false;
+  bool hasAnnotationsBefore = false;
+  bool hasAnnotationsAfter = false;
+  placeBoxesInBlockDirection(heightOfBlock, maxHeight, maxAscent, true, lineTop,
+                             lineBottom, selectionBottom, setLineTop,
+                             lineTopIncludingMargins,
+                             lineBottomIncludingMargins, hasAnnotationsBefore,
+                             hasAnnotationsAfter, baselineType());
+  m_hasAnnotationsBefore = hasAnnotationsBefore;
+  m_hasAnnotationsAfter = hasAnnotationsAfter;
+
+  maxHeight =
+      std::max(0, maxHeight);  // FIXME: Is this really necessary?
+
+  setLineTopBottomPositions(lineTop, lineBottom, heightOfBlock,
+                            heightOfBlock + maxHeight, selectionBottom);
+
+  LayoutUnit annotationsAdjustment = beforeAnnotationsAdjustment();
+  if (annotationsAdjustment) {
+    // FIXME: Need to handle pagination here. We might have to move to the next
+    // page/column as a result of the ruby expansion.
+    adjustBlockDirectionPosition(annotationsAdjustment.toFloat());
+    heightOfBlock += annotationsAdjustment;
+  }
+
+  return heightOfBlock + maxHeight;
+}
+
+float RootInlineBox::maxLogicalTop() const {
+  float maxLogicalTop = 0;
+  computeMaxLogicalTop(maxLogicalTop);
+  return maxLogicalTop;
+}
+
+LayoutUnit RootInlineBox::beforeAnnotationsAdjustment() const {
+  LayoutUnit result = 0;
+
+  // Annotations under the previous line may push us down.
+  if (prevRootBox() && prevRootBox()->hasAnnotationsAfter())
+    result = prevRootBox()->computeUnderAnnotationAdjustment(lineTop());
+
+  if (!hasAnnotationsBefore())
     return result;
-}
-
-GapRects RootInlineBox::lineSelectionGap(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock,
-                                         LayoutUnit selTop, LayoutUnit selHeight, const PaintInfo* paintInfo)
-{
-    RenderObject::SelectionState lineState = selectionState();
-
-    bool leftGap, rightGap;
-    block().getSelectionGapInfo(lineState, leftGap, rightGap);
 
-    GapRects result;
-
-    InlineBox* firstBox = firstSelectedBox();
-    InlineBox* lastBox = lastSelectedBox();
-    if (leftGap) {
-        result.uniteLeft(block().logicalLeftSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock,
-            &firstBox->parent()->renderer(), firstBox->logicalLeft(), selTop, selHeight, paintInfo));
-    }
-    if (rightGap) {
-        result.uniteRight(block().logicalRightSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock,
-            &lastBox->parent()->renderer(), lastBox->logicalRight(), selTop, selHeight, paintInfo));
-    }
-
-    // When dealing with bidi text, a non-contiguous selection region is possible.
-    // e.g. The logical text aaaAAAbbb (capitals denote RTL text and non-capitals LTR) is layed out
-    // visually as 3 text runs |aaa|bbb|AAA| if we select 4 characters from the start of the text the
-    // selection will look like (underline denotes selection):
-    // |aaa|bbb|AAA|
-    //  ___       _
-    // We can see that the |bbb| run is not part of the selection while the runs around it are.
-    if (firstBox && firstBox != lastBox) {
-        // Now fill in any gaps on the line that occurred between two selected elements.
-        LayoutUnit lastLogicalLeft = firstBox->logicalRight();
-        bool isPreviousBoxSelected = firstBox->selectionState() != RenderObject::SelectionNone;
-        for (InlineBox* box = firstBox->nextLeafChild(); box; box = box->nextLeafChild()) {
-            if (box->selectionState() != RenderObject::SelectionNone) {
-                LayoutRect logicalRect(lastLogicalLeft, selTop, box->logicalLeft() - lastLogicalLeft, selHeight);
-                logicalRect.move(offsetFromRootBlock);
-                LayoutRect gapRect = rootBlock->logicalRectToPhysicalRect(rootBlockPhysicalPosition, logicalRect);
-                if (isPreviousBoxSelected && gapRect.width() > 0 && gapRect.height() > 0) {
-                    if (paintInfo)
-                        paintInfo->context->fillRect(gapRect, box->parent()->renderer().selectionBackgroundColor());
-                    // VisibleSelection may be non-contiguous, see comment above.
-                    result.uniteCenter(gapRect);
-                }
-                lastLogicalLeft = box->logicalRight();
-            }
-            if (box == lastBox)
-                break;
-            isPreviousBoxSelected = box->selectionState() != RenderObject::SelectionNone;
+  // Annotations over this line may push us further down.
+  LayoutUnit highestAllowedPosition =
+      prevRootBox() ? std::min(prevRootBox()->lineBottom(), lineTop()) + result
+                    : static_cast(block().borderBefore());
+  result = computeOverAnnotationAdjustment(highestAllowedPosition);
+
+  return result;
+}
+
+GapRects RootInlineBox::lineSelectionGap(
+    RenderBlock* rootBlock,
+    const LayoutPoint& rootBlockPhysicalPosition,
+    const LayoutSize& offsetFromRootBlock,
+    LayoutUnit selTop,
+    LayoutUnit selHeight,
+    const PaintInfo* paintInfo) {
+  RenderObject::SelectionState lineState = selectionState();
+
+  bool leftGap, rightGap;
+  block().getSelectionGapInfo(lineState, leftGap, rightGap);
+
+  GapRects result;
+
+  InlineBox* firstBox = firstSelectedBox();
+  InlineBox* lastBox = lastSelectedBox();
+  if (leftGap) {
+    result.uniteLeft(block().logicalLeftSelectionGap(
+        rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock,
+        &firstBox->parent()->renderer(), firstBox->logicalLeft(), selTop,
+        selHeight, paintInfo));
+  }
+  if (rightGap) {
+    result.uniteRight(block().logicalRightSelectionGap(
+        rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock,
+        &lastBox->parent()->renderer(), lastBox->logicalRight(), selTop,
+        selHeight, paintInfo));
+  }
+
+  // When dealing with bidi text, a non-contiguous selection region is possible.
+  // e.g. The logical text aaaAAAbbb (capitals denote RTL text and non-capitals
+  // LTR) is layed out visually as 3 text runs |aaa|bbb|AAA| if we select 4
+  // characters from the start of the text the selection will look like
+  // (underline denotes selection): |aaa|bbb|AAA|
+  //  ___       _
+  // We can see that the |bbb| run is not part of the selection while the runs
+  // around it are.
+  if (firstBox && firstBox != lastBox) {
+    // Now fill in any gaps on the line that occurred between two selected
+    // elements.
+    LayoutUnit lastLogicalLeft = firstBox->logicalRight();
+    bool isPreviousBoxSelected =
+        firstBox->selectionState() != RenderObject::SelectionNone;
+    for (InlineBox* box = firstBox->nextLeafChild(); box;
+         box = box->nextLeafChild()) {
+      if (box->selectionState() != RenderObject::SelectionNone) {
+        LayoutRect logicalRect(lastLogicalLeft, selTop,
+                               box->logicalLeft() - lastLogicalLeft, selHeight);
+        logicalRect.move(offsetFromRootBlock);
+        LayoutRect gapRect = rootBlock->logicalRectToPhysicalRect(
+            rootBlockPhysicalPosition, logicalRect);
+        if (isPreviousBoxSelected && gapRect.width() > 0 &&
+            gapRect.height() > 0) {
+          if (paintInfo)
+            paintInfo->context->fillRect(
+                gapRect, box->parent()->renderer().selectionBackgroundColor());
+          // VisibleSelection may be non-contiguous, see comment above.
+          result.uniteCenter(gapRect);
         }
+        lastLogicalLeft = box->logicalRight();
+      }
+      if (box == lastBox)
+        break;
+      isPreviousBoxSelected =
+          box->selectionState() != RenderObject::SelectionNone;
     }
-
-    return result;
-}
-
-RenderObject::SelectionState RootInlineBox::selectionState()
-{
-    // Walk over all of the selected boxes.
-    RenderObject::SelectionState state = RenderObject::SelectionNone;
-    for (InlineBox* box = firstLeafChild(); box; box = box->nextLeafChild()) {
-        RenderObject::SelectionState boxState = box->selectionState();
-        if ((boxState == RenderObject::SelectionStart && state == RenderObject::SelectionEnd) ||
-            (boxState == RenderObject::SelectionEnd && state == RenderObject::SelectionStart))
-            state = RenderObject::SelectionBoth;
-        else if (state == RenderObject::SelectionNone ||
-                 ((boxState == RenderObject::SelectionStart || boxState == RenderObject::SelectionEnd) &&
-                  (state == RenderObject::SelectionNone || state == RenderObject::SelectionInside)))
-            state = boxState;
-        else if (boxState == RenderObject::SelectionNone && state == RenderObject::SelectionStart) {
-            // We are past the end of the selection.
-            state = RenderObject::SelectionBoth;
-        }
-        if (state == RenderObject::SelectionBoth)
-            break;
+  }
+
+  return result;
+}
+
+RenderObject::SelectionState RootInlineBox::selectionState() {
+  // Walk over all of the selected boxes.
+  RenderObject::SelectionState state = RenderObject::SelectionNone;
+  for (InlineBox* box = firstLeafChild(); box; box = box->nextLeafChild()) {
+    RenderObject::SelectionState boxState = box->selectionState();
+    if ((boxState == RenderObject::SelectionStart &&
+         state == RenderObject::SelectionEnd) ||
+        (boxState == RenderObject::SelectionEnd &&
+         state == RenderObject::SelectionStart))
+      state = RenderObject::SelectionBoth;
+    else if (state == RenderObject::SelectionNone ||
+             ((boxState == RenderObject::SelectionStart ||
+               boxState == RenderObject::SelectionEnd) &&
+              (state == RenderObject::SelectionNone ||
+               state == RenderObject::SelectionInside)))
+      state = boxState;
+    else if (boxState == RenderObject::SelectionNone &&
+             state == RenderObject::SelectionStart) {
+      // We are past the end of the selection.
+      state = RenderObject::SelectionBoth;
     }
+    if (state == RenderObject::SelectionBoth)
+      break;
+  }
 
-    return state;
+  return state;
 }
 
-InlineBox* RootInlineBox::firstSelectedBox()
-{
-    for (InlineBox* box = firstLeafChild(); box; box = box->nextLeafChild()) {
-        if (box->selectionState() != RenderObject::SelectionNone)
-            return box;
-    }
+InlineBox* RootInlineBox::firstSelectedBox() {
+  for (InlineBox* box = firstLeafChild(); box; box = box->nextLeafChild()) {
+    if (box->selectionState() != RenderObject::SelectionNone)
+      return box;
+  }
 
-    return 0;
+  return 0;
 }
 
-InlineBox* RootInlineBox::lastSelectedBox()
-{
-    for (InlineBox* box = lastLeafChild(); box; box = box->prevLeafChild()) {
-        if (box->selectionState() != RenderObject::SelectionNone)
-            return box;
-    }
+InlineBox* RootInlineBox::lastSelectedBox() {
+  for (InlineBox* box = lastLeafChild(); box; box = box->prevLeafChild()) {
+    if (box->selectionState() != RenderObject::SelectionNone)
+      return box;
+  }
 
-    return 0;
+  return 0;
 }
 
-LayoutUnit RootInlineBox::selectionTop() const
-{
-    LayoutUnit selectionTop = m_lineTop;
+LayoutUnit RootInlineBox::selectionTop() const {
+  LayoutUnit selectionTop = m_lineTop;
 
-    if (m_hasAnnotationsBefore)
-        selectionTop -= computeOverAnnotationAdjustment(m_lineTop);
+  if (m_hasAnnotationsBefore)
+    selectionTop -= computeOverAnnotationAdjustment(m_lineTop);
 
-    if (!prevRootBox())
-        return selectionTop;
+  if (!prevRootBox())
+    return selectionTop;
 
-    return prevRootBox()->selectionBottom();
+  return prevRootBox()->selectionBottom();
 }
 
-LayoutUnit RootInlineBox::selectionTopAdjustedForPrecedingBlock() const
-{
-    LayoutUnit top = selectionTop();
-
-    RenderObject::SelectionState blockSelectionState = root().block().selectionState();
-    if (blockSelectionState != RenderObject::SelectionInside && blockSelectionState != RenderObject::SelectionEnd)
-        return top;
+LayoutUnit RootInlineBox::selectionTopAdjustedForPrecedingBlock() const {
+  LayoutUnit top = selectionTop();
 
-    LayoutSize offsetToBlockBefore;
-    if (RenderBlock* block = root().block().blockBeforeWithinSelectionRoot(offsetToBlockBefore)) {
-        if (block->isRenderParagraph()) {
-            if (RootInlineBox* lastLine = toRenderParagraph(block)->lastRootBox()) {
-                RenderObject::SelectionState lastLineSelectionState = lastLine->selectionState();
-                if (lastLineSelectionState != RenderObject::SelectionInside && lastLineSelectionState != RenderObject::SelectionStart)
-                    return top;
+  RenderObject::SelectionState blockSelectionState =
+      root().block().selectionState();
+  if (blockSelectionState != RenderObject::SelectionInside &&
+      blockSelectionState != RenderObject::SelectionEnd)
+    return top;
 
-                LayoutUnit lastLineSelectionBottom = lastLine->selectionBottom() + offsetToBlockBefore.height();
-                top = std::max(top, lastLineSelectionBottom);
-            }
-        }
+  LayoutSize offsetToBlockBefore;
+  if (RenderBlock* block =
+          root().block().blockBeforeWithinSelectionRoot(offsetToBlockBefore)) {
+    if (block->isRenderParagraph()) {
+      if (RootInlineBox* lastLine = toRenderParagraph(block)->lastRootBox()) {
+        RenderObject::SelectionState lastLineSelectionState =
+            lastLine->selectionState();
+        if (lastLineSelectionState != RenderObject::SelectionInside &&
+            lastLineSelectionState != RenderObject::SelectionStart)
+          return top;
+
+        LayoutUnit lastLineSelectionBottom =
+            lastLine->selectionBottom() + offsetToBlockBefore.height();
+        top = std::max(top, lastLineSelectionBottom);
+      }
     }
+  }
 
-    return top;
+  return top;
 }
 
-LayoutUnit RootInlineBox::selectionBottom() const
-{
-    LayoutUnit selectionBottom = m_selectionBottom;
-    if (m_hasAnnotationsAfter)
-        selectionBottom += computeUnderAnnotationAdjustment(m_lineBottom);
-    return selectionBottom;
+LayoutUnit RootInlineBox::selectionBottom() const {
+  LayoutUnit selectionBottom = m_selectionBottom;
+  if (m_hasAnnotationsAfter)
+    selectionBottom += computeUnderAnnotationAdjustment(m_lineBottom);
+  return selectionBottom;
 }
 
-int RootInlineBox::blockDirectionPointInLine() const
-{
-    return std::max(lineTop(), selectionTop());
+int RootInlineBox::blockDirectionPointInLine() const {
+  return std::max(lineTop(), selectionTop());
 }
 
-RenderParagraph& RootInlineBox::block() const
-{
-    return toRenderParagraph(renderer());
+RenderParagraph& RootInlineBox::block() const {
+  return toRenderParagraph(renderer());
 }
 
-static bool isEditableLeaf(InlineBox* leaf)
-{
-    return false;
+static bool isEditableLeaf(InlineBox* leaf) {
+  return false;
 }
 
-InlineBox* RootInlineBox::closestLeafChildForPoint(const IntPoint& pointInContents, bool onlyEditableLeaves)
-{
-    return closestLeafChildForLogicalLeftPosition(pointInContents.x(), onlyEditableLeaves);
+InlineBox* RootInlineBox::closestLeafChildForPoint(
+    const IntPoint& pointInContents,
+    bool onlyEditableLeaves) {
+  return closestLeafChildForLogicalLeftPosition(pointInContents.x(),
+                                                onlyEditableLeaves);
 }
 
-InlineBox* RootInlineBox::closestLeafChildForLogicalLeftPosition(int leftPosition, bool onlyEditableLeaves)
-{
-    InlineBox* firstLeaf = firstLeafChild();
-    InlineBox* lastLeaf = lastLeafChild();
+InlineBox* RootInlineBox::closestLeafChildForLogicalLeftPosition(
+    int leftPosition,
+    bool onlyEditableLeaves) {
+  InlineBox* firstLeaf = firstLeafChild();
+  InlineBox* lastLeaf = lastLeafChild();
 
-    if (firstLeaf != lastLeaf) {
-        if (firstLeaf->isLineBreak())
-            firstLeaf = firstLeaf->nextLeafChildIgnoringLineBreak();
-        else if (lastLeaf->isLineBreak())
-            lastLeaf = lastLeaf->prevLeafChildIgnoringLineBreak();
-    }
+  if (firstLeaf != lastLeaf) {
+    if (firstLeaf->isLineBreak())
+      firstLeaf = firstLeaf->nextLeafChildIgnoringLineBreak();
+    else if (lastLeaf->isLineBreak())
+      lastLeaf = lastLeaf->prevLeafChildIgnoringLineBreak();
+  }
 
-    if (firstLeaf == lastLeaf && (!onlyEditableLeaves || isEditableLeaf(firstLeaf)))
-        return firstLeaf;
+  if (firstLeaf == lastLeaf &&
+      (!onlyEditableLeaves || isEditableLeaf(firstLeaf)))
+    return firstLeaf;
 
-    // Avoid returning a list marker when possible.
-    if (leftPosition <= firstLeaf->logicalLeft() && (!onlyEditableLeaves || isEditableLeaf(firstLeaf)))
-        // The leftPosition coordinate is less or equal to left edge of the firstLeaf.
-        // Return it.
-        return firstLeaf;
+  // Avoid returning a list marker when possible.
+  if (leftPosition <= firstLeaf->logicalLeft() &&
+      (!onlyEditableLeaves || isEditableLeaf(firstLeaf)))
+    // The leftPosition coordinate is less or equal to left edge of the
+    // firstLeaf. Return it.
+    return firstLeaf;
+
+  if (leftPosition >= lastLeaf->logicalRight() &&
+      (!onlyEditableLeaves || isEditableLeaf(lastLeaf)))
+    // The leftPosition coordinate is greater or equal to right edge of the
+    // lastLeaf. Return it.
+    return lastLeaf;
 
-    if (leftPosition >= lastLeaf->logicalRight() && (!onlyEditableLeaves || isEditableLeaf(lastLeaf)))
-        // The leftPosition coordinate is greater or equal to right edge of the lastLeaf.
+  InlineBox* closestLeaf = 0;
+  for (InlineBox* leaf = firstLeaf; leaf;
+       leaf = leaf->nextLeafChildIgnoringLineBreak()) {
+    if (!onlyEditableLeaves || isEditableLeaf(leaf)) {
+      closestLeaf = leaf;
+      if (leftPosition < leaf->logicalRight())
+        // The x coordinate is less than the right edge of the box.
         // Return it.
-        return lastLeaf;
-
-    InlineBox* closestLeaf = 0;
-    for (InlineBox* leaf = firstLeaf; leaf; leaf = leaf->nextLeafChildIgnoringLineBreak()) {
-        if (!onlyEditableLeaves || isEditableLeaf(leaf)) {
-            closestLeaf = leaf;
-            if (leftPosition < leaf->logicalRight())
-                // The x coordinate is less than the right edge of the box.
-                // Return it.
-                return leaf;
-        }
+        return leaf;
     }
+  }
 
-    return closestLeaf ? closestLeaf : lastLeaf;
+  return closestLeaf ? closestLeaf : lastLeaf;
 }
 
-BidiStatus RootInlineBox::lineBreakBidiStatus() const
-{
-    return BidiStatus(static_cast(m_lineBreakBidiStatusEor), static_cast(m_lineBreakBidiStatusLastStrong), static_cast(m_lineBreakBidiStatusLast), m_lineBreakContext);
+BidiStatus RootInlineBox::lineBreakBidiStatus() const {
+  return BidiStatus(
+      static_cast(m_lineBreakBidiStatusEor),
+      static_cast(m_lineBreakBidiStatusLastStrong),
+      static_cast(m_lineBreakBidiStatusLast),
+      m_lineBreakContext);
 }
 
-void RootInlineBox::setLineBreakInfo(RenderObject* obj, unsigned breakPos, const BidiStatus& status)
-{
-    // When setting lineBreakObj, the RenderObject must not be a RenderInline
-    // with no line boxes, otherwise all sorts of invariants are broken later.
-    // This has security implications because if the RenderObject does not
-    // point to at least one line box, then that RenderInline can be deleted
-    // later without resetting the lineBreakObj, leading to use-after-free.
-    ASSERT_WITH_SECURITY_IMPLICATION(!obj || obj->isText() || !(obj->isRenderInline() && obj->isBox() && !toRenderBox(obj)->inlineBoxWrapper()));
+void RootInlineBox::setLineBreakInfo(RenderObject* obj,
+                                     unsigned breakPos,
+                                     const BidiStatus& status) {
+  // When setting lineBreakObj, the RenderObject must not be a RenderInline
+  // with no line boxes, otherwise all sorts of invariants are broken later.
+  // This has security implications because if the RenderObject does not
+  // point to at least one line box, then that RenderInline can be deleted
+  // later without resetting the lineBreakObj, leading to use-after-free.
+  ASSERT_WITH_SECURITY_IMPLICATION(!obj || obj->isText() ||
+                                   !(obj->isRenderInline() && obj->isBox() &&
+                                     !toRenderBox(obj)->inlineBoxWrapper()));
 
-    m_lineBreakObj = obj;
-    m_lineBreakPos = breakPos;
-    m_lineBreakBidiStatusEor = status.eor;
-    m_lineBreakBidiStatusLastStrong = status.lastStrong;
-    m_lineBreakBidiStatusLast = status.last;
-    m_lineBreakContext = status.context;
+  m_lineBreakObj = obj;
+  m_lineBreakPos = breakPos;
+  m_lineBreakBidiStatusEor = status.eor;
+  m_lineBreakBidiStatusLastStrong = status.lastStrong;
+  m_lineBreakBidiStatusLast = status.last;
+  m_lineBreakContext = status.context;
 }
 
-void RootInlineBox::removeLineBoxFromRenderObject()
-{
-    block().lineBoxes()->removeLineBox(this);
+void RootInlineBox::removeLineBoxFromRenderObject() {
+  block().lineBoxes()->removeLineBox(this);
 }
 
-void RootInlineBox::extractLineBoxFromRenderObject()
-{
-    block().lineBoxes()->extractLineBox(this);
+void RootInlineBox::extractLineBoxFromRenderObject() {
+  block().lineBoxes()->extractLineBox(this);
 }
 
-void RootInlineBox::attachLineBoxToRenderObject()
-{
-    block().lineBoxes()->attachLineBox(this);
+void RootInlineBox::attachLineBoxToRenderObject() {
+  block().lineBoxes()->attachLineBox(this);
 }
 
-LayoutRect RootInlineBox::paddedLayoutOverflowRect(LayoutUnit endPadding) const
-{
-    LayoutRect lineLayoutOverflow = layoutOverflowRect(lineTop(), lineBottom());
-    if (!endPadding)
-        return lineLayoutOverflow;
-
-    if (isLeftToRightDirection())
-        lineLayoutOverflow.shiftMaxXEdgeTo(std::max(lineLayoutOverflow.maxX(), logicalRight() + endPadding));
-    else
-        lineLayoutOverflow.shiftXEdgeTo(std::min(lineLayoutOverflow.x(), logicalLeft() - endPadding));
-
+LayoutRect RootInlineBox::paddedLayoutOverflowRect(
+    LayoutUnit endPadding) const {
+  LayoutRect lineLayoutOverflow = layoutOverflowRect(lineTop(), lineBottom());
+  if (!endPadding)
     return lineLayoutOverflow;
-}
 
-static void setAscentAndDescent(int& ascent, int& descent, int newAscent, int newDescent, bool& ascentDescentSet)
-{
-    if (!ascentDescentSet) {
-        ascentDescentSet = true;
-        ascent = newAscent;
-        descent = newDescent;
-    } else {
-        ascent = std::max(ascent, newAscent);
-        descent = std::max(descent, newDescent);
+  if (isLeftToRightDirection())
+    lineLayoutOverflow.shiftMaxXEdgeTo(std::max(
+        lineLayoutOverflow.maxX(), logicalRight() + endPadding));
+  else
+    lineLayoutOverflow.shiftXEdgeTo(std::min(
+        lineLayoutOverflow.x(), logicalLeft() - endPadding));
+
+  return lineLayoutOverflow;
+}
+
+static void setAscentAndDescent(int& ascent,
+                                int& descent,
+                                int newAscent,
+                                int newDescent,
+                                bool& ascentDescentSet) {
+  if (!ascentDescentSet) {
+    ascentDescentSet = true;
+    ascent = newAscent;
+    descent = newDescent;
+  } else {
+    ascent = std::max(ascent, newAscent);
+    descent = std::max(descent, newDescent);
+  }
+}
+
+void RootInlineBox::ascentAndDescentForBox(
+    InlineBox* box,
+    GlyphOverflowAndFallbackFontsMap& textBoxDataMap,
+    int& ascent,
+    int& descent,
+    bool& affectsAscent,
+    bool& affectsDescent) const {
+  bool ascentDescentSet = false;
+
+  // Replaced boxes will return 0 for the line-height if line-box-contain says
+  // they are not to be included.
+  if (box->renderer().isReplaced()) {
+    if (renderer().style(isFirstLineStyle())->lineBoxContain() &
+        LineBoxContainReplaced) {
+      ascent = box->baselinePosition(baselineType());
+      descent = box->lineHeight() - ascent;
+
+      // Replaced elements always affect both the ascent and descent.
+      affectsAscent = true;
+      affectsDescent = true;
     }
-}
-
-void RootInlineBox::ascentAndDescentForBox(InlineBox* box, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, int& ascent, int& descent,
-                                           bool& affectsAscent, bool& affectsDescent) const
-{
-    bool ascentDescentSet = false;
-
-    // Replaced boxes will return 0 for the line-height if line-box-contain says they are
-    // not to be included.
-    if (box->renderer().isReplaced()) {
-        if (renderer().style(isFirstLineStyle())->lineBoxContain() & LineBoxContainReplaced) {
-            ascent = box->baselinePosition(baselineType());
-            descent = box->lineHeight() - ascent;
-
-            // Replaced elements always affect both the ascent and descent.
-            affectsAscent = true;
-            affectsDescent = true;
-        }
-        return;
+    return;
+  }
+
+  Vector* usedFonts = 0;
+  GlyphOverflow* glyphOverflow = 0;
+  if (box->isText()) {
+    GlyphOverflowAndFallbackFontsMap::iterator it =
+        textBoxDataMap.find(toInlineTextBox(box));
+    usedFonts = it == textBoxDataMap.end() ? 0 : &it->value.first;
+    glyphOverflow = it == textBoxDataMap.end() ? 0 : &it->value.second;
+  }
+
+  bool includeLeading = includeLeadingForBox(box);
+  bool includeFont = includeFontForBox(box);
+
+  bool setUsedFont = false;
+  bool setUsedFontWithLeading = false;
+
+  if (usedFonts && !usedFonts->isEmpty() &&
+      (includeFont ||
+       (box->renderer().style(isFirstLineStyle())->lineHeight().isNegative() &&
+        includeLeading))) {
+    usedFonts->append(
+        box->renderer().style(isFirstLineStyle())->font().primaryFont());
+    for (size_t i = 0; i < usedFonts->size(); ++i) {
+      const FontMetrics& fontMetrics = usedFonts->at(i)->fontMetrics();
+      int usedFontAscent = fontMetrics.ascent(baselineType());
+      int usedFontDescent = fontMetrics.descent(baselineType());
+      int halfLeading = (fontMetrics.lineSpacing() - fontMetrics.height()) / 2;
+      int usedFontAscentAndLeading = usedFontAscent + halfLeading;
+      int usedFontDescentAndLeading =
+          fontMetrics.lineSpacing() - usedFontAscentAndLeading;
+      if (includeFont) {
+        setAscentAndDescent(ascent, descent, usedFontAscent, usedFontDescent,
+                            ascentDescentSet);
+        setUsedFont = true;
+      }
+      if (includeLeading) {
+        setAscentAndDescent(ascent, descent, usedFontAscentAndLeading,
+                            usedFontDescentAndLeading, ascentDescentSet);
+        setUsedFontWithLeading = true;
+      }
+      if (!affectsAscent)
+        affectsAscent = usedFontAscent - box->logicalTop() > 0;
+      if (!affectsDescent)
+        affectsDescent = usedFontDescent + box->logicalTop() > 0;
     }
-
-    Vector* usedFonts = 0;
-    GlyphOverflow* glyphOverflow = 0;
-    if (box->isText()) {
-        GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.find(toInlineTextBox(box));
-        usedFonts = it == textBoxDataMap.end() ? 0 : &it->value.first;
-        glyphOverflow = it == textBoxDataMap.end() ? 0 : &it->value.second;
+  }
+
+  // If leading is included for the box, then we compute that box.
+  if (includeLeading && !setUsedFontWithLeading) {
+    int ascentWithLeading = box->baselinePosition(baselineType());
+    int descentWithLeading = box->lineHeight() - ascentWithLeading;
+    setAscentAndDescent(ascent, descent, ascentWithLeading, descentWithLeading,
+                        ascentDescentSet);
+
+    // Examine the font box for inline flows and text boxes to see if any part
+    // of it is above the baseline. If the top of our font box relative to the
+    // root box baseline is above the root box baseline, then we are
+    // contributing to the maxAscent value. Descent is similar. If any part of
+    // our font box is below the root box's baseline, then we contribute to the
+    // maxDescent value.
+    affectsAscent = ascentWithLeading - box->logicalTop() > 0;
+    affectsDescent = descentWithLeading + box->logicalTop() > 0;
+  }
+
+  if (includeFontForBox(box) && !setUsedFont) {
+    int fontAscent = box->renderer()
+                         .style(isFirstLineStyle())
+                         ->fontMetrics()
+                         .ascent(baselineType());
+    int fontDescent = box->renderer()
+                          .style(isFirstLineStyle())
+                          ->fontMetrics()
+                          .descent(baselineType());
+    setAscentAndDescent(ascent, descent, fontAscent, fontDescent,
+                        ascentDescentSet);
+    affectsAscent = fontAscent - box->logicalTop() > 0;
+    affectsDescent = fontDescent + box->logicalTop() > 0;
+  }
+
+  if (includeGlyphsForBox(box) && glyphOverflow &&
+      glyphOverflow->computeBounds) {
+    setAscentAndDescent(ascent, descent, glyphOverflow->top,
+                        glyphOverflow->bottom, ascentDescentSet);
+    affectsAscent = glyphOverflow->top - box->logicalTop() > 0;
+    affectsDescent = glyphOverflow->bottom + box->logicalTop() > 0;
+    glyphOverflow->top =
+        std::min(glyphOverflow->top,
+                 std::max(0, glyphOverflow->top - box->renderer()
+                                                      .style(isFirstLineStyle())
+                                                      ->fontMetrics()
+                                                      .ascent(baselineType())));
+    glyphOverflow->bottom = std::min(
+        glyphOverflow->bottom,
+        std::max(0, glyphOverflow->bottom - box->renderer()
+                                                .style(isFirstLineStyle())
+                                                ->fontMetrics()
+                                                .descent(baselineType())));
+  }
+
+  if (includeMarginForBox(box)) {
+    LayoutUnit ascentWithMargin = box->renderer()
+                                      .style(isFirstLineStyle())
+                                      ->fontMetrics()
+                                      .ascent(baselineType());
+    LayoutUnit descentWithMargin = box->renderer()
+                                       .style(isFirstLineStyle())
+                                       ->fontMetrics()
+                                       .descent(baselineType());
+    if (box->parent() && !box->renderer().isText()) {
+      ascentWithMargin += box->boxModelObject()->borderBefore() +
+                          box->boxModelObject()->paddingBefore() +
+                          box->boxModelObject()->marginBefore();
+      descentWithMargin += box->boxModelObject()->borderAfter() +
+                           box->boxModelObject()->paddingAfter() +
+                           box->boxModelObject()->marginAfter();
     }
+    setAscentAndDescent(ascent, descent, ascentWithMargin, descentWithMargin,
+                        ascentDescentSet);
 
-    bool includeLeading = includeLeadingForBox(box);
-    bool includeFont = includeFontForBox(box);
-
-    bool setUsedFont = false;
-    bool setUsedFontWithLeading = false;
-
-    if (usedFonts && !usedFonts->isEmpty() && (includeFont || (box->renderer().style(isFirstLineStyle())->lineHeight().isNegative() && includeLeading))) {
-        usedFonts->append(box->renderer().style(isFirstLineStyle())->font().primaryFont());
-        for (size_t i = 0; i < usedFonts->size(); ++i) {
-            const FontMetrics& fontMetrics = usedFonts->at(i)->fontMetrics();
-            int usedFontAscent = fontMetrics.ascent(baselineType());
-            int usedFontDescent = fontMetrics.descent(baselineType());
-            int halfLeading = (fontMetrics.lineSpacing() - fontMetrics.height()) / 2;
-            int usedFontAscentAndLeading = usedFontAscent + halfLeading;
-            int usedFontDescentAndLeading = fontMetrics.lineSpacing() - usedFontAscentAndLeading;
-            if (includeFont) {
-                setAscentAndDescent(ascent, descent, usedFontAscent, usedFontDescent, ascentDescentSet);
-                setUsedFont = true;
-            }
-            if (includeLeading) {
-                setAscentAndDescent(ascent, descent, usedFontAscentAndLeading, usedFontDescentAndLeading, ascentDescentSet);
-                setUsedFontWithLeading = true;
-            }
-            if (!affectsAscent)
-                affectsAscent = usedFontAscent - box->logicalTop() > 0;
-            if (!affectsDescent)
-                affectsDescent = usedFontDescent + box->logicalTop() > 0;
-        }
-    }
-
-    // If leading is included for the box, then we compute that box.
-    if (includeLeading && !setUsedFontWithLeading) {
-        int ascentWithLeading = box->baselinePosition(baselineType());
-        int descentWithLeading = box->lineHeight() - ascentWithLeading;
-        setAscentAndDescent(ascent, descent, ascentWithLeading, descentWithLeading, ascentDescentSet);
-
-        // Examine the font box for inline flows and text boxes to see if any part of it is above the baseline.
-        // If the top of our font box relative to the root box baseline is above the root box baseline, then
-        // we are contributing to the maxAscent value. Descent is similar. If any part of our font box is below
-        // the root box's baseline, then we contribute to the maxDescent value.
-        affectsAscent = ascentWithLeading - box->logicalTop() > 0;
-        affectsDescent = descentWithLeading + box->logicalTop() > 0;
-    }
-
-    if (includeFontForBox(box) && !setUsedFont) {
-        int fontAscent = box->renderer().style(isFirstLineStyle())->fontMetrics().ascent(baselineType());
-        int fontDescent = box->renderer().style(isFirstLineStyle())->fontMetrics().descent(baselineType());
-        setAscentAndDescent(ascent, descent, fontAscent, fontDescent, ascentDescentSet);
-        affectsAscent = fontAscent - box->logicalTop() > 0;
-        affectsDescent = fontDescent + box->logicalTop() > 0;
-    }
-
-    if (includeGlyphsForBox(box) && glyphOverflow && glyphOverflow->computeBounds) {
-        setAscentAndDescent(ascent, descent, glyphOverflow->top, glyphOverflow->bottom, ascentDescentSet);
-        affectsAscent = glyphOverflow->top - box->logicalTop() > 0;
-        affectsDescent = glyphOverflow->bottom + box->logicalTop() > 0;
-        glyphOverflow->top = std::min(glyphOverflow->top, std::max(0, glyphOverflow->top - box->renderer().style(isFirstLineStyle())->fontMetrics().ascent(baselineType())));
-        glyphOverflow->bottom = std::min(glyphOverflow->bottom, std::max(0, glyphOverflow->bottom - box->renderer().style(isFirstLineStyle())->fontMetrics().descent(baselineType())));
-    }
-
-    if (includeMarginForBox(box)) {
-        LayoutUnit ascentWithMargin = box->renderer().style(isFirstLineStyle())->fontMetrics().ascent(baselineType());
-        LayoutUnit descentWithMargin = box->renderer().style(isFirstLineStyle())->fontMetrics().descent(baselineType());
-        if (box->parent() && !box->renderer().isText()) {
-            ascentWithMargin += box->boxModelObject()->borderBefore() + box->boxModelObject()->paddingBefore() + box->boxModelObject()->marginBefore();
-            descentWithMargin += box->boxModelObject()->borderAfter() + box->boxModelObject()->paddingAfter() + box->boxModelObject()->marginAfter();
-        }
-        setAscentAndDescent(ascent, descent, ascentWithMargin, descentWithMargin, ascentDescentSet);
-
-        // Treat like a replaced element, since we're using the margin box.
-        affectsAscent = true;
-        affectsDescent = true;
-    }
+    // Treat like a replaced element, since we're using the margin box.
+    affectsAscent = true;
+    affectsDescent = true;
+  }
 }
 
-LayoutUnit RootInlineBox::verticalPositionForBox(InlineBox* box, VerticalPositionCache& verticalPositionCache)
-{
-    if (box->renderer().isText())
-        return box->parent()->logicalTop();
+LayoutUnit RootInlineBox::verticalPositionForBox(
+    InlineBox* box,
+    VerticalPositionCache& verticalPositionCache) {
+  if (box->renderer().isText())
+    return box->parent()->logicalTop();
 
-    RenderBoxModelObject* renderer = box->boxModelObject();
-    ASSERT(renderer->isInline());
-    if (!renderer->isInline())
-        return 0;
+  RenderBoxModelObject* renderer = box->boxModelObject();
+  ASSERT(renderer->isInline());
+  if (!renderer->isInline())
+    return 0;
 
-    bool firstLine = false;
+  bool firstLine = false;
 
-    // Check the cache.
-    bool isRenderInline = renderer->isRenderInline();
-    if (isRenderInline && !firstLine) {
-        LayoutUnit verticalPosition = verticalPositionCache.get(renderer, baselineType());
-        if (verticalPosition != PositionUndefined)
-            return verticalPosition;
-    }
+  // Check the cache.
+  bool isRenderInline = renderer->isRenderInline();
+  if (isRenderInline && !firstLine) {
+    LayoutUnit verticalPosition =
+        verticalPositionCache.get(renderer, baselineType());
+    if (verticalPosition != PositionUndefined)
+      return verticalPosition;
+  }
 
-    LayoutUnit verticalPosition = 0;
-    EVerticalAlign verticalAlign = renderer->style()->verticalAlign();
-    if (verticalAlign == TOP || verticalAlign == BOTTOM)
-        return 0;
-
-    RenderObject* parent = renderer->parent();
-    if (parent->isRenderInline() && parent->style()->verticalAlign() != TOP && parent->style()->verticalAlign() != BOTTOM)
-        verticalPosition = box->parent()->logicalTop();
-
-    if (verticalAlign != BASELINE) {
-        const Font& font = parent->style(firstLine)->font();
-        const FontMetrics& fontMetrics = font.fontMetrics();
-        int fontSize = font.fontDescription().computedPixelSize();
-
-        LineDirectionMode lineDirection = HorizontalLine;
-
-        if (verticalAlign == SUB)
-            verticalPosition += fontSize / 5 + 1;
-        else if (verticalAlign == SUPER)
-            verticalPosition -= fontSize / 3 + 1;
-        else if (verticalAlign == TEXT_TOP)
-            verticalPosition += renderer->baselinePosition(baselineType(), firstLine, lineDirection) - fontMetrics.ascent(baselineType());
-        else if (verticalAlign == MIDDLE)
-            verticalPosition = (verticalPosition - static_cast(fontMetrics.xHeight() / 2) - renderer->lineHeight(firstLine, lineDirection) / 2 + renderer->baselinePosition(baselineType(), firstLine, lineDirection)).round();
-        else if (verticalAlign == TEXT_BOTTOM) {
-            verticalPosition += fontMetrics.descent(baselineType());
-            // lineHeight - baselinePosition is always 0 for replaced elements (except inline blocks), so don't bother wasting time in that case.
-            if (!renderer->isReplaced() || renderer->isInlineBlock())
-                verticalPosition -= (renderer->lineHeight(firstLine, lineDirection) - renderer->baselinePosition(baselineType(), firstLine, lineDirection));
-        } else if (verticalAlign == BASELINE_MIDDLE)
-            verticalPosition += -renderer->lineHeight(firstLine, lineDirection) / 2 + renderer->baselinePosition(baselineType(), firstLine, lineDirection);
-        else if (verticalAlign == LENGTH) {
-            LayoutUnit lineHeight;
-            //Per http://www.w3.org/TR/CSS21/visudet.html#propdef-vertical-align: 'Percentages: refer to the 'line-height' of the element itself'.
-            if (renderer->style()->verticalAlignLength().isPercent())
-                lineHeight = renderer->style()->computedLineHeight();
-            else
-                lineHeight = renderer->lineHeight(firstLine, lineDirection);
-            verticalPosition -= valueForLength(renderer->style()->verticalAlignLength(), lineHeight);
-        }
+  LayoutUnit verticalPosition = 0;
+  EVerticalAlign verticalAlign = renderer->style()->verticalAlign();
+  if (verticalAlign == TOP || verticalAlign == BOTTOM)
+    return 0;
+
+  RenderObject* parent = renderer->parent();
+  if (parent->isRenderInline() && parent->style()->verticalAlign() != TOP &&
+      parent->style()->verticalAlign() != BOTTOM)
+    verticalPosition = box->parent()->logicalTop();
+
+  if (verticalAlign != BASELINE) {
+    const Font& font = parent->style(firstLine)->font();
+    const FontMetrics& fontMetrics = font.fontMetrics();
+    int fontSize = font.fontDescription().computedPixelSize();
+
+    LineDirectionMode lineDirection = HorizontalLine;
+
+    if (verticalAlign == SUB)
+      verticalPosition += fontSize / 5 + 1;
+    else if (verticalAlign == SUPER)
+      verticalPosition -= fontSize / 3 + 1;
+    else if (verticalAlign == TEXT_TOP)
+      verticalPosition +=
+          renderer->baselinePosition(baselineType(), firstLine, lineDirection) -
+          fontMetrics.ascent(baselineType());
+    else if (verticalAlign == MIDDLE)
+      verticalPosition =
+          (verticalPosition -
+           static_cast(fontMetrics.xHeight() / 2) -
+           renderer->lineHeight(firstLine, lineDirection) / 2 +
+           renderer->baselinePosition(baselineType(), firstLine, lineDirection))
+              .round();
+    else if (verticalAlign == TEXT_BOTTOM) {
+      verticalPosition += fontMetrics.descent(baselineType());
+      // lineHeight - baselinePosition is always 0 for replaced elements (except
+      // inline blocks), so don't bother wasting time in that case.
+      if (!renderer->isReplaced() || renderer->isInlineBlock())
+        verticalPosition -= (renderer->lineHeight(firstLine, lineDirection) -
+                             renderer->baselinePosition(
+                                 baselineType(), firstLine, lineDirection));
+    } else if (verticalAlign == BASELINE_MIDDLE)
+      verticalPosition +=
+          -renderer->lineHeight(firstLine, lineDirection) / 2 +
+          renderer->baselinePosition(baselineType(), firstLine, lineDirection);
+    else if (verticalAlign == LENGTH) {
+      LayoutUnit lineHeight;
+      // Per http://www.w3.org/TR/CSS21/visudet.html#propdef-vertical-align:
+      // 'Percentages: refer to the 'line-height' of the element itself'.
+      if (renderer->style()->verticalAlignLength().isPercent())
+        lineHeight = renderer->style()->computedLineHeight();
+      else
+        lineHeight = renderer->lineHeight(firstLine, lineDirection);
+      verticalPosition -=
+          valueForLength(renderer->style()->verticalAlignLength(), lineHeight);
     }
+  }
 
-    // Store the cached value.
-    if (isRenderInline && !firstLine)
-        verticalPositionCache.set(renderer, baselineType(), verticalPosition);
+  // Store the cached value.
+  if (isRenderInline && !firstLine)
+    verticalPositionCache.set(renderer, baselineType(), verticalPosition);
 
-    return verticalPosition;
+  return verticalPosition;
 }
 
-bool RootInlineBox::includeLeadingForBox(InlineBox* box) const
-{
-    if (box->renderer().isReplaced() || (box->renderer().isText() && !box->isText()))
-        return false;
+bool RootInlineBox::includeLeadingForBox(InlineBox* box) const {
+  if (box->renderer().isReplaced() ||
+      (box->renderer().isText() && !box->isText()))
+    return false;
 
-    LineBoxContain lineBoxContain = renderer().style()->lineBoxContain();
-    return (lineBoxContain & LineBoxContainInline) || (box == this && (lineBoxContain & LineBoxContainBlock));
+  LineBoxContain lineBoxContain = renderer().style()->lineBoxContain();
+  return (lineBoxContain & LineBoxContainInline) ||
+         (box == this && (lineBoxContain & LineBoxContainBlock));
 }
 
-bool RootInlineBox::includeFontForBox(InlineBox* box) const
-{
-    if (box->renderer().isReplaced() || (box->renderer().isText() && !box->isText()))
-        return false;
+bool RootInlineBox::includeFontForBox(InlineBox* box) const {
+  if (box->renderer().isReplaced() ||
+      (box->renderer().isText() && !box->isText()))
+    return false;
 
-    if (!box->isText() && box->isInlineFlowBox() && !toInlineFlowBox(box)->hasTextChildren())
-        return false;
+  if (!box->isText() && box->isInlineFlowBox() &&
+      !toInlineFlowBox(box)->hasTextChildren())
+    return false;
 
-    // For now map "glyphs" to "font" in vertical text mode until the bounds returned by glyphs aren't garbage.
-    LineBoxContain lineBoxContain = renderer().style()->lineBoxContain();
-    return lineBoxContain & LineBoxContainFont;
+  // For now map "glyphs" to "font" in vertical text mode until the bounds
+  // returned by glyphs aren't garbage.
+  LineBoxContain lineBoxContain = renderer().style()->lineBoxContain();
+  return lineBoxContain & LineBoxContainFont;
 }
 
-bool RootInlineBox::includeGlyphsForBox(InlineBox* box) const
-{
-    if (box->renderer().isReplaced() || (box->renderer().isText() && !box->isText()))
-        return false;
+bool RootInlineBox::includeGlyphsForBox(InlineBox* box) const {
+  if (box->renderer().isReplaced() ||
+      (box->renderer().isText() && !box->isText()))
+    return false;
 
-    if (!box->isText() && box->isInlineFlowBox() && !toInlineFlowBox(box)->hasTextChildren())
-        return false;
+  if (!box->isText() && box->isInlineFlowBox() &&
+      !toInlineFlowBox(box)->hasTextChildren())
+    return false;
 
-    // FIXME: We can't fit to glyphs yet for vertical text, since the bounds returned are garbage.
-    LineBoxContain lineBoxContain = renderer().style()->lineBoxContain();
-    return lineBoxContain & LineBoxContainGlyphs;
+  // FIXME: We can't fit to glyphs yet for vertical text, since the bounds
+  // returned are garbage.
+  LineBoxContain lineBoxContain = renderer().style()->lineBoxContain();
+  return lineBoxContain & LineBoxContainGlyphs;
 }
 
-bool RootInlineBox::includeMarginForBox(InlineBox* box) const
-{
-    if (box->renderer().isReplaced() || (box->renderer().isText() && !box->isText()))
-        return false;
+bool RootInlineBox::includeMarginForBox(InlineBox* box) const {
+  if (box->renderer().isReplaced() ||
+      (box->renderer().isText() && !box->isText()))
+    return false;
 
-    LineBoxContain lineBoxContain = renderer().style()->lineBoxContain();
-    return lineBoxContain & LineBoxContainInlineBox;
+  LineBoxContain lineBoxContain = renderer().style()->lineBoxContain();
+  return lineBoxContain & LineBoxContainInlineBox;
 }
 
-
-bool RootInlineBox::fitsToGlyphs() const
-{
-    // FIXME: We can't fit to glyphs yet for vertical text, since the bounds returned are garbage.
-    LineBoxContain lineBoxContain = renderer().style()->lineBoxContain();
-    return lineBoxContain & LineBoxContainGlyphs;
+bool RootInlineBox::fitsToGlyphs() const {
+  // FIXME: We can't fit to glyphs yet for vertical text, since the bounds
+  // returned are garbage.
+  LineBoxContain lineBoxContain = renderer().style()->lineBoxContain();
+  return lineBoxContain & LineBoxContainGlyphs;
 }
 
-bool RootInlineBox::includesRootLineBoxFontOrLeading() const
-{
-    LineBoxContain lineBoxContain = renderer().style()->lineBoxContain();
-    return (lineBoxContain & LineBoxContainBlock) || (lineBoxContain & LineBoxContainInline) || (lineBoxContain & LineBoxContainFont);
+bool RootInlineBox::includesRootLineBoxFontOrLeading() const {
+  LineBoxContain lineBoxContain = renderer().style()->lineBoxContain();
+  return (lineBoxContain & LineBoxContainBlock) ||
+         (lineBoxContain & LineBoxContainInline) ||
+         (lineBoxContain & LineBoxContainFont);
 }
 
 #ifndef NDEBUG
-const char* RootInlineBox::boxName() const
-{
-    return "RootInlineBox";
+const char* RootInlineBox::boxName() const {
+  return "RootInlineBox";
 }
 #endif
 
-} // namespace blink
+}  // namespace blink
diff --git a/sky/engine/core/rendering/RootInlineBox.h b/sky/engine/core/rendering/RootInlineBox.h
index 95d47ffdaf4bc..4fad3827b8c2d 100644
--- a/sky/engine/core/rendering/RootInlineBox.h
+++ b/sky/engine/core/rendering/RootInlineBox.h
@@ -34,144 +34,184 @@ struct BidiStatus;
 struct GapRects;
 
 class RootInlineBox : public InlineFlowBox {
-public:
-    explicit RootInlineBox(RenderParagraph&);
-
-    virtual void destroy() override final;
-
-    virtual bool isRootInlineBox() const override final { return true; }
-
-    void detachEllipsisBox();
-
-    RootInlineBox* nextRootBox() const { return static_cast(m_nextLineBox); }
-    RootInlineBox* prevRootBox() const { return static_cast(m_prevLineBox); }
-
-    virtual void adjustPosition(float dx, float dy) override final;
-
-    LayoutUnit lineTop() const { return m_lineTop; }
-    LayoutUnit lineBottom() const { return m_lineBottom; }
-
-    LayoutUnit lineTopWithLeading() const { return m_lineTopWithLeading; }
-    LayoutUnit lineBottomWithLeading() const { return m_lineBottomWithLeading; }
-
-    LayoutUnit selectionTop() const;
-    LayoutUnit selectionBottom() const;
-    LayoutUnit selectionHeight() const { return max(0, selectionBottom() - selectionTop()); }
-
-    LayoutUnit selectionTopAdjustedForPrecedingBlock() const;
-    LayoutUnit selectionHeightAdjustedForPrecedingBlock() const { return max(0, selectionBottom() - selectionTopAdjustedForPrecedingBlock()); }
-
-    int blockDirectionPointInLine() const;
-
-    LayoutUnit alignBoxesInBlockDirection(LayoutUnit heightOfBlock, GlyphOverflowAndFallbackFontsMap&, VerticalPositionCache&);
-    void setLineTopBottomPositions(LayoutUnit top, LayoutUnit bottom, LayoutUnit topWithLeading, LayoutUnit bottomWithLeading, LayoutUnit selectionBottom = LayoutUnit::min())
-    {
-        m_lineTop = top;
-        m_lineBottom = bottom;
-        m_lineTopWithLeading = topWithLeading;
-        m_lineBottomWithLeading = bottomWithLeading;
-        m_selectionBottom = selectionBottom == LayoutUnit::min() ? bottom : selectionBottom;
-    }
-
-    virtual RenderLineBoxList* rendererLineBoxes() const override final;
-
-    RenderObject* lineBreakObj() const { return m_lineBreakObj; }
-    BidiStatus lineBreakBidiStatus() const;
-    void setLineBreakInfo(RenderObject*, unsigned breakPos, const BidiStatus&);
-
-    unsigned lineBreakPos() const { return m_lineBreakPos; }
-    void setLineBreakPos(unsigned p) { m_lineBreakPos = p; }
-
-    using InlineBox::endsWithBreak;
-    using InlineBox::setEndsWithBreak;
-
-    void childRemoved(InlineBox* box);
-
-    void paintEllipsisBox(PaintInfo&, const LayoutPoint&, LayoutUnit lineTop, LayoutUnit lineBottom, Vector& layers) const;
-
-    virtual void clearTruncation() override final;
-
-    virtual int baselinePosition(FontBaseline baselineType) const override final;
-    virtual LayoutUnit lineHeight() const override final;
-
-    virtual void paint(PaintInfo&, const LayoutPoint&, LayoutUnit lineTop, LayoutUnit lineBottom, Vector& layers) override;
-    virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, LayoutUnit lineTop, LayoutUnit lineBottom) override final;
-
-    using InlineBox::hasSelectedChildren;
-    using InlineBox::setHasSelectedChildren;
-
-    virtual RenderObject::SelectionState selectionState() override final;
-    InlineBox* firstSelectedBox();
-    InlineBox* lastSelectedBox();
-
-    GapRects lineSelectionGap(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, LayoutUnit selTop, LayoutUnit selHeight, const PaintInfo*);
-
-    RenderParagraph& block() const;
-
-    InlineBox* closestLeafChildForPoint(const IntPoint&, bool onlyEditableLeaves);
-    InlineBox* closestLeafChildForLogicalLeftPosition(int, bool onlyEditableLeaves = false);
-
-    virtual void extractLineBoxFromRenderObject() override final;
-    virtual void attachLineBoxToRenderObject() override final;
-    virtual void removeLineBoxFromRenderObject() override final;
-
-    FontBaseline baselineType() const { return static_cast(m_baselineType); }
-
-    bool hasAnnotationsBefore() const { return m_hasAnnotationsBefore; }
-    bool hasAnnotationsAfter() const { return m_hasAnnotationsAfter; }
-
-    LayoutRect paddedLayoutOverflowRect(LayoutUnit endPadding) const;
-
-    void ascentAndDescentForBox(InlineBox*, GlyphOverflowAndFallbackFontsMap&, int& ascent, int& descent, bool& affectsAscent, bool& affectsDescent) const;
-    LayoutUnit verticalPositionForBox(InlineBox*, VerticalPositionCache&);
-    bool includeLeadingForBox(InlineBox*) const;
-    bool includeFontForBox(InlineBox*) const;
-    bool includeGlyphsForBox(InlineBox*) const;
-    bool includeMarginForBox(InlineBox*) const;
-    bool fitsToGlyphs() const;
-    bool includesRootLineBoxFontOrLeading() const;
-
-    LayoutUnit logicalTopVisualOverflow() const
-    {
-        return InlineFlowBox::logicalTopVisualOverflow(lineTop());
-    }
-    LayoutUnit logicalBottomVisualOverflow() const
-    {
-        return InlineFlowBox::logicalBottomVisualOverflow(lineBottom());
-    }
-    LayoutUnit logicalTopLayoutOverflow() const
-    {
-        return InlineFlowBox::logicalTopLayoutOverflow(lineTop());
-    }
-    LayoutUnit logicalBottomLayoutOverflow() const
-    {
-        return InlineFlowBox::logicalBottomLayoutOverflow(lineBottom());
-    }
-
-    // Used to calculate the underline offset for TextUnderlinePositionUnder.
-    float maxLogicalTop() const;
+ public:
+  explicit RootInlineBox(RenderParagraph&);
+
+  virtual void destroy() override final;
+
+  virtual bool isRootInlineBox() const override final { return true; }
+
+  void detachEllipsisBox();
+
+  RootInlineBox* nextRootBox() const {
+    return static_cast(m_nextLineBox);
+  }
+  RootInlineBox* prevRootBox() const {
+    return static_cast(m_prevLineBox);
+  }
+
+  virtual void adjustPosition(float dx, float dy) override final;
+
+  LayoutUnit lineTop() const { return m_lineTop; }
+  LayoutUnit lineBottom() const { return m_lineBottom; }
+
+  LayoutUnit lineTopWithLeading() const { return m_lineTopWithLeading; }
+  LayoutUnit lineBottomWithLeading() const { return m_lineBottomWithLeading; }
+
+  LayoutUnit selectionTop() const;
+  LayoutUnit selectionBottom() const;
+  LayoutUnit selectionHeight() const {
+    return max(0, selectionBottom() - selectionTop());
+  }
+
+  LayoutUnit selectionTopAdjustedForPrecedingBlock() const;
+  LayoutUnit selectionHeightAdjustedForPrecedingBlock() const {
+    return max(
+        0, selectionBottom() - selectionTopAdjustedForPrecedingBlock());
+  }
+
+  int blockDirectionPointInLine() const;
+
+  LayoutUnit alignBoxesInBlockDirection(LayoutUnit heightOfBlock,
+                                        GlyphOverflowAndFallbackFontsMap&,
+                                        VerticalPositionCache&);
+  void setLineTopBottomPositions(
+      LayoutUnit top,
+      LayoutUnit bottom,
+      LayoutUnit topWithLeading,
+      LayoutUnit bottomWithLeading,
+      LayoutUnit selectionBottom = LayoutUnit::min()) {
+    m_lineTop = top;
+    m_lineBottom = bottom;
+    m_lineTopWithLeading = topWithLeading;
+    m_lineBottomWithLeading = bottomWithLeading;
+    m_selectionBottom =
+        selectionBottom == LayoutUnit::min() ? bottom : selectionBottom;
+  }
+
+  virtual RenderLineBoxList* rendererLineBoxes() const override final;
+
+  RenderObject* lineBreakObj() const { return m_lineBreakObj; }
+  BidiStatus lineBreakBidiStatus() const;
+  void setLineBreakInfo(RenderObject*, unsigned breakPos, const BidiStatus&);
+
+  unsigned lineBreakPos() const { return m_lineBreakPos; }
+  void setLineBreakPos(unsigned p) { m_lineBreakPos = p; }
+
+  using InlineBox::endsWithBreak;
+  using InlineBox::setEndsWithBreak;
+
+  void childRemoved(InlineBox* box);
+
+  void paintEllipsisBox(PaintInfo&,
+                        const LayoutPoint&,
+                        LayoutUnit lineTop,
+                        LayoutUnit lineBottom,
+                        Vector& layers) const;
+
+  virtual void clearTruncation() override final;
+
+  virtual int baselinePosition(FontBaseline baselineType) const override final;
+  virtual LayoutUnit lineHeight() const override final;
+
+  virtual void paint(PaintInfo&,
+                     const LayoutPoint&,
+                     LayoutUnit lineTop,
+                     LayoutUnit lineBottom,
+                     Vector& layers) override;
+  virtual bool nodeAtPoint(const HitTestRequest&,
+                           HitTestResult&,
+                           const HitTestLocation& locationInContainer,
+                           const LayoutPoint& accumulatedOffset,
+                           LayoutUnit lineTop,
+                           LayoutUnit lineBottom) override final;
+
+  using InlineBox::hasSelectedChildren;
+  using InlineBox::setHasSelectedChildren;
+
+  virtual RenderObject::SelectionState selectionState() override final;
+  InlineBox* firstSelectedBox();
+  InlineBox* lastSelectedBox();
+
+  GapRects lineSelectionGap(RenderBlock* rootBlock,
+                            const LayoutPoint& rootBlockPhysicalPosition,
+                            const LayoutSize& offsetFromRootBlock,
+                            LayoutUnit selTop,
+                            LayoutUnit selHeight,
+                            const PaintInfo*);
+
+  RenderParagraph& block() const;
+
+  InlineBox* closestLeafChildForPoint(const IntPoint&, bool onlyEditableLeaves);
+  InlineBox* closestLeafChildForLogicalLeftPosition(
+      int,
+      bool onlyEditableLeaves = false);
+
+  virtual void extractLineBoxFromRenderObject() override final;
+  virtual void attachLineBoxToRenderObject() override final;
+  virtual void removeLineBoxFromRenderObject() override final;
+
+  FontBaseline baselineType() const {
+    return static_cast(m_baselineType);
+  }
+
+  bool hasAnnotationsBefore() const { return m_hasAnnotationsBefore; }
+  bool hasAnnotationsAfter() const { return m_hasAnnotationsAfter; }
+
+  LayoutRect paddedLayoutOverflowRect(LayoutUnit endPadding) const;
+
+  void ascentAndDescentForBox(InlineBox*,
+                              GlyphOverflowAndFallbackFontsMap&,
+                              int& ascent,
+                              int& descent,
+                              bool& affectsAscent,
+                              bool& affectsDescent) const;
+  LayoutUnit verticalPositionForBox(InlineBox*, VerticalPositionCache&);
+  bool includeLeadingForBox(InlineBox*) const;
+  bool includeFontForBox(InlineBox*) const;
+  bool includeGlyphsForBox(InlineBox*) const;
+  bool includeMarginForBox(InlineBox*) const;
+  bool fitsToGlyphs() const;
+  bool includesRootLineBoxFontOrLeading() const;
+
+  LayoutUnit logicalTopVisualOverflow() const {
+    return InlineFlowBox::logicalTopVisualOverflow(lineTop());
+  }
+  LayoutUnit logicalBottomVisualOverflow() const {
+    return InlineFlowBox::logicalBottomVisualOverflow(lineBottom());
+  }
+  LayoutUnit logicalTopLayoutOverflow() const {
+    return InlineFlowBox::logicalTopLayoutOverflow(lineTop());
+  }
+  LayoutUnit logicalBottomLayoutOverflow() const {
+    return InlineFlowBox::logicalBottomLayoutOverflow(lineBottom());
+  }
+
+  // Used to calculate the underline offset for TextUnderlinePositionUnder.
+  float maxLogicalTop() const;
 
 #ifndef NDEBUG
-    virtual const char* boxName() const override;
+  virtual const char* boxName() const override;
 #endif
-private:
-    LayoutUnit beforeAnnotationsAdjustment() const;
-
-    // This folds into the padding at the end of InlineFlowBox on 64-bit.
-    unsigned m_lineBreakPos;
-
-    // Where this line ended.  The exact object and the position within that object are stored so that
-    // we can create an InlineIterator beginning just after the end of this line.
-    RenderObject* m_lineBreakObj;
-    RefPtr m_lineBreakContext;
-
-    LayoutUnit m_lineTop;
-    LayoutUnit m_lineBottom;
-    LayoutUnit m_lineTopWithLeading;
-    LayoutUnit m_lineBottomWithLeading;
-    LayoutUnit m_selectionBottom;
+ private:
+  LayoutUnit beforeAnnotationsAdjustment() const;
+
+  // This folds into the padding at the end of InlineFlowBox on 64-bit.
+  unsigned m_lineBreakPos;
+
+  // Where this line ended.  The exact object and the position within that
+  // object are stored so that we can create an InlineIterator beginning just
+  // after the end of this line.
+  RenderObject* m_lineBreakObj;
+  RefPtr m_lineBreakContext;
+
+  LayoutUnit m_lineTop;
+  LayoutUnit m_lineBottom;
+  LayoutUnit m_lineTopWithLeading;
+  LayoutUnit m_lineBottomWithLeading;
+  LayoutUnit m_selectionBottom;
 };
 
-} // namespace blink
+}  // namespace blink
 
 #endif  // SKY_ENGINE_CORE_RENDERING_ROOTINLINEBOX_H_
diff --git a/sky/engine/core/rendering/ScrollAlignment.cpp b/sky/engine/core/rendering/ScrollAlignment.cpp
index 5ecfe3d22a0ef..3f7062ca8ad9b 100644
--- a/sky/engine/core/rendering/ScrollAlignment.cpp
+++ b/sky/engine/core/rendering/ScrollAlignment.cpp
@@ -24,7 +24,7 @@
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
  *
  * Alternatively, the contents of this file may be used under the terms
  * of either the Mozilla Public License Version 1.1, found at
@@ -47,98 +47,115 @@
 
 namespace blink {
 
-const ScrollAlignment ScrollAlignment::alignCenterIfNeeded = { ScrollAlignmentNoScroll, ScrollAlignmentCenter, ScrollAlignmentClosestEdge };
-const ScrollAlignment ScrollAlignment::alignToEdgeIfNeeded = { ScrollAlignmentNoScroll, ScrollAlignmentClosestEdge, ScrollAlignmentClosestEdge };
-const ScrollAlignment ScrollAlignment::alignCenterAlways = { ScrollAlignmentCenter, ScrollAlignmentCenter, ScrollAlignmentCenter };
-const ScrollAlignment ScrollAlignment::alignTopAlways = { ScrollAlignmentTop, ScrollAlignmentTop, ScrollAlignmentTop };
+const ScrollAlignment ScrollAlignment::alignCenterIfNeeded = {
+    ScrollAlignmentNoScroll, ScrollAlignmentCenter, ScrollAlignmentClosestEdge};
+const ScrollAlignment ScrollAlignment::alignToEdgeIfNeeded = {
+    ScrollAlignmentNoScroll, ScrollAlignmentClosestEdge,
+    ScrollAlignmentClosestEdge};
+const ScrollAlignment ScrollAlignment::alignCenterAlways = {
+    ScrollAlignmentCenter, ScrollAlignmentCenter, ScrollAlignmentCenter};
+const ScrollAlignment ScrollAlignment::alignTopAlways = {
+    ScrollAlignmentTop, ScrollAlignmentTop, ScrollAlignmentTop};
 
 #define MIN_INTERSECT_FOR_REVEAL 32
 
-LayoutRect ScrollAlignment::getRectToExpose(const LayoutRect& visibleRect, const LayoutRect& exposeRect, const ScrollAlignment& alignX, const ScrollAlignment& alignY)
-{
-    // Determine the appropriate X behavior.
-    ScrollAlignmentBehavior scrollX;
-    LayoutRect exposeRectX(exposeRect.x(), visibleRect.y(), exposeRect.width(), visibleRect.height());
-    LayoutUnit intersectWidth = intersection(visibleRect, exposeRectX).width();
-    if (intersectWidth == exposeRect.width() || intersectWidth >= MIN_INTERSECT_FOR_REVEAL) {
-        // If the rectangle is fully visible, use the specified visible behavior.
-        // If the rectangle is partially visible, but over a certain threshold,
-        // then treat it as fully visible to avoid unnecessary horizontal scrolling
-        scrollX = getVisibleBehavior(alignX);
-    } else if (intersectWidth == visibleRect.width()) {
-        // If the rect is bigger than the visible area, don't bother trying to center. Other alignments will work.
-        scrollX = getVisibleBehavior(alignX);
-        if (scrollX == ScrollAlignmentCenter)
-            scrollX = ScrollAlignmentNoScroll;
-    } else if (intersectWidth > 0) {
-        // If the rectangle is partially visible, but not above the minimum threshold, use the specified partial behavior
-        scrollX = getPartialBehavior(alignX);
-    } else {
-        scrollX = getHiddenBehavior(alignX);
-    }
+LayoutRect ScrollAlignment::getRectToExpose(const LayoutRect& visibleRect,
+                                            const LayoutRect& exposeRect,
+                                            const ScrollAlignment& alignX,
+                                            const ScrollAlignment& alignY) {
+  // Determine the appropriate X behavior.
+  ScrollAlignmentBehavior scrollX;
+  LayoutRect exposeRectX(exposeRect.x(), visibleRect.y(), exposeRect.width(),
+                         visibleRect.height());
+  LayoutUnit intersectWidth = intersection(visibleRect, exposeRectX).width();
+  if (intersectWidth == exposeRect.width() ||
+      intersectWidth >= MIN_INTERSECT_FOR_REVEAL) {
+    // If the rectangle is fully visible, use the specified visible behavior.
+    // If the rectangle is partially visible, but over a certain threshold,
+    // then treat it as fully visible to avoid unnecessary horizontal scrolling
+    scrollX = getVisibleBehavior(alignX);
+  } else if (intersectWidth == visibleRect.width()) {
+    // If the rect is bigger than the visible area, don't bother trying to
+    // center. Other alignments will work.
+    scrollX = getVisibleBehavior(alignX);
+    if (scrollX == ScrollAlignmentCenter)
+      scrollX = ScrollAlignmentNoScroll;
+  } else if (intersectWidth > 0) {
+    // If the rectangle is partially visible, but not above the minimum
+    // threshold, use the specified partial behavior
+    scrollX = getPartialBehavior(alignX);
+  } else {
+    scrollX = getHiddenBehavior(alignX);
+  }
 
-    if (scrollX == ScrollAlignmentClosestEdge) {
-        // Closest edge is the right in two cases:
-        // (1) exposeRect to the right of and smaller than visibleRect
-        // (2) exposeRect to the left of and larger than visibleRect
-        if ((exposeRect.maxX() > visibleRect.maxX() && exposeRect.width() < visibleRect.width())
-            || (exposeRect.maxX() < visibleRect.maxX() && exposeRect.width() > visibleRect.width())) {
-            scrollX = ScrollAlignmentRight;
-        }
+  if (scrollX == ScrollAlignmentClosestEdge) {
+    // Closest edge is the right in two cases:
+    // (1) exposeRect to the right of and smaller than visibleRect
+    // (2) exposeRect to the left of and larger than visibleRect
+    if ((exposeRect.maxX() > visibleRect.maxX() &&
+         exposeRect.width() < visibleRect.width()) ||
+        (exposeRect.maxX() < visibleRect.maxX() &&
+         exposeRect.width() > visibleRect.width())) {
+      scrollX = ScrollAlignmentRight;
     }
+  }
 
-    // Given the X behavior, compute the X coordinate.
-    LayoutUnit x;
-    if (scrollX == ScrollAlignmentNoScroll)
-        x = visibleRect.x();
-    else if (scrollX == ScrollAlignmentRight)
-        x = exposeRect.maxX() - visibleRect.width();
-    else if (scrollX == ScrollAlignmentCenter)
-        x = exposeRect.x() + (exposeRect.width() - visibleRect.width()) / 2;
-    else
-        x = exposeRect.x();
+  // Given the X behavior, compute the X coordinate.
+  LayoutUnit x;
+  if (scrollX == ScrollAlignmentNoScroll)
+    x = visibleRect.x();
+  else if (scrollX == ScrollAlignmentRight)
+    x = exposeRect.maxX() - visibleRect.width();
+  else if (scrollX == ScrollAlignmentCenter)
+    x = exposeRect.x() + (exposeRect.width() - visibleRect.width()) / 2;
+  else
+    x = exposeRect.x();
 
-    // Determine the appropriate Y behavior.
-    ScrollAlignmentBehavior scrollY;
-    LayoutRect exposeRectY(visibleRect.x(), exposeRect.y(), visibleRect.width(), exposeRect.height());
-    LayoutUnit intersectHeight = intersection(visibleRect, exposeRectY).height();
-    if (intersectHeight == exposeRect.height()) {
-        // If the rectangle is fully visible, use the specified visible behavior.
-        scrollY = getVisibleBehavior(alignY);
-    } else if (intersectHeight == visibleRect.height()) {
-        // If the rect is bigger than the visible area, don't bother trying to center. Other alignments will work.
-        scrollY = getVisibleBehavior(alignY);
-        if (scrollY == ScrollAlignmentCenter)
-            scrollY = ScrollAlignmentNoScroll;
-    } else if (intersectHeight > 0) {
-        // If the rectangle is partially visible, use the specified partial behavior
-        scrollY = getPartialBehavior(alignY);
-    } else {
-        scrollY = getHiddenBehavior(alignY);
-    }
+  // Determine the appropriate Y behavior.
+  ScrollAlignmentBehavior scrollY;
+  LayoutRect exposeRectY(visibleRect.x(), exposeRect.y(), visibleRect.width(),
+                         exposeRect.height());
+  LayoutUnit intersectHeight = intersection(visibleRect, exposeRectY).height();
+  if (intersectHeight == exposeRect.height()) {
+    // If the rectangle is fully visible, use the specified visible behavior.
+    scrollY = getVisibleBehavior(alignY);
+  } else if (intersectHeight == visibleRect.height()) {
+    // If the rect is bigger than the visible area, don't bother trying to
+    // center. Other alignments will work.
+    scrollY = getVisibleBehavior(alignY);
+    if (scrollY == ScrollAlignmentCenter)
+      scrollY = ScrollAlignmentNoScroll;
+  } else if (intersectHeight > 0) {
+    // If the rectangle is partially visible, use the specified partial behavior
+    scrollY = getPartialBehavior(alignY);
+  } else {
+    scrollY = getHiddenBehavior(alignY);
+  }
 
-    if (scrollY == ScrollAlignmentClosestEdge) {
-        // Closest edge is the bottom in two cases:
-        // (1) exposeRect below and smaller than visibleRect
-        // (2) exposeRect above and larger than visibleRect
-        if ((exposeRect.maxY() > visibleRect.maxY() && exposeRect.height() < visibleRect.height())
-            || (exposeRect.maxY() < visibleRect.maxY() && exposeRect.height() > visibleRect.height())) {
-            scrollY = ScrollAlignmentBottom;
-        }
+  if (scrollY == ScrollAlignmentClosestEdge) {
+    // Closest edge is the bottom in two cases:
+    // (1) exposeRect below and smaller than visibleRect
+    // (2) exposeRect above and larger than visibleRect
+    if ((exposeRect.maxY() > visibleRect.maxY() &&
+         exposeRect.height() < visibleRect.height()) ||
+        (exposeRect.maxY() < visibleRect.maxY() &&
+         exposeRect.height() > visibleRect.height())) {
+      scrollY = ScrollAlignmentBottom;
     }
+  }
 
-    // Given the Y behavior, compute the Y coordinate.
-    LayoutUnit y;
-    if (scrollY == ScrollAlignmentNoScroll)
-        y = visibleRect.y();
-    else if (scrollY == ScrollAlignmentBottom)
-        y = exposeRect.maxY() - visibleRect.height();
-    else if (scrollY == ScrollAlignmentCenter)
-        y = exposeRect.y() + (exposeRect.height() - visibleRect.height()) / 2;
-    else
-        y = exposeRect.y();
+  // Given the Y behavior, compute the Y coordinate.
+  LayoutUnit y;
+  if (scrollY == ScrollAlignmentNoScroll)
+    y = visibleRect.y();
+  else if (scrollY == ScrollAlignmentBottom)
+    y = exposeRect.maxY() - visibleRect.height();
+  else if (scrollY == ScrollAlignmentCenter)
+    y = exposeRect.y() + (exposeRect.height() - visibleRect.height()) / 2;
+  else
+    y = exposeRect.y();
 
-    return LayoutRect(LayoutPoint(x, y), visibleRect.size());
+  return LayoutRect(LayoutPoint(x, y), visibleRect.size());
 }
 
-}; // namespace blink
+};  // namespace blink
diff --git a/sky/engine/core/rendering/ScrollAlignment.h b/sky/engine/core/rendering/ScrollAlignment.h
index 0ac6cc45cef66..0a6cf32b53c31 100644
--- a/sky/engine/core/rendering/ScrollAlignment.h
+++ b/sky/engine/core/rendering/ScrollAlignment.h
@@ -24,7 +24,7 @@
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
  *
  * Alternatively, the contents of this file may be used under the terms
  * of either the Mozilla Public License Version 1.1, found at
@@ -47,36 +47,44 @@
 namespace blink {
 
 enum ScrollAlignmentBehavior {
-    ScrollAlignmentNoScroll,
-    ScrollAlignmentCenter,
-    ScrollAlignmentTop,
-    ScrollAlignmentBottom,
-    ScrollAlignmentLeft,
-    ScrollAlignmentRight,
-    ScrollAlignmentClosestEdge
+  ScrollAlignmentNoScroll,
+  ScrollAlignmentCenter,
+  ScrollAlignmentTop,
+  ScrollAlignmentBottom,
+  ScrollAlignmentLeft,
+  ScrollAlignmentRight,
+  ScrollAlignmentClosestEdge
 };
 
 class LayoutRect;
 
 struct ScrollAlignment {
-    static ScrollAlignmentBehavior getVisibleBehavior(const ScrollAlignment& s) { return s.m_rectVisible; }
-    static ScrollAlignmentBehavior getPartialBehavior(const ScrollAlignment& s) { return s.m_rectPartial; }
-    static ScrollAlignmentBehavior getHiddenBehavior(const ScrollAlignment& s) { return s.m_rectHidden; }
+  static ScrollAlignmentBehavior getVisibleBehavior(const ScrollAlignment& s) {
+    return s.m_rectVisible;
+  }
+  static ScrollAlignmentBehavior getPartialBehavior(const ScrollAlignment& s) {
+    return s.m_rectPartial;
+  }
+  static ScrollAlignmentBehavior getHiddenBehavior(const ScrollAlignment& s) {
+    return s.m_rectHidden;
+  }
 
-    // FIXME: This function should probably go somewhere else but where?
-    static LayoutRect getRectToExpose(const LayoutRect& visibleRect, const LayoutRect& exposeRect, const ScrollAlignment& alignX, const ScrollAlignment& alignY);
+  // FIXME: This function should probably go somewhere else but where?
+  static LayoutRect getRectToExpose(const LayoutRect& visibleRect,
+                                    const LayoutRect& exposeRect,
+                                    const ScrollAlignment& alignX,
+                                    const ScrollAlignment& alignY);
 
-    static const ScrollAlignment alignCenterIfNeeded;
-    static const ScrollAlignment alignToEdgeIfNeeded;
-    static const ScrollAlignment alignCenterAlways;
-    static const ScrollAlignment alignTopAlways;
+  static const ScrollAlignment alignCenterIfNeeded;
+  static const ScrollAlignment alignToEdgeIfNeeded;
+  static const ScrollAlignment alignCenterAlways;
+  static const ScrollAlignment alignTopAlways;
 
-    ScrollAlignmentBehavior m_rectVisible;
-    ScrollAlignmentBehavior m_rectHidden;
-    ScrollAlignmentBehavior m_rectPartial;
+  ScrollAlignmentBehavior m_rectVisible;
+  ScrollAlignmentBehavior m_rectHidden;
+  ScrollAlignmentBehavior m_rectPartial;
 };
 
-
-}; // namespace blink
+};  // namespace blink
 
 #endif  // SKY_ENGINE_CORE_RENDERING_SCROLLALIGNMENT_H_
diff --git a/sky/engine/core/rendering/SubtreeLayoutScope.cpp b/sky/engine/core/rendering/SubtreeLayoutScope.cpp
index d1940a50942c8..6700b7603c1d3 100644
--- a/sky/engine/core/rendering/SubtreeLayoutScope.cpp
+++ b/sky/engine/core/rendering/SubtreeLayoutScope.cpp
@@ -34,38 +34,32 @@
 
 namespace blink {
 
-SubtreeLayoutScope::SubtreeLayoutScope(RenderObject& root)
-    : m_root(root)
-{
-}
+SubtreeLayoutScope::SubtreeLayoutScope(RenderObject& root) : m_root(root) {}
 
-SubtreeLayoutScope::~SubtreeLayoutScope()
-{
-    RELEASE_ASSERT(!m_root.needsLayout());
+SubtreeLayoutScope::~SubtreeLayoutScope() {
+  RELEASE_ASSERT(!m_root.needsLayout());
 
 #if ENABLE(ASSERT)
-    for (HashSet::iterator it = m_renderersToLayout.begin(); it != m_renderersToLayout.end(); ++it)
-        (*it)->assertRendererLaidOut();
+  for (HashSet::iterator it = m_renderersToLayout.begin();
+       it != m_renderersToLayout.end(); ++it)
+    (*it)->assertRendererLaidOut();
 #endif
 }
 
-void SubtreeLayoutScope::setNeedsLayout(RenderObject* descendant)
-{
-    ASSERT(descendant->isDescendantOf(&m_root));
-    descendant->setNeedsLayout(MarkContainingBlockChain, this);
+void SubtreeLayoutScope::setNeedsLayout(RenderObject* descendant) {
+  ASSERT(descendant->isDescendantOf(&m_root));
+  descendant->setNeedsLayout(MarkContainingBlockChain, this);
 }
 
-void SubtreeLayoutScope::setChildNeedsLayout(RenderObject* descendant)
-{
-    ASSERT(descendant->isDescendantOf(&m_root));
-    descendant->setChildNeedsLayout(MarkContainingBlockChain, this);
+void SubtreeLayoutScope::setChildNeedsLayout(RenderObject* descendant) {
+  ASSERT(descendant->isDescendantOf(&m_root));
+  descendant->setChildNeedsLayout(MarkContainingBlockChain, this);
 }
 
-void SubtreeLayoutScope::addRendererToLayout(RenderObject* renderer)
-{
+void SubtreeLayoutScope::addRendererToLayout(RenderObject* renderer) {
 #if ENABLE(ASSERT)
-    m_renderersToLayout.add(renderer);
+  m_renderersToLayout.add(renderer);
 #endif
 }
 
-}
+}  // namespace blink
diff --git a/sky/engine/core/rendering/SubtreeLayoutScope.h b/sky/engine/core/rendering/SubtreeLayoutScope.h
index 7f9446589225d..04eb4e34c1372 100644
--- a/sky/engine/core/rendering/SubtreeLayoutScope.h
+++ b/sky/engine/core/rendering/SubtreeLayoutScope.h
@@ -47,24 +47,24 @@ namespace blink {
 class RenderObject;
 
 class SubtreeLayoutScope {
-public:
-    SubtreeLayoutScope(RenderObject& root);
-    ~SubtreeLayoutScope();
+ public:
+  SubtreeLayoutScope(RenderObject& root);
+  ~SubtreeLayoutScope();
 
-    void setNeedsLayout(RenderObject* descendant);
-    void setChildNeedsLayout(RenderObject* descendant);
+  void setNeedsLayout(RenderObject* descendant);
+  void setChildNeedsLayout(RenderObject* descendant);
 
-    RenderObject& root() { return m_root; }
-    void addRendererToLayout(RenderObject* renderer);
+  RenderObject& root() { return m_root; }
+  void addRendererToLayout(RenderObject* renderer);
 
-private:
-    RenderObject& m_root;
+ private:
+  RenderObject& m_root;
 
 #if ENABLE(ASSERT)
-    HashSet m_renderersToLayout;
+  HashSet m_renderersToLayout;
 #endif
 };
 
-}
+}  // namespace blink
 
 #endif  // SKY_ENGINE_CORE_RENDERING_SUBTREELAYOUTSCOPE_H_
diff --git a/sky/engine/core/rendering/TextRunConstructor.cpp b/sky/engine/core/rendering/TextRunConstructor.cpp
index b8cff5a76d75b..aca2b1bedaaed 100644
--- a/sky/engine/core/rendering/TextRunConstructor.cpp
+++ b/sky/engine/core/rendering/TextRunConstructor.cpp
@@ -37,81 +37,148 @@
 namespace blink {
 
 template 
-static inline TextRun constructTextRunInternal(RenderObject* context, const Font& font, const CharacterType* characters, int length, RenderStyle* style, TextDirection direction, TextRun::ExpansionBehavior expansion)
-{
-    ASSERT(style);
-
-    bool directionalOverride = style->rtlOrdering() == VisualOrder;
-    return TextRun(characters, length, 0, 0, expansion, direction, directionalOverride);
+static inline TextRun constructTextRunInternal(
+    RenderObject* context,
+    const Font& font,
+    const CharacterType* characters,
+    int length,
+    RenderStyle* style,
+    TextDirection direction,
+    TextRun::ExpansionBehavior expansion) {
+  ASSERT(style);
+
+  bool directionalOverride = style->rtlOrdering() == VisualOrder;
+  return TextRun(characters, length, 0, 0, expansion, direction,
+                 directionalOverride);
 }
 
 template 
-static inline TextRun constructTextRunInternal(RenderObject* context, const Font& font, const CharacterType* characters, int length, RenderStyle* style, TextDirection direction, TextRun::ExpansionBehavior expansion, TextRunFlags flags)
-{
-    ASSERT(style);
-
-    TextDirection textDirection = direction;
-    bool directionalOverride = style->rtlOrdering() == VisualOrder;
-    if (flags != DefaultTextRunFlags) {
-        if (flags & RespectDirection)
-            textDirection = style->direction();
-        if (flags & RespectDirectionOverride)
-            directionalOverride |= isOverride(style->unicodeBidi());
-    }
-
-    return TextRun(characters, length, 0, 0, expansion, textDirection, directionalOverride);
+static inline TextRun constructTextRunInternal(
+    RenderObject* context,
+    const Font& font,
+    const CharacterType* characters,
+    int length,
+    RenderStyle* style,
+    TextDirection direction,
+    TextRun::ExpansionBehavior expansion,
+    TextRunFlags flags) {
+  ASSERT(style);
+
+  TextDirection textDirection = direction;
+  bool directionalOverride = style->rtlOrdering() == VisualOrder;
+  if (flags != DefaultTextRunFlags) {
+    if (flags & RespectDirection)
+      textDirection = style->direction();
+    if (flags & RespectDirectionOverride)
+      directionalOverride |= isOverride(style->unicodeBidi());
+  }
+
+  return TextRun(characters, length, 0, 0, expansion, textDirection,
+                 directionalOverride);
 }
 
-TextRun constructTextRun(RenderObject* context, const Font& font, const LChar* characters, int length, RenderStyle* style, TextDirection direction, TextRun::ExpansionBehavior expansion)
-{
-    return constructTextRunInternal(context, font, characters, length, style, direction, expansion);
+TextRun constructTextRun(RenderObject* context,
+                         const Font& font,
+                         const LChar* characters,
+                         int length,
+                         RenderStyle* style,
+                         TextDirection direction,
+                         TextRun::ExpansionBehavior expansion) {
+  return constructTextRunInternal(context, font, characters, length, style,
+                                  direction, expansion);
 }
 
-TextRun constructTextRun(RenderObject* context, const Font& font, const UChar* characters, int length, RenderStyle* style, TextDirection direction, TextRun::ExpansionBehavior expansion)
-{
-    return constructTextRunInternal(context, font, characters, length, style, direction, expansion);
+TextRun constructTextRun(RenderObject* context,
+                         const Font& font,
+                         const UChar* characters,
+                         int length,
+                         RenderStyle* style,
+                         TextDirection direction,
+                         TextRun::ExpansionBehavior expansion) {
+  return constructTextRunInternal(context, font, characters, length, style,
+                                  direction, expansion);
 }
 
-TextRun constructTextRun(RenderObject* context, const Font& font, const RenderText* text, RenderStyle* style, TextDirection direction, TextRun::ExpansionBehavior expansion)
-{
-    if (text->is8Bit())
-        return constructTextRunInternal(context, font, text->characters8(), text->textLength(), style, direction, expansion);
-    return constructTextRunInternal(context, font, text->characters16(), text->textLength(), style, direction, expansion);
+TextRun constructTextRun(RenderObject* context,
+                         const Font& font,
+                         const RenderText* text,
+                         RenderStyle* style,
+                         TextDirection direction,
+                         TextRun::ExpansionBehavior expansion) {
+  if (text->is8Bit())
+    return constructTextRunInternal(context, font, text->characters8(),
+                                    text->textLength(), style, direction,
+                                    expansion);
+  return constructTextRunInternal(context, font, text->characters16(),
+                                  text->textLength(), style, direction,
+                                  expansion);
 }
 
-TextRun constructTextRun(RenderObject* context, const Font& font, const RenderText* text, unsigned offset, unsigned length, RenderStyle* style, TextDirection direction, TextRun::ExpansionBehavior expansion)
-{
-    ASSERT(offset + length <= text->textLength());
-    if (text->is8Bit())
-        return constructTextRunInternal(context, font, text->characters8() + offset, length, style, direction, expansion);
-    return constructTextRunInternal(context, font, text->characters16() + offset, length, style, direction, expansion);
+TextRun constructTextRun(RenderObject* context,
+                         const Font& font,
+                         const RenderText* text,
+                         unsigned offset,
+                         unsigned length,
+                         RenderStyle* style,
+                         TextDirection direction,
+                         TextRun::ExpansionBehavior expansion) {
+  ASSERT(offset + length <= text->textLength());
+  if (text->is8Bit())
+    return constructTextRunInternal(context, font, text->characters8() + offset,
+                                    length, style, direction, expansion);
+  return constructTextRunInternal(context, font, text->characters16() + offset,
+                                  length, style, direction, expansion);
 }
 
-TextRun constructTextRun(RenderObject* context, const Font& font, const String& string, RenderStyle* style, TextDirection direction, TextRun::ExpansionBehavior expansion, TextRunFlags flags)
-{
-    unsigned length = string.length();
-    if (!length)
-        return constructTextRunInternal(context, font, static_cast(0), length, style, direction, expansion, flags);
-    if (string.is8Bit())
-        return constructTextRunInternal(context, font, string.characters8(), length, style, direction, expansion, flags);
-    return constructTextRunInternal(context, font, string.characters16(), length, style, direction, expansion, flags);
+TextRun constructTextRun(RenderObject* context,
+                         const Font& font,
+                         const String& string,
+                         RenderStyle* style,
+                         TextDirection direction,
+                         TextRun::ExpansionBehavior expansion,
+                         TextRunFlags flags) {
+  unsigned length = string.length();
+  if (!length)
+    return constructTextRunInternal(context, font, static_cast(0),
+                                    length, style, direction, expansion, flags);
+  if (string.is8Bit())
+    return constructTextRunInternal(context, font, string.characters8(), length,
+                                    style, direction, expansion, flags);
+  return constructTextRunInternal(context, font, string.characters16(), length,
+                                  style, direction, expansion, flags);
 }
 
-TextRun constructTextRun(RenderObject* context, const Font& font, const String& string, RenderStyle* style, TextRun::ExpansionBehavior expansion, TextRunFlags flags)
-{
-    bool hasStrongDirectionality;
-    return constructTextRun(context, font, string, style, determineDirectionality(string, hasStrongDirectionality), expansion, flags);
+TextRun constructTextRun(RenderObject* context,
+                         const Font& font,
+                         const String& string,
+                         RenderStyle* style,
+                         TextRun::ExpansionBehavior expansion,
+                         TextRunFlags flags) {
+  bool hasStrongDirectionality;
+  return constructTextRun(
+      context, font, string, style,
+      determineDirectionality(string, hasStrongDirectionality), expansion,
+      flags);
 }
 
-TextRun constructTextRun(RenderObject* context, const Font& font, const RenderText* text, unsigned offset, unsigned length, RenderStyle* style, TextRun::ExpansionBehavior expansion)
-{
-    ASSERT(offset + length <= text->textLength());
-    TextRun run = text->is8Bit()
-        ? constructTextRunInternal(context, font, text->characters8() + offset, length, style, LTR, expansion)
-        : constructTextRunInternal(context, font, text->characters16() + offset, length, style, LTR, expansion);
-    bool hasStrongDirectionality;
-    run.setDirection(directionForRun(run, hasStrongDirectionality));
-    return run;
+TextRun constructTextRun(RenderObject* context,
+                         const Font& font,
+                         const RenderText* text,
+                         unsigned offset,
+                         unsigned length,
+                         RenderStyle* style,
+                         TextRun::ExpansionBehavior expansion) {
+  ASSERT(offset + length <= text->textLength());
+  TextRun run = text->is8Bit()
+                    ? constructTextRunInternal(context, font,
+                                               text->characters8() + offset,
+                                               length, style, LTR, expansion)
+                    : constructTextRunInternal(context, font,
+                                               text->characters16() + offset,
+                                               length, style, LTR, expansion);
+  bool hasStrongDirectionality;
+  run.setDirection(directionForRun(run, hasStrongDirectionality));
+  return run;
 }
 
-} // namespace blink
+}  // namespace blink
diff --git a/sky/engine/core/rendering/TextRunConstructor.h b/sky/engine/core/rendering/TextRunConstructor.h
index 9eb99a12882a1..87881cca71a80 100644
--- a/sky/engine/core/rendering/TextRunConstructor.h
+++ b/sky/engine/core/rendering/TextRunConstructor.h
@@ -49,38 +49,92 @@ class RenderStyle;
 class RenderText;
 
 enum TextRunFlag {
-    DefaultTextRunFlags = 0,
-    RespectDirection = 1 << 0,
-    RespectDirectionOverride = 1 << 1
+  DefaultTextRunFlags = 0,
+  RespectDirection = 1 << 0,
+  RespectDirectionOverride = 1 << 1
 };
 
 typedef unsigned TextRunFlags;
 
 // Direction resolved from string value.
-TextRun constructTextRun(RenderObject* context, const Font&, const String&, RenderStyle*,
-    TextRun::ExpansionBehavior = TextRun::AllowTrailingExpansion | TextRun::ForbidLeadingExpansion, TextRunFlags = DefaultTextRunFlags);
-TextRun constructTextRun(RenderObject* context, const Font&, const RenderText*, unsigned offset, unsigned length, RenderStyle*,
-    TextRun::ExpansionBehavior = TextRun::AllowTrailingExpansion | TextRun::ForbidLeadingExpansion);
+TextRun constructTextRun(
+    RenderObject* context,
+    const Font&,
+    const String&,
+    RenderStyle*,
+    TextRun::ExpansionBehavior = TextRun::AllowTrailingExpansion |
+                                 TextRun::ForbidLeadingExpansion,
+    TextRunFlags = DefaultTextRunFlags);
+TextRun constructTextRun(
+    RenderObject* context,
+    const Font&,
+    const RenderText*,
+    unsigned offset,
+    unsigned length,
+    RenderStyle*,
+    TextRun::ExpansionBehavior = TextRun::AllowTrailingExpansion |
+                                 TextRun::ForbidLeadingExpansion);
 
 // Explicit direction.
-TextRun constructTextRun(RenderObject* context, const Font&, const String&, RenderStyle*, TextDirection,
-    TextRun::ExpansionBehavior = TextRun::AllowTrailingExpansion | TextRun::ForbidLeadingExpansion, TextRunFlags = DefaultTextRunFlags);
+TextRun constructTextRun(
+    RenderObject* context,
+    const Font&,
+    const String&,
+    RenderStyle*,
+    TextDirection,
+    TextRun::ExpansionBehavior = TextRun::AllowTrailingExpansion |
+                                 TextRun::ForbidLeadingExpansion,
+    TextRunFlags = DefaultTextRunFlags);
 
-TextRun constructTextRun(RenderObject* context, const Font&, const RenderText*, RenderStyle*, TextDirection,
-    TextRun::ExpansionBehavior = TextRun::AllowTrailingExpansion | TextRun::ForbidLeadingExpansion);
+TextRun constructTextRun(
+    RenderObject* context,
+    const Font&,
+    const RenderText*,
+    RenderStyle*,
+    TextDirection,
+    TextRun::ExpansionBehavior = TextRun::AllowTrailingExpansion |
+                                 TextRun::ForbidLeadingExpansion);
 
-TextRun constructTextRun(RenderObject* context, const Font&, const RenderText*, unsigned offset, unsigned length, RenderStyle*, TextDirection,
-    TextRun::ExpansionBehavior = TextRun::AllowTrailingExpansion | TextRun::ForbidLeadingExpansion);
+TextRun constructTextRun(
+    RenderObject* context,
+    const Font&,
+    const RenderText*,
+    unsigned offset,
+    unsigned length,
+    RenderStyle*,
+    TextDirection,
+    TextRun::ExpansionBehavior = TextRun::AllowTrailingExpansion |
+                                 TextRun::ForbidLeadingExpansion);
 
-TextRun constructTextRun(RenderObject* context, const Font&, const RenderText*, unsigned offset, RenderStyle*,
-    TextRun::ExpansionBehavior = TextRun::AllowTrailingExpansion | TextRun::ForbidLeadingExpansion);
+TextRun constructTextRun(
+    RenderObject* context,
+    const Font&,
+    const RenderText*,
+    unsigned offset,
+    RenderStyle*,
+    TextRun::ExpansionBehavior = TextRun::AllowTrailingExpansion |
+                                 TextRun::ForbidLeadingExpansion);
 
-TextRun constructTextRun(RenderObject* context, const Font&, const LChar* characters, int length, RenderStyle*, TextDirection,
-    TextRun::ExpansionBehavior = TextRun::AllowTrailingExpansion | TextRun::ForbidLeadingExpansion);
+TextRun constructTextRun(
+    RenderObject* context,
+    const Font&,
+    const LChar* characters,
+    int length,
+    RenderStyle*,
+    TextDirection,
+    TextRun::ExpansionBehavior = TextRun::AllowTrailingExpansion |
+                                 TextRun::ForbidLeadingExpansion);
 
-TextRun constructTextRun(RenderObject* context, const Font&, const UChar* characters, int length, RenderStyle*, TextDirection,
-    TextRun::ExpansionBehavior = TextRun::AllowTrailingExpansion | TextRun::ForbidLeadingExpansion);
+TextRun constructTextRun(
+    RenderObject* context,
+    const Font&,
+    const UChar* characters,
+    int length,
+    RenderStyle*,
+    TextDirection,
+    TextRun::ExpansionBehavior = TextRun::AllowTrailingExpansion |
+                                 TextRun::ForbidLeadingExpansion);
 
-} // namespace blink
+}  // namespace blink
 
 #endif  // SKY_ENGINE_CORE_RENDERING_TEXTRUNCONSTRUCTOR_H_
diff --git a/sky/engine/core/rendering/VerticalPositionCache.h b/sky/engine/core/rendering/VerticalPositionCache.h
index 4ca85229b8f45..ad42b6c6a7f3c 100644
--- a/sky/engine/core/rendering/VerticalPositionCache.h
+++ b/sky/engine/core/rendering/VerticalPositionCache.h
@@ -37,33 +37,34 @@ class RenderObject;
 const int PositionUndefined = 0x80000000;
 
 class VerticalPositionCache {
-    WTF_MAKE_NONCOPYABLE(VerticalPositionCache);
-public:
-    VerticalPositionCache()
-    { }
+  WTF_MAKE_NONCOPYABLE(VerticalPositionCache);
 
-    int get(RenderObject* renderer, FontBaseline baselineType) const
-    {
-        const HashMap& mapToCheck = baselineType == AlphabeticBaseline ? m_alphabeticPositions : m_ideographicPositions;
-        const HashMap::const_iterator it = mapToCheck.find(renderer);
-        if (it == mapToCheck.end())
-            return PositionUndefined;
-        return it->value;
-    }
+ public:
+  VerticalPositionCache() {}
 
-    void set(RenderObject* renderer, FontBaseline baselineType, int position)
-    {
-        if (baselineType == AlphabeticBaseline)
-            m_alphabeticPositions.set(renderer, position);
-        else
-            m_ideographicPositions.set(renderer, position);
-    }
+  int get(RenderObject* renderer, FontBaseline baselineType) const {
+    const HashMap& mapToCheck =
+        baselineType == AlphabeticBaseline ? m_alphabeticPositions
+                                           : m_ideographicPositions;
+    const HashMap::const_iterator it =
+        mapToCheck.find(renderer);
+    if (it == mapToCheck.end())
+      return PositionUndefined;
+    return it->value;
+  }
 
-private:
-    HashMap m_alphabeticPositions;
-    HashMap m_ideographicPositions;
+  void set(RenderObject* renderer, FontBaseline baselineType, int position) {
+    if (baselineType == AlphabeticBaseline)
+      m_alphabeticPositions.set(renderer, position);
+    else
+      m_ideographicPositions.set(renderer, position);
+  }
+
+ private:
+  HashMap m_alphabeticPositions;
+  HashMap m_ideographicPositions;
 };
 
-} // namespace blink
+}  // namespace blink
 
 #endif  // SKY_ENGINE_CORE_RENDERING_VERTICALPOSITIONCACHE_H_
diff --git a/sky/engine/core/rendering/break_lines.cpp b/sky/engine/core/rendering/break_lines.cpp
index 7bb60fe63404a..5ae4acf79c635 100644
--- a/sky/engine/core/rendering/break_lines.cpp
+++ b/sky/engine/core/rendering/break_lines.cpp
@@ -33,79 +33,213 @@
 
 namespace blink {
 
-template
-static inline bool isBreakableSpace(UChar ch)
-{
-    switch (ch) {
+template 
+static inline bool isBreakableSpace(UChar ch) {
+  switch (ch) {
     case ' ':
     case '\n':
     case '\t':
-        return true;
+      return true;
     case noBreakSpace:
-        return treatNoBreakSpaceAsBreak;
+      return treatNoBreakSpaceAsBreak;
     default:
-        return false;
-    }
+      return false;
+  }
 }
 
 static const UChar asciiLineBreakTableFirstChar = '!';
 static const UChar asciiLineBreakTableLastChar = 127;
 
 // Pack 8 bits into one byte
-#define B(a, b, c, d, e, f, g, h) \
-    ((a) | ((b) << 1) | ((c) << 2) | ((d) << 3) | ((e) << 4) | ((f) << 5) | ((g) << 6) | ((h) << 7))
+#define B(a, b, c, d, e, f, g, h)                                         \
+  ((a) | ((b) << 1) | ((c) << 2) | ((d) << 3) | ((e) << 4) | ((f) << 5) | \
+   ((g) << 6) | ((h) << 7))
 
 // Line breaking table row for each digit (0-9)
-#define DI { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
+#define DI \
+  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
 
 // Line breaking table row for ascii letters (a-z A-Z)
-#define AL { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
+#define AL \
+  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
 
 #define F 0xFF
 
-// Line breaking table for printable ASCII characters. Line breaking opportunities in this table are as below:
-// - before opening punctuations such as '(', '<', '[', '{' after certain characters (compatible with Firefox 3.6);
-// - after '-' and '?' (backward-compatible, and compatible with Internet Explorer).
-// Please refer to  for line breaking matrixes of different browsers
-// and the ICU standard.
-static const unsigned char asciiLineBreakTable[][(asciiLineBreakTableLastChar - asciiLineBreakTableFirstChar) / 8 + 1] = {
-    //  !  "  #  $  %  &  '  (     )  *  +  ,  -  .  /  0  1-8   9  :  ;  <  =  >  ?  @     A-X      Y  Z  [  \  ]  ^  _  `     a-x      y  z  {  |  }  ~  DEL
-    { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // !
-    { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // "
-    { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // #
-    { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0) }, // $
-    { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // %
-    { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // &
-    { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0) }, // '
-    { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0) }, // (
-    { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // )
-    { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // *
-    { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // +
-    { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // ,
-    { B(1, 1, 1, 1, 1, 1, 1, 1), B(1, 1, 1, 1, 1, 0, 1, 0), 0, B(0, 1, 1, 1, 1, 1, 1, 1), F, F, F, B(1, 1, 1, 1, 1, 1, 1, 1), F, F, F, B(1, 1, 1, 1, 1, 1, 1, 1) }, // - Note: breaking before '0'-'9' is handled hard-coded in shouldBreakAfter().
-    { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // .
-    { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0) }, // /
-    DI,  DI,  DI,  DI,  DI,  DI,  DI,  DI,  DI,  DI, // 0-9
-    { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // :
-    { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // ;
-    { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0) }, // <
-    { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // =
-    { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // >
-    { B(0, 0, 1, 1, 1, 1, 0, 1), B(0, 1, 1, 0, 1, 0, 0, 1), F, B(1, 0, 0, 1, 1, 1, 0, 1), F, F, F, B(1, 1, 1, 1, 0, 1, 1, 1), F, F, F, B(1, 1, 1, 1, 0, 1, 1, 0) }, // ?
-    { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0) }, // @
-    AL,  AL,  AL,  AL,  AL,  AL,  AL,  AL,  AL,  AL,  AL,  AL,  AL,  AL,  AL,  AL,  AL,  AL,  AL,  AL,  AL,  AL,  AL,  AL,  AL,  AL, // A-Z
-    { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0) }, // [
-    { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // '\'
-    { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // ]
-    { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0) }, // ^
-    { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0) }, // _
-    { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0) }, // `
-    AL,  AL,  AL,  AL,  AL,  AL,  AL,  AL,  AL,  AL,  AL,  AL,  AL,  AL,  AL,  AL,  AL,  AL,  AL,  AL,  AL,  AL,  AL,  AL,  AL,  AL, // a-z
-    { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0) }, // {
-    { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // |
-    { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // }
-    { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // ~
-    { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0) }, // DEL
+// Line breaking table for printable ASCII characters. Line breaking
+// opportunities in this table are as below:
+// - before opening punctuations such as '(', '<', '[', '{' after certain
+// characters (compatible with Firefox 3.6);
+// - after '-' and '?' (backward-compatible, and compatible with Internet
+// Explorer). Please refer to 
+// for line breaking matrixes of different browsers and the ICU standard.
+static const unsigned char asciiLineBreakTable
+    [][(asciiLineBreakTableLastChar - asciiLineBreakTableFirstChar) / 8 + 1] = {
+        //  !  "  #  $  %  &  '  (     )  *  +  ,  -  .  /  0  1-8   9  :  ;  <
+        //  =  >  ?  @     A-X      Y  Z  [  \  ]  ^  _  `     a-x      y  z  {
+        //  |  }  ~  DEL
+        {B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0,
+         B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0,
+         B(0, 0, 1, 0, 0, 0, 0, 0)},  // !
+        {B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0,
+         B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0,
+         B(0, 0, 1, 0, 0, 0, 0, 0)},  // "
+        {B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0,
+         B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0,
+         B(0, 0, 1, 0, 0, 0, 0, 0)},  // #
+        {B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), 0,
+         B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0,
+         B(0, 0, 0, 0, 0, 0, 0, 0)},  // $
+        {B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0,
+         B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0,
+         B(0, 0, 1, 0, 0, 0, 0, 0)},  // %
+        {B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0,
+         B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0,
+         B(0, 0, 1, 0, 0, 0, 0, 0)},  // &
+        {B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), 0,
+         B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0,
+         B(0, 0, 0, 0, 0, 0, 0, 0)},  // '
+        {B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), 0,
+         B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0,
+         B(0, 0, 0, 0, 0, 0, 0, 0)},  // (
+        {B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0,
+         B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0,
+         B(0, 0, 1, 0, 0, 0, 0, 0)},  // )
+        {B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0,
+         B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0,
+         B(0, 0, 1, 0, 0, 0, 0, 0)},  // *
+        {B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0,
+         B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0,
+         B(0, 0, 1, 0, 0, 0, 0, 0)},  // +
+        {B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0,
+         B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0,
+         B(0, 0, 1, 0, 0, 0, 0, 0)},  // ,
+        {B(1, 1, 1, 1, 1, 1, 1, 1), B(1, 1, 1, 1, 1, 0, 1, 0), 0,
+         B(0, 1, 1, 1, 1, 1, 1, 1), F, F, F, B(1, 1, 1, 1, 1, 1, 1, 1), F, F, F,
+         B(1, 1, 1, 1, 1, 1, 1, 1)},  // - Note: breaking before '0'-'9' is
+                                      // handled hard-coded in
+                                      // shouldBreakAfter().
+        {B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0,
+         B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0,
+         B(0, 0, 1, 0, 0, 0, 0, 0)},  // .
+        {B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), 0,
+         B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0,
+         B(0, 0, 0, 0, 0, 0, 0, 0)},  // /
+        DI,
+        DI,
+        DI,
+        DI,
+        DI,
+        DI,
+        DI,
+        DI,
+        DI,
+        DI,  // 0-9
+        {B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0,
+         B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0,
+         B(0, 0, 1, 0, 0, 0, 0, 0)},  // :
+        {B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0,
+         B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0,
+         B(0, 0, 1, 0, 0, 0, 0, 0)},  // ;
+        {B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), 0,
+         B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0,
+         B(0, 0, 0, 0, 0, 0, 0, 0)},  // <
+        {B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0,
+         B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0,
+         B(0, 0, 1, 0, 0, 0, 0, 0)},  // =
+        {B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0,
+         B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0,
+         B(0, 0, 1, 0, 0, 0, 0, 0)},  // >
+        {B(0, 0, 1, 1, 1, 1, 0, 1), B(0, 1, 1, 0, 1, 0, 0, 1), F,
+         B(1, 0, 0, 1, 1, 1, 0, 1), F, F, F, B(1, 1, 1, 1, 0, 1, 1, 1), F, F, F,
+         B(1, 1, 1, 1, 0, 1, 1, 0)},  // ?
+        {B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), 0,
+         B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0,
+         B(0, 0, 0, 0, 0, 0, 0, 0)},  // @
+        AL,
+        AL,
+        AL,
+        AL,
+        AL,
+        AL,
+        AL,
+        AL,
+        AL,
+        AL,
+        AL,
+        AL,
+        AL,
+        AL,
+        AL,
+        AL,
+        AL,
+        AL,
+        AL,
+        AL,
+        AL,
+        AL,
+        AL,
+        AL,
+        AL,
+        AL,  // A-Z
+        {B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), 0,
+         B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0,
+         B(0, 0, 0, 0, 0, 0, 0, 0)},  // [
+        {B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0,
+         B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0,
+         B(0, 0, 1, 0, 0, 0, 0, 0)},  // '\'
+        {B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0,
+         B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0,
+         B(0, 0, 1, 0, 0, 0, 0, 0)},  // ]
+        {B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), 0,
+         B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0,
+         B(0, 0, 0, 0, 0, 0, 0, 0)},  // ^
+        {B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), 0,
+         B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0,
+         B(0, 0, 0, 0, 0, 0, 0, 0)},  // _
+        {B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), 0,
+         B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0,
+         B(0, 0, 0, 0, 0, 0, 0, 0)},  // `
+        AL,
+        AL,
+        AL,
+        AL,
+        AL,
+        AL,
+        AL,
+        AL,
+        AL,
+        AL,
+        AL,
+        AL,
+        AL,
+        AL,
+        AL,
+        AL,
+        AL,
+        AL,
+        AL,
+        AL,
+        AL,
+        AL,
+        AL,
+        AL,
+        AL,
+        AL,  // a-z
+        {B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), 0,
+         B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0,
+         B(0, 0, 0, 0, 0, 0, 0, 0)},  // {
+        {B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0,
+         B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0,
+         B(0, 0, 1, 0, 0, 0, 0, 0)},  // |
+        {B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0,
+         B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0,
+         B(0, 0, 1, 0, 0, 0, 0, 0)},  // }
+        {B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0,
+         B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0,
+         B(0, 0, 1, 0, 0, 0, 0, 0)},  // ~
+        {B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), 0,
+         B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0,
+         B(0, 0, 0, 0, 0, 0, 0, 0)},  // DEL
 };
 
 #undef B
@@ -113,80 +247,99 @@ static const unsigned char asciiLineBreakTable[][(asciiLineBreakTableLastChar -
 #undef DI
 #undef AL
 
-COMPILE_ASSERT(WTF_ARRAY_LENGTH(asciiLineBreakTable) == asciiLineBreakTableLastChar - asciiLineBreakTableFirstChar + 1, TestLineBreakTableConsistency);
-
-static inline bool shouldBreakAfter(UChar lastCh, UChar ch, UChar nextCh)
-{
-    // Don't allow line breaking between '-' and a digit if the '-' may mean a minus sign in the context,
-    // while allow breaking in 'ABCD-1234' and '1234-5678' which may be in long URLs.
-    if (ch == '-' && isASCIIDigit(nextCh))
-        return isASCIIAlphanumeric(lastCh);
-
-    // If both ch and nextCh are ASCII characters, use a lookup table for enhanced speed and for compatibility
-    // with other browsers (see comments for asciiLineBreakTable for details).
-    if (ch >= asciiLineBreakTableFirstChar && ch <= asciiLineBreakTableLastChar
-            && nextCh >= asciiLineBreakTableFirstChar && nextCh <= asciiLineBreakTableLastChar) {
-        const unsigned char* tableRow = asciiLineBreakTable[ch - asciiLineBreakTableFirstChar];
-        int nextChIndex = nextCh - asciiLineBreakTableFirstChar;
-        return tableRow[nextChIndex / 8] & (1 << (nextChIndex % 8));
-    }
-    // Otherwise defer to the Unicode algorithm by returning false.
-    return false;
+COMPILE_ASSERT(WTF_ARRAY_LENGTH(asciiLineBreakTable) ==
+                   asciiLineBreakTableLastChar - asciiLineBreakTableFirstChar +
+                       1,
+               TestLineBreakTableConsistency);
+
+static inline bool shouldBreakAfter(UChar lastCh, UChar ch, UChar nextCh) {
+  // Don't allow line breaking between '-' and a digit if the '-' may mean a
+  // minus sign in the context, while allow breaking in 'ABCD-1234' and
+  // '1234-5678' which may be in long URLs.
+  if (ch == '-' && isASCIIDigit(nextCh))
+    return isASCIIAlphanumeric(lastCh);
+
+  // If both ch and nextCh are ASCII characters, use a lookup table for enhanced
+  // speed and for compatibility with other browsers (see comments for
+  // asciiLineBreakTable for details).
+  if (ch >= asciiLineBreakTableFirstChar && ch <= asciiLineBreakTableLastChar &&
+      nextCh >= asciiLineBreakTableFirstChar &&
+      nextCh <= asciiLineBreakTableLastChar) {
+    const unsigned char* tableRow =
+        asciiLineBreakTable[ch - asciiLineBreakTableFirstChar];
+    int nextChIndex = nextCh - asciiLineBreakTableFirstChar;
+    return tableRow[nextChIndex / 8] & (1 << (nextChIndex % 8));
+  }
+  // Otherwise defer to the Unicode algorithm by returning false.
+  return false;
 }
 
-template
-inline bool needsLineBreakIterator(UChar ch)
-{
-    if (treatNoBreakSpaceAsBreak)
-        return ch > asciiLineBreakTableLastChar;
-    return ch > asciiLineBreakTableLastChar && ch != noBreakSpace;
+template 
+inline bool needsLineBreakIterator(UChar ch) {
+  if (treatNoBreakSpaceAsBreak)
+    return ch > asciiLineBreakTableLastChar;
+  return ch > asciiLineBreakTableLastChar && ch != noBreakSpace;
 }
 
-template
-static inline int nextBreakablePosition(LazyLineBreakIterator& lazyBreakIterator, const CharacterType* str, unsigned length, int pos)
-{
-    int len = static_cast(length);
-    int nextBreak = -1;
-
-    CharacterType lastLastCh = pos > 1 ? str[pos - 2] : static_cast(lazyBreakIterator.secondToLastCharacter());
-    CharacterType lastCh = pos > 0 ? str[pos - 1] : static_cast(lazyBreakIterator.lastCharacter());
-    unsigned priorContextLength = lazyBreakIterator.priorContextLength();
-    for (int i = pos; i < len; i++) {
-        CharacterType ch = str[i];
-
-        if (isBreakableSpace(ch) || shouldBreakAfter(lastLastCh, lastCh, ch))
-            return i;
-
-        if (needsLineBreakIterator(ch) || needsLineBreakIterator(lastCh)) {
-            if (nextBreak < i) {
-                // Don't break if positioned at start of primary context and there is no prior context.
-                if (i || priorContextLength) {
-                    TextBreakIterator* breakIterator = lazyBreakIterator.get(priorContextLength);
-                    if (breakIterator) {
-                        nextBreak = breakIterator->following(i - 1 + priorContextLength);
-                        if (nextBreak >= 0) {
-                            nextBreak -= priorContextLength;
-                        }
-                    }
-                }
+template 
+static inline int nextBreakablePosition(
+    LazyLineBreakIterator& lazyBreakIterator,
+    const CharacterType* str,
+    unsigned length,
+    int pos) {
+  int len = static_cast(length);
+  int nextBreak = -1;
+
+  CharacterType lastLastCh =
+      pos > 1 ? str[pos - 2]
+              : static_cast(
+                    lazyBreakIterator.secondToLastCharacter());
+  CharacterType lastCh =
+      pos > 0 ? str[pos - 1]
+              : static_cast(lazyBreakIterator.lastCharacter());
+  unsigned priorContextLength = lazyBreakIterator.priorContextLength();
+  for (int i = pos; i < len; i++) {
+    CharacterType ch = str[i];
+
+    if (isBreakableSpace(ch) ||
+        shouldBreakAfter(lastLastCh, lastCh, ch))
+      return i;
+
+    if (needsLineBreakIterator(ch) ||
+        needsLineBreakIterator(lastCh)) {
+      if (nextBreak < i) {
+        // Don't break if positioned at start of primary context and there is no
+        // prior context.
+        if (i || priorContextLength) {
+          TextBreakIterator* breakIterator =
+              lazyBreakIterator.get(priorContextLength);
+          if (breakIterator) {
+            nextBreak = breakIterator->following(i - 1 + priorContextLength);
+            if (nextBreak >= 0) {
+              nextBreak -= priorContextLength;
             }
-            if (i == nextBreak && !isBreakableSpace(lastCh))
-                return i;
+          }
         }
-
-        lastLastCh = lastCh;
-        lastCh = ch;
+      }
+      if (i == nextBreak && !isBreakableSpace(lastCh))
+        return i;
     }
 
-    return len;
+    lastLastCh = lastCh;
+    lastCh = ch;
+  }
+
+  return len;
 }
 
-int nextBreakablePositionIgnoringNBSP(LazyLineBreakIterator& lazyBreakIterator, int pos)
-{
-    String string = lazyBreakIterator.string();
-    if (string.is8Bit())
-        return nextBreakablePosition(lazyBreakIterator, string.characters8(), string.length(), pos);
-    return nextBreakablePosition(lazyBreakIterator, string.characters16(), string.length(), pos);
+int nextBreakablePositionIgnoringNBSP(LazyLineBreakIterator& lazyBreakIterator,
+                                      int pos) {
+  String string = lazyBreakIterator.string();
+  if (string.is8Bit())
+    return nextBreakablePosition(
+        lazyBreakIterator, string.characters8(), string.length(), pos);
+  return nextBreakablePosition(
+      lazyBreakIterator, string.characters16(), string.length(), pos);
 }
 
-} // namespace blink
+}  // namespace blink
diff --git a/sky/engine/core/rendering/break_lines.h b/sky/engine/core/rendering/break_lines.h
index 4d2722f030ff3..806c4e367bc50 100644
--- a/sky/engine/core/rendering/break_lines.h
+++ b/sky/engine/core/rendering/break_lines.h
@@ -29,13 +29,14 @@ class LazyLineBreakIterator;
 
 int nextBreakablePositionIgnoringNBSP(LazyLineBreakIterator&, int pos);
 
-inline bool isBreakable(LazyLineBreakIterator& lazyBreakIterator, int pos, int& nextBreakable)
-{
-    if (pos > nextBreakable)
-        nextBreakable = nextBreakablePositionIgnoringNBSP(lazyBreakIterator, pos);
-    return pos == nextBreakable;
+inline bool isBreakable(LazyLineBreakIterator& lazyBreakIterator,
+                        int pos,
+                        int& nextBreakable) {
+  if (pos > nextBreakable)
+    nextBreakable = nextBreakablePositionIgnoringNBSP(lazyBreakIterator, pos);
+  return pos == nextBreakable;
 }
 
-} // namespace blink
+}  // namespace blink
 
 #endif  // SKY_ENGINE_CORE_RENDERING_BREAK_LINES_H_
diff --git a/sky/engine/core/rendering/line/BreakingContext.cpp b/sky/engine/core/rendering/line/BreakingContext.cpp
index 0320c0ff9408f..9c658ce2d9886 100644
--- a/sky/engine/core/rendering/line/BreakingContext.cpp
+++ b/sky/engine/core/rendering/line/BreakingContext.cpp
@@ -1,6 +1,7 @@
 /*
  * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
- * Copyright (C) 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All right reserved.
+ * Copyright (C) 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All
+ * right reserved.
  * Copyright (C) 2010 Google Inc. All rights reserved.
  * Copyright (C) 2013 Adobe Systems Incorporated.
  *
@@ -25,46 +26,47 @@
 
 namespace blink {
 
-InlineIterator BreakingContext::handleEndOfLine()
-{
-    if (m_lineBreak == m_resolver.position() && !m_lineBreak.object()) {
-        // we just add as much as possible
-        if (m_blockStyle->whiteSpace() == PRE && !m_current.offset()) {
-            m_lineBreak.moveTo(m_lastObject, m_lastObject->isText() ? m_lastObject->length() : 0);
-        } else if (m_lineBreak.object()) {
-            // Don't ever break in the middle of a word if we can help it.
-            // There's no room at all. We just have to be on this line,
-            // even though we'll spill out.
-            m_lineBreak.moveTo(m_current.object(), m_current.offset());
-        }
+InlineIterator BreakingContext::handleEndOfLine() {
+  if (m_lineBreak == m_resolver.position() && !m_lineBreak.object()) {
+    // we just add as much as possible
+    if (m_blockStyle->whiteSpace() == PRE && !m_current.offset()) {
+      m_lineBreak.moveTo(m_lastObject,
+                         m_lastObject->isText() ? m_lastObject->length() : 0);
+    } else if (m_lineBreak.object()) {
+      // Don't ever break in the middle of a word if we can help it.
+      // There's no room at all. We just have to be on this line,
+      // even though we'll spill out.
+      m_lineBreak.moveTo(m_current.object(), m_current.offset());
     }
+  }
 
-    // FIXME Bug 100049: We do not need to consume input in a multi-segment line
-    // unless no segment will.
-    if (m_lineBreak == m_resolver.position())
-        m_lineBreak.increment();
+  // FIXME Bug 100049: We do not need to consume input in a multi-segment line
+  // unless no segment will.
+  if (m_lineBreak == m_resolver.position())
+    m_lineBreak.increment();
 
-    // Sanity check our midpoints.
-    m_lineMidpointState.checkMidpoints(m_lineBreak);
+  // Sanity check our midpoints.
+  m_lineMidpointState.checkMidpoints(m_lineBreak);
 
-    m_trailingObjects.updateMidpointsForTrailingObjects(m_lineMidpointState, m_lineBreak, TrailingObjects::CollapseFirstSpace);
+  m_trailingObjects.updateMidpointsForTrailingObjects(
+      m_lineMidpointState, m_lineBreak, TrailingObjects::CollapseFirstSpace);
 
-    // We might have made lineBreak an iterator that points past the end
-    // of the object. Do this adjustment to make it point to the start
-    // of the next object instead to avoid confusing the rest of the
-    // code.
-    if (m_lineBreak.offset()) {
-        // This loop enforces the invariant that line breaks should never point
-        // at an empty inline. See http://crbug.com/305904.
-        do {
-            m_lineBreak.setOffset(m_lineBreak.offset() - 1);
-            m_lineBreak.increment();
-        } while (!m_lineBreak.atEnd() && isEmptyInline(m_lineBreak.object()));
-    }
+  // We might have made lineBreak an iterator that points past the end
+  // of the object. Do this adjustment to make it point to the start
+  // of the next object instead to avoid confusing the rest of the
+  // code.
+  if (m_lineBreak.offset()) {
+    // This loop enforces the invariant that line breaks should never point
+    // at an empty inline. See http://crbug.com/305904.
+    do {
+      m_lineBreak.setOffset(m_lineBreak.offset() - 1);
+      m_lineBreak.increment();
+    } while (!m_lineBreak.atEnd() && isEmptyInline(m_lineBreak.object()));
+  }
 
-    m_lineInfo.incrementLineIndex();
+  m_lineInfo.incrementLineIndex();
 
-    return m_lineBreak;
+  return m_lineBreak;
 }
 
-}
+}  // namespace blink
diff --git a/sky/engine/core/rendering/line/BreakingContextInlineHeaders.h b/sky/engine/core/rendering/line/BreakingContextInlineHeaders.h
index 00011c298ec12..b06f67edaef75 100644
--- a/sky/engine/core/rendering/line/BreakingContextInlineHeaders.h
+++ b/sky/engine/core/rendering/line/BreakingContextInlineHeaders.h
@@ -1,6 +1,7 @@
 /*
  * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
- * Copyright (C) 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All right reserved.
+ * Copyright (C) 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All
+ * right reserved.
  * Copyright (C) 2010 Google Inc. All rights reserved.
  * Copyright (C) 2013 Adobe Systems Incorporated.
  *
@@ -44,756 +45,899 @@ namespace blink {
 const unsigned cMaxLineDepth = 200;
 
 class BreakingContext {
-public:
-    BreakingContext(InlineBidiResolver& resolver, LineInfo& inLineInfo, LineWidth& lineWidth, RenderTextInfo& inRenderTextInfo, FloatingObject* inLastFloatFromPreviousLine, bool appliedStartWidth, RenderParagraph* block)
-        : m_resolver(resolver)
-        , m_current(resolver.position())
-        , m_lineBreak(resolver.position())
-        , m_block(block)
-        , m_lastObject(m_current.object())
-        , m_nextObject(0)
-        , m_currentStyle(0)
-        , m_blockStyle(block->style())
-        , m_lineInfo(inLineInfo)
-        , m_renderTextInfo(inRenderTextInfo)
-        , m_width(lineWidth)
-        , m_currWS(NORMAL)
-        , m_lastWS(NORMAL)
-        , m_preservesNewline(false)
-        , m_atStart(true)
-        , m_ignoringSpaces(false)
-        , m_currentCharacterIsSpace(false)
-        , m_currentCharacterShouldCollapseIfPreWap(false)
-        , m_appliedStartWidth(appliedStartWidth)
-        , m_includeEndWidth(true)
-        , m_autoWrap(false)
-        , m_autoWrapWasEverTrueOnLine(false)
-        , m_collapseWhiteSpace(false)
-        , m_startingNewParagraph(m_lineInfo.previousLineBrokeCleanly())
-        , m_atEnd(false)
-        , m_lineMidpointState(resolver.midpointState())
-    {
-        m_lineInfo.setPreviousLineBrokeCleanly(false);
-    }
-
-    RenderObject* currentObject() { return m_current.object(); }
-    InlineIterator lineBreak() { return m_lineBreak; }
-    bool atEnd() { return m_atEnd; }
-
-    void initializeForCurrentObject();
-
-    void increment();
-
-    void handleOutOfFlowPositioned(Vector& positionedObjects);
-    void handleEmptyInline();
-    void handleReplaced();
-    bool handleText(WordMeasurements&, bool& hyphenated, bool& ellipsized);
-    void commitAndUpdateLineBreakIfNeeded();
-    InlineIterator handleEndOfLine();
-
-    void clearLineBreakIfFitsOnLine()
-    {
-        if (m_width.fitsOnLine() || m_lastWS == NOWRAP)
-            m_lineBreak.clear();
-    }
-
-private:
-    void skipTrailingWhitespace(InlineIterator&, const LineInfo&);
-
-    InlineBidiResolver& m_resolver;
-
-    InlineIterator m_current;
-    InlineIterator m_lineBreak;
-    InlineIterator m_startOfIgnoredSpaces;
-
-    RenderParagraph* m_block;
-    RenderObject* m_lastObject;
-    RenderObject* m_nextObject;
-
-    RenderStyle* m_currentStyle;
-    RenderStyle* m_blockStyle;
-
-    LineInfo& m_lineInfo;
-
-    RenderTextInfo& m_renderTextInfo;
-
-    LineWidth m_width;
-
-    EWhiteSpace m_currWS;
-    EWhiteSpace m_lastWS;
-
-    bool m_preservesNewline;
-    bool m_atStart;
-    bool m_ignoringSpaces;
-    bool m_currentCharacterIsSpace;
-    bool m_currentCharacterShouldCollapseIfPreWap;
-    bool m_appliedStartWidth;
-    bool m_includeEndWidth;
-    bool m_autoWrap;
-    bool m_autoWrapWasEverTrueOnLine;
-    bool m_collapseWhiteSpace;
-    bool m_startingNewParagraph;
-    bool m_atEnd;
-
-    LineMidpointState& m_lineMidpointState;
-
-    TrailingObjects m_trailingObjects;
+ public:
+  BreakingContext(InlineBidiResolver& resolver,
+                  LineInfo& inLineInfo,
+                  LineWidth& lineWidth,
+                  RenderTextInfo& inRenderTextInfo,
+                  FloatingObject* inLastFloatFromPreviousLine,
+                  bool appliedStartWidth,
+                  RenderParagraph* block)
+      : m_resolver(resolver),
+        m_current(resolver.position()),
+        m_lineBreak(resolver.position()),
+        m_block(block),
+        m_lastObject(m_current.object()),
+        m_nextObject(0),
+        m_currentStyle(0),
+        m_blockStyle(block->style()),
+        m_lineInfo(inLineInfo),
+        m_renderTextInfo(inRenderTextInfo),
+        m_width(lineWidth),
+        m_currWS(NORMAL),
+        m_lastWS(NORMAL),
+        m_preservesNewline(false),
+        m_atStart(true),
+        m_ignoringSpaces(false),
+        m_currentCharacterIsSpace(false),
+        m_currentCharacterShouldCollapseIfPreWap(false),
+        m_appliedStartWidth(appliedStartWidth),
+        m_includeEndWidth(true),
+        m_autoWrap(false),
+        m_autoWrapWasEverTrueOnLine(false),
+        m_collapseWhiteSpace(false),
+        m_startingNewParagraph(m_lineInfo.previousLineBrokeCleanly()),
+        m_atEnd(false),
+        m_lineMidpointState(resolver.midpointState()) {
+    m_lineInfo.setPreviousLineBrokeCleanly(false);
+  }
+
+  RenderObject* currentObject() { return m_current.object(); }
+  InlineIterator lineBreak() { return m_lineBreak; }
+  bool atEnd() { return m_atEnd; }
+
+  void initializeForCurrentObject();
+
+  void increment();
+
+  void handleOutOfFlowPositioned(Vector& positionedObjects);
+  void handleEmptyInline();
+  void handleReplaced();
+  bool handleText(WordMeasurements&, bool& hyphenated, bool& ellipsized);
+  void commitAndUpdateLineBreakIfNeeded();
+  InlineIterator handleEndOfLine();
+
+  void clearLineBreakIfFitsOnLine() {
+    if (m_width.fitsOnLine() || m_lastWS == NOWRAP)
+      m_lineBreak.clear();
+  }
+
+ private:
+  void skipTrailingWhitespace(InlineIterator&, const LineInfo&);
+
+  InlineBidiResolver& m_resolver;
+
+  InlineIterator m_current;
+  InlineIterator m_lineBreak;
+  InlineIterator m_startOfIgnoredSpaces;
+
+  RenderParagraph* m_block;
+  RenderObject* m_lastObject;
+  RenderObject* m_nextObject;
+
+  RenderStyle* m_currentStyle;
+  RenderStyle* m_blockStyle;
+
+  LineInfo& m_lineInfo;
+
+  RenderTextInfo& m_renderTextInfo;
+
+  LineWidth m_width;
+
+  EWhiteSpace m_currWS;
+  EWhiteSpace m_lastWS;
+
+  bool m_preservesNewline;
+  bool m_atStart;
+  bool m_ignoringSpaces;
+  bool m_currentCharacterIsSpace;
+  bool m_currentCharacterShouldCollapseIfPreWap;
+  bool m_appliedStartWidth;
+  bool m_includeEndWidth;
+  bool m_autoWrap;
+  bool m_autoWrapWasEverTrueOnLine;
+  bool m_collapseWhiteSpace;
+  bool m_startingNewParagraph;
+  bool m_atEnd;
+
+  LineMidpointState& m_lineMidpointState;
+
+  TrailingObjects m_trailingObjects;
 };
 
-inline bool shouldCollapseWhiteSpace(const RenderStyle* style, const LineInfo& lineInfo, WhitespacePosition whitespacePosition)
-{
-    // CSS2 16.6.1
-    // If a space (U+0020) at the beginning of a line has 'white-space' set to 'normal', 'nowrap', or 'pre-line', it is removed.
-    // If a space (U+0020) at the end of a line has 'white-space' set to 'normal', 'nowrap', or 'pre-line', it is also removed.
-    // If spaces (U+0020) or tabs (U+0009) at the end of a line have 'white-space' set to 'pre-wrap', UAs may visually collapse them.
-    return style->collapseWhiteSpace()
-        || (whitespacePosition == TrailingWhitespace && style->whiteSpace() == PRE_WRAP && (!lineInfo.isEmpty() || !lineInfo.previousLineBrokeCleanly()));
+inline bool shouldCollapseWhiteSpace(const RenderStyle* style,
+                                     const LineInfo& lineInfo,
+                                     WhitespacePosition whitespacePosition) {
+  // CSS2 16.6.1
+  // If a space (U+0020) at the beginning of a line has 'white-space' set to
+  // 'normal', 'nowrap', or 'pre-line', it is removed. If a space (U+0020) at
+  // the end of a line has 'white-space' set to 'normal', 'nowrap', or
+  // 'pre-line', it is also removed. If spaces (U+0020) or tabs (U+0009) at the
+  // end of a line have 'white-space' set to 'pre-wrap', UAs may visually
+  // collapse them.
+  return style->collapseWhiteSpace() ||
+         (whitespacePosition == TrailingWhitespace &&
+          style->whiteSpace() == PRE_WRAP &&
+          (!lineInfo.isEmpty() || !lineInfo.previousLineBrokeCleanly()));
 }
 
-inline bool requiresLineBoxForContent(RenderInline* flow, const LineInfo& lineInfo)
-{
-    RenderObject* parent = flow->parent();
-    if ((flow->style(lineInfo.isFirstLine())->lineHeight() != parent->style(lineInfo.isFirstLine())->lineHeight()
-        || flow->style()->verticalAlign() != parent->style()->verticalAlign()
-        || !parent->style()->font().fontMetrics().hasIdenticalAscentDescentAndLineGap(flow->style()->font().fontMetrics())))
-        return true;
-    return false;
+inline bool requiresLineBoxForContent(RenderInline* flow,
+                                      const LineInfo& lineInfo) {
+  RenderObject* parent = flow->parent();
+  if ((flow->style(lineInfo.isFirstLine())->lineHeight() !=
+           parent->style(lineInfo.isFirstLine())->lineHeight() ||
+       flow->style()->verticalAlign() != parent->style()->verticalAlign() ||
+       !parent->style()
+            ->font()
+            .fontMetrics()
+            .hasIdenticalAscentDescentAndLineGap(
+                flow->style()->font().fontMetrics())))
+    return true;
+  return false;
 }
 
-inline bool alwaysRequiresLineBox(RenderObject* flow)
-{
-    // FIXME: Right now, we only allow line boxes for inlines that are truly empty.
-    // We need to fix this, though, because at the very least, inlines containing only
-    // ignorable whitespace should should also have line boxes.
-    return isEmptyInline(flow) && toRenderInline(flow)->hasInlineDirectionBordersPaddingOrMargin();
+inline bool alwaysRequiresLineBox(RenderObject* flow) {
+  // FIXME: Right now, we only allow line boxes for inlines that are truly
+  // empty. We need to fix this, though, because at the very least, inlines
+  // containing only ignorable whitespace should should also have line boxes.
+  return isEmptyInline(flow) &&
+         toRenderInline(flow)->hasInlineDirectionBordersPaddingOrMargin();
 }
 
-inline bool requiresLineBox(const InlineIterator& it, const LineInfo& lineInfo = LineInfo(), WhitespacePosition whitespacePosition = LeadingWhitespace)
-{
-    if (it.object()->isFloatingOrOutOfFlowPositioned())
-        return false;
+inline bool requiresLineBox(
+    const InlineIterator& it,
+    const LineInfo& lineInfo = LineInfo(),
+    WhitespacePosition whitespacePosition = LeadingWhitespace) {
+  if (it.object()->isFloatingOrOutOfFlowPositioned())
+    return false;
 
-    if (it.object()->isRenderInline() && !alwaysRequiresLineBox(it.object()) && !requiresLineBoxForContent(toRenderInline(it.object()), lineInfo))
-        return false;
+  if (it.object()->isRenderInline() && !alwaysRequiresLineBox(it.object()) &&
+      !requiresLineBoxForContent(toRenderInline(it.object()), lineInfo))
+    return false;
 
-    if (!shouldCollapseWhiteSpace(it.object()->style(), lineInfo, whitespacePosition))
-        return true;
+  if (!shouldCollapseWhiteSpace(it.object()->style(), lineInfo,
+                                whitespacePosition))
+    return true;
 
-    UChar current = it.current();
-    bool notJustWhitespace = current != ' ' && current != '\t' && current != softHyphen && (current != '\n' || it.object()->preservesNewline());
-    return notJustWhitespace || isEmptyInline(it.object());
+  UChar current = it.current();
+  bool notJustWhitespace = current != ' ' && current != '\t' &&
+                           current != softHyphen &&
+                           (current != '\n' || it.object()->preservesNewline());
+  return notJustWhitespace || isEmptyInline(it.object());
 }
 
-// FIXME: The entire concept of the skipTrailingWhitespace function is flawed, since we really need to be building
-// line boxes even for containers that may ultimately collapse away. Otherwise we'll never get positioned
-// elements quite right. In other words, we need to build this function's work into the normal line
-// object iteration process.
-// NB. this function will insert any floating elements that would otherwise
-// be skipped but it will not position them.
-inline void BreakingContext::skipTrailingWhitespace(InlineIterator& iterator, const LineInfo& lineInfo)
-{
-    while (!iterator.atEnd() && !requiresLineBox(iterator, lineInfo, TrailingWhitespace))
-        iterator.increment();
+// FIXME: The entire concept of the skipTrailingWhitespace function is flawed,
+// since we really need to be building line boxes even for containers that may
+// ultimately collapse away. Otherwise we'll never get positioned elements quite
+// right. In other words, we need to build this function's work into the normal
+// line object iteration process. NB. this function will insert any floating
+// elements that would otherwise be skipped but it will not position them.
+inline void BreakingContext::skipTrailingWhitespace(InlineIterator& iterator,
+                                                    const LineInfo& lineInfo) {
+  while (!iterator.atEnd() &&
+         !requiresLineBox(iterator, lineInfo, TrailingWhitespace))
+    iterator.increment();
 }
 
-inline void BreakingContext::initializeForCurrentObject()
-{
-    m_currentStyle = m_current.object()->style();
-    m_nextObject = bidiNextSkippingEmptyInlines(m_block, m_current.object());
-    if (m_nextObject && m_nextObject->parent() && !m_nextObject->parent()->isDescendantOf(m_current.object()->parent()))
-        m_includeEndWidth = true;
-
-    m_currWS = m_current.object()->isReplaced() ? m_current.object()->parent()->style()->whiteSpace() : m_currentStyle->whiteSpace();
-    m_lastWS = m_lastObject->isReplaced() ? m_lastObject->parent()->style()->whiteSpace() : m_lastObject->style()->whiteSpace();
-
-    m_autoWrap = RenderStyle::autoWrap(m_currWS);
-    m_autoWrapWasEverTrueOnLine = m_autoWrapWasEverTrueOnLine || m_autoWrap;
-
-    m_preservesNewline = RenderStyle::preserveNewline(m_currWS);
-    m_collapseWhiteSpace = RenderStyle::collapseWhiteSpace(m_currWS);
+inline void BreakingContext::initializeForCurrentObject() {
+  m_currentStyle = m_current.object()->style();
+  m_nextObject = bidiNextSkippingEmptyInlines(m_block, m_current.object());
+  if (m_nextObject && m_nextObject->parent() &&
+      !m_nextObject->parent()->isDescendantOf(m_current.object()->parent()))
+    m_includeEndWidth = true;
+
+  m_currWS = m_current.object()->isReplaced()
+                 ? m_current.object()->parent()->style()->whiteSpace()
+                 : m_currentStyle->whiteSpace();
+  m_lastWS = m_lastObject->isReplaced()
+                 ? m_lastObject->parent()->style()->whiteSpace()
+                 : m_lastObject->style()->whiteSpace();
+
+  m_autoWrap = RenderStyle::autoWrap(m_currWS);
+  m_autoWrapWasEverTrueOnLine = m_autoWrapWasEverTrueOnLine || m_autoWrap;
+
+  m_preservesNewline = RenderStyle::preserveNewline(m_currWS);
+  m_collapseWhiteSpace = RenderStyle::collapseWhiteSpace(m_currWS);
 }
 
-inline void BreakingContext::increment()
-{
-    // Clear out our character space bool, since inline 
s don't collapse whitespace
-    // with adjacent inline normal/nowrap spans.
-    if (!m_collapseWhiteSpace)
-        m_currentCharacterIsSpace = false;
-
-    m_current.moveToStartOf(m_nextObject);
-    m_atStart = false;
-}
+inline void BreakingContext::increment() {
+  // Clear out our character space bool, since inline 
s don't collapse
+  // whitespace with adjacent inline normal/nowrap spans.
+  if (!m_collapseWhiteSpace)
+    m_currentCharacterIsSpace = false;
 
-inline LayoutUnit borderPaddingMarginStart(RenderInline* child)
-{
-    return child->marginStart() + child->paddingStart() + child->borderStart();
+  m_current.moveToStartOf(m_nextObject);
+  m_atStart = false;
 }
 
-inline LayoutUnit borderPaddingMarginEnd(RenderInline* child)
-{
-    return child->marginEnd() + child->paddingEnd() + child->borderEnd();
+inline LayoutUnit borderPaddingMarginStart(RenderInline* child) {
+  return child->marginStart() + child->paddingStart() + child->borderStart();
 }
 
-inline bool shouldAddBorderPaddingMargin(RenderObject* child, bool &checkSide)
-{
-    if (!child || (child->isText() && !toRenderText(child)->textLength()))
-        return true;
-    checkSide = false;
-    return checkSide;
+inline LayoutUnit borderPaddingMarginEnd(RenderInline* child) {
+  return child->marginEnd() + child->paddingEnd() + child->borderEnd();
 }
 
-inline LayoutUnit inlineLogicalWidth(RenderObject* child, bool start = true, bool end = true)
-{
-    unsigned lineDepth = 1;
-    LayoutUnit extraWidth = 0;
-    RenderObject* parent = child->parent();
-    while (parent->isRenderInline() && lineDepth++ < cMaxLineDepth) {
-        RenderInline* parentAsRenderInline = toRenderInline(parent);
-        if (!isEmptyInline(parentAsRenderInline)) {
-            if (start && shouldAddBorderPaddingMargin(child->previousSibling(), start))
-                extraWidth += borderPaddingMarginStart(parentAsRenderInline);
-            if (end && shouldAddBorderPaddingMargin(child->nextSibling(), end))
-                extraWidth += borderPaddingMarginEnd(parentAsRenderInline);
-            if (!start && !end)
-                return extraWidth;
-        }
-        child = parent;
-        parent = child->parent();
-    }
-    return extraWidth;
+inline bool shouldAddBorderPaddingMargin(RenderObject* child, bool& checkSide) {
+  if (!child || (child->isText() && !toRenderText(child)->textLength()))
+    return true;
+  checkSide = false;
+  return checkSide;
 }
 
-inline void BreakingContext::handleOutOfFlowPositioned(Vector& positionedObjects)
-{
-    // If our original display wasn't an inline type, then we can
-    // go ahead and determine our static inline position now.
-    RenderBox* box = toRenderBox(m_current.object());
-
-    // If we're ignoring spaces, we have to stop and include this object and
-    // then start ignoring spaces again.
-    if (box->style()->isOriginalDisplayInlineType() || box->container()->isRenderInline()) {
-        if (m_ignoringSpaces)
-            m_lineMidpointState.ensureLineBoxInsideIgnoredSpaces(box);
-        m_trailingObjects.appendObjectIfNeeded(box);
-    } else {
-        positionedObjects.append(box);
+inline LayoutUnit inlineLogicalWidth(RenderObject* child,
+                                     bool start = true,
+                                     bool end = true) {
+  unsigned lineDepth = 1;
+  LayoutUnit extraWidth = 0;
+  RenderObject* parent = child->parent();
+  while (parent->isRenderInline() && lineDepth++ < cMaxLineDepth) {
+    RenderInline* parentAsRenderInline = toRenderInline(parent);
+    if (!isEmptyInline(parentAsRenderInline)) {
+      if (start &&
+          shouldAddBorderPaddingMargin(child->previousSibling(), start))
+        extraWidth += borderPaddingMarginStart(parentAsRenderInline);
+      if (end && shouldAddBorderPaddingMargin(child->nextSibling(), end))
+        extraWidth += borderPaddingMarginEnd(parentAsRenderInline);
+      if (!start && !end)
+        return extraWidth;
     }
-    m_width.addUncommittedWidth(inlineLogicalWidth(box).toFloat());
-    // Reset prior line break context characters.
-    m_renderTextInfo.m_lineBreakIterator.resetPriorContext();
+    child = parent;
+    parent = child->parent();
+  }
+  return extraWidth;
 }
 
-// This is currently just used for list markers and inline flows that have line boxes. Neither should
-// have an effect on whitespace at the start of the line.
-inline bool shouldSkipWhitespaceAfterStartObject(RenderParagraph* block, RenderObject* o, LineMidpointState& lineMidpointState)
-{
-    RenderObject* next = bidiNextSkippingEmptyInlines(block, o);
-    while (next && next->isFloatingOrOutOfFlowPositioned())
-        next = bidiNextSkippingEmptyInlines(block, next);
-
-    if (next && next->isText() && toRenderText(next)->textLength() > 0) {
-        RenderText* nextText = toRenderText(next);
-        UChar nextChar = nextText->characterAt(0);
-        if (nextText->style()->isCollapsibleWhiteSpace(nextChar)) {
-            lineMidpointState.startIgnoringSpaces(InlineIterator(0, o, 0));
-            return true;
-        }
-    }
+inline void BreakingContext::handleOutOfFlowPositioned(
+    Vector& positionedObjects) {
+  // If our original display wasn't an inline type, then we can
+  // go ahead and determine our static inline position now.
+  RenderBox* box = toRenderBox(m_current.object());
 
-    return false;
+  // If we're ignoring spaces, we have to stop and include this object and
+  // then start ignoring spaces again.
+  if (box->style()->isOriginalDisplayInlineType() ||
+      box->container()->isRenderInline()) {
+    if (m_ignoringSpaces)
+      m_lineMidpointState.ensureLineBoxInsideIgnoredSpaces(box);
+    m_trailingObjects.appendObjectIfNeeded(box);
+  } else {
+    positionedObjects.append(box);
+  }
+  m_width.addUncommittedWidth(inlineLogicalWidth(box).toFloat());
+  // Reset prior line break context characters.
+  m_renderTextInfo.m_lineBreakIterator.resetPriorContext();
 }
 
-inline void BreakingContext::handleEmptyInline()
-{
-    // This should only end up being called on empty inlines
-    ASSERT(isEmptyInline(m_current.object()));
-
-    RenderInline* flowBox = toRenderInline(m_current.object());
-
-    bool requiresLineBox = alwaysRequiresLineBox(m_current.object());
-    if (requiresLineBox || requiresLineBoxForContent(flowBox, m_lineInfo)) {
-        // An empty inline that only has line-height, vertical-align or font-metrics will
-        // not force linebox creation (and thus affect the height of the line) if the rest of the line is empty.
-        if (requiresLineBox)
-            m_lineInfo.setEmpty(false, m_block, &m_width);
-        if (m_ignoringSpaces) {
-            // If we are in a run of ignored spaces then ensure we get a linebox if lineboxes are eventually
-            // created for the line...
-            m_trailingObjects.clear();
-            m_lineMidpointState.ensureLineBoxInsideIgnoredSpaces(m_current.object());
-        } else if (m_blockStyle->collapseWhiteSpace() && m_resolver.position().object() == m_current.object()
-            && shouldSkipWhitespaceAfterStartObject(m_block, m_current.object(), m_lineMidpointState)) {
-            // If this object is at the start of the line, we need to behave like list markers and
-            // start ignoring spaces.
-            m_currentCharacterShouldCollapseIfPreWap = m_currentCharacterIsSpace = true;
-            m_ignoringSpaces = true;
-        } else {
-            // If we are after a trailing space but aren't ignoring spaces yet then ensure we get a linebox
-            // if we encounter collapsible whitepace.
-            m_trailingObjects.appendObjectIfNeeded(m_current.object());
-        }
+// This is currently just used for list markers and inline flows that have line
+// boxes. Neither should have an effect on whitespace at the start of the line.
+inline bool shouldSkipWhitespaceAfterStartObject(
+    RenderParagraph* block,
+    RenderObject* o,
+    LineMidpointState& lineMidpointState) {
+  RenderObject* next = bidiNextSkippingEmptyInlines(block, o);
+  while (next && next->isFloatingOrOutOfFlowPositioned())
+    next = bidiNextSkippingEmptyInlines(block, next);
+
+  if (next && next->isText() && toRenderText(next)->textLength() > 0) {
+    RenderText* nextText = toRenderText(next);
+    UChar nextChar = nextText->characterAt(0);
+    if (nextText->style()->isCollapsibleWhiteSpace(nextChar)) {
+      lineMidpointState.startIgnoringSpaces(InlineIterator(0, o, 0));
+      return true;
     }
+  }
 
-    m_width.addUncommittedWidth((inlineLogicalWidth(m_current.object()) + borderPaddingMarginStart(flowBox) + borderPaddingMarginEnd(flowBox)).toFloat());
+  return false;
 }
 
-inline void BreakingContext::handleReplaced()
-{
-    RenderBox* replacedBox = toRenderBox(m_current.object());
-
-    if (m_atStart)
-        m_width.updateAvailableWidth();
-
-    // Break on replaced elements if either has normal white-space.
-    if (m_autoWrap || RenderStyle::autoWrap(m_lastWS)) {
-        m_width.commit();
-        m_lineBreak.moveToStartOf(m_current.object());
+inline void BreakingContext::handleEmptyInline() {
+  // This should only end up being called on empty inlines
+  ASSERT(isEmptyInline(m_current.object()));
+
+  RenderInline* flowBox = toRenderInline(m_current.object());
+
+  bool requiresLineBox = alwaysRequiresLineBox(m_current.object());
+  if (requiresLineBox || requiresLineBoxForContent(flowBox, m_lineInfo)) {
+    // An empty inline that only has line-height, vertical-align or font-metrics
+    // will not force linebox creation (and thus affect the height of the line)
+    // if the rest of the line is empty.
+    if (requiresLineBox)
+      m_lineInfo.setEmpty(false, m_block, &m_width);
+    if (m_ignoringSpaces) {
+      // If we are in a run of ignored spaces then ensure we get a linebox if
+      // lineboxes are eventually created for the line...
+      m_trailingObjects.clear();
+      m_lineMidpointState.ensureLineBoxInsideIgnoredSpaces(m_current.object());
+    } else if (m_blockStyle->collapseWhiteSpace() &&
+               m_resolver.position().object() == m_current.object() &&
+               shouldSkipWhitespaceAfterStartObject(m_block, m_current.object(),
+                                                    m_lineMidpointState)) {
+      // If this object is at the start of the line, we need to behave like list
+      // markers and start ignoring spaces.
+      m_currentCharacterShouldCollapseIfPreWap = m_currentCharacterIsSpace =
+          true;
+      m_ignoringSpaces = true;
+    } else {
+      // If we are after a trailing space but aren't ignoring spaces yet then
+      // ensure we get a linebox if we encounter collapsible whitepace.
+      m_trailingObjects.appendObjectIfNeeded(m_current.object());
     }
+  }
 
-    if (m_ignoringSpaces)
-        m_lineMidpointState.stopIgnoringSpaces(InlineIterator(0, m_current.object(), 0));
-
-    m_lineInfo.setEmpty(false, m_block, &m_width);
-    m_ignoringSpaces = false;
-    m_currentCharacterShouldCollapseIfPreWap = m_currentCharacterIsSpace = false;
-    m_trailingObjects.clear();
-
-    // Optimize for a common case. If we can't find whitespace after the list
-    // item, then this is all moot.
-    LayoutUnit replacedLogicalWidth = m_block->logicalWidthForChild(replacedBox) + m_block->marginStartForChild(replacedBox) + m_block->marginEndForChild(replacedBox) + inlineLogicalWidth(m_current.object());
-    m_width.addUncommittedWidth(replacedLogicalWidth.toFloat());
-    // Update prior line break context characters, using U+FFFD (OBJECT REPLACEMENT CHARACTER) for replaced element.
-    m_renderTextInfo.m_lineBreakIterator.updatePriorContext(replacementCharacter);
+  m_width.addUncommittedWidth((inlineLogicalWidth(m_current.object()) +
+                               borderPaddingMarginStart(flowBox) +
+                               borderPaddingMarginEnd(flowBox))
+                                  .toFloat());
 }
 
-inline void nextCharacter(UChar& currentCharacter, UChar& lastCharacter, UChar& secondToLastCharacter)
-{
-    secondToLastCharacter = lastCharacter;
-    lastCharacter = currentCharacter;
+inline void BreakingContext::handleReplaced() {
+  RenderBox* replacedBox = toRenderBox(m_current.object());
+
+  if (m_atStart)
+    m_width.updateAvailableWidth();
+
+  // Break on replaced elements if either has normal white-space.
+  if (m_autoWrap || RenderStyle::autoWrap(m_lastWS)) {
+    m_width.commit();
+    m_lineBreak.moveToStartOf(m_current.object());
+  }
+
+  if (m_ignoringSpaces)
+    m_lineMidpointState.stopIgnoringSpaces(
+        InlineIterator(0, m_current.object(), 0));
+
+  m_lineInfo.setEmpty(false, m_block, &m_width);
+  m_ignoringSpaces = false;
+  m_currentCharacterShouldCollapseIfPreWap = m_currentCharacterIsSpace = false;
+  m_trailingObjects.clear();
+
+  // Optimize for a common case. If we can't find whitespace after the list
+  // item, then this is all moot.
+  LayoutUnit replacedLogicalWidth = m_block->logicalWidthForChild(replacedBox) +
+                                    m_block->marginStartForChild(replacedBox) +
+                                    m_block->marginEndForChild(replacedBox) +
+                                    inlineLogicalWidth(m_current.object());
+  m_width.addUncommittedWidth(replacedLogicalWidth.toFloat());
+  // Update prior line break context characters, using U+FFFD (OBJECT
+  // REPLACEMENT CHARACTER) for replaced element.
+  m_renderTextInfo.m_lineBreakIterator.updatePriorContext(replacementCharacter);
 }
 
-inline float firstPositiveWidth(const WordMeasurements& wordMeasurements)
-{
-    for (size_t i = 0; i < wordMeasurements.size(); ++i) {
-        if (wordMeasurements[i].width > 0)
-            return wordMeasurements[i].width;
-    }
-    return 0;
+inline void nextCharacter(UChar& currentCharacter,
+                          UChar& lastCharacter,
+                          UChar& secondToLastCharacter) {
+  secondToLastCharacter = lastCharacter;
+  lastCharacter = currentCharacter;
 }
 
-inline float measureHyphenWidth(RenderText* renderer, const Font& font, TextDirection textDirection)
-{
-    RenderStyle* style = renderer->style();
-    return font.width(constructTextRun(renderer, font,
-        style->hyphenString().string(), style, style->direction()));
+inline float firstPositiveWidth(const WordMeasurements& wordMeasurements) {
+  for (size_t i = 0; i < wordMeasurements.size(); ++i) {
+    if (wordMeasurements[i].width > 0)
+      return wordMeasurements[i].width;
+  }
+  return 0;
 }
 
-inline float measureEllipsisWidth(RenderText* renderer, const Font& font, const String& ellipsis)
-{
-    RenderStyle* style = renderer->style();
-    return font.width(constructTextRun(renderer, font, ellipsis, style, style->direction()));
+inline float measureHyphenWidth(RenderText* renderer,
+                                const Font& font,
+                                TextDirection textDirection) {
+  RenderStyle* style = renderer->style();
+  return font.width(constructTextRun(renderer, font,
+                                     style->hyphenString().string(), style,
+                                     style->direction()));
 }
 
-ALWAYS_INLINE TextDirection textDirectionFromUnicode(WTF::Unicode::Direction direction)
-{
-    return direction == WTF::Unicode::RightToLeft
-        || direction == WTF::Unicode::RightToLeftArabic ? RTL : LTR;
+inline float measureEllipsisWidth(RenderText* renderer,
+                                  const Font& font,
+                                  const String& ellipsis) {
+  RenderStyle* style = renderer->style();
+  return font.width(
+      constructTextRun(renderer, font, ellipsis, style, style->direction()));
 }
 
-ALWAYS_INLINE float textWidth(RenderText* text, unsigned from, unsigned len, const Font& font, float xPos, bool isFixedPitch, bool collapseWhiteSpace, HashSet* fallbackFonts = 0)
-{
-    GlyphOverflow glyphOverflow;
-    if (isFixedPitch || (!from && len == text->textLength()))
-        return text->width(from, len, font, xPos, text->style()->direction(), fallbackFonts, &glyphOverflow);
-
-    TextRun run = constructTextRun(text, font, text, from, len, text->style());
-    run.setCharacterScanForCodePath(!text->canUseSimpleFontCodePath());
-    run.setTabSize(!collapseWhiteSpace, text->style()->tabSize());
-    run.setXPos(xPos);
-    return font.width(run, fallbackFonts, &glyphOverflow);
+ALWAYS_INLINE TextDirection
+textDirectionFromUnicode(WTF::Unicode::Direction direction) {
+  return direction == WTF::Unicode::RightToLeft ||
+                 direction == WTF::Unicode::RightToLeftArabic
+             ? RTL
+             : LTR;
 }
 
-inline bool BreakingContext::handleText(WordMeasurements& wordMeasurements, bool& hyphenated, bool& ellipsized)
-{
-    if (!m_current.offset())
-        m_appliedStartWidth = false;
-
-    RenderText* renderText = toRenderText(m_current.object());
+ALWAYS_INLINE float textWidth(
+    RenderText* text,
+    unsigned from,
+    unsigned len,
+    const Font& font,
+    float xPos,
+    bool isFixedPitch,
+    bool collapseWhiteSpace,
+    HashSet* fallbackFonts = 0) {
+  GlyphOverflow glyphOverflow;
+  if (isFixedPitch || (!from && len == text->textLength()))
+    return text->width(from, len, font, xPos, text->style()->direction(),
+                       fallbackFonts, &glyphOverflow);
+
+  TextRun run = constructTextRun(text, font, text, from, len, text->style());
+  run.setCharacterScanForCodePath(!text->canUseSimpleFontCodePath());
+  run.setTabSize(!collapseWhiteSpace, text->style()->tabSize());
+  run.setXPos(xPos);
+  return font.width(run, fallbackFonts, &glyphOverflow);
+}
 
-    // If we have left a no-wrap inline and entered an autowrap inline while ignoring spaces
-    // then we need to mark the start of the autowrap inline as a potential linebreak now.
-    if (m_autoWrap && !RenderStyle::autoWrap(m_lastWS) && m_ignoringSpaces) {
-        m_width.commit();
-        m_lineBreak.moveToStartOf(m_current.object());
+inline bool BreakingContext::handleText(WordMeasurements& wordMeasurements,
+                                        bool& hyphenated,
+                                        bool& ellipsized) {
+  if (!m_current.offset())
+    m_appliedStartWidth = false;
+
+  RenderText* renderText = toRenderText(m_current.object());
+
+  // If we have left a no-wrap inline and entered an autowrap inline while
+  // ignoring spaces then we need to mark the start of the autowrap inline as a
+  // potential linebreak now.
+  if (m_autoWrap && !RenderStyle::autoWrap(m_lastWS) && m_ignoringSpaces) {
+    m_width.commit();
+    m_lineBreak.moveToStartOf(m_current.object());
+  }
+
+  RenderStyle* style = renderText->style(m_lineInfo.isFirstLine());
+  const Font& font = style->font();
+  bool isFixedPitch = font.isFixedPitch();
+
+  unsigned lastSpace = m_current.offset();
+  float wordSpacing = m_currentStyle->wordSpacing();
+  float lastSpaceWordSpacing = 0;
+  float wordSpacingForWordMeasurement = 0;
+
+  float wrapW =
+      m_width.uncommittedWidth() +
+      inlineLogicalWidth(m_current.object(), !m_appliedStartWidth, true);
+  float charWidth = 0;
+  // Auto-wrapping text should wrap in the middle of a word only if it could not
+  // wrap before the word, which is only possible if the word is the first thing
+  // on the line, that is, if |w| is zero.
+  bool breakWords =
+      m_currentStyle->breakWords() &&
+      ((m_autoWrap && !m_width.committedWidth()) || m_currWS == PRE);
+  bool midWordBreak = false;
+  bool breakAll =
+      m_currentStyle->wordBreak() == BreakAllWordBreak && m_autoWrap;
+
+  float hyphenWidth = 0;
+
+  bool ellipsizeMode = false;
+  float ellipsisWidth = 0;
+  unsigned ellipsisBreakOffset = 0;
+  if (m_lineInfo.lineIndex() == m_blockStyle->maxLines() - 1 ||
+      m_blockStyle->maxLines() == INT_MAX) {
+    ellipsizeMode = !m_blockStyle->ellipsis().isEmpty();
+    if (ellipsizeMode) {
+      ellipsisWidth = measureEllipsisWidth(renderText, font,
+                                           m_blockStyle->ellipsis().string());
+      breakAll = true;
     }
-
-    RenderStyle* style = renderText->style(m_lineInfo.isFirstLine());
-    const Font& font = style->font();
-    bool isFixedPitch = font.isFixedPitch();
-
-    unsigned lastSpace = m_current.offset();
-    float wordSpacing = m_currentStyle->wordSpacing();
-    float lastSpaceWordSpacing = 0;
-    float wordSpacingForWordMeasurement = 0;
-
-    float wrapW = m_width.uncommittedWidth() + inlineLogicalWidth(m_current.object(), !m_appliedStartWidth, true);
-    float charWidth = 0;
-    // Auto-wrapping text should wrap in the middle of a word only if it could not wrap before the word,
-    // which is only possible if the word is the first thing on the line, that is, if |w| is zero.
-    bool breakWords = m_currentStyle->breakWords() && ((m_autoWrap && !m_width.committedWidth()) || m_currWS == PRE);
-    bool midWordBreak = false;
-    bool breakAll = m_currentStyle->wordBreak() == BreakAllWordBreak && m_autoWrap;
-
-    float hyphenWidth = 0;
-
-    bool ellipsizeMode = false;
-    float ellipsisWidth = 0;
-    unsigned ellipsisBreakOffset = 0;
-    if (m_lineInfo.lineIndex() == m_blockStyle->maxLines() - 1 ||
-        m_blockStyle->maxLines() == INT_MAX) {
-        ellipsizeMode = !m_blockStyle->ellipsis().isEmpty();
-        if (ellipsizeMode) {
-            ellipsisWidth = measureEllipsisWidth(renderText, font, m_blockStyle->ellipsis().string());
-            breakAll = true;
-        }
+  }
+
+  if (m_renderTextInfo.m_text != renderText) {
+    m_renderTextInfo.m_text = renderText;
+    m_renderTextInfo.m_font = &font;
+    m_renderTextInfo.m_lineBreakIterator.resetStringAndReleaseIterator(
+        renderText->text(), style->locale());
+  } else if (m_renderTextInfo.m_font != &font) {
+    m_renderTextInfo.m_font = &font;
+  }
+
+  // Non-zero only when kerning is enabled, in which case we measure
+  // words with their trailing space, then subtract its width.
+  float wordTrailingSpaceWidth =
+      (font.fontDescription().typesettingFeatures() & Kerning)
+          ? font.width(constructTextRun(renderText, font, &space, 1, style,
+                                        style->direction())) +
+                wordSpacing
+          : 0;
+
+  UChar lastCharacter = m_renderTextInfo.m_lineBreakIterator.lastCharacter();
+  UChar secondToLastCharacter =
+      m_renderTextInfo.m_lineBreakIterator.secondToLastCharacter();
+  for (; m_current.offset() < renderText->textLength();
+       m_current.fastIncrementInTextNode()) {
+    bool previousCharacterIsSpace = m_currentCharacterIsSpace;
+    bool previousCharacterShouldCollapseIfPreWap =
+        m_currentCharacterShouldCollapseIfPreWap;
+    UChar c = m_current.current();
+    m_currentCharacterShouldCollapseIfPreWap = m_currentCharacterIsSpace =
+        c == ' ' || c == '\t' || (!m_preservesNewline && (c == '\n'));
+
+    if (!m_collapseWhiteSpace || !m_currentCharacterIsSpace)
+      m_lineInfo.setEmpty(false, m_block, &m_width);
+
+    if (c == softHyphen && m_autoWrap && !hyphenWidth) {
+      hyphenWidth = measureHyphenWidth(
+          renderText, font,
+          textDirectionFromUnicode(m_resolver.position().direction()));
+      m_width.addUncommittedWidth(hyphenWidth);
     }
 
-    if (m_renderTextInfo.m_text != renderText) {
-        m_renderTextInfo.m_text = renderText;
-        m_renderTextInfo.m_font = &font;
-        m_renderTextInfo.m_lineBreakIterator.resetStringAndReleaseIterator(renderText->text(), style->locale());
-    } else if (m_renderTextInfo.m_font != &font) {
-        m_renderTextInfo.m_font = &font;
+    bool applyWordSpacing = false;
+
+    if ((breakAll || breakWords) && !midWordBreak) {
+      wrapW += charWidth;
+      bool midWordBreakIsBeforeSurrogatePair =
+          U16_IS_LEAD(c) && m_current.offset() + 1 < renderText->textLength() &&
+          U16_IS_TRAIL((*renderText)[m_current.offset() + 1]);
+      charWidth = textWidth(renderText, m_current.offset(),
+                            midWordBreakIsBeforeSurrogatePair ? 2 : 1, font,
+                            m_width.committedWidth() + wrapW, isFixedPitch,
+                            m_collapseWhiteSpace);
+
+      float midWordWidth = m_width.committedWidth() + wrapW + charWidth;
+      midWordBreak = midWordWidth > m_width.availableWidth();
+
+      // Check whether there is enough space to fit this character plus an
+      // ellipsis.
+      if (ellipsizeMode &&
+          midWordWidth + ellipsisWidth <= m_width.availableWidth()) {
+        ellipsisBreakOffset = m_current.offset();
+      }
     }
 
-    // Non-zero only when kerning is enabled, in which case we measure
-    // words with their trailing space, then subtract its width.
-    float wordTrailingSpaceWidth = (font.fontDescription().typesettingFeatures() & Kerning) ?
-        font.width(constructTextRun(renderText, font, &space, 1, style, style->direction())) + wordSpacing
-        : 0;
-
-    UChar lastCharacter = m_renderTextInfo.m_lineBreakIterator.lastCharacter();
-    UChar secondToLastCharacter = m_renderTextInfo.m_lineBreakIterator.secondToLastCharacter();
-    for (; m_current.offset() < renderText->textLength(); m_current.fastIncrementInTextNode()) {
-        bool previousCharacterIsSpace = m_currentCharacterIsSpace;
-        bool previousCharacterShouldCollapseIfPreWap = m_currentCharacterShouldCollapseIfPreWap;
-        UChar c = m_current.current();
-        m_currentCharacterShouldCollapseIfPreWap = m_currentCharacterIsSpace = c == ' ' || c == '\t' || (!m_preservesNewline && (c == '\n'));
-
-        if (!m_collapseWhiteSpace || !m_currentCharacterIsSpace)
-            m_lineInfo.setEmpty(false, m_block, &m_width);
-
-        if (c == softHyphen && m_autoWrap && !hyphenWidth) {
-            hyphenWidth = measureHyphenWidth(renderText, font, textDirectionFromUnicode(m_resolver.position().direction()));
-            m_width.addUncommittedWidth(hyphenWidth);
+    int nextBreakablePosition = m_current.nextBreakablePosition();
+    bool betweenWords =
+        c == '\n' || (m_currWS != PRE && !m_atStart &&
+                      isBreakable(m_renderTextInfo.m_lineBreakIterator,
+                                  m_current.offset(), nextBreakablePosition));
+    m_current.setNextBreakablePosition(nextBreakablePosition);
+
+    if (betweenWords || midWordBreak) {
+      bool stoppedIgnoringSpaces = false;
+      if (m_ignoringSpaces) {
+        lastSpaceWordSpacing = 0;
+        if (!m_currentCharacterIsSpace) {
+          // Stop ignoring spaces and begin at this
+          // new point.
+          m_ignoringSpaces = false;
+          wordSpacingForWordMeasurement = 0;
+          lastSpace = m_current.offset();  // e.g., "Foo    goo", don't add in
+                                           // any of the ignored spaces.
+          m_lineMidpointState.stopIgnoringSpaces(
+              InlineIterator(0, m_current.object(), m_current.offset()));
+          stoppedIgnoringSpaces = true;
+        } else {
+          // Just keep ignoring these spaces.
+          nextCharacter(c, lastCharacter, secondToLastCharacter);
+          continue;
         }
+      }
+
+      wordMeasurements.grow(wordMeasurements.size() + 1);
+      WordMeasurement& wordMeasurement = wordMeasurements.last();
+
+      wordMeasurement.renderer = renderText;
+      wordMeasurement.endOffset = m_current.offset();
+      wordMeasurement.startOffset = lastSpace;
+
+      float additionalTempWidth;
+      if (wordTrailingSpaceWidth && c == ' ')
+        additionalTempWidth =
+            textWidth(renderText, lastSpace, m_current.offset() + 1 - lastSpace,
+                      font, m_width.currentWidth(), isFixedPitch,
+                      m_collapseWhiteSpace, &wordMeasurement.fallbackFonts) -
+            wordTrailingSpaceWidth;
+      else
+        additionalTempWidth =
+            textWidth(renderText, lastSpace, m_current.offset() - lastSpace,
+                      font, m_width.currentWidth(), isFixedPitch,
+                      m_collapseWhiteSpace, &wordMeasurement.fallbackFonts);
+
+      wordMeasurement.width =
+          additionalTempWidth + wordSpacingForWordMeasurement;
+      additionalTempWidth += lastSpaceWordSpacing;
+      m_width.addUncommittedWidth(additionalTempWidth);
+
+      if (m_collapseWhiteSpace && previousCharacterIsSpace &&
+          m_currentCharacterIsSpace && additionalTempWidth)
+        m_width.setTrailingWhitespaceWidth(additionalTempWidth);
+
+      if (!m_appliedStartWidth) {
+        m_width.addUncommittedWidth(
+            inlineLogicalWidth(m_current.object(), true, false).toFloat());
+        m_appliedStartWidth = true;
+      }
+
+      applyWordSpacing = wordSpacing && m_currentCharacterIsSpace;
+
+      if (!m_width.committedWidth() && m_autoWrap && !m_width.fitsOnLine())
+        m_width.fitBelowFloats(m_lineInfo.isFirstLine());
 
-        bool applyWordSpacing = false;
-
-        if ((breakAll || breakWords) && !midWordBreak) {
-            wrapW += charWidth;
-            bool midWordBreakIsBeforeSurrogatePair = U16_IS_LEAD(c) && m_current.offset() + 1 < renderText->textLength() && U16_IS_TRAIL((*renderText)[m_current.offset() + 1]);
-            charWidth = textWidth(renderText, m_current.offset(), midWordBreakIsBeforeSurrogatePair ? 2 : 1, font, m_width.committedWidth() + wrapW, isFixedPitch, m_collapseWhiteSpace);
-
-            float midWordWidth = m_width.committedWidth() + wrapW + charWidth;
-            midWordBreak = midWordWidth > m_width.availableWidth();
-
-            // Check whether there is enough space to fit this character plus an ellipsis.
-            if (ellipsizeMode && midWordWidth + ellipsisWidth <= m_width.availableWidth()) {
-                ellipsisBreakOffset = m_current.offset();
-            }
+      if (m_autoWrap || breakWords) {
+        // If we break only after white-space, consider the current character
+        // as candidate width for this line.
+        bool lineWasTooWide = false;
+        if (m_width.fitsOnLine() && m_currentCharacterIsSpace &&
+            m_currentStyle->breakOnlyAfterWhiteSpace() && !midWordBreak) {
+          float charWidth =
+              textWidth(renderText, m_current.offset(), 1, font,
+                        m_width.currentWidth(), isFixedPitch,
+                        m_collapseWhiteSpace, &wordMeasurement.fallbackFonts) +
+              (applyWordSpacing ? wordSpacing : 0);
+          // Check if line is too big even without the extra space
+          // at the end of the line. If it is not, do nothing.
+          // If the line needs the extra whitespace to be too long,
+          // then move the line break to the space.
+          if (!m_width.fitsOnLine(charWidth)) {
+            lineWasTooWide = true;
+            m_lineBreak.moveTo(m_current.object(), m_current.offset(),
+                               m_current.nextBreakablePosition());
+            skipTrailingWhitespace(m_lineBreak, m_lineInfo);
+          }
         }
-
-        int nextBreakablePosition = m_current.nextBreakablePosition();
-        bool betweenWords = c == '\n' || (m_currWS != PRE && !m_atStart && isBreakable(m_renderTextInfo.m_lineBreakIterator, m_current.offset(), nextBreakablePosition));
-        m_current.setNextBreakablePosition(nextBreakablePosition);
-
-        if (betweenWords || midWordBreak) {
-            bool stoppedIgnoringSpaces = false;
-            if (m_ignoringSpaces) {
-                lastSpaceWordSpacing = 0;
-                if (!m_currentCharacterIsSpace) {
-                    // Stop ignoring spaces and begin at this
-                    // new point.
-                    m_ignoringSpaces = false;
-                    wordSpacingForWordMeasurement = 0;
-                    lastSpace = m_current.offset(); // e.g., "Foo    goo", don't add in any of the ignored spaces.
-                    m_lineMidpointState.stopIgnoringSpaces(InlineIterator(0, m_current.object(), m_current.offset()));
-                    stoppedIgnoringSpaces = true;
-                } else {
-                    // Just keep ignoring these spaces.
-                    nextCharacter(c, lastCharacter, secondToLastCharacter);
-                    continue;
-                }
-            }
-
-            wordMeasurements.grow(wordMeasurements.size() + 1);
-            WordMeasurement& wordMeasurement = wordMeasurements.last();
-
-            wordMeasurement.renderer = renderText;
-            wordMeasurement.endOffset = m_current.offset();
-            wordMeasurement.startOffset = lastSpace;
-
-            float additionalTempWidth;
-            if (wordTrailingSpaceWidth && c == ' ')
-                additionalTempWidth = textWidth(renderText, lastSpace, m_current.offset() + 1 - lastSpace, font, m_width.currentWidth(), isFixedPitch, m_collapseWhiteSpace, &wordMeasurement.fallbackFonts) - wordTrailingSpaceWidth;
-            else
-                additionalTempWidth = textWidth(renderText, lastSpace, m_current.offset() - lastSpace, font, m_width.currentWidth(), isFixedPitch, m_collapseWhiteSpace, &wordMeasurement.fallbackFonts);
-
-            wordMeasurement.width = additionalTempWidth + wordSpacingForWordMeasurement;
-            additionalTempWidth += lastSpaceWordSpacing;
-            m_width.addUncommittedWidth(additionalTempWidth);
-
-            if (m_collapseWhiteSpace && previousCharacterIsSpace && m_currentCharacterIsSpace && additionalTempWidth)
-                m_width.setTrailingWhitespaceWidth(additionalTempWidth);
-
-            if (!m_appliedStartWidth) {
-                m_width.addUncommittedWidth(inlineLogicalWidth(m_current.object(), true, false).toFloat());
-                m_appliedStartWidth = true;
-            }
-
-            applyWordSpacing = wordSpacing && m_currentCharacterIsSpace;
-
-            if (!m_width.committedWidth() && m_autoWrap && !m_width.fitsOnLine())
-                m_width.fitBelowFloats(m_lineInfo.isFirstLine());
-
-            if (m_autoWrap || breakWords) {
-                // If we break only after white-space, consider the current character
-                // as candidate width for this line.
-                bool lineWasTooWide = false;
-                if (m_width.fitsOnLine() && m_currentCharacterIsSpace && m_currentStyle->breakOnlyAfterWhiteSpace() && !midWordBreak) {
-                    float charWidth = textWidth(renderText, m_current.offset(), 1, font, m_width.currentWidth(), isFixedPitch, m_collapseWhiteSpace, &wordMeasurement.fallbackFonts) + (applyWordSpacing ? wordSpacing : 0);
-                    // Check if line is too big even without the extra space
-                    // at the end of the line. If it is not, do nothing.
-                    // If the line needs the extra whitespace to be too long,
-                    // then move the line break to the space.
-                    if (!m_width.fitsOnLine(charWidth)) {
-                        lineWasTooWide = true;
-                        m_lineBreak.moveTo(m_current.object(), m_current.offset(), m_current.nextBreakablePosition());
-                        skipTrailingWhitespace(m_lineBreak, m_lineInfo);
-                    }
-                }
-                if (lineWasTooWide || !m_width.fitsOnLine()) {
-                    if (m_lineBreak.atTextParagraphSeparator()) {
-                        if (!stoppedIgnoringSpaces && m_current.offset() > 0)
-                            m_lineMidpointState.ensureCharacterGetsLineBox(m_current);
-                        m_lineBreak.increment();
-                        m_lineInfo.setPreviousLineBrokeCleanly(true);
-                        wordMeasurement.endOffset = m_lineBreak.offset();
-                    }
-                    if (m_lineBreak.object() && m_lineBreak.offset() && m_lineBreak.object()->isText() && toRenderText(m_lineBreak.object())->textLength() && toRenderText(m_lineBreak.object())->characterAt(m_lineBreak.offset() - 1) == softHyphen)
-                        hyphenated = true;
-                    if (m_lineBreak.offset() && m_lineBreak.offset() != (unsigned)wordMeasurement.endOffset && !wordMeasurement.width) {
-                        if (charWidth) {
-                            wordMeasurement.endOffset = m_lineBreak.offset();
-                            wordMeasurement.width = charWidth;
-                        }
-                    }
-
-                    // Didn't fit. Jump to the end unless there's still an opportunity to collapse whitespace.
-                    if (m_ignoringSpaces || !m_collapseWhiteSpace || !m_currentCharacterIsSpace || !previousCharacterIsSpace) {
-                        m_atEnd = true;
-                        return false;
-                    }
-                } else {
-                    if (!betweenWords || (midWordBreak && !m_autoWrap))
-                        m_width.addUncommittedWidth(-additionalTempWidth);
-                    if (hyphenWidth) {
-                        // Subtract the width of the soft hyphen out since we fit on a line.
-                        m_width.addUncommittedWidth(-hyphenWidth);
-                        hyphenWidth = 0;
-                    }
-                }
-            }
-
-            if (c == '\n' && m_preservesNewline) {
-                if (!stoppedIgnoringSpaces && m_current.offset())
-                    m_lineMidpointState.ensureCharacterGetsLineBox(m_current);
-                m_lineBreak.moveTo(m_current.object(), m_current.offset(), m_current.nextBreakablePosition());
-                m_lineBreak.increment();
-                m_lineInfo.setPreviousLineBrokeCleanly(true);
-                m_lineInfo.incrementLineIndex(); // caller only calls this if we return false
-                return true;
-            }
-
-            if (m_autoWrap && betweenWords) {
-                m_width.commit();
-                wrapW = 0;
-                m_lineBreak.moveTo(m_current.object(), m_current.offset(), m_current.nextBreakablePosition());
-                // Auto-wrapping text should not wrap in the middle of a word once it has had an
-                // opportunity to break after a word.
-                breakWords = false;
-            }
-
-            if (midWordBreak && !U16_IS_TRAIL(c) && !(WTF::Unicode::category(c) & (WTF::Unicode::Mark_NonSpacing | WTF::Unicode::Mark_Enclosing | WTF::Unicode::Mark_SpacingCombining))) {
-                if (ellipsizeMode) {
-                    // Break the line at the position where an ellipsis would fit.
-                    m_lineBreak.moveTo(m_current.object(), ellipsisBreakOffset, m_current.nextBreakablePosition());
-                    ellipsized = true;
-                } else {
-                    // Remember this as a breakable position in case
-                    // adding the end width forces a break.
-                    m_lineBreak.moveTo(m_current.object(), m_current.offset(), m_current.nextBreakablePosition());
-                    midWordBreak &= (breakWords || breakAll);
-                }
-            }
-
-            if (betweenWords) {
-                lastSpaceWordSpacing = applyWordSpacing ? wordSpacing : 0;
-                wordSpacingForWordMeasurement = (applyWordSpacing && wordMeasurement.width) ? wordSpacing : 0;
-                lastSpace = m_current.offset();
+        if (lineWasTooWide || !m_width.fitsOnLine()) {
+          if (m_lineBreak.atTextParagraphSeparator()) {
+            if (!stoppedIgnoringSpaces && m_current.offset() > 0)
+              m_lineMidpointState.ensureCharacterGetsLineBox(m_current);
+            m_lineBreak.increment();
+            m_lineInfo.setPreviousLineBrokeCleanly(true);
+            wordMeasurement.endOffset = m_lineBreak.offset();
+          }
+          if (m_lineBreak.object() && m_lineBreak.offset() &&
+              m_lineBreak.object()->isText() &&
+              toRenderText(m_lineBreak.object())->textLength() &&
+              toRenderText(m_lineBreak.object())
+                      ->characterAt(m_lineBreak.offset() - 1) == softHyphen)
+            hyphenated = true;
+          if (m_lineBreak.offset() &&
+              m_lineBreak.offset() != (unsigned)wordMeasurement.endOffset &&
+              !wordMeasurement.width) {
+            if (charWidth) {
+              wordMeasurement.endOffset = m_lineBreak.offset();
+              wordMeasurement.width = charWidth;
             }
+          }
 
-            if (!m_ignoringSpaces && m_currentStyle->collapseWhiteSpace()) {
-                // If we encounter a newline, or if we encounter a
-                // second space, we need to go ahead and break up this
-                // run and enter a mode where we start collapsing spaces.
-                if (m_currentCharacterIsSpace && previousCharacterIsSpace) {
-                    m_ignoringSpaces = true;
-
-                    // We just entered a mode where we are ignoring
-                    // spaces. Create a midpoint to terminate the run
-                    // before the second space.
-                    m_lineMidpointState.startIgnoringSpaces(m_startOfIgnoredSpaces);
-                    m_trailingObjects.updateMidpointsForTrailingObjects(m_lineMidpointState, InlineIterator(), TrailingObjects::DoNotCollapseFirstSpace);
-                }
-            }
-        } else if (m_ignoringSpaces) {
-            // Stop ignoring spaces and begin at this
-            // new point.
-            m_ignoringSpaces = false;
-            lastSpaceWordSpacing = applyWordSpacing ? wordSpacing : 0;
-            wordSpacingForWordMeasurement = (applyWordSpacing && wordMeasurements.last().width) ? wordSpacing : 0;
-            lastSpace = m_current.offset(); // e.g., "Foo    goo", don't add in any of the ignored spaces.
-            m_lineMidpointState.stopIgnoringSpaces(InlineIterator(0, m_current.object(), m_current.offset()));
+          // Didn't fit. Jump to the end unless there's still an opportunity to
+          // collapse whitespace.
+          if (m_ignoringSpaces || !m_collapseWhiteSpace ||
+              !m_currentCharacterIsSpace || !previousCharacterIsSpace) {
+            m_atEnd = true;
+            return false;
+          }
+        } else {
+          if (!betweenWords || (midWordBreak && !m_autoWrap))
+            m_width.addUncommittedWidth(-additionalTempWidth);
+          if (hyphenWidth) {
+            // Subtract the width of the soft hyphen out since we fit on a line.
+            m_width.addUncommittedWidth(-hyphenWidth);
+            hyphenWidth = 0;
+          }
         }
+      }
+
+      if (c == '\n' && m_preservesNewline) {
+        if (!stoppedIgnoringSpaces && m_current.offset())
+          m_lineMidpointState.ensureCharacterGetsLineBox(m_current);
+        m_lineBreak.moveTo(m_current.object(), m_current.offset(),
+                           m_current.nextBreakablePosition());
+        m_lineBreak.increment();
+        m_lineInfo.setPreviousLineBrokeCleanly(true);
+        m_lineInfo
+            .incrementLineIndex();  // caller only calls this if we return false
+        return true;
+      }
 
-        if (m_currentCharacterIsSpace && !previousCharacterIsSpace) {
-            m_startOfIgnoredSpaces.setObject(m_current.object());
-            m_startOfIgnoredSpaces.setOffset(m_current.offset());
+      if (m_autoWrap && betweenWords) {
+        m_width.commit();
+        wrapW = 0;
+        m_lineBreak.moveTo(m_current.object(), m_current.offset(),
+                           m_current.nextBreakablePosition());
+        // Auto-wrapping text should not wrap in the middle of a word once it
+        // has had an opportunity to break after a word.
+        breakWords = false;
+      }
+
+      if (midWordBreak && !U16_IS_TRAIL(c) &&
+          !(WTF::Unicode::category(c) &
+            (WTF::Unicode::Mark_NonSpacing | WTF::Unicode::Mark_Enclosing |
+             WTF::Unicode::Mark_SpacingCombining))) {
+        if (ellipsizeMode) {
+          // Break the line at the position where an ellipsis would fit.
+          m_lineBreak.moveTo(m_current.object(), ellipsisBreakOffset,
+                             m_current.nextBreakablePosition());
+          ellipsized = true;
+        } else {
+          // Remember this as a breakable position in case
+          // adding the end width forces a break.
+          m_lineBreak.moveTo(m_current.object(), m_current.offset(),
+                             m_current.nextBreakablePosition());
+          midWordBreak &= (breakWords || breakAll);
         }
-
-        if (!m_currentCharacterIsSpace && previousCharacterShouldCollapseIfPreWap) {
-            if (m_autoWrap && m_currentStyle->breakOnlyAfterWhiteSpace() && !ellipsizeMode) {
-                m_lineBreak.moveTo(m_current.object(), m_current.offset(), m_current.nextBreakablePosition());
-            }
+      }
+
+      if (betweenWords) {
+        lastSpaceWordSpacing = applyWordSpacing ? wordSpacing : 0;
+        wordSpacingForWordMeasurement =
+            (applyWordSpacing && wordMeasurement.width) ? wordSpacing : 0;
+        lastSpace = m_current.offset();
+      }
+
+      if (!m_ignoringSpaces && m_currentStyle->collapseWhiteSpace()) {
+        // If we encounter a newline, or if we encounter a
+        // second space, we need to go ahead and break up this
+        // run and enter a mode where we start collapsing spaces.
+        if (m_currentCharacterIsSpace && previousCharacterIsSpace) {
+          m_ignoringSpaces = true;
+
+          // We just entered a mode where we are ignoring
+          // spaces. Create a midpoint to terminate the run
+          // before the second space.
+          m_lineMidpointState.startIgnoringSpaces(m_startOfIgnoredSpaces);
+          m_trailingObjects.updateMidpointsForTrailingObjects(
+              m_lineMidpointState, InlineIterator(),
+              TrailingObjects::DoNotCollapseFirstSpace);
         }
-
-        if (m_collapseWhiteSpace && m_currentCharacterIsSpace && !m_ignoringSpaces)
-            m_trailingObjects.setTrailingWhitespace(toRenderText(m_current.object()));
-        else if (!m_currentStyle->collapseWhiteSpace() || !m_currentCharacterIsSpace)
-            m_trailingObjects.clear();
-
-        m_atStart = false;
-        nextCharacter(c, lastCharacter, secondToLastCharacter);
+      }
+    } else if (m_ignoringSpaces) {
+      // Stop ignoring spaces and begin at this
+      // new point.
+      m_ignoringSpaces = false;
+      lastSpaceWordSpacing = applyWordSpacing ? wordSpacing : 0;
+      wordSpacingForWordMeasurement =
+          (applyWordSpacing && wordMeasurements.last().width) ? wordSpacing : 0;
+      lastSpace = m_current.offset();  // e.g., "Foo    goo", don't add in any
+                                       // of the ignored spaces.
+      m_lineMidpointState.stopIgnoringSpaces(
+          InlineIterator(0, m_current.object(), m_current.offset()));
     }
 
-    m_renderTextInfo.m_lineBreakIterator.setPriorContext(lastCharacter, secondToLastCharacter);
-
-    wordMeasurements.grow(wordMeasurements.size() + 1);
-    WordMeasurement& wordMeasurement = wordMeasurements.last();
-    wordMeasurement.renderer = renderText;
-
-    // IMPORTANT: current.m_pos is > length here!
-    float additionalTempWidth = m_ignoringSpaces ? 0 : textWidth(renderText, lastSpace, m_current.offset() - lastSpace, font, m_width.currentWidth(), isFixedPitch, m_collapseWhiteSpace, &wordMeasurement.fallbackFonts);
-    wordMeasurement.startOffset = lastSpace;
-    wordMeasurement.endOffset = m_current.offset();
-    wordMeasurement.width = m_ignoringSpaces ? 0 : additionalTempWidth + wordSpacingForWordMeasurement;
-    additionalTempWidth += lastSpaceWordSpacing;
-
-    LayoutUnit inlineLogicalTempWidth = inlineLogicalWidth(m_current.object(), !m_appliedStartWidth, m_includeEndWidth);
-    m_width.addUncommittedWidth(additionalTempWidth + inlineLogicalTempWidth);
+    if (m_currentCharacterIsSpace && !previousCharacterIsSpace) {
+      m_startOfIgnoredSpaces.setObject(m_current.object());
+      m_startOfIgnoredSpaces.setOffset(m_current.offset());
+    }
 
-    if (m_collapseWhiteSpace && m_currentCharacterIsSpace && additionalTempWidth)
-        m_width.setTrailingWhitespaceWidth(additionalTempWidth + inlineLogicalTempWidth);
+    if (!m_currentCharacterIsSpace && previousCharacterShouldCollapseIfPreWap) {
+      if (m_autoWrap && m_currentStyle->breakOnlyAfterWhiteSpace() &&
+          !ellipsizeMode) {
+        m_lineBreak.moveTo(m_current.object(), m_current.offset(),
+                           m_current.nextBreakablePosition());
+      }
+    }
 
-    m_includeEndWidth = false;
+    if (m_collapseWhiteSpace && m_currentCharacterIsSpace && !m_ignoringSpaces)
+      m_trailingObjects.setTrailingWhitespace(toRenderText(m_current.object()));
+    else if (!m_currentStyle->collapseWhiteSpace() ||
+             !m_currentCharacterIsSpace)
+      m_trailingObjects.clear();
 
-    if (!m_width.fitsOnLine()) {
-        if (!hyphenated && m_lineBreak.previousInSameNode() == softHyphen) {
-            hyphenated = true;
-            m_atEnd = true;
-        }
+    m_atStart = false;
+    nextCharacter(c, lastCharacter, secondToLastCharacter);
+  }
+
+  m_renderTextInfo.m_lineBreakIterator.setPriorContext(lastCharacter,
+                                                       secondToLastCharacter);
+
+  wordMeasurements.grow(wordMeasurements.size() + 1);
+  WordMeasurement& wordMeasurement = wordMeasurements.last();
+  wordMeasurement.renderer = renderText;
+
+  // IMPORTANT: current.m_pos is > length here!
+  float additionalTempWidth =
+      m_ignoringSpaces
+          ? 0
+          : textWidth(renderText, lastSpace, m_current.offset() - lastSpace,
+                      font, m_width.currentWidth(), isFixedPitch,
+                      m_collapseWhiteSpace, &wordMeasurement.fallbackFonts);
+  wordMeasurement.startOffset = lastSpace;
+  wordMeasurement.endOffset = m_current.offset();
+  wordMeasurement.width =
+      m_ignoringSpaces ? 0
+                       : additionalTempWidth + wordSpacingForWordMeasurement;
+  additionalTempWidth += lastSpaceWordSpacing;
+
+  LayoutUnit inlineLogicalTempWidth = inlineLogicalWidth(
+      m_current.object(), !m_appliedStartWidth, m_includeEndWidth);
+  m_width.addUncommittedWidth(additionalTempWidth + inlineLogicalTempWidth);
+
+  if (m_collapseWhiteSpace && m_currentCharacterIsSpace && additionalTempWidth)
+    m_width.setTrailingWhitespaceWidth(additionalTempWidth +
+                                       inlineLogicalTempWidth);
+
+  m_includeEndWidth = false;
+
+  if (!m_width.fitsOnLine()) {
+    if (!hyphenated && m_lineBreak.previousInSameNode() == softHyphen) {
+      hyphenated = true;
+      m_atEnd = true;
     }
-    return false;
+  }
+  return false;
 }
 
-inline void BreakingContext::commitAndUpdateLineBreakIfNeeded()
-{
-    bool checkForBreak = m_autoWrap;
-    if (m_width.committedWidth() && !m_width.fitsOnLine() && m_lineBreak.object() && m_currWS == NOWRAP) {
-        checkForBreak = true;
-    } else if (m_nextObject && m_current.object()->isText() && m_nextObject->isText() && (m_autoWrap || m_nextObject->style()->autoWrap())) {
-        if (m_autoWrap && m_currentCharacterIsSpace) {
-            checkForBreak = true;
-        } else {
-            RenderText* nextText = toRenderText(m_nextObject);
-            if (nextText->textLength()) {
-                UChar c = nextText->characterAt(0);
-                // If the next item on the line is text, and if we did not end with
-                // a space, then the next text run continues our word (and so it needs to
-                // keep adding to the uncommitted width. Just update and continue.
-                checkForBreak = !m_currentCharacterIsSpace && (c == ' ' || c == '\t' || (c == '\n' && !m_nextObject->preservesNewline()));
-            }
-
-            if (!m_width.fitsOnLine() && !m_width.committedWidth())
-                m_width.fitBelowFloats(m_lineInfo.isFirstLine());
+inline void BreakingContext::commitAndUpdateLineBreakIfNeeded() {
+  bool checkForBreak = m_autoWrap;
+  if (m_width.committedWidth() && !m_width.fitsOnLine() &&
+      m_lineBreak.object() && m_currWS == NOWRAP) {
+    checkForBreak = true;
+  } else if (m_nextObject && m_current.object()->isText() &&
+             m_nextObject->isText() &&
+             (m_autoWrap || m_nextObject->style()->autoWrap())) {
+    if (m_autoWrap && m_currentCharacterIsSpace) {
+      checkForBreak = true;
+    } else {
+      RenderText* nextText = toRenderText(m_nextObject);
+      if (nextText->textLength()) {
+        UChar c = nextText->characterAt(0);
+        // If the next item on the line is text, and if we did not end with
+        // a space, then the next text run continues our word (and so it needs
+        // to keep adding to the uncommitted width. Just update and continue.
+        checkForBreak = !m_currentCharacterIsSpace &&
+                        (c == ' ' || c == '\t' ||
+                         (c == '\n' && !m_nextObject->preservesNewline()));
+      }
+
+      if (!m_width.fitsOnLine() && !m_width.committedWidth())
+        m_width.fitBelowFloats(m_lineInfo.isFirstLine());
 
-            bool canPlaceOnLine = m_width.fitsOnLine() || !m_autoWrapWasEverTrueOnLine;
-            if (canPlaceOnLine && checkForBreak) {
-                m_width.commit();
-                m_lineBreak.moveToStartOf(m_nextObject);
-            }
-        }
+      bool canPlaceOnLine =
+          m_width.fitsOnLine() || !m_autoWrapWasEverTrueOnLine;
+      if (canPlaceOnLine && checkForBreak) {
+        m_width.commit();
+        m_lineBreak.moveToStartOf(m_nextObject);
+      }
+    }
+  }
+
+  ASSERT_WITH_SECURITY_IMPLICATION(m_currentStyle->refCount() > 0);
+  if (checkForBreak && !m_width.fitsOnLine()) {
+    // if we have floats, try to get below them.
+    if (m_currentCharacterIsSpace && !m_ignoringSpaces &&
+        m_currentStyle->collapseWhiteSpace())
+      m_trailingObjects.clear();
+
+    if (m_width.committedWidth()) {
+      m_atEnd = true;
+      return;
     }
 
-    ASSERT_WITH_SECURITY_IMPLICATION(m_currentStyle->refCount() > 0);
-    if (checkForBreak && !m_width.fitsOnLine()) {
-        // if we have floats, try to get below them.
-        if (m_currentCharacterIsSpace && !m_ignoringSpaces && m_currentStyle->collapseWhiteSpace())
-            m_trailingObjects.clear();
-
-        if (m_width.committedWidth()) {
-            m_atEnd = true;
-            return;
-        }
-
-        m_width.fitBelowFloats(m_lineInfo.isFirstLine());
+    m_width.fitBelowFloats(m_lineInfo.isFirstLine());
 
-        // |width| may have been adjusted because we got shoved down past a float (thus
-        // giving us more room), so we need to retest, and only jump to
-        // the end label if we still don't fit on the line. -dwh
-        if (!m_width.fitsOnLine()) {
-            m_atEnd = true;
-            return;
-        }
-    } else if (m_blockStyle->autoWrap() && !m_width.fitsOnLine() && !m_width.committedWidth()) {
-        // If the container autowraps but the current child does not then we still need to ensure that it
-        // wraps and moves below any floats.
-        m_width.fitBelowFloats(m_lineInfo.isFirstLine());
+    // |width| may have been adjusted because we got shoved down past a float
+    // (thus giving us more room), so we need to retest, and only jump to the
+    // end label if we still don't fit on the line. -dwh
+    if (!m_width.fitsOnLine()) {
+      m_atEnd = true;
+      return;
     }
-
-    if (!m_current.object()->isFloatingOrOutOfFlowPositioned()) {
-        m_lastObject = m_current.object();
-        if (m_lastObject->isReplaced() && m_autoWrap) {
-            m_width.commit();
-            m_lineBreak.moveToStartOf(m_nextObject);
-        }
+  } else if (m_blockStyle->autoWrap() && !m_width.fitsOnLine() &&
+             !m_width.committedWidth()) {
+    // If the container autowraps but the current child does not then we still
+    // need to ensure that it wraps and moves below any floats.
+    m_width.fitBelowFloats(m_lineInfo.isFirstLine());
+  }
+
+  if (!m_current.object()->isFloatingOrOutOfFlowPositioned()) {
+    m_lastObject = m_current.object();
+    if (m_lastObject->isReplaced() && m_autoWrap) {
+      m_width.commit();
+      m_lineBreak.moveToStartOf(m_nextObject);
     }
+  }
 }
 
-inline IndentTextOrNot requiresIndent(bool isFirstLine, bool isAfterHardLineBreak, RenderStyle* style)
-{
-    IndentTextOrNot shouldIndentText = DoNotIndentText;
-    if (isFirstLine || (isAfterHardLineBreak && style->textIndentLine()) == TextIndentEachLine)
-        shouldIndentText = IndentText;
+inline IndentTextOrNot requiresIndent(bool isFirstLine,
+                                      bool isAfterHardLineBreak,
+                                      RenderStyle* style) {
+  IndentTextOrNot shouldIndentText = DoNotIndentText;
+  if (isFirstLine ||
+      (isAfterHardLineBreak && style->textIndentLine()) == TextIndentEachLine)
+    shouldIndentText = IndentText;
 
-    if (style->textIndentType() == TextIndentHanging)
-        shouldIndentText = shouldIndentText == IndentText ? DoNotIndentText : IndentText;
+  if (style->textIndentType() == TextIndentHanging)
+    shouldIndentText =
+        shouldIndentText == IndentText ? DoNotIndentText : IndentText;
 
-    return shouldIndentText;
+  return shouldIndentText;
 }
 
-}
+}  // namespace blink
 
 #endif  // SKY_ENGINE_CORE_RENDERING_LINE_BREAKINGCONTEXTINLINEHEADERS_H_
diff --git a/sky/engine/core/rendering/line/LineBreaker.cpp b/sky/engine/core/rendering/line/LineBreaker.cpp
index 354f6184d178b..3e16bb9c1610e 100644
--- a/sky/engine/core/rendering/line/LineBreaker.cpp
+++ b/sky/engine/core/rendering/line/LineBreaker.cpp
@@ -1,6 +1,7 @@
 /*
  * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
- * Copyright (C) 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All right reserved.
+ * Copyright (C) 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All
+ * right reserved.
  * Copyright (C) 2010 Google Inc. All rights reserved.
  *
  * This library is free software; you can redistribute it and/or
@@ -26,78 +27,88 @@
 
 namespace blink {
 
-void LineBreaker::skipLeadingWhitespace(InlineBidiResolver& resolver, LineInfo& lineInfo,
-    FloatingObject* lastFloatFromPreviousLine, LineWidth& width)
-{
-    while (!resolver.position().atEnd() && !requiresLineBox(resolver.position(), lineInfo, LeadingWhitespace)) {
-        RenderObject* object = resolver.position().object();
-        if (object->isOutOfFlowPositioned()
-            && object->style()->isOriginalDisplayInlineType()) {
-            resolver.runs().addRun(createRun(0, 1, object, resolver));
-            lineInfo.incrementRunsFromLeadingWhitespace();
-        }
-        resolver.position().increment(&resolver);
+void LineBreaker::skipLeadingWhitespace(
+    InlineBidiResolver& resolver,
+    LineInfo& lineInfo,
+    FloatingObject* lastFloatFromPreviousLine,
+    LineWidth& width) {
+  while (!resolver.position().atEnd() &&
+         !requiresLineBox(resolver.position(), lineInfo, LeadingWhitespace)) {
+    RenderObject* object = resolver.position().object();
+    if (object->isOutOfFlowPositioned() &&
+        object->style()->isOriginalDisplayInlineType()) {
+      resolver.runs().addRun(createRun(0, 1, object, resolver));
+      lineInfo.incrementRunsFromLeadingWhitespace();
     }
-    resolver.commitExplicitEmbedding(resolver.runs());
+    resolver.position().increment(&resolver);
+  }
+  resolver.commitExplicitEmbedding(resolver.runs());
 }
 
-void LineBreaker::reset()
-{
-    m_positionedObjects.clear();
-    m_hyphenated = false;
-    m_ellipsized = false;
+void LineBreaker::reset() {
+  m_positionedObjects.clear();
+  m_hyphenated = false;
+  m_ellipsized = false;
 }
 
-InlineIterator LineBreaker::nextLineBreak(InlineBidiResolver& resolver, LineInfo& lineInfo,
-    RenderTextInfo& renderTextInfo, FloatingObject* lastFloatFromPreviousLine,
-    WordMeasurements& wordMeasurements)
-{
-    reset();
-
-    ASSERT(resolver.position().root() == m_block);
-
-    bool appliedStartWidth = resolver.position().offset() > 0;
-
-    LineWidth width(*m_block, lineInfo.isFirstLine(), requiresIndent(lineInfo.isFirstLine(), lineInfo.previousLineBrokeCleanly(), m_block->style()));
-
-    skipLeadingWhitespace(resolver, lineInfo, lastFloatFromPreviousLine, width);
-
-    if (resolver.position().atEnd())
-        return resolver.position();
-
-    BreakingContext context(resolver, lineInfo, width, renderTextInfo, lastFloatFromPreviousLine, appliedStartWidth, m_block);
+InlineIterator LineBreaker::nextLineBreak(
+    InlineBidiResolver& resolver,
+    LineInfo& lineInfo,
+    RenderTextInfo& renderTextInfo,
+    FloatingObject* lastFloatFromPreviousLine,
+    WordMeasurements& wordMeasurements) {
+  reset();
+
+  ASSERT(resolver.position().root() == m_block);
+
+  bool appliedStartWidth = resolver.position().offset() > 0;
+
+  LineWidth width(
+      *m_block, lineInfo.isFirstLine(),
+      requiresIndent(lineInfo.isFirstLine(),
+                     lineInfo.previousLineBrokeCleanly(), m_block->style()));
+
+  skipLeadingWhitespace(resolver, lineInfo, lastFloatFromPreviousLine, width);
+
+  if (resolver.position().atEnd())
+    return resolver.position();
+
+  BreakingContext context(resolver, lineInfo, width, renderTextInfo,
+                          lastFloatFromPreviousLine, appliedStartWidth,
+                          m_block);
+
+  while (context.currentObject()) {
+    context.initializeForCurrentObject();
+    if (context.currentObject()->isOutOfFlowPositioned()) {
+      context.handleOutOfFlowPositioned(m_positionedObjects);
+    } else if (context.currentObject()->isRenderInline()) {
+      context.handleEmptyInline();
+    } else if (context.currentObject()->isReplaced()) {
+      context.handleReplaced();
+    } else if (context.currentObject()->isText()) {
+      if (context.handleText(wordMeasurements, m_hyphenated, m_ellipsized)) {
+        // We've hit a hard text line break. Our line break iterator is updated,
+        // so go ahead and early return.
+        return context.lineBreak();
+      }
+    } else {
+      ASSERT_NOT_REACHED();
+    }
 
-    while (context.currentObject()) {
-        context.initializeForCurrentObject();
-        if (context.currentObject()->isOutOfFlowPositioned()) {
-            context.handleOutOfFlowPositioned(m_positionedObjects);
-        } else if (context.currentObject()->isRenderInline()) {
-            context.handleEmptyInline();
-        } else if (context.currentObject()->isReplaced()) {
-            context.handleReplaced();
-        } else if (context.currentObject()->isText()) {
-            if (context.handleText(wordMeasurements, m_hyphenated, m_ellipsized)) {
-                // We've hit a hard text line break. Our line break iterator is updated, so go ahead and early return.
-                return context.lineBreak();
-            }
-        } else {
-            ASSERT_NOT_REACHED();
-        }
+    if (context.atEnd())
+      return context.handleEndOfLine();
 
-        if (context.atEnd())
-            return context.handleEndOfLine();
+    context.commitAndUpdateLineBreakIfNeeded();
 
-        context.commitAndUpdateLineBreakIfNeeded();
+    if (context.atEnd())
+      return context.handleEndOfLine();
 
-        if (context.atEnd())
-            return context.handleEndOfLine();
+    context.increment();
+  }
 
-        context.increment();
-    }
+  context.clearLineBreakIfFitsOnLine();
 
-    context.clearLineBreakIfFitsOnLine();
-
-    return context.handleEndOfLine();
+  return context.handleEndOfLine();
 }
 
-}
+}  // namespace blink
diff --git a/sky/engine/core/rendering/line/LineBreaker.h b/sky/engine/core/rendering/line/LineBreaker.h
index f5d7158640c53..8f61f2f387a3c 100644
--- a/sky/engine/core/rendering/line/LineBreaker.h
+++ b/sky/engine/core/rendering/line/LineBreaker.h
@@ -1,6 +1,7 @@
 /*
  * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
- * Copyright (C) 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All right reserved.
+ * Copyright (C) 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All
+ * right reserved.
  * Copyright (C) 2010 Google Inc. All rights reserved.
  *
  * This library is free software; you can redistribute it and/or
@@ -34,31 +35,34 @@ enum WhitespacePosition { LeadingWhitespace, TrailingWhitespace };
 struct RenderTextInfo;
 
 class LineBreaker {
-public:
-    friend class BreakingContext;
-    LineBreaker(RenderParagraph* block)
-        : m_block(block)
-    {
-        reset();
-    }
+ public:
+  friend class BreakingContext;
+  LineBreaker(RenderParagraph* block) : m_block(block) { reset(); }
 
-    InlineIterator nextLineBreak(InlineBidiResolver&, LineInfo&, RenderTextInfo&,
-        FloatingObject* lastFloatFromPreviousLine, WordMeasurements&);
+  InlineIterator nextLineBreak(InlineBidiResolver&,
+                               LineInfo&,
+                               RenderTextInfo&,
+                               FloatingObject* lastFloatFromPreviousLine,
+                               WordMeasurements&);
 
-    bool lineWasHyphenated() { return m_hyphenated; }
-    bool lineWasEllipsized() { return m_ellipsized; }
-    const Vector& positionedObjects() { return m_positionedObjects; }
-private:
-    void reset();
+  bool lineWasHyphenated() { return m_hyphenated; }
+  bool lineWasEllipsized() { return m_ellipsized; }
+  const Vector& positionedObjects() { return m_positionedObjects; }
 
-    void skipLeadingWhitespace(InlineBidiResolver&, LineInfo&, FloatingObject* lastFloatFromPreviousLine, LineWidth&);
+ private:
+  void reset();
 
-    RenderParagraph* m_block;
-    bool m_hyphenated;
-    bool m_ellipsized;
-    Vector m_positionedObjects;
+  void skipLeadingWhitespace(InlineBidiResolver&,
+                             LineInfo&,
+                             FloatingObject* lastFloatFromPreviousLine,
+                             LineWidth&);
+
+  RenderParagraph* m_block;
+  bool m_hyphenated;
+  bool m_ellipsized;
+  Vector m_positionedObjects;
 };
 
-}
+}  // namespace blink
 
 #endif  // SKY_ENGINE_CORE_RENDERING_LINE_LINEBREAKER_H_
diff --git a/sky/engine/core/rendering/line/LineInfo.h b/sky/engine/core/rendering/line/LineInfo.h
index ea4b338828b2c..64b3410d33acc 100644
--- a/sky/engine/core/rendering/line/LineInfo.h
+++ b/sky/engine/core/rendering/line/LineInfo.h
@@ -1,6 +1,7 @@
 /*
  * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
- * Copyright (C) 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All right reserved.
+ * Copyright (C) 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All
+ * right reserved.
  * Copyright (C) 2010 Google Inc. All rights reserved.
  * Copyright (C) 2013 Adobe Systems Incorporated.
  *
@@ -29,45 +30,47 @@
 namespace blink {
 
 class LineInfo {
-public:
-    LineInfo()
-        : m_isFirstLine(true)
-        , m_isLastLine(false)
-        , m_isEmpty(true)
-        , m_previousLineBrokeCleanly(true)
-        , m_runsFromLeadingWhitespace(0)
-        , m_lineIndex(0)
-    { }
+ public:
+  LineInfo()
+      : m_isFirstLine(true),
+        m_isLastLine(false),
+        m_isEmpty(true),
+        m_previousLineBrokeCleanly(true),
+        m_runsFromLeadingWhitespace(0),
+        m_lineIndex(0) {}
 
-    bool isFirstLine() const { return m_isFirstLine; }
-    bool isLastLine() const { return m_isLastLine; }
-    bool isEmpty() const { return m_isEmpty; }
-    bool previousLineBrokeCleanly() const { return m_previousLineBrokeCleanly; }
-    unsigned runsFromLeadingWhitespace() const { return m_runsFromLeadingWhitespace; }
-    void resetRunsFromLeadingWhitespace() { m_runsFromLeadingWhitespace = 0; }
-    void incrementRunsFromLeadingWhitespace() { m_runsFromLeadingWhitespace++; }
+  bool isFirstLine() const { return m_isFirstLine; }
+  bool isLastLine() const { return m_isLastLine; }
+  bool isEmpty() const { return m_isEmpty; }
+  bool previousLineBrokeCleanly() const { return m_previousLineBrokeCleanly; }
+  unsigned runsFromLeadingWhitespace() const {
+    return m_runsFromLeadingWhitespace;
+  }
+  void resetRunsFromLeadingWhitespace() { m_runsFromLeadingWhitespace = 0; }
+  void incrementRunsFromLeadingWhitespace() { m_runsFromLeadingWhitespace++; }
 
-    void setFirstLine(bool firstLine) { m_isFirstLine = firstLine; }
-    void setLastLine(bool lastLine) { m_isLastLine = lastLine; }
-    void setEmpty(bool empty, RenderBlock* block = 0, LineWidth* lineWidth = 0)
-    {
-        m_isEmpty = empty;
-    }
+  void setFirstLine(bool firstLine) { m_isFirstLine = firstLine; }
+  void setLastLine(bool lastLine) { m_isLastLine = lastLine; }
+  void setEmpty(bool empty, RenderBlock* block = 0, LineWidth* lineWidth = 0) {
+    m_isEmpty = empty;
+  }
 
-    void incrementLineIndex() { ++m_lineIndex; }
-    int lineIndex() const { return m_lineIndex; }
+  void incrementLineIndex() { ++m_lineIndex; }
+  int lineIndex() const { return m_lineIndex; }
 
-    void setPreviousLineBrokeCleanly(bool previousLineBrokeCleanly) { m_previousLineBrokeCleanly = previousLineBrokeCleanly; }
+  void setPreviousLineBrokeCleanly(bool previousLineBrokeCleanly) {
+    m_previousLineBrokeCleanly = previousLineBrokeCleanly;
+  }
 
-private:
-    bool m_isFirstLine;
-    bool m_isLastLine;
-    bool m_isEmpty;
-    bool m_previousLineBrokeCleanly;
-    unsigned m_runsFromLeadingWhitespace;
-    int m_lineIndex;
+ private:
+  bool m_isFirstLine;
+  bool m_isLastLine;
+  bool m_isEmpty;
+  bool m_previousLineBrokeCleanly;
+  unsigned m_runsFromLeadingWhitespace;
+  int m_lineIndex;
 };
 
-}
+}  // namespace blink
 
 #endif  // SKY_ENGINE_CORE_RENDERING_LINE_LINEINFO_H_
diff --git a/sky/engine/core/rendering/line/LineLayoutState.h b/sky/engine/core/rendering/line/LineLayoutState.h
index 5dca6c1111b05..b454e54ffa7a0 100644
--- a/sky/engine/core/rendering/line/LineLayoutState.h
+++ b/sky/engine/core/rendering/line/LineLayoutState.h
@@ -1,6 +1,7 @@
 /*
  * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
- * Copyright (C) 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All right reserved.
+ * Copyright (C) 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All
+ * right reserved.
  * Copyright (C) 2010 Google Inc. All rights reserved.
  * Copyright (C) 2014 Adobe Systems Incorporated. All rights reserved.
  *
@@ -30,54 +31,62 @@
 namespace blink {
 
 // LineLayoutState keeps track of global information
-// during an entire linebox tree layout pass (aka RenderParagraph::layoutChildren).
+// during an entire linebox tree layout pass (aka
+// RenderParagraph::layoutChildren).
 class LineLayoutState {
-public:
-    LineLayoutState(bool fullLayout)
-        : m_endLine(0)
-        , m_endLineLogicalTop(0)
-        , m_endLineMatched(false)
-        , m_hasInlineChild(false)
-        , m_isFullLayout(fullLayout)
-        , m_adjustedLogicalLineTop(0)
-    { }
-
-    void markForFullLayout() { m_isFullLayout = true; }
-    bool isFullLayout() const { return m_isFullLayout; }
-
-    bool endLineMatched() const { return m_endLineMatched; }
-    void setEndLineMatched(bool endLineMatched) { m_endLineMatched = endLineMatched; }
-
-    bool hasInlineChild() const { return m_hasInlineChild; }
-    void setHasInlineChild(bool hasInlineChild) { m_hasInlineChild = hasInlineChild; }
-
-    LineInfo& lineInfo() { return m_lineInfo; }
-    const LineInfo& lineInfo() const { return m_lineInfo; }
-
-    LayoutUnit endLineLogicalTop() const { return m_endLineLogicalTop; }
-    void setEndLineLogicalTop(LayoutUnit logicalTop) { m_endLineLogicalTop = logicalTop; }
-
-    RootInlineBox* endLine() const { return m_endLine; }
-    void setEndLine(RootInlineBox* line) { m_endLine = line; }
-
-    LayoutUnit adjustedLogicalLineTop() const { return m_adjustedLogicalLineTop; }
-    void setAdjustedLogicalLineTop(LayoutUnit value) { m_adjustedLogicalLineTop = value; }
-
-private:
-    RootInlineBox* m_endLine;
-    LineInfo m_lineInfo;
-    LayoutUnit m_endLineLogicalTop;
-    bool m_endLineMatched;
-    // FIXME(sky): Do we still need this?
-    // Used as a performance optimization to avoid doing a full paint invalidation when our floats
-    // change but we don't have any inline children.
-    bool m_hasInlineChild;
-
-    bool m_isFullLayout;
-
-    LayoutUnit m_adjustedLogicalLineTop;
+ public:
+  LineLayoutState(bool fullLayout)
+      : m_endLine(0),
+        m_endLineLogicalTop(0),
+        m_endLineMatched(false),
+        m_hasInlineChild(false),
+        m_isFullLayout(fullLayout),
+        m_adjustedLogicalLineTop(0) {}
+
+  void markForFullLayout() { m_isFullLayout = true; }
+  bool isFullLayout() const { return m_isFullLayout; }
+
+  bool endLineMatched() const { return m_endLineMatched; }
+  void setEndLineMatched(bool endLineMatched) {
+    m_endLineMatched = endLineMatched;
+  }
+
+  bool hasInlineChild() const { return m_hasInlineChild; }
+  void setHasInlineChild(bool hasInlineChild) {
+    m_hasInlineChild = hasInlineChild;
+  }
+
+  LineInfo& lineInfo() { return m_lineInfo; }
+  const LineInfo& lineInfo() const { return m_lineInfo; }
+
+  LayoutUnit endLineLogicalTop() const { return m_endLineLogicalTop; }
+  void setEndLineLogicalTop(LayoutUnit logicalTop) {
+    m_endLineLogicalTop = logicalTop;
+  }
+
+  RootInlineBox* endLine() const { return m_endLine; }
+  void setEndLine(RootInlineBox* line) { m_endLine = line; }
+
+  LayoutUnit adjustedLogicalLineTop() const { return m_adjustedLogicalLineTop; }
+  void setAdjustedLogicalLineTop(LayoutUnit value) {
+    m_adjustedLogicalLineTop = value;
+  }
+
+ private:
+  RootInlineBox* m_endLine;
+  LineInfo m_lineInfo;
+  LayoutUnit m_endLineLogicalTop;
+  bool m_endLineMatched;
+  // FIXME(sky): Do we still need this?
+  // Used as a performance optimization to avoid doing a full paint invalidation
+  // when our floats change but we don't have any inline children.
+  bool m_hasInlineChild;
+
+  bool m_isFullLayout;
+
+  LayoutUnit m_adjustedLogicalLineTop;
 };
 
-}
+}  // namespace blink
 
 #endif  // SKY_ENGINE_CORE_RENDERING_LINE_LINELAYOUTSTATE_H_
diff --git a/sky/engine/core/rendering/line/LineWidth.cpp b/sky/engine/core/rendering/line/LineWidth.cpp
index 189934a8447d6..64f9e0f7be5e3 100644
--- a/sky/engine/core/rendering/line/LineWidth.cpp
+++ b/sky/engine/core/rendering/line/LineWidth.cpp
@@ -34,75 +34,76 @@
 
 namespace blink {
 
-LineWidth::LineWidth(RenderParagraph& block, bool isFirstLine, IndentTextOrNot shouldIndentText)
-    : m_block(block)
-    , m_uncommittedWidth(0)
-    , m_committedWidth(0)
-    , m_trailingWhitespaceWidth(0)
-    , m_left(0)
-    , m_right(0)
-    , m_availableWidth(0)
-    , m_shouldIndentText(shouldIndentText)
-{
-    updateAvailableWidth();
+LineWidth::LineWidth(RenderParagraph& block,
+                     bool isFirstLine,
+                     IndentTextOrNot shouldIndentText)
+    : m_block(block),
+      m_uncommittedWidth(0),
+      m_committedWidth(0),
+      m_trailingWhitespaceWidth(0),
+      m_left(0),
+      m_right(0),
+      m_availableWidth(0),
+      m_shouldIndentText(shouldIndentText) {
+  updateAvailableWidth();
 }
 
-void LineWidth::updateAvailableWidth()
-{
-    m_left = m_block.logicalLeftOffsetForLine(shouldIndentText()).toFloat();
-    m_right = m_block.logicalRightOffsetForLine(shouldIndentText()).toFloat();
-    computeAvailableWidthFromLeftAndRight();
+void LineWidth::updateAvailableWidth() {
+  m_left = m_block.logicalLeftOffsetForLine(shouldIndentText()).toFloat();
+  m_right = m_block.logicalRightOffsetForLine(shouldIndentText()).toFloat();
+  computeAvailableWidthFromLeftAndRight();
 }
 
-void LineWidth::commit()
-{
-    m_committedWidth += m_uncommittedWidth;
-    m_uncommittedWidth = 0;
+void LineWidth::commit() {
+  m_committedWidth += m_uncommittedWidth;
+  m_uncommittedWidth = 0;
 }
 
-void LineWidth::updateLineDimension(LayoutUnit newLineTop, LayoutUnit newLineWidth, const float& newLineLeft, const float& newLineRight)
-{
-    if (newLineWidth <= m_availableWidth)
-        return;
-
-    m_block.setLogicalHeight(newLineTop);
-    m_availableWidth = newLineWidth.toFloat();
-    m_left = newLineLeft;
-    m_right = newLineRight;
+void LineWidth::updateLineDimension(LayoutUnit newLineTop,
+                                    LayoutUnit newLineWidth,
+                                    const float& newLineLeft,
+                                    const float& newLineRight) {
+  if (newLineWidth <= m_availableWidth)
+    return;
+
+  m_block.setLogicalHeight(newLineTop);
+  m_availableWidth = newLineWidth.toFloat();
+  m_left = newLineLeft;
+  m_right = newLineRight;
 }
 
-
-void LineWidth::fitBelowFloats(bool isFirstLine)
-{
-    ASSERT(!m_committedWidth);
-    ASSERT(!fitsOnLine());
-
-    LayoutUnit floatLogicalBottom;
-    LayoutUnit lastFloatLogicalBottom = m_block.logicalHeight();
-    float newLineWidth = m_availableWidth;
-    float newLineLeft = m_left;
-    float newLineRight = m_right;
-
-    while (true) {
-        floatLogicalBottom = lastFloatLogicalBottom;
-        if (floatLogicalBottom <= lastFloatLogicalBottom)
-            break;
-
-        newLineLeft = m_block.logicalLeftOffsetForLine(shouldIndentText()).toFloat();
-        newLineRight = m_block.logicalRightOffsetForLine(shouldIndentText()).toFloat();
-        newLineWidth = std::max(0.0f, newLineRight - newLineLeft);
-
-        lastFloatLogicalBottom = floatLogicalBottom;
-
-        if (newLineWidth >= m_uncommittedWidth)
-            break;
-    }
-    updateLineDimension(lastFloatLogicalBottom, newLineWidth, newLineLeft, newLineRight);
+void LineWidth::fitBelowFloats(bool isFirstLine) {
+  ASSERT(!m_committedWidth);
+  ASSERT(!fitsOnLine());
+
+  LayoutUnit floatLogicalBottom;
+  LayoutUnit lastFloatLogicalBottom = m_block.logicalHeight();
+  float newLineWidth = m_availableWidth;
+  float newLineLeft = m_left;
+  float newLineRight = m_right;
+
+  while (true) {
+    floatLogicalBottom = lastFloatLogicalBottom;
+    if (floatLogicalBottom <= lastFloatLogicalBottom)
+      break;
+
+    newLineLeft =
+        m_block.logicalLeftOffsetForLine(shouldIndentText()).toFloat();
+    newLineRight =
+        m_block.logicalRightOffsetForLine(shouldIndentText()).toFloat();
+    newLineWidth = std::max(0.0f, newLineRight - newLineLeft);
+
+    lastFloatLogicalBottom = floatLogicalBottom;
+
+    if (newLineWidth >= m_uncommittedWidth)
+      break;
+  }
+  updateLineDimension(lastFloatLogicalBottom, newLineWidth, newLineLeft,
+                      newLineRight);
 }
 
-void LineWidth::computeAvailableWidthFromLeftAndRight()
-{
-    m_availableWidth = max(0.0f, m_right - m_left);
+void LineWidth::computeAvailableWidthFromLeftAndRight() {
+  m_availableWidth = max(0.0f, m_right - m_left);
 }
 
-}
+}  // namespace blink
diff --git a/sky/engine/core/rendering/line/LineWidth.h b/sky/engine/core/rendering/line/LineWidth.h
index eea27070eade6..8ef514fbf3090 100644
--- a/sky/engine/core/rendering/line/LineWidth.h
+++ b/sky/engine/core/rendering/line/LineWidth.h
@@ -42,45 +42,61 @@ enum IndentTextOrNot { DoNotIndentText, IndentText };
 enum WhitespaceTreatment { ExcludeWhitespace, IncludeWhitespace };
 
 class LineWidth {
-public:
-    LineWidth(RenderParagraph&, bool isFirstLine, IndentTextOrNot shouldIndentText);
+ public:
+  LineWidth(RenderParagraph&,
+            bool isFirstLine,
+            IndentTextOrNot shouldIndentText);
 
-    bool fitsOnLine() const { return currentWidth() <= (m_availableWidth + LayoutUnit::epsilon()); }
-    bool fitsOnLine(float extra) const { return currentWidth() + extra <= (m_availableWidth + LayoutUnit::epsilon()); }
-    bool fitsOnLine(float extra, WhitespaceTreatment whitespaceTreatment) const
-    {
-        return currentWidth() - (whitespaceTreatment == ExcludeWhitespace ? trailingWhitespaceWidth() : 0) + extra <= (m_availableWidth + LayoutUnit::epsilon());
-    }
+  bool fitsOnLine() const {
+    return currentWidth() <= (m_availableWidth + LayoutUnit::epsilon());
+  }
+  bool fitsOnLine(float extra) const {
+    return currentWidth() + extra <= (m_availableWidth + LayoutUnit::epsilon());
+  }
+  bool fitsOnLine(float extra, WhitespaceTreatment whitespaceTreatment) const {
+    return currentWidth() -
+               (whitespaceTreatment == ExcludeWhitespace
+                    ? trailingWhitespaceWidth()
+                    : 0) +
+               extra <=
+           (m_availableWidth + LayoutUnit::epsilon());
+  }
 
-    float currentWidth() const { return m_committedWidth + m_uncommittedWidth; }
-    // FIXME: We should eventually replace these three functions by ones that work on a higher abstraction.
-    float uncommittedWidth() const { return m_uncommittedWidth; }
-    float committedWidth() const { return m_committedWidth; }
-    float availableWidth() const { return m_availableWidth; }
-    float trailingWhitespaceWidth() const { return m_trailingWhitespaceWidth; }
+  float currentWidth() const { return m_committedWidth + m_uncommittedWidth; }
+  // FIXME: We should eventually replace these three functions by ones that work
+  // on a higher abstraction.
+  float uncommittedWidth() const { return m_uncommittedWidth; }
+  float committedWidth() const { return m_committedWidth; }
+  float availableWidth() const { return m_availableWidth; }
+  float trailingWhitespaceWidth() const { return m_trailingWhitespaceWidth; }
 
-    void updateAvailableWidth();
-    void addUncommittedWidth(float delta) { m_uncommittedWidth += delta; }
-    void commit();
-    void fitBelowFloats(bool isFirstLine = false);
-    void setTrailingWhitespaceWidth(float width) { m_trailingWhitespaceWidth = width; }
+  void updateAvailableWidth();
+  void addUncommittedWidth(float delta) { m_uncommittedWidth += delta; }
+  void commit();
+  void fitBelowFloats(bool isFirstLine = false);
+  void setTrailingWhitespaceWidth(float width) {
+    m_trailingWhitespaceWidth = width;
+  }
 
-    bool shouldIndentText() const { return m_shouldIndentText == IndentText; }
+  bool shouldIndentText() const { return m_shouldIndentText == IndentText; }
 
-private:
-    void computeAvailableWidthFromLeftAndRight();
-    void updateLineDimension(LayoutUnit newLineTop, LayoutUnit newLineWidth, const float& newLineLeft, const float& newLineRight);
+ private:
+  void computeAvailableWidthFromLeftAndRight();
+  void updateLineDimension(LayoutUnit newLineTop,
+                           LayoutUnit newLineWidth,
+                           const float& newLineLeft,
+                           const float& newLineRight);
 
-    RenderParagraph& m_block;
-    float m_uncommittedWidth;
-    float m_committedWidth;
-    float m_trailingWhitespaceWidth;
-    float m_left;
-    float m_right;
-    float m_availableWidth;
-    IndentTextOrNot m_shouldIndentText;
+  RenderParagraph& m_block;
+  float m_uncommittedWidth;
+  float m_committedWidth;
+  float m_trailingWhitespaceWidth;
+  float m_left;
+  float m_right;
+  float m_availableWidth;
+  IndentTextOrNot m_shouldIndentText;
 };
 
-}
+}  // namespace blink
 
 #endif  // SKY_ENGINE_CORE_RENDERING_LINE_LINEWIDTH_H_
diff --git a/sky/engine/core/rendering/line/RenderTextInfo.h b/sky/engine/core/rendering/line/RenderTextInfo.h
index 4288fd6f8235b..672aa1fd647a8 100644
--- a/sky/engine/core/rendering/line/RenderTextInfo.h
+++ b/sky/engine/core/rendering/line/RenderTextInfo.h
@@ -1,6 +1,7 @@
 /*
  * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
- * Copyright (C) 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All right reserved.
+ * Copyright (C) 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All
+ * right reserved.
  * Copyright (C) 2010 Google Inc. All rights reserved.
  *
  * This library is free software; you can redistribute it and/or
@@ -31,17 +32,13 @@ class Font;
 class RenderText;
 
 struct RenderTextInfo {
-    RenderTextInfo()
-        : m_text(0)
-        , m_font(0)
-    {
-    }
-
-    RenderText* m_text;
-    LazyLineBreakIterator m_lineBreakIterator;
-    const Font* m_font;
+  RenderTextInfo() : m_text(0), m_font(0) {}
+
+  RenderText* m_text;
+  LazyLineBreakIterator m_lineBreakIterator;
+  const Font* m_font;
 };
 
-} // namespace blink
+}  // namespace blink
 
 #endif  // SKY_ENGINE_CORE_RENDERING_LINE_RENDERTEXTINFO_H_
diff --git a/sky/engine/core/rendering/line/TrailingObjects.cpp b/sky/engine/core/rendering/line/TrailingObjects.cpp
index 279507c4721dc..1235976270e46 100644
--- a/sky/engine/core/rendering/line/TrailingObjects.cpp
+++ b/sky/engine/core/rendering/line/TrailingObjects.cpp
@@ -1,6 +1,7 @@
 /*
  * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
- * Copyright (C) 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All right reserved.
+ * Copyright (C) 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All
+ * right reserved.
  * Copyright (C) 2010 Google Inc. All rights reserved.
  * Copyright (C) 2014 Adobe Systems Inc.
  *
@@ -27,45 +28,56 @@
 
 namespace blink {
 
-void TrailingObjects::updateMidpointsForTrailingObjects(LineMidpointState& lineMidpointState, const InlineIterator& lBreak, CollapseFirstSpaceOrNot collapseFirstSpace)
-{
-    if (!m_whitespace)
-        return;
+void TrailingObjects::updateMidpointsForTrailingObjects(
+    LineMidpointState& lineMidpointState,
+    const InlineIterator& lBreak,
+    CollapseFirstSpaceOrNot collapseFirstSpace) {
+  if (!m_whitespace)
+    return;
 
-    // This object is either going to be part of the last midpoint, or it is going to be the actual endpoint.
-    // In both cases we just decrease our pos by 1 level to exclude the space, allowing it to - in effect - collapse into the newline.
-    if (lineMidpointState.numMidpoints() % 2) {
-        // Find the trailing space object's midpoint.
-        int trailingSpaceMidpoint = lineMidpointState.numMidpoints() - 1;
-        for ( ; trailingSpaceMidpoint > 0 && lineMidpointState.midpoints()[trailingSpaceMidpoint].object() != m_whitespace; --trailingSpaceMidpoint) { }
-        ASSERT(trailingSpaceMidpoint >= 0);
-        if (collapseFirstSpace == CollapseFirstSpace)
-            lineMidpointState.midpoints()[trailingSpaceMidpoint].setOffset(lineMidpointState.midpoints()[trailingSpaceMidpoint].offset() -1);
+  // This object is either going to be part of the last midpoint, or it is going
+  // to be the actual endpoint. In both cases we just decrease our pos by 1
+  // level to exclude the space, allowing it to - in effect - collapse into the
+  // newline.
+  if (lineMidpointState.numMidpoints() % 2) {
+    // Find the trailing space object's midpoint.
+    int trailingSpaceMidpoint = lineMidpointState.numMidpoints() - 1;
+    for (; trailingSpaceMidpoint > 0 &&
+           lineMidpointState.midpoints()[trailingSpaceMidpoint].object() !=
+               m_whitespace;
+         --trailingSpaceMidpoint) {
+    }
+    ASSERT(trailingSpaceMidpoint >= 0);
+    if (collapseFirstSpace == CollapseFirstSpace)
+      lineMidpointState.midpoints()[trailingSpaceMidpoint].setOffset(
+          lineMidpointState.midpoints()[trailingSpaceMidpoint].offset() - 1);
 
-        // Now make sure every single trailingPositionedBox following the trailingSpaceMidpoint properly stops and starts
-        // ignoring spaces.
-        size_t currentMidpoint = trailingSpaceMidpoint + 1;
-        for (size_t i = 0; i < m_objects.size(); ++i) {
-            if (currentMidpoint >= lineMidpointState.numMidpoints()) {
-                // We don't have a midpoint for this box yet.
-                lineMidpointState.ensureLineBoxInsideIgnoredSpaces(m_objects[i]);
-            } else {
-                ASSERT(lineMidpointState.midpoints()[currentMidpoint].object() == m_objects[i]);
-                ASSERT(lineMidpointState.midpoints()[currentMidpoint + 1].object() == m_objects[i]);
-            }
-            currentMidpoint += 2;
-        }
-    } else if (!lBreak.object()) {
-        ASSERT(collapseFirstSpace == CollapseFirstSpace);
-        // Add a new end midpoint that stops right at the very end.
-        unsigned length = m_whitespace->textLength();
-        unsigned pos = length >= 2 ? length - 2 : UINT_MAX;
-        InlineIterator endMid(0, m_whitespace, pos);
-        lineMidpointState.startIgnoringSpaces(endMid);
-        for (size_t i = 0; i < m_objects.size(); ++i) {
-            lineMidpointState.ensureLineBoxInsideIgnoredSpaces(m_objects[i]);
-        }
+    // Now make sure every single trailingPositionedBox following the
+    // trailingSpaceMidpoint properly stops and starts ignoring spaces.
+    size_t currentMidpoint = trailingSpaceMidpoint + 1;
+    for (size_t i = 0; i < m_objects.size(); ++i) {
+      if (currentMidpoint >= lineMidpointState.numMidpoints()) {
+        // We don't have a midpoint for this box yet.
+        lineMidpointState.ensureLineBoxInsideIgnoredSpaces(m_objects[i]);
+      } else {
+        ASSERT(lineMidpointState.midpoints()[currentMidpoint].object() ==
+               m_objects[i]);
+        ASSERT(lineMidpointState.midpoints()[currentMidpoint + 1].object() ==
+               m_objects[i]);
+      }
+      currentMidpoint += 2;
+    }
+  } else if (!lBreak.object()) {
+    ASSERT(collapseFirstSpace == CollapseFirstSpace);
+    // Add a new end midpoint that stops right at the very end.
+    unsigned length = m_whitespace->textLength();
+    unsigned pos = length >= 2 ? length - 2 : UINT_MAX;
+    InlineIterator endMid(0, m_whitespace, pos);
+    lineMidpointState.startIgnoringSpaces(endMid);
+    for (size_t i = 0; i < m_objects.size(); ++i) {
+      lineMidpointState.ensureLineBoxInsideIgnoredSpaces(m_objects[i]);
     }
+  }
 }
 
-}
+}  // namespace blink
diff --git a/sky/engine/core/rendering/line/TrailingObjects.h b/sky/engine/core/rendering/line/TrailingObjects.h
index d6ed8a07735f5..19910495d8ece 100644
--- a/sky/engine/core/rendering/line/TrailingObjects.h
+++ b/sky/engine/core/rendering/line/TrailingObjects.h
@@ -1,6 +1,7 @@
 /*
  * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
- * Copyright (C) 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All right reserved.
+ * Copyright (C) 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All
+ * right reserved.
  * Copyright (C) 2010 Google Inc. All rights reserved.
  * Copyright (C) 2014 Adobe Systems Inc.
  *
@@ -34,61 +35,62 @@ class RenderText;
 
 struct BidiRun;
 
-template  class BidiResolver;
-template  class MidpointState;
+template 
+class BidiResolver;
+template 
+class MidpointState;
 typedef BidiResolver InlineBidiResolver;
 typedef MidpointState LineMidpointState;
 
-// This class allows us to ensure lineboxes are created in the right place on the line when
-// an out-of-flow positioned object or an empty inline is encountered between a trailing space
-// and subsequent spaces and we want to ignore (i.e. collapse) surplus whitespace. So for example:
+// This class allows us to ensure lineboxes are created in the right place on
+// the line when an out-of-flow positioned object or an empty inline is
+// encountered between a trailing space and subsequent spaces and we want to
+// ignore (i.e. collapse) surplus whitespace. So for example:
 //   
X Y
// or //
X
Y
-// In both of the above snippets the inline and the positioned object occur after a trailing space -// and before a space that will cause our line breaking algorithm to start ignoring spaces. When it -// does that we want to ensure that the inline/positioned object gets a linebox and that it is part -// of the collapsed whitespace. So to achieve this we use appendObjectIfNeeded() to keep track of -// objects encountered after a trailing whitespace and updateMidpointsForTrailingObjects() to put -// them in the right place when we start ignoring surplus whitespace. +// In both of the above snippets the inline and the positioned object occur +// after a trailing space and before a space that will cause our line breaking +// algorithm to start ignoring spaces. When it does that we want to ensure that +// the inline/positioned object gets a linebox and that it is part of the +// collapsed whitespace. So to achieve this we use appendObjectIfNeeded() to +// keep track of objects encountered after a trailing whitespace and +// updateMidpointsForTrailingObjects() to put them in the right place when we +// start ignoring surplus whitespace. class TrailingObjects { -public: - TrailingObjects() - : m_whitespace(0) - { - } - - void setTrailingWhitespace(RenderText* whitespace) - { - ASSERT(whitespace); - m_whitespace = whitespace; - } - - void clear() - { - m_whitespace = 0; - // Using resize(0) rather than clear() here saves 2% on - // PerformanceTests/Layout/line-layout.html because we avoid freeing and - // re-allocating the underlying buffer repeatedly. - m_objects.resize(0); - } - - void appendObjectIfNeeded(RenderObject* object) - { - if (m_whitespace) - m_objects.append(object); - } - - enum CollapseFirstSpaceOrNot { DoNotCollapseFirstSpace, CollapseFirstSpace }; - - void updateMidpointsForTrailingObjects(LineMidpointState&, const InlineIterator& lBreak, CollapseFirstSpaceOrNot); - -private: - RenderText* m_whitespace; - Vector m_objects; + public: + TrailingObjects() : m_whitespace(0) {} + + void setTrailingWhitespace(RenderText* whitespace) { + ASSERT(whitespace); + m_whitespace = whitespace; + } + + void clear() { + m_whitespace = 0; + // Using resize(0) rather than clear() here saves 2% on + // PerformanceTests/Layout/line-layout.html because we avoid freeing and + // re-allocating the underlying buffer repeatedly. + m_objects.resize(0); + } + + void appendObjectIfNeeded(RenderObject* object) { + if (m_whitespace) + m_objects.append(object); + } + + enum CollapseFirstSpaceOrNot { DoNotCollapseFirstSpace, CollapseFirstSpace }; + + void updateMidpointsForTrailingObjects(LineMidpointState&, + const InlineIterator& lBreak, + CollapseFirstSpaceOrNot); + + private: + RenderText* m_whitespace; + Vector m_objects; }; -} +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_LINE_TRAILINGOBJECTS_H_ diff --git a/sky/engine/core/rendering/line/WordMeasurement.h b/sky/engine/core/rendering/line/WordMeasurement.h index 1f9c875a28ea4..35821be188a0e 100644 --- a/sky/engine/core/rendering/line/WordMeasurement.h +++ b/sky/engine/core/rendering/line/WordMeasurement.h @@ -1,6 +1,7 @@ /* * Copyright (C) 2000 Lars Knoll (knoll@kde.org) - * Copyright (C) 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All right reserved. + * Copyright (C) 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All + * right reserved. * Copyright (C) 2010 Google Inc. All rights reserved. * Copyright (C) 2013 Adobe Systems Incorporated. * @@ -32,22 +33,16 @@ namespace blink { class RenderText; class WordMeasurement { -public: - WordMeasurement() - : renderer(0) - , width(0) - , startOffset(0) - , endOffset(0) - { - } - - RenderText* renderer; - float width; - int startOffset; - int endOffset; - HashSet fallbackFonts; + public: + WordMeasurement() : renderer(0), width(0), startOffset(0), endOffset(0) {} + + RenderText* renderer; + float width; + int startOffset; + int endOffset; + HashSet fallbackFonts; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_LINE_WORDMEASUREMENT_H_ diff --git a/sky/engine/core/rendering/style/AppliedTextDecoration.cpp b/sky/engine/core/rendering/style/AppliedTextDecoration.cpp index 01ebbb6fa8da4..eea0c16965ea2 100644 --- a/sky/engine/core/rendering/style/AppliedTextDecoration.cpp +++ b/sky/engine/core/rendering/style/AppliedTextDecoration.cpp @@ -6,30 +6,23 @@ namespace blink { -AppliedTextDecoration::AppliedTextDecoration(TextDecoration line, TextDecorationStyle style, StyleColor color) - : m_line(line) - , m_style(style) - , m_color(color) -{ -} +AppliedTextDecoration::AppliedTextDecoration(TextDecoration line, + TextDecorationStyle style, + StyleColor color) + : m_line(line), m_style(style), m_color(color) {} AppliedTextDecoration::AppliedTextDecoration(TextDecoration line) - : m_line(line) - , m_style(TextDecorationStyleSolid) - , m_color(StyleColor::currentColor()) -{ -} + : m_line(line), + m_style(TextDecorationStyleSolid), + m_color(StyleColor::currentColor()) {} AppliedTextDecoration::AppliedTextDecoration() - : m_line(TextDecorationUnderline) - , m_style(TextDecorationStyleSolid) - , m_color(StyleColor::currentColor()) -{ -} + : m_line(TextDecorationUnderline), + m_style(TextDecorationStyleSolid), + m_color(StyleColor::currentColor()) {} -bool AppliedTextDecoration::operator==(const AppliedTextDecoration& o) const -{ - return m_color == o.m_color && m_line == o.m_line && m_style == o.m_style; +bool AppliedTextDecoration::operator==(const AppliedTextDecoration& o) const { + return m_color == o.m_color && m_line == o.m_line && m_style == o.m_style; } -} // namespace blink +} // namespace blink diff --git a/sky/engine/core/rendering/style/AppliedTextDecoration.h b/sky/engine/core/rendering/style/AppliedTextDecoration.h index dc7a00d1dce36..3db2cb1b8a583 100644 --- a/sky/engine/core/rendering/style/AppliedTextDecoration.h +++ b/sky/engine/core/rendering/style/AppliedTextDecoration.h @@ -11,24 +11,31 @@ namespace blink { class AppliedTextDecoration { -public: - AppliedTextDecoration(TextDecoration, TextDecorationStyle, StyleColor); - explicit AppliedTextDecoration(TextDecoration); - AppliedTextDecoration(); - - TextDecoration line() const { return static_cast(m_line); } - TextDecorationStyle style() const { return static_cast(m_style); } - - bool isSimpleUnderline() const { return m_line == TextDecorationUnderline && m_style == TextDecorationStyleSolid && m_color.isCurrentColor(); } - bool operator==(const AppliedTextDecoration&) const; - bool operator!=(const AppliedTextDecoration& o) const { return !(*this == o); } - -private: - unsigned m_line : TextDecorationBits; - unsigned m_style : 3; // TextDecorationStyle - StyleColor m_color; + public: + AppliedTextDecoration(TextDecoration, TextDecorationStyle, StyleColor); + explicit AppliedTextDecoration(TextDecoration); + AppliedTextDecoration(); + + TextDecoration line() const { return static_cast(m_line); } + TextDecorationStyle style() const { + return static_cast(m_style); + } + + bool isSimpleUnderline() const { + return m_line == TextDecorationUnderline && + m_style == TextDecorationStyleSolid && m_color.isCurrentColor(); + } + bool operator==(const AppliedTextDecoration&) const; + bool operator!=(const AppliedTextDecoration& o) const { + return !(*this == o); + } + + private: + unsigned m_line : TextDecorationBits; + unsigned m_style : 3; // TextDecorationStyle + StyleColor m_color; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_STYLE_APPLIEDTEXTDECORATION_H_ diff --git a/sky/engine/core/rendering/style/BorderData.h b/sky/engine/core/rendering/style/BorderData.h index 2ba6d4269a61e..30efea937c067 100644 --- a/sky/engine/core/rendering/style/BorderData.h +++ b/sky/engine/core/rendering/style/BorderData.h @@ -32,105 +32,93 @@ namespace blink { class BorderData { -friend class RenderStyle; -public: - BorderData() : m_topLeft(Length(0, Fixed), Length(0, Fixed)) - , m_topRight(Length(0, Fixed), Length(0, Fixed)) - , m_bottomLeft(Length(0, Fixed), Length(0, Fixed)) - , m_bottomRight(Length(0, Fixed), Length(0, Fixed)) - { - } - bool hasBorder() const - { - return m_left.nonZero() || m_right.nonZero() || m_top.nonZero() || m_bottom.nonZero(); - } - - bool hasBorderRadius() const - { - if (!m_topLeft.width().isZero()) - return true; - if (!m_topRight.width().isZero()) - return true; - if (!m_bottomLeft.width().isZero()) - return true; - if (!m_bottomRight.width().isZero()) - return true; - return false; - } - - unsigned borderLeftWidth() const - { - if ((m_left.style() == BNONE || m_left.style() == BHIDDEN)) - return 0; - return m_left.width(); - } - - unsigned borderRightWidth() const - { - if ((m_right.style() == BNONE || m_right.style() == BHIDDEN)) - return 0; - return m_right.width(); - } - - unsigned borderTopWidth() const - { - if ((m_top.style() == BNONE || m_top.style() == BHIDDEN)) - return 0; - return m_top.width(); - } - - unsigned borderBottomWidth() const - { - if ((m_bottom.style() == BNONE || m_bottom.style() == BHIDDEN)) - return 0; - return m_bottom.width(); - } - - bool operator==(const BorderData& o) const - { - return m_left == o.m_left && m_right == o.m_right && m_top == o.m_top && m_bottom == o.m_bottom - && m_topLeft == o.m_topLeft && m_topRight == o.m_topRight && m_bottomLeft == o.m_bottomLeft && m_bottomRight == o.m_bottomRight; - } - - bool visuallyEqual(const BorderData& o) const - { - return m_left.visuallyEqual(o.m_left) - && m_right.visuallyEqual(o.m_right) - && m_top.visuallyEqual(o.m_top) - && m_bottom.visuallyEqual(o.m_bottom) - && m_topLeft == o.m_topLeft - && m_topRight == o.m_topRight - && m_bottomLeft == o.m_bottomLeft - && m_bottomRight == o.m_bottomRight; - } - - bool operator!=(const BorderData& o) const - { - return !(*this == o); - } - - const BorderValue& left() const { return m_left; } - const BorderValue& right() const { return m_right; } - const BorderValue& top() const { return m_top; } - const BorderValue& bottom() const { return m_bottom; } - - const LengthSize& topLeft() const { return m_topLeft; } - const LengthSize& topRight() const { return m_topRight; } - const LengthSize& bottomLeft() const { return m_bottomLeft; } - const LengthSize& bottomRight() const { return m_bottomRight; } - -private: - BorderValue m_left; - BorderValue m_right; - BorderValue m_top; - BorderValue m_bottom; - - LengthSize m_topLeft; - LengthSize m_topRight; - LengthSize m_bottomLeft; - LengthSize m_bottomRight; + friend class RenderStyle; + + public: + BorderData() + : m_topLeft(Length(0, Fixed), Length(0, Fixed)), + m_topRight(Length(0, Fixed), Length(0, Fixed)), + m_bottomLeft(Length(0, Fixed), Length(0, Fixed)), + m_bottomRight(Length(0, Fixed), Length(0, Fixed)) {} + bool hasBorder() const { + return m_left.nonZero() || m_right.nonZero() || m_top.nonZero() || + m_bottom.nonZero(); + } + + bool hasBorderRadius() const { + if (!m_topLeft.width().isZero()) + return true; + if (!m_topRight.width().isZero()) + return true; + if (!m_bottomLeft.width().isZero()) + return true; + if (!m_bottomRight.width().isZero()) + return true; + return false; + } + + unsigned borderLeftWidth() const { + if ((m_left.style() == BNONE || m_left.style() == BHIDDEN)) + return 0; + return m_left.width(); + } + + unsigned borderRightWidth() const { + if ((m_right.style() == BNONE || m_right.style() == BHIDDEN)) + return 0; + return m_right.width(); + } + + unsigned borderTopWidth() const { + if ((m_top.style() == BNONE || m_top.style() == BHIDDEN)) + return 0; + return m_top.width(); + } + + unsigned borderBottomWidth() const { + if ((m_bottom.style() == BNONE || m_bottom.style() == BHIDDEN)) + return 0; + return m_bottom.width(); + } + + bool operator==(const BorderData& o) const { + return m_left == o.m_left && m_right == o.m_right && m_top == o.m_top && + m_bottom == o.m_bottom && m_topLeft == o.m_topLeft && + m_topRight == o.m_topRight && m_bottomLeft == o.m_bottomLeft && + m_bottomRight == o.m_bottomRight; + } + + bool visuallyEqual(const BorderData& o) const { + return m_left.visuallyEqual(o.m_left) && m_right.visuallyEqual(o.m_right) && + m_top.visuallyEqual(o.m_top) && m_bottom.visuallyEqual(o.m_bottom) && + m_topLeft == o.m_topLeft && m_topRight == o.m_topRight && + m_bottomLeft == o.m_bottomLeft && m_bottomRight == o.m_bottomRight; + } + + bool operator!=(const BorderData& o) const { return !(*this == o); } + + const BorderValue& left() const { return m_left; } + const BorderValue& right() const { return m_right; } + const BorderValue& top() const { return m_top; } + const BorderValue& bottom() const { return m_bottom; } + + const LengthSize& topLeft() const { return m_topLeft; } + const LengthSize& topRight() const { return m_topRight; } + const LengthSize& bottomLeft() const { return m_bottomLeft; } + const LengthSize& bottomRight() const { return m_bottomRight; } + + private: + BorderValue m_left; + BorderValue m_right; + BorderValue m_top; + BorderValue m_bottom; + + LengthSize m_topLeft; + LengthSize m_topRight; + LengthSize m_bottomLeft; + LengthSize m_bottomRight; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_STYLE_BORDERDATA_H_ diff --git a/sky/engine/core/rendering/style/BorderValue.h b/sky/engine/core/rendering/style/BorderValue.h index c78acbda390fa..4325b2c0a203c 100644 --- a/sky/engine/core/rendering/style/BorderValue.h +++ b/sky/engine/core/rendering/style/BorderValue.h @@ -32,76 +32,73 @@ namespace blink { class BorderValue { -friend class RenderStyle; -public: - BorderValue() - : m_color(0) - , m_colorIsCurrentColor(true) - , m_width(3) - , m_style(BNONE) - , m_isAuto(AUTO_OFF) - { - } - - bool nonZero(bool checkStyle = true) const - { - return width() && (!checkStyle || m_style != BNONE); - } - - bool isTransparent() const - { - return !m_colorIsCurrentColor && !m_color.alpha(); - } - - bool isVisible(bool checkStyle = true) const - { - return nonZero(checkStyle) && !isTransparent() && (!checkStyle || m_style != BHIDDEN); - } - - bool operator==(const BorderValue& o) const - { - return m_width == o.m_width && m_style == o.m_style && m_color == o.m_color && m_colorIsCurrentColor == o.m_colorIsCurrentColor; - } - - // The default width is 3px, but if the style is none we compute a value of 0 (in RenderStyle itself) - bool visuallyEqual(const BorderValue& o) const - { - if (m_style == BNONE && o.m_style == BNONE) - return true; - if (m_style == BHIDDEN && o.m_style == BHIDDEN) - return true; - return *this == o; - } - - bool operator!=(const BorderValue& o) const - { - return !(*this == o); - } - - void setColor(const StyleColor& color) - { - m_color = color.resolve(Color()); - m_colorIsCurrentColor = color.isCurrentColor(); - } - - StyleColor color() const { return m_colorIsCurrentColor ? StyleColor::currentColor() : StyleColor(m_color); } - - unsigned width() const { return m_width; } - - EBorderStyle style() const { return static_cast(m_style); } - void setStyle(EBorderStyle style) { m_style = style; } - -protected: - Color m_color; - unsigned m_colorIsCurrentColor : 1; - - unsigned m_width : 26; - unsigned m_style : 4; // EBorderStyle - - // This is only used by OutlineValue but moved here to keep the bits packed. - unsigned m_isAuto : 1; // OutlineIsAuto + friend class RenderStyle; + + public: + BorderValue() + : m_color(0), + m_colorIsCurrentColor(true), + m_width(3), + m_style(BNONE), + m_isAuto(AUTO_OFF) {} + + bool nonZero(bool checkStyle = true) const { + return width() && (!checkStyle || m_style != BNONE); + } + + bool isTransparent() const { + return !m_colorIsCurrentColor && !m_color.alpha(); + } + + bool isVisible(bool checkStyle = true) const { + return nonZero(checkStyle) && !isTransparent() && + (!checkStyle || m_style != BHIDDEN); + } + + bool operator==(const BorderValue& o) const { + return m_width == o.m_width && m_style == o.m_style && + m_color == o.m_color && + m_colorIsCurrentColor == o.m_colorIsCurrentColor; + } + + // The default width is 3px, but if the style is none we compute a value of 0 + // (in RenderStyle itself) + bool visuallyEqual(const BorderValue& o) const { + if (m_style == BNONE && o.m_style == BNONE) + return true; + if (m_style == BHIDDEN && o.m_style == BHIDDEN) + return true; + return *this == o; + } + + bool operator!=(const BorderValue& o) const { return !(*this == o); } + + void setColor(const StyleColor& color) { + m_color = color.resolve(Color()); + m_colorIsCurrentColor = color.isCurrentColor(); + } + + StyleColor color() const { + return m_colorIsCurrentColor ? StyleColor::currentColor() + : StyleColor(m_color); + } + + unsigned width() const { return m_width; } + + EBorderStyle style() const { return static_cast(m_style); } + void setStyle(EBorderStyle style) { m_style = style; } + + protected: + Color m_color; + unsigned m_colorIsCurrentColor : 1; + + unsigned m_width : 26; + unsigned m_style : 4; // EBorderStyle + + // This is only used by OutlineValue but moved here to keep the bits packed. + unsigned m_isAuto : 1; // OutlineIsAuto }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_STYLE_BORDERVALUE_H_ diff --git a/sky/engine/core/rendering/style/CollapsedBorderValue.h b/sky/engine/core/rendering/style/CollapsedBorderValue.h index 7372f184df3db..2b6f1c0f3333b 100644 --- a/sky/engine/core/rendering/style/CollapsedBorderValue.h +++ b/sky/engine/core/rendering/style/CollapsedBorderValue.h @@ -30,48 +30,51 @@ namespace blink { class CollapsedBorderValue { -public: - CollapsedBorderValue() - : m_color(0) - , m_colorIsCurrentColor(true) - , m_width(0) - , m_style(BNONE) - , m_precedence(BOFF) - , m_transparent(false) - { - } + public: + CollapsedBorderValue() + : m_color(0), + m_colorIsCurrentColor(true), + m_width(0), + m_style(BNONE), + m_precedence(BOFF), + m_transparent(false) {} - CollapsedBorderValue(const BorderValue& border, const StyleColor& color, EBorderPrecedence precedence) - : m_color(color.resolve(Color())) - , m_colorIsCurrentColor(color.isCurrentColor()) - , m_width(border.nonZero() ? border.width() : 0) - , m_style(border.style()) - , m_precedence(precedence) - , m_transparent(border.isTransparent()) - { - } + CollapsedBorderValue(const BorderValue& border, + const StyleColor& color, + EBorderPrecedence precedence) + : m_color(color.resolve(Color())), + m_colorIsCurrentColor(color.isCurrentColor()), + m_width(border.nonZero() ? border.width() : 0), + m_style(border.style()), + m_precedence(precedence), + m_transparent(border.isTransparent()) {} - unsigned width() const { return m_style > BHIDDEN ? m_width : 0; } - EBorderStyle style() const { return static_cast(m_style); } - bool exists() const { return m_precedence != BOFF; } - StyleColor color() const { return m_colorIsCurrentColor ? StyleColor::currentColor() : StyleColor(m_color); } - bool isTransparent() const { return m_transparent; } - EBorderPrecedence precedence() const { return static_cast(m_precedence); } + unsigned width() const { return m_style > BHIDDEN ? m_width : 0; } + EBorderStyle style() const { return static_cast(m_style); } + bool exists() const { return m_precedence != BOFF; } + StyleColor color() const { + return m_colorIsCurrentColor ? StyleColor::currentColor() + : StyleColor(m_color); + } + bool isTransparent() const { return m_transparent; } + EBorderPrecedence precedence() const { + return static_cast(m_precedence); + } - bool isSameIgnoringColor(const CollapsedBorderValue& o) const - { - return width() == o.width() && style() == o.style() && precedence() == o.precedence(); - } + bool isSameIgnoringColor(const CollapsedBorderValue& o) const { + return width() == o.width() && style() == o.style() && + precedence() == o.precedence(); + } -private: - Color m_color; - unsigned m_colorIsCurrentColor : 1; - unsigned m_width : 23; - unsigned m_style : 4; // EBorderStyle - unsigned m_precedence : 3; // EBorderPrecedence - unsigned m_transparent : 1; + private: + Color m_color; + unsigned m_colorIsCurrentColor : 1; + unsigned m_width : 23; + unsigned m_style : 4; // EBorderStyle + unsigned m_precedence : 3; // EBorderPrecedence + unsigned m_transparent : 1; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_STYLE_COLLAPSEDBORDERVALUE_H_ diff --git a/sky/engine/core/rendering/style/CounterDirectives.cpp b/sky/engine/core/rendering/style/CounterDirectives.cpp index 7bb043ccaafa3..fb6de7e5d1821 100644 --- a/sky/engine/core/rendering/style/CounterDirectives.cpp +++ b/sky/engine/core/rendering/style/CounterDirectives.cpp @@ -24,19 +24,17 @@ namespace blink { -bool operator==(const CounterDirectives& a, const CounterDirectives& b) -{ - return a.isIncrement() == b.isIncrement() - && a.incrementValue() == b.incrementValue() - && a.isReset() == b.isReset() - && a.resetValue() == b.resetValue(); +bool operator==(const CounterDirectives& a, const CounterDirectives& b) { + return a.isIncrement() == b.isIncrement() && + a.incrementValue() == b.incrementValue() && + a.isReset() == b.isReset() && a.resetValue() == b.resetValue(); } -PassOwnPtr clone(const CounterDirectiveMap& counterDirectives) -{ - OwnPtr result = adoptPtr(new CounterDirectiveMap); - *result = counterDirectives; - return result.release(); +PassOwnPtr clone( + const CounterDirectiveMap& counterDirectives) { + OwnPtr result = adoptPtr(new CounterDirectiveMap); + *result = counterDirectives; + return result.release(); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/core/rendering/style/CounterDirectives.h b/sky/engine/core/rendering/style/CounterDirectives.h index ba7d751abc5db..db76a29feffcc 100644 --- a/sky/engine/core/rendering/style/CounterDirectives.h +++ b/sky/engine/core/rendering/style/CounterDirectives.h @@ -34,78 +34,71 @@ namespace blink { class CounterDirectives { -public: - CounterDirectives() - : m_isResetSet(false) - , m_isIncrementSet(false) - , m_resetValue(0) - , m_incrementValue(0) - { - } + public: + CounterDirectives() + : m_isResetSet(false), + m_isIncrementSet(false), + m_resetValue(0), + m_incrementValue(0) {} - // FIXME: The code duplication here could possibly be replaced by using two - // maps, or by using a container that held two generic Directive objects. + // FIXME: The code duplication here could possibly be replaced by using two + // maps, or by using a container that held two generic Directive objects. - bool isReset() const { return m_isResetSet; } - int resetValue() const { return m_resetValue; } - void setResetValue(int value) - { - m_resetValue = value; - m_isResetSet = true; - } - void clearReset() - { - m_resetValue = 0; - m_isResetSet = false; - } - void inheritReset(CounterDirectives& parent) - { - m_resetValue = parent.m_resetValue; - m_isResetSet = parent.m_isResetSet; - } + bool isReset() const { return m_isResetSet; } + int resetValue() const { return m_resetValue; } + void setResetValue(int value) { + m_resetValue = value; + m_isResetSet = true; + } + void clearReset() { + m_resetValue = 0; + m_isResetSet = false; + } + void inheritReset(CounterDirectives& parent) { + m_resetValue = parent.m_resetValue; + m_isResetSet = parent.m_isResetSet; + } - bool isIncrement() const { return m_isIncrementSet; } - int incrementValue() const { return m_incrementValue; } - void addIncrementValue(int value) - { - m_incrementValue = clampToInteger((double)m_incrementValue + value); - m_isIncrementSet = true; - } - void clearIncrement() - { - m_incrementValue = 0; - m_isIncrementSet = false; - } - void inheritIncrement(CounterDirectives& parent) - { - m_incrementValue = parent.m_incrementValue; - m_isIncrementSet = parent.m_isIncrementSet; - } + bool isIncrement() const { return m_isIncrementSet; } + int incrementValue() const { return m_incrementValue; } + void addIncrementValue(int value) { + m_incrementValue = clampToInteger((double)m_incrementValue + value); + m_isIncrementSet = true; + } + void clearIncrement() { + m_incrementValue = 0; + m_isIncrementSet = false; + } + void inheritIncrement(CounterDirectives& parent) { + m_incrementValue = parent.m_incrementValue; + m_isIncrementSet = parent.m_isIncrementSet; + } - bool isDefined() const { return isReset() || isIncrement(); } + bool isDefined() const { return isReset() || isIncrement(); } - int combinedValue() const - { - ASSERT(m_isResetSet || !m_resetValue); - ASSERT(m_isIncrementSet || !m_incrementValue); - // FIXME: Shouldn't allow overflow here. - return m_resetValue + m_incrementValue; - } + int combinedValue() const { + ASSERT(m_isResetSet || !m_resetValue); + ASSERT(m_isIncrementSet || !m_incrementValue); + // FIXME: Shouldn't allow overflow here. + return m_resetValue + m_incrementValue; + } -private: - bool m_isResetSet; - bool m_isIncrementSet; - int m_resetValue; - int m_incrementValue; + private: + bool m_isResetSet; + bool m_isIncrementSet; + int m_resetValue; + int m_incrementValue; }; bool operator==(const CounterDirectives&, const CounterDirectives&); -inline bool operator!=(const CounterDirectives& a, const CounterDirectives& b) { return !(a == b); } +inline bool operator!=(const CounterDirectives& a, const CounterDirectives& b) { + return !(a == b); +} typedef HashMap CounterDirectiveMap; PassOwnPtr clone(const CounterDirectiveMap&); -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_STYLE_COUNTERDIRECTIVES_H_ diff --git a/sky/engine/core/rendering/style/DataEquivalency.h b/sky/engine/core/rendering/style/DataEquivalency.h index 9d5f95ca6bf5d..5c9bcbcb1ee78 100644 --- a/sky/engine/core/rendering/style/DataEquivalency.h +++ b/sky/engine/core/rendering/style/DataEquivalency.h @@ -11,27 +11,24 @@ namespace blink { template -bool dataEquivalent(const T* a, const T* b) -{ - if (a == b) - return true; - if (!a || !b) - return false; - return *a == *b; +bool dataEquivalent(const T* a, const T* b) { + if (a == b) + return true; + if (!a || !b) + return false; + return *a == *b; } template -bool dataEquivalent(const RefPtr& a, const RefPtr& b) -{ - return dataEquivalent(a.get(), b.get()); +bool dataEquivalent(const RefPtr& a, const RefPtr& b) { + return dataEquivalent(a.get(), b.get()); } template -bool dataEquivalent(const OwnPtr& a, const OwnPtr& b) -{ - return dataEquivalent(a.get(), b.get()); +bool dataEquivalent(const OwnPtr& a, const OwnPtr& b) { + return dataEquivalent(a.get(), b.get()); } -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_STYLE_DATAEQUIVALENCY_H_ diff --git a/sky/engine/core/rendering/style/DataRef.h b/sky/engine/core/rendering/style/DataRef.h index b2bb9facb4e26..9b30a58f8c8c2 100644 --- a/sky/engine/core/rendering/style/DataRef.h +++ b/sky/engine/core/rendering/style/DataRef.h @@ -28,44 +28,41 @@ namespace blink { -template class DataRef { -public: - const T* get() const { return m_data.get(); } +template +class DataRef { + public: + const T* get() const { return m_data.get(); } - const T& operator*() const { return *get(); } - const T* operator->() const { return get(); } + const T& operator*() const { return *get(); } + const T* operator->() const { return get(); } - T* access() - { - if (!m_data->hasOneRef()) - m_data = m_data->copy(); - return m_data.get(); - } + T* access() { + if (!m_data->hasOneRef()) + m_data = m_data->copy(); + return m_data.get(); + } - void init() - { - ASSERT(!m_data); - m_data = T::create(); - } + void init() { + ASSERT(!m_data); + m_data = T::create(); + } - bool operator==(const DataRef& o) const - { - ASSERT(m_data); - ASSERT(o.m_data); - return m_data == o.m_data || *m_data == *o.m_data; - } + bool operator==(const DataRef& o) const { + ASSERT(m_data); + ASSERT(o.m_data); + return m_data == o.m_data || *m_data == *o.m_data; + } - bool operator!=(const DataRef& o) const - { - ASSERT(m_data); - ASSERT(o.m_data); - return m_data != o.m_data && *m_data != *o.m_data; - } + bool operator!=(const DataRef& o) const { + ASSERT(m_data); + ASSERT(o.m_data); + return m_data != o.m_data && *m_data != *o.m_data; + } -private: - RefPtr m_data; + private: + RefPtr m_data; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_STYLE_DATAREF_H_ diff --git a/sky/engine/core/rendering/style/FillLayer.cpp b/sky/engine/core/rendering/style/FillLayer.cpp index fae1db78a83e7..f2dc2a96d5710 100644 --- a/sky/engine/core/rendering/style/FillLayer.cpp +++ b/sky/engine/core/rendering/style/FillLayer.cpp @@ -26,346 +26,346 @@ namespace blink { struct SameSizeAsFillLayer { - FillLayer* m_next; + FillLayer* m_next; - RefPtr m_image; + RefPtr m_image; - Length m_xPosition; - Length m_yPosition; + Length m_xPosition; + Length m_yPosition; - LengthSize m_sizeLength; + LengthSize m_sizeLength; - unsigned m_bitfields1; - unsigned m_bitfields2; + unsigned m_bitfields1; + unsigned m_bitfields2; }; -COMPILE_ASSERT(sizeof(FillLayer) == sizeof(SameSizeAsFillLayer), FillLayer_should_stay_small); +COMPILE_ASSERT(sizeof(FillLayer) == sizeof(SameSizeAsFillLayer), + FillLayer_should_stay_small); FillLayer::FillLayer(EFillLayerType type, bool useInitialValues) - : m_next(0) - , m_image(FillLayer::initialFillImage(type)) - , m_xPosition(FillLayer::initialFillXPosition(type)) - , m_yPosition(FillLayer::initialFillYPosition(type)) - , m_sizeLength(FillLayer::initialFillSizeLength(type)) - , m_attachment(FillLayer::initialFillAttachment(type)) - , m_clip(FillLayer::initialFillClip(type)) - , m_origin(FillLayer::initialFillOrigin(type)) - , m_repeatX(FillLayer::initialFillRepeatX(type)) - , m_repeatY(FillLayer::initialFillRepeatY(type)) - , m_composite(FillLayer::initialFillComposite(type)) - , m_sizeType(useInitialValues ? FillLayer::initialFillSizeType(type) : SizeNone) - , m_blendMode(FillLayer::initialFillBlendMode(type)) - , m_backgroundXOrigin(LeftEdge) - , m_backgroundYOrigin(TopEdge) - , m_imageSet(useInitialValues) - , m_attachmentSet(useInitialValues) - , m_clipSet(useInitialValues) - , m_originSet(useInitialValues) - , m_repeatXSet(useInitialValues) - , m_repeatYSet(useInitialValues) - , m_xPosSet(useInitialValues) - , m_yPosSet(useInitialValues) - , m_backgroundXOriginSet(false) - , m_backgroundYOriginSet(false) - , m_compositeSet(useInitialValues) - , m_blendModeSet(useInitialValues) - , m_type(type) -{ -} + : m_next(0), + m_image(FillLayer::initialFillImage(type)), + m_xPosition(FillLayer::initialFillXPosition(type)), + m_yPosition(FillLayer::initialFillYPosition(type)), + m_sizeLength(FillLayer::initialFillSizeLength(type)), + m_attachment(FillLayer::initialFillAttachment(type)), + m_clip(FillLayer::initialFillClip(type)), + m_origin(FillLayer::initialFillOrigin(type)), + m_repeatX(FillLayer::initialFillRepeatX(type)), + m_repeatY(FillLayer::initialFillRepeatY(type)), + m_composite(FillLayer::initialFillComposite(type)), + m_sizeType(useInitialValues ? FillLayer::initialFillSizeType(type) + : SizeNone), + m_blendMode(FillLayer::initialFillBlendMode(type)), + m_backgroundXOrigin(LeftEdge), + m_backgroundYOrigin(TopEdge), + m_imageSet(useInitialValues), + m_attachmentSet(useInitialValues), + m_clipSet(useInitialValues), + m_originSet(useInitialValues), + m_repeatXSet(useInitialValues), + m_repeatYSet(useInitialValues), + m_xPosSet(useInitialValues), + m_yPosSet(useInitialValues), + m_backgroundXOriginSet(false), + m_backgroundYOriginSet(false), + m_compositeSet(useInitialValues), + m_blendModeSet(useInitialValues), + m_type(type) {} FillLayer::FillLayer(const FillLayer& o) - : m_next(o.m_next ? new FillLayer(*o.m_next) : 0) - , m_image(o.m_image) - , m_xPosition(o.m_xPosition) - , m_yPosition(o.m_yPosition) - , m_sizeLength(o.m_sizeLength) - , m_attachment(o.m_attachment) - , m_clip(o.m_clip) - , m_origin(o.m_origin) - , m_repeatX(o.m_repeatX) - , m_repeatY(o.m_repeatY) - , m_composite(o.m_composite) - , m_sizeType(o.m_sizeType) - , m_blendMode(o.m_blendMode) - , m_backgroundXOrigin(o.m_backgroundXOrigin) - , m_backgroundYOrigin(o.m_backgroundYOrigin) - , m_imageSet(o.m_imageSet) - , m_attachmentSet(o.m_attachmentSet) - , m_clipSet(o.m_clipSet) - , m_originSet(o.m_originSet) - , m_repeatXSet(o.m_repeatXSet) - , m_repeatYSet(o.m_repeatYSet) - , m_xPosSet(o.m_xPosSet) - , m_yPosSet(o.m_yPosSet) - , m_backgroundXOriginSet(o.m_backgroundXOriginSet) - , m_backgroundYOriginSet(o.m_backgroundYOriginSet) - , m_compositeSet(o.m_compositeSet) - , m_blendModeSet(o.m_blendModeSet) - , m_type(o.m_type) -{ + : m_next(o.m_next ? new FillLayer(*o.m_next) : 0), + m_image(o.m_image), + m_xPosition(o.m_xPosition), + m_yPosition(o.m_yPosition), + m_sizeLength(o.m_sizeLength), + m_attachment(o.m_attachment), + m_clip(o.m_clip), + m_origin(o.m_origin), + m_repeatX(o.m_repeatX), + m_repeatY(o.m_repeatY), + m_composite(o.m_composite), + m_sizeType(o.m_sizeType), + m_blendMode(o.m_blendMode), + m_backgroundXOrigin(o.m_backgroundXOrigin), + m_backgroundYOrigin(o.m_backgroundYOrigin), + m_imageSet(o.m_imageSet), + m_attachmentSet(o.m_attachmentSet), + m_clipSet(o.m_clipSet), + m_originSet(o.m_originSet), + m_repeatXSet(o.m_repeatXSet), + m_repeatYSet(o.m_repeatYSet), + m_xPosSet(o.m_xPosSet), + m_yPosSet(o.m_yPosSet), + m_backgroundXOriginSet(o.m_backgroundXOriginSet), + m_backgroundYOriginSet(o.m_backgroundYOriginSet), + m_compositeSet(o.m_compositeSet), + m_blendModeSet(o.m_blendModeSet), + m_type(o.m_type) {} + +FillLayer::~FillLayer() { + delete m_next; } -FillLayer::~FillLayer() -{ +FillLayer& FillLayer::operator=(const FillLayer& o) { + if (m_next != o.m_next) { delete m_next; + m_next = o.m_next ? new FillLayer(*o.m_next) : 0; + } + + m_image = o.m_image; + m_xPosition = o.m_xPosition; + m_yPosition = o.m_yPosition; + m_backgroundXOrigin = o.m_backgroundXOrigin; + m_backgroundYOrigin = o.m_backgroundYOrigin; + m_backgroundXOriginSet = o.m_backgroundXOriginSet; + m_backgroundYOriginSet = o.m_backgroundYOriginSet; + m_sizeLength = o.m_sizeLength; + m_attachment = o.m_attachment; + m_clip = o.m_clip; + m_composite = o.m_composite; + m_blendMode = o.m_blendMode; + m_origin = o.m_origin; + m_repeatX = o.m_repeatX; + m_repeatY = o.m_repeatY; + m_sizeType = o.m_sizeType; + + m_imageSet = o.m_imageSet; + m_attachmentSet = o.m_attachmentSet; + m_clipSet = o.m_clipSet; + m_compositeSet = o.m_compositeSet; + m_blendModeSet = o.m_blendModeSet; + m_originSet = o.m_originSet; + m_repeatXSet = o.m_repeatXSet; + m_repeatYSet = o.m_repeatYSet; + m_xPosSet = o.m_xPosSet; + m_yPosSet = o.m_yPosSet; + + m_type = o.m_type; + + return *this; } -FillLayer& FillLayer::operator=(const FillLayer& o) -{ - if (m_next != o.m_next) { - delete m_next; - m_next = o.m_next ? new FillLayer(*o.m_next) : 0; - } - - m_image = o.m_image; - m_xPosition = o.m_xPosition; - m_yPosition = o.m_yPosition; - m_backgroundXOrigin = o.m_backgroundXOrigin; - m_backgroundYOrigin = o.m_backgroundYOrigin; - m_backgroundXOriginSet = o.m_backgroundXOriginSet; - m_backgroundYOriginSet = o.m_backgroundYOriginSet; - m_sizeLength = o.m_sizeLength; - m_attachment = o.m_attachment; - m_clip = o.m_clip; - m_composite = o.m_composite; - m_blendMode = o.m_blendMode; - m_origin = o.m_origin; - m_repeatX = o.m_repeatX; - m_repeatY = o.m_repeatY; - m_sizeType = o.m_sizeType; - - m_imageSet = o.m_imageSet; - m_attachmentSet = o.m_attachmentSet; - m_clipSet = o.m_clipSet; - m_compositeSet = o.m_compositeSet; - m_blendModeSet = o.m_blendModeSet; - m_originSet = o.m_originSet; - m_repeatXSet = o.m_repeatXSet; - m_repeatYSet = o.m_repeatYSet; - m_xPosSet = o.m_xPosSet; - m_yPosSet = o.m_yPosSet; - - m_type = o.m_type; - - return *this; -} - -bool FillLayer::operator==(const FillLayer& o) const -{ - // We do not check the "isSet" booleans for each property, since those are only used during initial construction - // to propagate patterns into layers. All layer comparisons happen after values have all been filled in anyway. - return dataEquivalent(m_image, o.m_image) && m_xPosition == o.m_xPosition && m_yPosition == o.m_yPosition - && m_backgroundXOrigin == o.m_backgroundXOrigin && m_backgroundYOrigin == o.m_backgroundYOrigin - && m_attachment == o.m_attachment && m_clip == o.m_clip && m_composite == o.m_composite - && m_blendMode == o.m_blendMode && m_origin == o.m_origin && m_repeatX == o.m_repeatX - && m_repeatY == o.m_repeatY && m_sizeType == o.m_sizeType - && m_sizeLength == o.m_sizeLength && m_type == o.m_type - && ((m_next && o.m_next) ? *m_next == *o.m_next : m_next == o.m_next); +bool FillLayer::operator==(const FillLayer& o) const { + // We do not check the "isSet" booleans for each property, since those are + // only used during initial construction to propagate patterns into layers. + // All layer comparisons happen after values have all been filled in anyway. + return dataEquivalent(m_image, o.m_image) && m_xPosition == o.m_xPosition && + m_yPosition == o.m_yPosition && + m_backgroundXOrigin == o.m_backgroundXOrigin && + m_backgroundYOrigin == o.m_backgroundYOrigin && + m_attachment == o.m_attachment && m_clip == o.m_clip && + m_composite == o.m_composite && m_blendMode == o.m_blendMode && + m_origin == o.m_origin && m_repeatX == o.m_repeatX && + m_repeatY == o.m_repeatY && m_sizeType == o.m_sizeType && + m_sizeLength == o.m_sizeLength && m_type == o.m_type && + ((m_next && o.m_next) ? *m_next == *o.m_next : m_next == o.m_next); } -void FillLayer::fillUnsetProperties() -{ - FillLayer* curr; - for (curr = this; curr && curr->isXPositionSet(); curr = curr->next()) { } - if (curr && curr != this) { - // We need to fill in the remaining values with the pattern specified. - for (FillLayer* pattern = this; curr; curr = curr->next()) { - curr->m_xPosition = pattern->m_xPosition; - if (pattern->isBackgroundXOriginSet()) - curr->m_backgroundXOrigin = pattern->m_backgroundXOrigin; - if (pattern->isBackgroundYOriginSet()) - curr->m_backgroundYOrigin = pattern->m_backgroundYOrigin; - pattern = pattern->next(); - if (pattern == curr || !pattern) - pattern = this; - } +void FillLayer::fillUnsetProperties() { + FillLayer* curr; + for (curr = this; curr && curr->isXPositionSet(); curr = curr->next()) { + } + if (curr && curr != this) { + // We need to fill in the remaining values with the pattern specified. + for (FillLayer* pattern = this; curr; curr = curr->next()) { + curr->m_xPosition = pattern->m_xPosition; + if (pattern->isBackgroundXOriginSet()) + curr->m_backgroundXOrigin = pattern->m_backgroundXOrigin; + if (pattern->isBackgroundYOriginSet()) + curr->m_backgroundYOrigin = pattern->m_backgroundYOrigin; + pattern = pattern->next(); + if (pattern == curr || !pattern) + pattern = this; } - - for (curr = this; curr && curr->isYPositionSet(); curr = curr->next()) { } - if (curr && curr != this) { - // We need to fill in the remaining values with the pattern specified. - for (FillLayer* pattern = this; curr; curr = curr->next()) { - curr->m_yPosition = pattern->m_yPosition; - if (pattern->isBackgroundXOriginSet()) - curr->m_backgroundXOrigin = pattern->m_backgroundXOrigin; - if (pattern->isBackgroundYOriginSet()) - curr->m_backgroundYOrigin = pattern->m_backgroundYOrigin; - pattern = pattern->next(); - if (pattern == curr || !pattern) - pattern = this; - } + } + + for (curr = this; curr && curr->isYPositionSet(); curr = curr->next()) { + } + if (curr && curr != this) { + // We need to fill in the remaining values with the pattern specified. + for (FillLayer* pattern = this; curr; curr = curr->next()) { + curr->m_yPosition = pattern->m_yPosition; + if (pattern->isBackgroundXOriginSet()) + curr->m_backgroundXOrigin = pattern->m_backgroundXOrigin; + if (pattern->isBackgroundYOriginSet()) + curr->m_backgroundYOrigin = pattern->m_backgroundYOrigin; + pattern = pattern->next(); + if (pattern == curr || !pattern) + pattern = this; } - - for (curr = this; curr && curr->isAttachmentSet(); curr = curr->next()) { } - if (curr && curr != this) { - // We need to fill in the remaining values with the pattern specified. - for (FillLayer* pattern = this; curr; curr = curr->next()) { - curr->m_attachment = pattern->m_attachment; - pattern = pattern->next(); - if (pattern == curr || !pattern) - pattern = this; - } + } + + for (curr = this; curr && curr->isAttachmentSet(); curr = curr->next()) { + } + if (curr && curr != this) { + // We need to fill in the remaining values with the pattern specified. + for (FillLayer* pattern = this; curr; curr = curr->next()) { + curr->m_attachment = pattern->m_attachment; + pattern = pattern->next(); + if (pattern == curr || !pattern) + pattern = this; } - - for (curr = this; curr && curr->isClipSet(); curr = curr->next()) { } - if (curr && curr != this) { - // We need to fill in the remaining values with the pattern specified. - for (FillLayer* pattern = this; curr; curr = curr->next()) { - curr->m_clip = pattern->m_clip; - pattern = pattern->next(); - if (pattern == curr || !pattern) - pattern = this; - } + } + + for (curr = this; curr && curr->isClipSet(); curr = curr->next()) { + } + if (curr && curr != this) { + // We need to fill in the remaining values with the pattern specified. + for (FillLayer* pattern = this; curr; curr = curr->next()) { + curr->m_clip = pattern->m_clip; + pattern = pattern->next(); + if (pattern == curr || !pattern) + pattern = this; } - - for (curr = this; curr && curr->isCompositeSet(); curr = curr->next()) { } - if (curr && curr != this) { - // We need to fill in the remaining values with the pattern specified. - for (FillLayer* pattern = this; curr; curr = curr->next()) { - curr->m_composite = pattern->m_composite; - pattern = pattern->next(); - if (pattern == curr || !pattern) - pattern = this; - } + } + + for (curr = this; curr && curr->isCompositeSet(); curr = curr->next()) { + } + if (curr && curr != this) { + // We need to fill in the remaining values with the pattern specified. + for (FillLayer* pattern = this; curr; curr = curr->next()) { + curr->m_composite = pattern->m_composite; + pattern = pattern->next(); + if (pattern == curr || !pattern) + pattern = this; } - - for (curr = this; curr && curr->isBlendModeSet(); curr = curr->next()) { } - if (curr && curr != this) { - // We need to fill in the remaining values with the pattern specified. - for (FillLayer* pattern = this; curr; curr = curr->next()) { - curr->m_blendMode = pattern->m_blendMode; - pattern = pattern->next(); - if (pattern == curr || !pattern) - pattern = this; - } + } + + for (curr = this; curr && curr->isBlendModeSet(); curr = curr->next()) { + } + if (curr && curr != this) { + // We need to fill in the remaining values with the pattern specified. + for (FillLayer* pattern = this; curr; curr = curr->next()) { + curr->m_blendMode = pattern->m_blendMode; + pattern = pattern->next(); + if (pattern == curr || !pattern) + pattern = this; } - - for (curr = this; curr && curr->isOriginSet(); curr = curr->next()) { } - if (curr && curr != this) { - // We need to fill in the remaining values with the pattern specified. - for (FillLayer* pattern = this; curr; curr = curr->next()) { - curr->m_origin = pattern->m_origin; - pattern = pattern->next(); - if (pattern == curr || !pattern) - pattern = this; - } + } + + for (curr = this; curr && curr->isOriginSet(); curr = curr->next()) { + } + if (curr && curr != this) { + // We need to fill in the remaining values with the pattern specified. + for (FillLayer* pattern = this; curr; curr = curr->next()) { + curr->m_origin = pattern->m_origin; + pattern = pattern->next(); + if (pattern == curr || !pattern) + pattern = this; } - - for (curr = this; curr && curr->isRepeatXSet(); curr = curr->next()) { } - if (curr && curr != this) { - // We need to fill in the remaining values with the pattern specified. - for (FillLayer* pattern = this; curr; curr = curr->next()) { - curr->m_repeatX = pattern->m_repeatX; - pattern = pattern->next(); - if (pattern == curr || !pattern) - pattern = this; - } + } + + for (curr = this; curr && curr->isRepeatXSet(); curr = curr->next()) { + } + if (curr && curr != this) { + // We need to fill in the remaining values with the pattern specified. + for (FillLayer* pattern = this; curr; curr = curr->next()) { + curr->m_repeatX = pattern->m_repeatX; + pattern = pattern->next(); + if (pattern == curr || !pattern) + pattern = this; } - - for (curr = this; curr && curr->isRepeatYSet(); curr = curr->next()) { } - if (curr && curr != this) { - // We need to fill in the remaining values with the pattern specified. - for (FillLayer* pattern = this; curr; curr = curr->next()) { - curr->m_repeatY = pattern->m_repeatY; - pattern = pattern->next(); - if (pattern == curr || !pattern) - pattern = this; - } + } + + for (curr = this; curr && curr->isRepeatYSet(); curr = curr->next()) { + } + if (curr && curr != this) { + // We need to fill in the remaining values with the pattern specified. + for (FillLayer* pattern = this; curr; curr = curr->next()) { + curr->m_repeatY = pattern->m_repeatY; + pattern = pattern->next(); + if (pattern == curr || !pattern) + pattern = this; } - - for (curr = this; curr && curr->isSizeSet(); curr = curr->next()) { } - if (curr && curr != this) { - // We need to fill in the remaining values with the pattern specified. - for (FillLayer* pattern = this; curr; curr = curr->next()) { - curr->m_sizeType = pattern->m_sizeType; - curr->m_sizeLength = pattern->m_sizeLength; - pattern = pattern->next(); - if (pattern == curr || !pattern) - pattern = this; - } + } + + for (curr = this; curr && curr->isSizeSet(); curr = curr->next()) { + } + if (curr && curr != this) { + // We need to fill in the remaining values with the pattern specified. + for (FillLayer* pattern = this; curr; curr = curr->next()) { + curr->m_sizeType = pattern->m_sizeType; + curr->m_sizeLength = pattern->m_sizeLength; + pattern = pattern->next(); + if (pattern == curr || !pattern) + pattern = this; } + } } -void FillLayer::cullEmptyLayers() -{ - FillLayer* next; - for (FillLayer* p = this; p; p = next) { - next = p->m_next; - if (next && !next->isImageSet()) { - delete next; - p->m_next = 0; - break; - } +void FillLayer::cullEmptyLayers() { + FillLayer* next; + for (FillLayer* p = this; p; p = next) { + next = p->m_next; + if (next && !next->isImageSet()) { + delete next; + p->m_next = 0; + break; } + } } -static EFillBox clipMax(EFillBox clipA, EFillBox clipB) -{ - if (clipA == BorderFillBox || clipB == BorderFillBox) - return BorderFillBox; - if (clipA == PaddingFillBox || clipB == PaddingFillBox) - return PaddingFillBox; - return ContentFillBox; +static EFillBox clipMax(EFillBox clipA, EFillBox clipB) { + if (clipA == BorderFillBox || clipB == BorderFillBox) + return BorderFillBox; + if (clipA == PaddingFillBox || clipB == PaddingFillBox) + return PaddingFillBox; + return ContentFillBox; } -void FillLayer::computeClipMax() const -{ - if (m_next) { - m_next->computeClipMax(); - m_clipMax = clipMax(clip(), m_next->clip()); - } else - m_clipMax = m_clip; +void FillLayer::computeClipMax() const { + if (m_next) { + m_next->computeClipMax(); + m_clipMax = clipMax(clip(), m_next->clip()); + } else + m_clipMax = m_clip; } -bool FillLayer::clipOccludesNextLayers(bool firstLayer) const -{ - if (firstLayer) - computeClipMax(); - return m_clip == m_clipMax; +bool FillLayer::clipOccludesNextLayers(bool firstLayer) const { + if (firstLayer) + computeClipMax(); + return m_clip == m_clipMax; } -bool FillLayer::containsImage(StyleImage* s) const -{ - if (!s) - return false; - if (m_image && *s == *m_image) - return true; - if (m_next) - return m_next->containsImage(s); +bool FillLayer::containsImage(StyleImage* s) const { + if (!s) return false; + if (m_image && *s == *m_image) + return true; + if (m_next) + return m_next->containsImage(s); + return false; } -bool FillLayer::imagesAreLoaded() const -{ - const FillLayer* curr; - for (curr = this; curr; curr = curr->next()) { - if (curr->m_image && !curr->m_image->isLoaded()) - return false; - } +bool FillLayer::imagesAreLoaded() const { + const FillLayer* curr; + for (curr = this; curr; curr = curr->next()) { + if (curr->m_image && !curr->m_image->isLoaded()) + return false; + } - return true; + return true; } -bool FillLayer::hasOpaqueImage(const RenderObject* renderer) const -{ - if (!m_image) - return false; +bool FillLayer::hasOpaqueImage(const RenderObject* renderer) const { + if (!m_image) + return false; - if (m_composite == CompositeClear || m_composite == CompositeCopy) - return true; + if (m_composite == CompositeClear || m_composite == CompositeCopy) + return true; - if (m_blendMode != WebBlendModeNormal) - return false; + if (m_blendMode != WebBlendModeNormal) + return false; - if (m_composite == CompositeSourceOver) - return m_image->knownToBeOpaque(renderer); + if (m_composite == CompositeSourceOver) + return m_image->knownToBeOpaque(renderer); - return false; + return false; } -bool FillLayer::hasRepeatXY() const -{ - return m_repeatX == RepeatFill && m_repeatY == RepeatFill; +bool FillLayer::hasRepeatXY() const { + return m_repeatX == RepeatFill && m_repeatY == RepeatFill; } -} // namespace blink +} // namespace blink diff --git a/sky/engine/core/rendering/style/FillLayer.h b/sky/engine/core/rendering/style/FillLayer.h index c8242218828f7..55046f0d0366d 100644 --- a/sky/engine/core/rendering/style/FillLayer.h +++ b/sky/engine/core/rendering/style/FillLayer.h @@ -35,206 +35,267 @@ namespace blink { struct FillSize { - FillSize() - : type(SizeLength) - { - } - - FillSize(EFillSizeType t, const LengthSize& l) - : type(t) - , size(l) - { - } - - bool operator==(const FillSize& o) const - { - return type == o.type && size == o.size; - } - bool operator!=(const FillSize& o) const - { - return !(*this == o); - } - - EFillSizeType type; - LengthSize size; + FillSize() : type(SizeLength) {} + + FillSize(EFillSizeType t, const LengthSize& l) : type(t), size(l) {} + + bool operator==(const FillSize& o) const { + return type == o.type && size == o.size; + } + bool operator!=(const FillSize& o) const { return !(*this == o); } + + EFillSizeType type; + LengthSize size; }; class FillLayer { - WTF_MAKE_FAST_ALLOCATED; -public: - FillLayer(EFillLayerType, bool useInitialValues = false); - ~FillLayer(); - - StyleImage* image() const { return m_image.get(); } - const Length& xPosition() const { return m_xPosition; } - const Length& yPosition() const { return m_yPosition; } - BackgroundEdgeOrigin backgroundXOrigin() const { return static_cast(m_backgroundXOrigin); } - BackgroundEdgeOrigin backgroundYOrigin() const { return static_cast(m_backgroundYOrigin); } - EFillAttachment attachment() const { return static_cast(m_attachment); } - EFillBox clip() const { return static_cast(m_clip); } - EFillBox origin() const { return static_cast(m_origin); } - EFillRepeat repeatX() const { return static_cast(m_repeatX); } - EFillRepeat repeatY() const { return static_cast(m_repeatY); } - CompositeOperator composite() const { return static_cast(m_composite); } - WebBlendMode blendMode() const { return static_cast(m_blendMode); } - const LengthSize& sizeLength() const { return m_sizeLength; } - EFillSizeType sizeType() const { return static_cast(m_sizeType); } - FillSize size() const { return FillSize(static_cast(m_sizeType), m_sizeLength); } - - const FillLayer* next() const { return m_next; } - FillLayer* next() { return m_next; } - FillLayer* ensureNext() - { - if (!m_next) - m_next = new FillLayer(type()); - return m_next; - } - - bool isImageSet() const { return m_imageSet; } - bool isXPositionSet() const { return m_xPosSet; } - bool isYPositionSet() const { return m_yPosSet; } - bool isBackgroundXOriginSet() const { return m_backgroundXOriginSet; } - bool isBackgroundYOriginSet() const { return m_backgroundYOriginSet; } - bool isAttachmentSet() const { return m_attachmentSet; } - bool isClipSet() const { return m_clipSet; } - bool isOriginSet() const { return m_originSet; } - bool isRepeatXSet() const { return m_repeatXSet; } - bool isRepeatYSet() const { return m_repeatYSet; } - bool isCompositeSet() const { return m_compositeSet; } - bool isBlendModeSet() const { return m_blendModeSet; } - bool isSizeSet() const { return m_sizeType != SizeNone; } - - void setImage(PassRefPtr i) { m_image = i; m_imageSet = true; } - void setXPosition(const Length& position) { m_xPosition = position; m_xPosSet = true; m_backgroundXOriginSet = false; m_backgroundXOrigin = LeftEdge; } - void setYPosition(const Length& position) { m_yPosition = position; m_yPosSet = true; m_backgroundYOriginSet = false; m_backgroundYOrigin = TopEdge; } - void setBackgroundXOrigin(BackgroundEdgeOrigin origin) { m_backgroundXOrigin = origin; m_backgroundXOriginSet = true; } - void setBackgroundYOrigin(BackgroundEdgeOrigin origin) { m_backgroundYOrigin = origin; m_backgroundYOriginSet = true; } - void setAttachment(EFillAttachment attachment) { m_attachment = attachment; m_attachmentSet = true; } - void setClip(EFillBox b) { m_clip = b; m_clipSet = true; } - void setOrigin(EFillBox b) { m_origin = b; m_originSet = true; } - void setRepeatX(EFillRepeat r) { m_repeatX = r; m_repeatXSet = true; } - void setRepeatY(EFillRepeat r) { m_repeatY = r; m_repeatYSet = true; } - void setComposite(CompositeOperator c) { m_composite = c; m_compositeSet = true; } - void setBlendMode(WebBlendMode b) { m_blendMode = b; m_blendModeSet = true; } - void setSizeType(EFillSizeType b) { m_sizeType = b; } - void setSizeLength(const LengthSize& l) { m_sizeLength = l; } - void setSize(FillSize f) { m_sizeType = f.type; m_sizeLength = f.size; } - - void clearImage() { m_image.clear(); m_imageSet = false; } - void clearXPosition() - { - m_xPosSet = false; - m_backgroundXOriginSet = false; - } - void clearYPosition() - { - m_yPosSet = false; - m_backgroundYOriginSet = false; - } - - void clearAttachment() { m_attachmentSet = false; } - void clearClip() { m_clipSet = false; } - void clearOrigin() { m_originSet = false; } - void clearRepeatX() { m_repeatXSet = false; } - void clearRepeatY() { m_repeatYSet = false; } - void clearComposite() { m_compositeSet = false; } - void clearBlendMode() { m_blendModeSet = false; } - void clearSize() { m_sizeType = SizeNone; } - - FillLayer& operator=(const FillLayer& o); - FillLayer(const FillLayer& o); - - bool operator==(const FillLayer& o) const; - bool operator!=(const FillLayer& o) const - { - return !(*this == o); - } - - bool containsImage(StyleImage*) const; - bool imagesAreLoaded() const; - - bool hasImage() const - { - if (m_image) - return true; - return m_next ? m_next->hasImage() : false; - } - - bool hasFixedImage() const - { - if (m_image && m_attachment == FixedBackgroundAttachment) - return true; - return m_next ? m_next->hasFixedImage() : false; - } - - bool hasOpaqueImage(const RenderObject*) const; - bool hasRepeatXY() const; - bool clipOccludesNextLayers(bool firstLayer) const; - - EFillLayerType type() const { return static_cast(m_type); } - - void fillUnsetProperties(); - void cullEmptyLayers(); - - static EFillAttachment initialFillAttachment(EFillLayerType) { return LocalBackgroundAttachment; } - static EFillBox initialFillClip(EFillLayerType) { return BorderFillBox; } - static EFillBox initialFillOrigin(EFillLayerType type) { return type == BackgroundFillLayer ? PaddingFillBox : BorderFillBox; } - static EFillRepeat initialFillRepeatX(EFillLayerType) { return NoRepeatFill; } - static EFillRepeat initialFillRepeatY(EFillLayerType) { return NoRepeatFill; } - static CompositeOperator initialFillComposite(EFillLayerType) { return CompositeSourceOver; } - static WebBlendMode initialFillBlendMode(EFillLayerType) { return WebBlendModeNormal; } - static EFillSizeType initialFillSizeType(EFillLayerType) { return SizeLength; } - static LengthSize initialFillSizeLength(EFillLayerType) { return LengthSize(); } - static FillSize initialFillSize(EFillLayerType type) { return FillSize(initialFillSizeType(type), initialFillSizeLength(type)); } - static Length initialFillXPosition(EFillLayerType) { return Length(0.0, Percent); } - static Length initialFillYPosition(EFillLayerType) { return Length(0.0, Percent); } - static StyleImage* initialFillImage(EFillLayerType) { return 0; } - -private: - friend class RenderStyle; - - void computeClipMax() const; - - FillLayer() { } - - FillLayer* m_next; - - RefPtr m_image; - - Length m_xPosition; - Length m_yPosition; - - LengthSize m_sizeLength; - - unsigned m_attachment : 2; // EFillAttachment - unsigned m_clip : 2; // EFillBox - unsigned m_origin : 2; // EFillBox - unsigned m_repeatX : 3; // EFillRepeat - unsigned m_repeatY : 3; // EFillRepeat - unsigned m_composite : 4; // CompositeOperator - unsigned m_sizeType : 2; // EFillSizeType - unsigned m_blendMode : 5; // WebBlendMode - unsigned m_backgroundXOrigin : 2; // BackgroundEdgeOrigin - unsigned m_backgroundYOrigin : 2; // BackgroundEdgeOrigin - - unsigned m_imageSet : 1; - unsigned m_attachmentSet : 1; - unsigned m_clipSet : 1; - unsigned m_originSet : 1; - unsigned m_repeatXSet : 1; - unsigned m_repeatYSet : 1; - unsigned m_xPosSet : 1; - unsigned m_yPosSet : 1; - unsigned m_backgroundXOriginSet : 1; - unsigned m_backgroundYOriginSet : 1; - unsigned m_compositeSet : 1; - unsigned m_blendModeSet : 1; - - unsigned m_type : 1; // EFillLayerType - - mutable unsigned m_clipMax : 2; // EFillBox, maximum m_clip value from this to bottom layer + WTF_MAKE_FAST_ALLOCATED; + + public: + FillLayer(EFillLayerType, bool useInitialValues = false); + ~FillLayer(); + + StyleImage* image() const { return m_image.get(); } + const Length& xPosition() const { return m_xPosition; } + const Length& yPosition() const { return m_yPosition; } + BackgroundEdgeOrigin backgroundXOrigin() const { + return static_cast(m_backgroundXOrigin); + } + BackgroundEdgeOrigin backgroundYOrigin() const { + return static_cast(m_backgroundYOrigin); + } + EFillAttachment attachment() const { + return static_cast(m_attachment); + } + EFillBox clip() const { return static_cast(m_clip); } + EFillBox origin() const { return static_cast(m_origin); } + EFillRepeat repeatX() const { return static_cast(m_repeatX); } + EFillRepeat repeatY() const { return static_cast(m_repeatY); } + CompositeOperator composite() const { + return static_cast(m_composite); + } + WebBlendMode blendMode() const { + return static_cast(m_blendMode); + } + const LengthSize& sizeLength() const { return m_sizeLength; } + EFillSizeType sizeType() const { + return static_cast(m_sizeType); + } + FillSize size() const { + return FillSize(static_cast(m_sizeType), m_sizeLength); + } + + const FillLayer* next() const { return m_next; } + FillLayer* next() { return m_next; } + FillLayer* ensureNext() { + if (!m_next) + m_next = new FillLayer(type()); + return m_next; + } + + bool isImageSet() const { return m_imageSet; } + bool isXPositionSet() const { return m_xPosSet; } + bool isYPositionSet() const { return m_yPosSet; } + bool isBackgroundXOriginSet() const { return m_backgroundXOriginSet; } + bool isBackgroundYOriginSet() const { return m_backgroundYOriginSet; } + bool isAttachmentSet() const { return m_attachmentSet; } + bool isClipSet() const { return m_clipSet; } + bool isOriginSet() const { return m_originSet; } + bool isRepeatXSet() const { return m_repeatXSet; } + bool isRepeatYSet() const { return m_repeatYSet; } + bool isCompositeSet() const { return m_compositeSet; } + bool isBlendModeSet() const { return m_blendModeSet; } + bool isSizeSet() const { return m_sizeType != SizeNone; } + + void setImage(PassRefPtr i) { + m_image = i; + m_imageSet = true; + } + void setXPosition(const Length& position) { + m_xPosition = position; + m_xPosSet = true; + m_backgroundXOriginSet = false; + m_backgroundXOrigin = LeftEdge; + } + void setYPosition(const Length& position) { + m_yPosition = position; + m_yPosSet = true; + m_backgroundYOriginSet = false; + m_backgroundYOrigin = TopEdge; + } + void setBackgroundXOrigin(BackgroundEdgeOrigin origin) { + m_backgroundXOrigin = origin; + m_backgroundXOriginSet = true; + } + void setBackgroundYOrigin(BackgroundEdgeOrigin origin) { + m_backgroundYOrigin = origin; + m_backgroundYOriginSet = true; + } + void setAttachment(EFillAttachment attachment) { + m_attachment = attachment; + m_attachmentSet = true; + } + void setClip(EFillBox b) { + m_clip = b; + m_clipSet = true; + } + void setOrigin(EFillBox b) { + m_origin = b; + m_originSet = true; + } + void setRepeatX(EFillRepeat r) { + m_repeatX = r; + m_repeatXSet = true; + } + void setRepeatY(EFillRepeat r) { + m_repeatY = r; + m_repeatYSet = true; + } + void setComposite(CompositeOperator c) { + m_composite = c; + m_compositeSet = true; + } + void setBlendMode(WebBlendMode b) { + m_blendMode = b; + m_blendModeSet = true; + } + void setSizeType(EFillSizeType b) { m_sizeType = b; } + void setSizeLength(const LengthSize& l) { m_sizeLength = l; } + void setSize(FillSize f) { + m_sizeType = f.type; + m_sizeLength = f.size; + } + + void clearImage() { + m_image.clear(); + m_imageSet = false; + } + void clearXPosition() { + m_xPosSet = false; + m_backgroundXOriginSet = false; + } + void clearYPosition() { + m_yPosSet = false; + m_backgroundYOriginSet = false; + } + + void clearAttachment() { m_attachmentSet = false; } + void clearClip() { m_clipSet = false; } + void clearOrigin() { m_originSet = false; } + void clearRepeatX() { m_repeatXSet = false; } + void clearRepeatY() { m_repeatYSet = false; } + void clearComposite() { m_compositeSet = false; } + void clearBlendMode() { m_blendModeSet = false; } + void clearSize() { m_sizeType = SizeNone; } + + FillLayer& operator=(const FillLayer& o); + FillLayer(const FillLayer& o); + + bool operator==(const FillLayer& o) const; + bool operator!=(const FillLayer& o) const { return !(*this == o); } + + bool containsImage(StyleImage*) const; + bool imagesAreLoaded() const; + + bool hasImage() const { + if (m_image) + return true; + return m_next ? m_next->hasImage() : false; + } + + bool hasFixedImage() const { + if (m_image && m_attachment == FixedBackgroundAttachment) + return true; + return m_next ? m_next->hasFixedImage() : false; + } + + bool hasOpaqueImage(const RenderObject*) const; + bool hasRepeatXY() const; + bool clipOccludesNextLayers(bool firstLayer) const; + + EFillLayerType type() const { return static_cast(m_type); } + + void fillUnsetProperties(); + void cullEmptyLayers(); + + static EFillAttachment initialFillAttachment(EFillLayerType) { + return LocalBackgroundAttachment; + } + static EFillBox initialFillClip(EFillLayerType) { return BorderFillBox; } + static EFillBox initialFillOrigin(EFillLayerType type) { + return type == BackgroundFillLayer ? PaddingFillBox : BorderFillBox; + } + static EFillRepeat initialFillRepeatX(EFillLayerType) { return NoRepeatFill; } + static EFillRepeat initialFillRepeatY(EFillLayerType) { return NoRepeatFill; } + static CompositeOperator initialFillComposite(EFillLayerType) { + return CompositeSourceOver; + } + static WebBlendMode initialFillBlendMode(EFillLayerType) { + return WebBlendModeNormal; + } + static EFillSizeType initialFillSizeType(EFillLayerType) { + return SizeLength; + } + static LengthSize initialFillSizeLength(EFillLayerType) { + return LengthSize(); + } + static FillSize initialFillSize(EFillLayerType type) { + return FillSize(initialFillSizeType(type), initialFillSizeLength(type)); + } + static Length initialFillXPosition(EFillLayerType) { + return Length(0.0, Percent); + } + static Length initialFillYPosition(EFillLayerType) { + return Length(0.0, Percent); + } + static StyleImage* initialFillImage(EFillLayerType) { return 0; } + + private: + friend class RenderStyle; + + void computeClipMax() const; + + FillLayer() {} + + FillLayer* m_next; + + RefPtr m_image; + + Length m_xPosition; + Length m_yPosition; + + LengthSize m_sizeLength; + + unsigned m_attachment : 2; // EFillAttachment + unsigned m_clip : 2; // EFillBox + unsigned m_origin : 2; // EFillBox + unsigned m_repeatX : 3; // EFillRepeat + unsigned m_repeatY : 3; // EFillRepeat + unsigned m_composite : 4; // CompositeOperator + unsigned m_sizeType : 2; // EFillSizeType + unsigned m_blendMode : 5; // WebBlendMode + unsigned m_backgroundXOrigin : 2; // BackgroundEdgeOrigin + unsigned m_backgroundYOrigin : 2; // BackgroundEdgeOrigin + + unsigned m_imageSet : 1; + unsigned m_attachmentSet : 1; + unsigned m_clipSet : 1; + unsigned m_originSet : 1; + unsigned m_repeatXSet : 1; + unsigned m_repeatYSet : 1; + unsigned m_xPosSet : 1; + unsigned m_yPosSet : 1; + unsigned m_backgroundXOriginSet : 1; + unsigned m_backgroundYOriginSet : 1; + unsigned m_compositeSet : 1; + unsigned m_blendModeSet : 1; + + unsigned m_type : 1; // EFillLayerType + + mutable unsigned m_clipMax : 2; // EFillBox, maximum m_clip value from this + // to bottom layer }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_STYLE_FILLLAYER_H_ diff --git a/sky/engine/core/rendering/style/OutlineValue.h b/sky/engine/core/rendering/style/OutlineValue.h index d2dfd2c2c44a5..9f31a5cca0f1d 100644 --- a/sky/engine/core/rendering/style/OutlineValue.h +++ b/sky/engine/core/rendering/style/OutlineValue.h @@ -30,40 +30,34 @@ namespace blink { class OutlineValue : public BorderValue { -friend class RenderStyle; -public: - OutlineValue() - : m_offset(0) - { - } + friend class RenderStyle; - bool operator==(const OutlineValue& o) const - { - return BorderValue::operator==(o) && m_offset == o.m_offset && m_isAuto == o.m_isAuto; - } + public: + OutlineValue() : m_offset(0) {} - bool operator!=(const OutlineValue& o) const - { - return !(*this == o); - } + bool operator==(const OutlineValue& o) const { + return BorderValue::operator==(o) && m_offset == o.m_offset && + m_isAuto == o.m_isAuto; + } - bool visuallyEqual(const OutlineValue& o) const - { - if (m_style == BNONE && o.m_style == BNONE) - return true; - return *this == o; - } + bool operator!=(const OutlineValue& o) const { return !(*this == o); } - int offset() const { return m_offset; } - void setOffset(int offset) { m_offset = offset; } + bool visuallyEqual(const OutlineValue& o) const { + if (m_style == BNONE && o.m_style == BNONE) + return true; + return *this == o; + } - OutlineIsAuto isAuto() const { return static_cast(m_isAuto); } - void setIsAuto(OutlineIsAuto isAuto) { m_isAuto = isAuto; } + int offset() const { return m_offset; } + void setOffset(int offset) { m_offset = offset; } -private: - int m_offset; + OutlineIsAuto isAuto() const { return static_cast(m_isAuto); } + void setIsAuto(OutlineIsAuto isAuto) { m_isAuto = isAuto; } + + private: + int m_offset; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_STYLE_OUTLINEVALUE_H_ diff --git a/sky/engine/core/rendering/style/OutlineValueTest.cpp b/sky/engine/core/rendering/style/OutlineValueTest.cpp index 3548e6c1eafd9..4603a0a61e562 100644 --- a/sky/engine/core/rendering/style/OutlineValueTest.cpp +++ b/sky/engine/core/rendering/style/OutlineValueTest.cpp @@ -10,51 +10,48 @@ using namespace blink; namespace { -TEST(OutlineValueTest, VisuallyEqualStyle) -{ - OutlineValue outline1; - OutlineValue outline2; - - // Outlines visually equal if their styles are all BNONE. - EXPECT_TRUE(outline1.visuallyEqual(outline2)); - outline2.setOffset(10); - EXPECT_TRUE(outline1.visuallyEqual(outline2)); - - outline2.setStyle(DOTTED); - outline1.setOffset(10); - EXPECT_FALSE(outline1.visuallyEqual(outline2)); +TEST(OutlineValueTest, VisuallyEqualStyle) { + OutlineValue outline1; + OutlineValue outline2; + + // Outlines visually equal if their styles are all BNONE. + EXPECT_TRUE(outline1.visuallyEqual(outline2)); + outline2.setOffset(10); + EXPECT_TRUE(outline1.visuallyEqual(outline2)); + + outline2.setStyle(DOTTED); + outline1.setOffset(10); + EXPECT_FALSE(outline1.visuallyEqual(outline2)); } -TEST(OutlineValueTest, VisuallyEqualOffset) -{ - OutlineValue outline1; - OutlineValue outline2; +TEST(OutlineValueTest, VisuallyEqualOffset) { + OutlineValue outline1; + OutlineValue outline2; - outline1.setStyle(DOTTED); - outline2.setStyle(DOTTED); - EXPECT_TRUE(outline1.visuallyEqual(outline2)); + outline1.setStyle(DOTTED); + outline2.setStyle(DOTTED); + EXPECT_TRUE(outline1.visuallyEqual(outline2)); - outline1.setOffset(10); - EXPECT_FALSE(outline1.visuallyEqual(outline2)); + outline1.setOffset(10); + EXPECT_FALSE(outline1.visuallyEqual(outline2)); - outline2.setOffset(10); - EXPECT_TRUE(outline1.visuallyEqual(outline2)); + outline2.setOffset(10); + EXPECT_TRUE(outline1.visuallyEqual(outline2)); } -TEST(OutlineValueTest, VisuallyEqualIsAuto) -{ - OutlineValue outline1; - OutlineValue outline2; +TEST(OutlineValueTest, VisuallyEqualIsAuto) { + OutlineValue outline1; + OutlineValue outline2; - outline1.setStyle(DOTTED); - outline2.setStyle(DOTTED); - EXPECT_TRUE(outline1.visuallyEqual(outline2)); + outline1.setStyle(DOTTED); + outline2.setStyle(DOTTED); + EXPECT_TRUE(outline1.visuallyEqual(outline2)); - outline1.setIsAuto(AUTO_ON); - EXPECT_FALSE(outline1.visuallyEqual(outline2)); + outline1.setIsAuto(AUTO_ON); + EXPECT_FALSE(outline1.visuallyEqual(outline2)); - outline2.setIsAuto(AUTO_ON); - EXPECT_TRUE(outline1.visuallyEqual(outline2)); + outline2.setIsAuto(AUTO_ON); + EXPECT_TRUE(outline1.visuallyEqual(outline2)); } -} +} // namespace diff --git a/sky/engine/core/rendering/style/RenderStyle.cpp b/sky/engine/core/rendering/style/RenderStyle.cpp index 59f4ddb666d6e..c10113cb93cfc 100644 --- a/sky/engine/core/rendering/style/RenderStyle.cpp +++ b/sky/engine/core/rendering/style/RenderStyle.cpp @@ -1,6 +1,7 @@ /* * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org) - * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. + * All rights reserved. * Copyright (C) 2011 Adobe Systems Incorporated. All rights reserved. * * This library is free software; you can redistribute it and/or @@ -38,973 +39,1064 @@ namespace blink { struct SameSizeAsBorderValue { - RGBA32 m_color; - unsigned m_width; + RGBA32 m_color; + unsigned m_width; }; -COMPILE_ASSERT(sizeof(BorderValue) == sizeof(SameSizeAsBorderValue), BorderValue_should_not_grow); +COMPILE_ASSERT(sizeof(BorderValue) == sizeof(SameSizeAsBorderValue), + BorderValue_should_not_grow); -inline RenderStyle* defaultStyle() -{ - DEFINE_STATIC_REF(RenderStyle, s_defaultStyle, (RenderStyle::createDefaultStyle())); - return s_defaultStyle; +inline RenderStyle* defaultStyle() { + DEFINE_STATIC_REF(RenderStyle, s_defaultStyle, + (RenderStyle::createDefaultStyle())); + return s_defaultStyle; } -PassRefPtr RenderStyle::create() -{ - return adoptRef(new RenderStyle()); +PassRefPtr RenderStyle::create() { + return adoptRef(new RenderStyle()); } -PassRefPtr RenderStyle::createDefaultStyle() -{ - return adoptRef(new RenderStyle(DefaultStyle)); +PassRefPtr RenderStyle::createDefaultStyle() { + return adoptRef(new RenderStyle(DefaultStyle)); } -PassRefPtr RenderStyle::clone(const RenderStyle* other) -{ - return adoptRef(new RenderStyle(*other)); +PassRefPtr RenderStyle::clone(const RenderStyle* other) { + return adoptRef(new RenderStyle(*other)); } ALWAYS_INLINE RenderStyle::RenderStyle() - : m_box(defaultStyle()->m_box) - , visual(defaultStyle()->visual) - , m_background(defaultStyle()->m_background) - , surround(defaultStyle()->surround) - , rareNonInheritedData(defaultStyle()->rareNonInheritedData) - , rareInheritedData(defaultStyle()->rareInheritedData) - , inherited(defaultStyle()->inherited) -{ - setBitDefaults(); // Would it be faster to copy this from the default style? - COMPILE_ASSERT((sizeof(InheritedFlags) <= 8), InheritedFlags_does_not_grow); - COMPILE_ASSERT((sizeof(NonInheritedFlags) <= 8), NonInheritedFlags_does_not_grow); -} - -ALWAYS_INLINE RenderStyle::RenderStyle(DefaultStyleTag) -{ - setBitDefaults(); - - m_box.init(); - visual.init(); - m_background.init(); - surround.init(); - rareNonInheritedData.init(); - rareNonInheritedData.access()->m_flexibleBox.init(); - rareNonInheritedData.access()->m_transform.init(); - rareNonInheritedData.access()->m_filter.init(); - rareInheritedData.init(); - inherited.init(); + : m_box(defaultStyle()->m_box), + visual(defaultStyle()->visual), + m_background(defaultStyle()->m_background), + surround(defaultStyle()->surround), + rareNonInheritedData(defaultStyle()->rareNonInheritedData), + rareInheritedData(defaultStyle()->rareInheritedData), + inherited(defaultStyle()->inherited) { + setBitDefaults(); // Would it be faster to copy this from the default style? + COMPILE_ASSERT((sizeof(InheritedFlags) <= 8), InheritedFlags_does_not_grow); + COMPILE_ASSERT((sizeof(NonInheritedFlags) <= 8), + NonInheritedFlags_does_not_grow); +} + +ALWAYS_INLINE RenderStyle::RenderStyle(DefaultStyleTag) { + setBitDefaults(); + + m_box.init(); + visual.init(); + m_background.init(); + surround.init(); + rareNonInheritedData.init(); + rareNonInheritedData.access()->m_flexibleBox.init(); + rareNonInheritedData.access()->m_transform.init(); + rareNonInheritedData.access()->m_filter.init(); + rareInheritedData.init(); + inherited.init(); } ALWAYS_INLINE RenderStyle::RenderStyle(const RenderStyle& o) - : RefCounted() - , m_box(o.m_box) - , visual(o.visual) - , m_background(o.m_background) - , surround(o.surround) - , rareNonInheritedData(o.rareNonInheritedData) - , rareInheritedData(o.rareInheritedData) - , inherited(o.inherited) - , inherited_flags(o.inherited_flags) - , noninherited_flags(o.noninherited_flags) -{ -} - -StyleRecalcChange RenderStyle::stylePropagationDiff(const RenderStyle* oldStyle, const RenderStyle* newStyle) -{ - if ((!oldStyle && newStyle) || (oldStyle && !newStyle)) - return Reattach; - - if (!oldStyle && !newStyle) - return NoChange; - - if (oldStyle->display() != newStyle->display() - || oldStyle->justifyItems() != newStyle->justifyItems() - || oldStyle->alignItems() != newStyle->alignItems()) - return Reattach; - - if (*oldStyle == *newStyle) - return NoChange; - - if (oldStyle->inheritedNotEqual(newStyle) - || oldStyle->hasExplicitlyInheritedProperties() - || newStyle->hasExplicitlyInheritedProperties()) - return Inherit; - - return NoInherit; -} - -void RenderStyle::inheritFrom(const RenderStyle* inheritParent) -{ - rareInheritedData = inheritParent->rareInheritedData; - inherited = inheritParent->inherited; - inherited_flags = inheritParent->inherited_flags; -} - -void RenderStyle::copyNonInheritedFrom(const RenderStyle* other) -{ - m_box = other->m_box; - visual = other->visual; - m_background = other->m_background; - surround = other->surround; - rareNonInheritedData = other->rareNonInheritedData; - // The flags are copied one-by-one because noninherited_flags contains a bunch of stuff other than real style data. - noninherited_flags.effectiveDisplay = other->noninherited_flags.effectiveDisplay; - noninherited_flags.originalDisplay = other->noninherited_flags.originalDisplay; - noninherited_flags.overflowX = other->noninherited_flags.overflowX; - noninherited_flags.overflowY = other->noninherited_flags.overflowY; - noninherited_flags.verticalAlign = other->noninherited_flags.verticalAlign; - noninherited_flags.position = other->noninherited_flags.position; - noninherited_flags.unicodeBidi = other->noninherited_flags.unicodeBidi; - noninherited_flags.explicitInheritance = other->noninherited_flags.explicitInheritance; - noninherited_flags.currentColor = other->noninherited_flags.currentColor; - noninherited_flags.hasViewportUnits = other->noninherited_flags.hasViewportUnits; -} - -bool RenderStyle::operator==(const RenderStyle& o) const -{ - // compare everything except the pseudoStyle pointer - return inherited_flags == o.inherited_flags - && noninherited_flags == o.noninherited_flags - && m_box == o.m_box - && visual == o.visual - && m_background == o.m_background - && surround == o.surround - && rareNonInheritedData == o.rareNonInheritedData - && rareInheritedData == o.rareInheritedData - && inherited == o.inherited; -} - -bool RenderStyle::inheritedNotEqual(const RenderStyle* other) const -{ - return inherited_flags != other->inherited_flags - || inherited != other->inherited - || rareInheritedData != other->rareInheritedData; -} - -bool RenderStyle::inheritedDataShared(const RenderStyle* other) const -{ - // This is a fast check that only looks if the data structures are shared. - return inherited_flags == other->inherited_flags - && inherited.get() == other->inherited.get() - && rareInheritedData.get() == other->rareInheritedData.get(); -} - -bool RenderStyle::requiresOnlyBlockChildren() const -{ - switch (display()) { + : RefCounted(), + m_box(o.m_box), + visual(o.visual), + m_background(o.m_background), + surround(o.surround), + rareNonInheritedData(o.rareNonInheritedData), + rareInheritedData(o.rareInheritedData), + inherited(o.inherited), + inherited_flags(o.inherited_flags), + noninherited_flags(o.noninherited_flags) {} + +StyleRecalcChange RenderStyle::stylePropagationDiff( + const RenderStyle* oldStyle, + const RenderStyle* newStyle) { + if ((!oldStyle && newStyle) || (oldStyle && !newStyle)) + return Reattach; + + if (!oldStyle && !newStyle) + return NoChange; + + if (oldStyle->display() != newStyle->display() || + oldStyle->justifyItems() != newStyle->justifyItems() || + oldStyle->alignItems() != newStyle->alignItems()) + return Reattach; + + if (*oldStyle == *newStyle) + return NoChange; + + if (oldStyle->inheritedNotEqual(newStyle) || + oldStyle->hasExplicitlyInheritedProperties() || + newStyle->hasExplicitlyInheritedProperties()) + return Inherit; + + return NoInherit; +} + +void RenderStyle::inheritFrom(const RenderStyle* inheritParent) { + rareInheritedData = inheritParent->rareInheritedData; + inherited = inheritParent->inherited; + inherited_flags = inheritParent->inherited_flags; +} + +void RenderStyle::copyNonInheritedFrom(const RenderStyle* other) { + m_box = other->m_box; + visual = other->visual; + m_background = other->m_background; + surround = other->surround; + rareNonInheritedData = other->rareNonInheritedData; + // The flags are copied one-by-one because noninherited_flags contains a bunch + // of stuff other than real style data. + noninherited_flags.effectiveDisplay = + other->noninherited_flags.effectiveDisplay; + noninherited_flags.originalDisplay = + other->noninherited_flags.originalDisplay; + noninherited_flags.overflowX = other->noninherited_flags.overflowX; + noninherited_flags.overflowY = other->noninherited_flags.overflowY; + noninherited_flags.verticalAlign = other->noninherited_flags.verticalAlign; + noninherited_flags.position = other->noninherited_flags.position; + noninherited_flags.unicodeBidi = other->noninherited_flags.unicodeBidi; + noninherited_flags.explicitInheritance = + other->noninherited_flags.explicitInheritance; + noninherited_flags.currentColor = other->noninherited_flags.currentColor; + noninherited_flags.hasViewportUnits = + other->noninherited_flags.hasViewportUnits; +} + +bool RenderStyle::operator==(const RenderStyle& o) const { + // compare everything except the pseudoStyle pointer + return inherited_flags == o.inherited_flags && + noninherited_flags == o.noninherited_flags && m_box == o.m_box && + visual == o.visual && m_background == o.m_background && + surround == o.surround && + rareNonInheritedData == o.rareNonInheritedData && + rareInheritedData == o.rareInheritedData && inherited == o.inherited; +} + +bool RenderStyle::inheritedNotEqual(const RenderStyle* other) const { + return inherited_flags != other->inherited_flags || + inherited != other->inherited || + rareInheritedData != other->rareInheritedData; +} + +bool RenderStyle::inheritedDataShared(const RenderStyle* other) const { + // This is a fast check that only looks if the data structures are shared. + return inherited_flags == other->inherited_flags && + inherited.get() == other->inherited.get() && + rareInheritedData.get() == other->rareInheritedData.get(); +} + +bool RenderStyle::requiresOnlyBlockChildren() const { + switch (display()) { case PARAGRAPH: case INLINE: - return false; + return false; case FLEX: case INLINE_FLEX: - return true; + return true; case NONE: - ASSERT_NOT_REACHED(); - return false; - } + ASSERT_NOT_REACHED(); + return false; + } + + ASSERT_NOT_REACHED(); + return false; +} + +static bool positionedObjectMovedOnly(const LengthBox& a, + const LengthBox& b, + const Length& width) { + // If any unit types are different, then we can't guarantee + // that this was just a movement. + if (a.left().type() != b.left().type() || + a.right().type() != b.right().type() || + a.top().type() != b.top().type() || + a.bottom().type() != b.bottom().type()) + return false; - ASSERT_NOT_REACHED(); + // Only one unit can be non-auto in the horizontal direction and + // in the vertical direction. Otherwise the adjustment of values + // is changing the size of the box. + if (!a.left().isIntrinsicOrAuto() && !a.right().isIntrinsicOrAuto()) + return false; + if (!a.top().isIntrinsicOrAuto() && !a.bottom().isIntrinsicOrAuto()) + return false; + // If our width is auto and left or right is specified and changed then this + // is not just a movement - we need to resize to our container. + if (width.isIntrinsicOrAuto() && + ((!a.left().isIntrinsicOrAuto() && a.left() != b.left()) || + (!a.right().isIntrinsicOrAuto() && a.right() != b.right()))) return false; -} -static bool positionedObjectMovedOnly(const LengthBox& a, const LengthBox& b, const Length& width) -{ - // If any unit types are different, then we can't guarantee - // that this was just a movement. - if (a.left().type() != b.left().type() - || a.right().type() != b.right().type() - || a.top().type() != b.top().type() - || a.bottom().type() != b.bottom().type()) - return false; - - // Only one unit can be non-auto in the horizontal direction and - // in the vertical direction. Otherwise the adjustment of values - // is changing the size of the box. - if (!a.left().isIntrinsicOrAuto() && !a.right().isIntrinsicOrAuto()) - return false; - if (!a.top().isIntrinsicOrAuto() && !a.bottom().isIntrinsicOrAuto()) - return false; - // If our width is auto and left or right is specified and changed then this - // is not just a movement - we need to resize to our container. - if (width.isIntrinsicOrAuto() - && ((!a.left().isIntrinsicOrAuto() && a.left() != b.left()) - || (!a.right().isIntrinsicOrAuto() && a.right() != b.right()))) - return false; - - // One of the units is fixed or percent in both directions and stayed - // that way in the new style. Therefore all we are doing is moving. - return true; + // One of the units is fixed or percent in both directions and stayed + // that way in the new style. Therefore all we are doing is moving. + return true; } -StyleDifference RenderStyle::visualInvalidationDiff(const RenderStyle& other) const -{ - // Note, we use .get() on each DataRef below because DataRef::operator== will do a deep - // compare, which is duplicate work when we're going to compare each property inside - // this function anyway. +StyleDifference RenderStyle::visualInvalidationDiff( + const RenderStyle& other) const { + // Note, we use .get() on each DataRef below because DataRef::operator== will + // do a deep compare, which is duplicate work when we're going to compare each + // property inside this function anyway. - StyleDifference diff; + StyleDifference diff; - if (diffNeedsFullLayout(other)) { - diff.setNeedsFullLayout(); - } else if (position() != StaticPosition && surround->offset != other.surround->offset) { - // Optimize for the case where a positioned layer is moving but not changing size. - if (positionedObjectMovedOnly(surround->offset, other.surround->offset, m_box->width())) - diff.setNeedsPositionedMovementLayout(); - else - diff.setNeedsFullLayout(); + if (diffNeedsFullLayout(other)) { + diff.setNeedsFullLayout(); + } else if (position() != StaticPosition && + surround->offset != other.surround->offset) { + // Optimize for the case where a positioned layer is moving but not changing + // size. + if (positionedObjectMovedOnly(surround->offset, other.surround->offset, + m_box->width())) + diff.setNeedsPositionedMovementLayout(); + else + diff.setNeedsFullLayout(); + } + + updatePropertySpecificDifferences(other, diff); + + // Cursors are not checked, since they will be set appropriately in response + // to mouse events, so they don't need to cause any paint invalidation or + // layout. + + return diff; +} + +bool RenderStyle::diffNeedsFullLayout(const RenderStyle& other) const { + // FIXME: Not all cases in this method need both full layout and paint + // invalidation. Should move cases into diffNeedsFullLayout() if + // - don't need paint invalidation at all; + // - or the renderer knows how to exactly invalidate paints caused by the + // layout change + // instead of forced full paint invalidation. + + if (surround.get() != other.surround.get()) { + // If our border widths change, then we need to layout. Other changes to + // borders only necessitate a paint invalidation. + if (borderLeftWidth() != other.borderLeftWidth() || + borderTopWidth() != other.borderTopWidth() || + borderBottomWidth() != other.borderBottomWidth() || + borderRightWidth() != other.borderRightWidth()) + return true; + } + + if (rareNonInheritedData.get() != other.rareNonInheritedData.get()) { + if (rareNonInheritedData->textOverflow != + other.rareNonInheritedData->textOverflow || + rareNonInheritedData->m_wrapFlow != + other.rareNonInheritedData->m_wrapFlow || + rareNonInheritedData->m_wrapThrough != + other.rareNonInheritedData->m_wrapThrough || + rareNonInheritedData->m_order != other.rareNonInheritedData->m_order || + rareNonInheritedData->m_alignContent != + other.rareNonInheritedData->m_alignContent || + rareNonInheritedData->m_alignItems != + other.rareNonInheritedData->m_alignItems || + rareNonInheritedData->m_alignSelf != + other.rareNonInheritedData->m_alignSelf || + rareNonInheritedData->m_justifyContent != + other.rareNonInheritedData->m_justifyContent) + return true; + + if (rareNonInheritedData->m_flexibleBox.get() != + other.rareNonInheritedData->m_flexibleBox.get() && + *rareNonInheritedData->m_flexibleBox.get() != + *other.rareNonInheritedData->m_flexibleBox.get()) + return true; + + // FIXME: We should add an optimized form of layout that just recomputes + // visual overflow. + if (!rareNonInheritedData->shadowDataEquivalent( + *other.rareNonInheritedData.get())) + return true; + + // If the counter directives change, trigger a relayout to re-calculate + // counter values and rebuild the counter node tree. + const CounterDirectiveMap* mapA = + rareNonInheritedData->m_counterDirectives.get(); + const CounterDirectiveMap* mapB = + other.rareNonInheritedData->m_counterDirectives.get(); + if (!(mapA == mapB || (mapA && mapB && *mapA == *mapB))) + return true; + + // We only need do layout for opacity changes if adding or losing opacity + // could trigger a change in us being a stacking context. + if (hasAutoZIndex() != other.hasAutoZIndex() && + rareNonInheritedData->hasOpacity() != + other.rareNonInheritedData->hasOpacity()) { + // FIXME: We would like to use SimplifiedLayout here, but we can't quite + // do that yet. We need to make sure SimplifiedLayout can operate + // correctly on RenderInlines (we will need to add a + // selfNeedsSimplifiedLayout bit in order to not get confused and taint + // every line). In addition we need to solve the floating object issue + // when layers come and go. Right now a full layout is necessary to keep + // floating object lists sane. + return true; } + } + + if (rareInheritedData.get() != other.rareInheritedData.get()) { + if (rareInheritedData->highlight != other.rareInheritedData->highlight || + rareInheritedData->indent != other.rareInheritedData->indent || + rareInheritedData->m_textAlignLast != + other.rareInheritedData->m_textAlignLast || + rareInheritedData->m_textIndentLine != + other.rareInheritedData->m_textIndentLine || + rareInheritedData->wordBreak != other.rareInheritedData->wordBreak || + rareInheritedData->overflowWrap != + other.rareInheritedData->overflowWrap || + rareInheritedData->lineBreak != other.rareInheritedData->lineBreak || + rareInheritedData->hyphens != other.rareInheritedData->hyphens || + rareInheritedData->hyphenationLimitBefore != + other.rareInheritedData->hyphenationLimitBefore || + rareInheritedData->hyphenationLimitAfter != + other.rareInheritedData->hyphenationLimitAfter || + rareInheritedData->hyphenationString != + other.rareInheritedData->hyphenationString || + rareInheritedData->locale != other.rareInheritedData->locale || + rareInheritedData->textEmphasisMark != + other.rareInheritedData->textEmphasisMark || + rareInheritedData->textEmphasisPosition != + other.rareInheritedData->textEmphasisPosition || + rareInheritedData->textEmphasisCustomMark != + other.rareInheritedData->textEmphasisCustomMark || + rareInheritedData->m_textJustify != + other.rareInheritedData->m_textJustify || + rareInheritedData->m_textOrientation != + other.rareInheritedData->m_textOrientation || + rareInheritedData->m_tabSize != other.rareInheritedData->m_tabSize || + rareInheritedData->m_lineBoxContain != + other.rareInheritedData->m_lineBoxContain || + rareInheritedData->textStrokeWidth != + other.rareInheritedData->textStrokeWidth) + return true; + + if (!rareInheritedData->shadowDataEquivalent( + *other.rareInheritedData.get())) + return true; + } + + if (inherited.get() != other.inherited.get()) { + if (inherited->line_height != other.inherited->line_height || + inherited->font != other.inherited->font || + inherited->horizontal_border_spacing != + other.inherited->horizontal_border_spacing || + inherited->vertical_border_spacing != + other.inherited->vertical_border_spacing) + return true; + } + + if (inherited_flags.m_rtlOrdering != other.inherited_flags.m_rtlOrdering || + inherited_flags._text_align != other.inherited_flags._text_align || + inherited_flags._direction != other.inherited_flags._direction || + inherited_flags._white_space != other.inherited_flags._white_space) + return true; - updatePropertySpecificDifferences(other, diff); - - // Cursors are not checked, since they will be set appropriately in response to mouse events, - // so they don't need to cause any paint invalidation or layout. - - return diff; -} - -bool RenderStyle::diffNeedsFullLayout(const RenderStyle& other) const -{ - // FIXME: Not all cases in this method need both full layout and paint invalidation. - // Should move cases into diffNeedsFullLayout() if - // - don't need paint invalidation at all; - // - or the renderer knows how to exactly invalidate paints caused by the layout change - // instead of forced full paint invalidation. - - if (surround.get() != other.surround.get()) { - // If our border widths change, then we need to layout. Other changes to borders only necessitate a paint invalidation. - if (borderLeftWidth() != other.borderLeftWidth() - || borderTopWidth() != other.borderTopWidth() - || borderBottomWidth() != other.borderBottomWidth() - || borderRightWidth() != other.borderRightWidth()) - return true; - } + if (noninherited_flags.overflowX != other.noninherited_flags.overflowX || + noninherited_flags.overflowY != other.noninherited_flags.overflowY || + noninherited_flags.unicodeBidi != other.noninherited_flags.unicodeBidi || + noninherited_flags.position != other.noninherited_flags.position || + noninherited_flags.originalDisplay != + other.noninherited_flags.originalDisplay) + return true; - if (rareNonInheritedData.get() != other.rareNonInheritedData.get()) { - if (rareNonInheritedData->textOverflow != other.rareNonInheritedData->textOverflow - || rareNonInheritedData->m_wrapFlow != other.rareNonInheritedData->m_wrapFlow - || rareNonInheritedData->m_wrapThrough != other.rareNonInheritedData->m_wrapThrough - || rareNonInheritedData->m_order != other.rareNonInheritedData->m_order - || rareNonInheritedData->m_alignContent != other.rareNonInheritedData->m_alignContent - || rareNonInheritedData->m_alignItems != other.rareNonInheritedData->m_alignItems - || rareNonInheritedData->m_alignSelf != other.rareNonInheritedData->m_alignSelf - || rareNonInheritedData->m_justifyContent != other.rareNonInheritedData->m_justifyContent) - return true; - - if (rareNonInheritedData->m_flexibleBox.get() != other.rareNonInheritedData->m_flexibleBox.get() - && *rareNonInheritedData->m_flexibleBox.get() != *other.rareNonInheritedData->m_flexibleBox.get()) - return true; - - // FIXME: We should add an optimized form of layout that just recomputes visual overflow. - if (!rareNonInheritedData->shadowDataEquivalent(*other.rareNonInheritedData.get())) - return true; - - // If the counter directives change, trigger a relayout to re-calculate counter values and rebuild the counter node tree. - const CounterDirectiveMap* mapA = rareNonInheritedData->m_counterDirectives.get(); - const CounterDirectiveMap* mapB = other.rareNonInheritedData->m_counterDirectives.get(); - if (!(mapA == mapB || (mapA && mapB && *mapA == *mapB))) - return true; - - // We only need do layout for opacity changes if adding or losing opacity could trigger a change - // in us being a stacking context. - if (hasAutoZIndex() != other.hasAutoZIndex() && rareNonInheritedData->hasOpacity() != other.rareNonInheritedData->hasOpacity()) { - // FIXME: We would like to use SimplifiedLayout here, but we can't quite do that yet. - // We need to make sure SimplifiedLayout can operate correctly on RenderInlines (we will need - // to add a selfNeedsSimplifiedLayout bit in order to not get confused and taint every line). - // In addition we need to solve the floating object issue when layers come and go. Right now - // a full layout is necessary to keep floating object lists sane. - return true; - } - } + if (!m_background->outline().visuallyEqual(other.m_background->outline())) { + // FIXME: We only really need to recompute the overflow but we don't have an + // optimized layout for it. + return true; + } + + if (m_box.get() != other.m_box.get()) { + if (m_box->width() != other.m_box->width() || + m_box->minWidth() != other.m_box->minWidth() || + m_box->maxWidth() != other.m_box->maxWidth() || + m_box->height() != other.m_box->height() || + m_box->minHeight() != other.m_box->minHeight() || + m_box->maxHeight() != other.m_box->maxHeight()) + return true; + + if (m_box->verticalAlign() != other.m_box->verticalAlign()) + return true; + + if (m_box->boxSizing() != other.m_box->boxSizing()) + return true; + } + + if (noninherited_flags.verticalAlign != + other.noninherited_flags.verticalAlign) + return true; - if (rareInheritedData.get() != other.rareInheritedData.get()) { - if (rareInheritedData->highlight != other.rareInheritedData->highlight - || rareInheritedData->indent != other.rareInheritedData->indent - || rareInheritedData->m_textAlignLast != other.rareInheritedData->m_textAlignLast - || rareInheritedData->m_textIndentLine != other.rareInheritedData->m_textIndentLine - || rareInheritedData->wordBreak != other.rareInheritedData->wordBreak - || rareInheritedData->overflowWrap != other.rareInheritedData->overflowWrap - || rareInheritedData->lineBreak != other.rareInheritedData->lineBreak - || rareInheritedData->hyphens != other.rareInheritedData->hyphens - || rareInheritedData->hyphenationLimitBefore != other.rareInheritedData->hyphenationLimitBefore - || rareInheritedData->hyphenationLimitAfter != other.rareInheritedData->hyphenationLimitAfter - || rareInheritedData->hyphenationString != other.rareInheritedData->hyphenationString - || rareInheritedData->locale != other.rareInheritedData->locale - || rareInheritedData->textEmphasisMark != other.rareInheritedData->textEmphasisMark - || rareInheritedData->textEmphasisPosition != other.rareInheritedData->textEmphasisPosition - || rareInheritedData->textEmphasisCustomMark != other.rareInheritedData->textEmphasisCustomMark - || rareInheritedData->m_textJustify != other.rareInheritedData->m_textJustify - || rareInheritedData->m_textOrientation != other.rareInheritedData->m_textOrientation - || rareInheritedData->m_tabSize != other.rareInheritedData->m_tabSize - || rareInheritedData->m_lineBoxContain != other.rareInheritedData->m_lineBoxContain - || rareInheritedData->textStrokeWidth != other.rareInheritedData->textStrokeWidth) - return true; - - if (!rareInheritedData->shadowDataEquivalent(*other.rareInheritedData.get())) - return true; - } + if (surround.get() != other.surround.get()) { + if (surround->margin != other.surround->margin) + return true; - if (inherited.get() != other.inherited.get()) { - if (inherited->line_height != other.inherited->line_height - || inherited->font != other.inherited->font - || inherited->horizontal_border_spacing != other.inherited->horizontal_border_spacing - || inherited->vertical_border_spacing != other.inherited->vertical_border_spacing) - return true; - } + if (surround->padding != other.surround->padding) + return true; + } - if (inherited_flags.m_rtlOrdering != other.inherited_flags.m_rtlOrdering - || inherited_flags._text_align != other.inherited_flags._text_align - || inherited_flags._direction != other.inherited_flags._direction - || inherited_flags._white_space != other.inherited_flags._white_space) - return true; - - if (noninherited_flags.overflowX != other.noninherited_flags.overflowX - || noninherited_flags.overflowY != other.noninherited_flags.overflowY - || noninherited_flags.unicodeBidi != other.noninherited_flags.unicodeBidi - || noninherited_flags.position != other.noninherited_flags.position - || noninherited_flags.originalDisplay != other.noninherited_flags.originalDisplay) - return true; - - if (!m_background->outline().visuallyEqual(other.m_background->outline())) { - // FIXME: We only really need to recompute the overflow but we don't have an optimized layout for it. - return true; - } + // Movement of non-static-positioned object is special cased in + // RenderStyle::visualInvalidationDiff(). - if (m_box.get() != other.m_box.get()) { - if (m_box->width() != other.m_box->width() - || m_box->minWidth() != other.m_box->minWidth() - || m_box->maxWidth() != other.m_box->maxWidth() - || m_box->height() != other.m_box->height() - || m_box->minHeight() != other.m_box->minHeight() - || m_box->maxHeight() != other.m_box->maxHeight()) - return true; + return false; +} - if (m_box->verticalAlign() != other.m_box->verticalAlign()) - return true; +void RenderStyle::updatePropertySpecificDifferences( + const RenderStyle& other, + StyleDifference& diff) const { + // StyleAdjuster has ensured that zIndex is non-auto only if it's applicable. + if (m_box->zIndex() != other.m_box->zIndex() || + m_box->hasAutoZIndex() != other.m_box->hasAutoZIndex()) + diff.setZIndexChanged(); - if (m_box->boxSizing() != other.m_box->boxSizing()) - return true; - } + if (rareNonInheritedData.get() != other.rareNonInheritedData.get()) { + if (!transformDataEquivalent(other)) + diff.setTransformChanged(); - if (noninherited_flags.verticalAlign != other.noninherited_flags.verticalAlign) - return true; + if (rareNonInheritedData->opacity != other.rareNonInheritedData->opacity) + diff.setOpacityChanged(); - if (surround.get() != other.surround.get()) { - if (surround->margin != other.surround->margin) - return true; + if (rareNonInheritedData->m_filter != other.rareNonInheritedData->m_filter) + diff.setFilterChanged(); + } +} - if (surround->padding != other.surround->padding) - return true; - } +inline bool requireTransformOrigin( + const Vector>& transformOperations, + RenderStyle::ApplyTransformOrigin applyOrigin) { + // transform-origin brackets the transform with translate operations. + // Optimize for the case where the only transform is a translation, since the + // transform-origin is irrelevant in that case. + if (applyOrigin != RenderStyle::IncludeTransformOrigin) + return false; - // Movement of non-static-positioned object is special cased in RenderStyle::visualInvalidationDiff(). + unsigned size = transformOperations.size(); + for (unsigned i = 0; i < size; ++i) { + TransformOperation::OperationType type = transformOperations[i]->type(); + if (type != TransformOperation::TranslateX && + type != TransformOperation::TranslateY && + type != TransformOperation::Translate && + type != TransformOperation::TranslateZ && + type != TransformOperation::Translate3D) + return true; + } - return false; + return false; } -void RenderStyle::updatePropertySpecificDifferences(const RenderStyle& other, StyleDifference& diff) const -{ - // StyleAdjuster has ensured that zIndex is non-auto only if it's applicable. - if (m_box->zIndex() != other.m_box->zIndex() || m_box->hasAutoZIndex() != other.m_box->hasAutoZIndex()) - diff.setZIndexChanged(); +void RenderStyle::applyTransform(TransformationMatrix& transform, + const LayoutSize& borderBoxSize, + ApplyTransformOrigin applyOrigin) const { + applyTransform(transform, FloatRect(FloatPoint(), borderBoxSize), + applyOrigin); +} - if (rareNonInheritedData.get() != other.rareNonInheritedData.get()) { - if (!transformDataEquivalent(other)) - diff.setTransformChanged(); +void RenderStyle::applyTransform(TransformationMatrix& transform, + const FloatRect& boundingBox, + ApplyTransformOrigin applyOrigin) const { + const Vector>& transformOperations = + rareNonInheritedData->m_transform->m_operations.operations(); + bool applyTransformOrigin = + requireTransformOrigin(transformOperations, applyOrigin); + + float offsetX = transformOriginX().type() == Percent ? boundingBox.x() : 0; + float offsetY = transformOriginY().type() == Percent ? boundingBox.y() : 0; + + if (applyTransformOrigin) { + transform.translate3d( + floatValueForLength(transformOriginX(), boundingBox.width()) + offsetX, + floatValueForLength(transformOriginY(), boundingBox.height()) + offsetY, + transformOriginZ()); + } + + unsigned size = transformOperations.size(); + for (unsigned i = 0; i < size; ++i) + transformOperations[i]->apply(transform, boundingBox.size()); + + if (applyTransformOrigin) { + transform.translate3d( + -floatValueForLength(transformOriginX(), boundingBox.width()) - offsetX, + -floatValueForLength(transformOriginY(), boundingBox.height()) - + offsetY, + -transformOriginZ()); + } +} + +void RenderStyle::setTextShadow(PassRefPtr s) { + rareInheritedData.access()->textShadow = s; +} + +void RenderStyle::setBoxShadow(PassRefPtr s) { + rareNonInheritedData.access()->m_boxShadow = s; +} - if (rareNonInheritedData->opacity != other.rareNonInheritedData->opacity) - diff.setOpacityChanged(); - - if (rareNonInheritedData->m_filter != other.rareNonInheritedData->m_filter) - diff.setFilterChanged(); - } +static RoundedRect::Radii calcRadiiFor(const BorderData& border, IntSize size) { + return RoundedRect::Radii( + IntSize(valueForLength(border.topLeft().width(), size.width()), + valueForLength(border.topLeft().height(), size.height())), + IntSize(valueForLength(border.topRight().width(), size.width()), + valueForLength(border.topRight().height(), size.height())), + IntSize(valueForLength(border.bottomLeft().width(), size.width()), + valueForLength(border.bottomLeft().height(), size.height())), + IntSize(valueForLength(border.bottomRight().width(), size.width()), + valueForLength(border.bottomRight().height(), size.height()))); } -inline bool requireTransformOrigin(const Vector >& transformOperations, RenderStyle::ApplyTransformOrigin applyOrigin) -{ - // transform-origin brackets the transform with translate operations. - // Optimize for the case where the only transform is a translation, since the transform-origin is irrelevant - // in that case. - if (applyOrigin != RenderStyle::IncludeTransformOrigin) - return false; - - unsigned size = transformOperations.size(); - for (unsigned i = 0; i < size; ++i) { - TransformOperation::OperationType type = transformOperations[i]->type(); - if (type != TransformOperation::TranslateX - && type != TransformOperation::TranslateY - && type != TransformOperation::Translate - && type != TransformOperation::TranslateZ - && type != TransformOperation::Translate3D) - return true; - } - - return false; +Color RenderStyle::color() const { + return inherited->color; } - -void RenderStyle::applyTransform(TransformationMatrix& transform, const LayoutSize& borderBoxSize, ApplyTransformOrigin applyOrigin) const -{ - applyTransform(transform, FloatRect(FloatPoint(), borderBoxSize), applyOrigin); +void RenderStyle::setColor(const Color& v) { + SET_VAR(inherited, color, v); } -void RenderStyle::applyTransform(TransformationMatrix& transform, const FloatRect& boundingBox, ApplyTransformOrigin applyOrigin) const -{ - const Vector >& transformOperations = rareNonInheritedData->m_transform->m_operations.operations(); - bool applyTransformOrigin = requireTransformOrigin(transformOperations, applyOrigin); - - float offsetX = transformOriginX().type() == Percent ? boundingBox.x() : 0; - float offsetY = transformOriginY().type() == Percent ? boundingBox.y() : 0; - - if (applyTransformOrigin) { - transform.translate3d(floatValueForLength(transformOriginX(), boundingBox.width()) + offsetX, - floatValueForLength(transformOriginY(), boundingBox.height()) + offsetY, - transformOriginZ()); - } - - unsigned size = transformOperations.size(); - for (unsigned i = 0; i < size; ++i) - transformOperations[i]->apply(transform, boundingBox.size()); - - if (applyTransformOrigin) { - transform.translate3d(-floatValueForLength(transformOriginX(), boundingBox.width()) - offsetX, - -floatValueForLength(transformOriginY(), boundingBox.height()) - offsetY, - -transformOriginZ()); - } +short RenderStyle::horizontalBorderSpacing() const { + return inherited->horizontal_border_spacing; } - -void RenderStyle::setTextShadow(PassRefPtr s) -{ - rareInheritedData.access()->textShadow = s; +short RenderStyle::verticalBorderSpacing() const { + return inherited->vertical_border_spacing; } - -void RenderStyle::setBoxShadow(PassRefPtr s) -{ - rareNonInheritedData.access()->m_boxShadow = s; +void RenderStyle::setHorizontalBorderSpacing(short v) { + SET_VAR(inherited, horizontal_border_spacing, v); } - -static RoundedRect::Radii calcRadiiFor(const BorderData& border, IntSize size) -{ - return RoundedRect::Radii( - IntSize(valueForLength(border.topLeft().width(), size.width()), - valueForLength(border.topLeft().height(), size.height())), - IntSize(valueForLength(border.topRight().width(), size.width()), - valueForLength(border.topRight().height(), size.height())), - IntSize(valueForLength(border.bottomLeft().width(), size.width()), - valueForLength(border.bottomLeft().height(), size.height())), - IntSize(valueForLength(border.bottomRight().width(), size.width()), - valueForLength(border.bottomRight().height(), size.height()))); +void RenderStyle::setVerticalBorderSpacing(short v) { + SET_VAR(inherited, vertical_border_spacing, v); } -Color RenderStyle::color() const { return inherited->color; } -void RenderStyle::setColor(const Color& v) { SET_VAR(inherited, color, v); } - -short RenderStyle::horizontalBorderSpacing() const { return inherited->horizontal_border_spacing; } -short RenderStyle::verticalBorderSpacing() const { return inherited->vertical_border_spacing; } -void RenderStyle::setHorizontalBorderSpacing(short v) { SET_VAR(inherited, horizontal_border_spacing, v); } -void RenderStyle::setVerticalBorderSpacing(short v) { SET_VAR(inherited, vertical_border_spacing, v); } - -RoundedRect RenderStyle::getRoundedBorderFor(const LayoutRect& borderRect, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) const -{ - IntRect snappedBorderRect(pixelSnappedIntRect(borderRect)); - RoundedRect roundedRect(snappedBorderRect); - if (hasBorderRadius()) { - RoundedRect::Radii radii = calcRadiiFor(surround->border, snappedBorderRect.size()); - radii.scale(calcBorderRadiiConstraintScaleFor(borderRect, radii)); - roundedRect.includeLogicalEdges(radii, includeLogicalLeftEdge, includeLogicalRightEdge); - } - return roundedRect; +RoundedRect RenderStyle::getRoundedBorderFor( + const LayoutRect& borderRect, + bool includeLogicalLeftEdge, + bool includeLogicalRightEdge) const { + IntRect snappedBorderRect(pixelSnappedIntRect(borderRect)); + RoundedRect roundedRect(snappedBorderRect); + if (hasBorderRadius()) { + RoundedRect::Radii radii = + calcRadiiFor(surround->border, snappedBorderRect.size()); + radii.scale(calcBorderRadiiConstraintScaleFor(borderRect, radii)); + roundedRect.includeLogicalEdges(radii, includeLogicalLeftEdge, + includeLogicalRightEdge); + } + return roundedRect; } -RoundedRect RenderStyle::getRoundedInnerBorderFor(const LayoutRect& borderRect, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) const -{ - int leftWidth = (includeLogicalLeftEdge) ? borderLeftWidth() : 0; - int rightWidth = (includeLogicalRightEdge) ? borderRightWidth() : 0; - int topWidth = borderTopWidth(); - int bottomWidth = borderBottomWidth(); +RoundedRect RenderStyle::getRoundedInnerBorderFor( + const LayoutRect& borderRect, + bool includeLogicalLeftEdge, + bool includeLogicalRightEdge) const { + int leftWidth = (includeLogicalLeftEdge) ? borderLeftWidth() : 0; + int rightWidth = (includeLogicalRightEdge) ? borderRightWidth() : 0; + int topWidth = borderTopWidth(); + int bottomWidth = borderBottomWidth(); - return getRoundedInnerBorderFor(borderRect, topWidth, bottomWidth, leftWidth, rightWidth, includeLogicalLeftEdge, includeLogicalRightEdge); + return getRoundedInnerBorderFor(borderRect, topWidth, bottomWidth, leftWidth, + rightWidth, includeLogicalLeftEdge, + includeLogicalRightEdge); } -RoundedRect RenderStyle::getRoundedInnerBorderFor(const LayoutRect& borderRect, - int topWidth, int bottomWidth, int leftWidth, int rightWidth, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) const -{ - LayoutRect innerRect(borderRect.x() + leftWidth, - borderRect.y() + topWidth, - borderRect.width() - leftWidth - rightWidth, - borderRect.height() - topWidth - bottomWidth); +RoundedRect RenderStyle::getRoundedInnerBorderFor( + const LayoutRect& borderRect, + int topWidth, + int bottomWidth, + int leftWidth, + int rightWidth, + bool includeLogicalLeftEdge, + bool includeLogicalRightEdge) const { + LayoutRect innerRect(borderRect.x() + leftWidth, borderRect.y() + topWidth, + borderRect.width() - leftWidth - rightWidth, + borderRect.height() - topWidth - bottomWidth); - RoundedRect roundedRect(pixelSnappedIntRect(innerRect)); + RoundedRect roundedRect(pixelSnappedIntRect(innerRect)); - if (hasBorderRadius()) { - RoundedRect::Radii radii = getRoundedBorderFor(borderRect).radii(); - radii.shrink(topWidth, bottomWidth, leftWidth, rightWidth); - roundedRect.includeLogicalEdges(radii, includeLogicalLeftEdge, includeLogicalRightEdge); - } - return roundedRect; + if (hasBorderRadius()) { + RoundedRect::Radii radii = getRoundedBorderFor(borderRect).radii(); + radii.shrink(topWidth, bottomWidth, leftWidth, rightWidth); + roundedRect.includeLogicalEdges(radii, includeLogicalLeftEdge, + includeLogicalRightEdge); + } + return roundedRect; } -static bool allLayersAreFixed(const FillLayer& layer) -{ - for (const FillLayer* currLayer = &layer; currLayer; currLayer = currLayer->next()) { - if (!currLayer->image() || currLayer->attachment() != FixedBackgroundAttachment) - return false; - } +static bool allLayersAreFixed(const FillLayer& layer) { + for (const FillLayer* currLayer = &layer; currLayer; + currLayer = currLayer->next()) { + if (!currLayer->image() || + currLayer->attachment() != FixedBackgroundAttachment) + return false; + } - return true; + return true; } -bool RenderStyle::hasEntirelyFixedBackground() const -{ - return allLayersAreFixed(backgroundLayers()); +bool RenderStyle::hasEntirelyFixedBackground() const { + return allLayersAreFixed(backgroundLayers()); } -const CounterDirectiveMap* RenderStyle::counterDirectives() const -{ - return rareNonInheritedData->m_counterDirectives.get(); +const CounterDirectiveMap* RenderStyle::counterDirectives() const { + return rareNonInheritedData->m_counterDirectives.get(); } -CounterDirectiveMap& RenderStyle::accessCounterDirectives() -{ - OwnPtr& map = rareNonInheritedData.access()->m_counterDirectives; - if (!map) - map = adoptPtr(new CounterDirectiveMap); - return *map; +CounterDirectiveMap& RenderStyle::accessCounterDirectives() { + OwnPtr& map = + rareNonInheritedData.access()->m_counterDirectives; + if (!map) + map = adoptPtr(new CounterDirectiveMap); + return *map; } -const CounterDirectives RenderStyle::getCounterDirectives(const AtomicString& identifier) const -{ - if (const CounterDirectiveMap* directives = counterDirectives()) - return directives->get(identifier); - return CounterDirectives(); +const CounterDirectives RenderStyle::getCounterDirectives( + const AtomicString& identifier) const { + if (const CounterDirectiveMap* directives = counterDirectives()) + return directives->get(identifier); + return CounterDirectives(); } -const AtomicString& RenderStyle::hyphenString() const -{ - const AtomicString& hyphenationString = rareInheritedData.get()->hyphenationString; - if (!hyphenationString.isNull()) - return hyphenationString; +const AtomicString& RenderStyle::hyphenString() const { + const AtomicString& hyphenationString = + rareInheritedData.get()->hyphenationString; + if (!hyphenationString.isNull()) + return hyphenationString; - // FIXME: This should depend on locale. - DEFINE_STATIC_LOCAL(AtomicString, hyphenMinusString, (&hyphenMinus, 1)); - DEFINE_STATIC_LOCAL(AtomicString, hyphenString, (&hyphen, 1)); - return font().primaryFontHasGlyphForCharacter(hyphen) ? hyphenString : hyphenMinusString; -} - -const AtomicString& RenderStyle::textEmphasisMarkString() const -{ - switch (textEmphasisMark()) { + // FIXME: This should depend on locale. + DEFINE_STATIC_LOCAL(AtomicString, hyphenMinusString, (&hyphenMinus, 1)); + DEFINE_STATIC_LOCAL(AtomicString, hyphenString, (&hyphen, 1)); + return font().primaryFontHasGlyphForCharacter(hyphen) ? hyphenString + : hyphenMinusString; +} + +const AtomicString& RenderStyle::textEmphasisMarkString() const { + switch (textEmphasisMark()) { case TextEmphasisMarkNone: - return nullAtom; + return nullAtom; case TextEmphasisMarkCustom: - return textEmphasisCustomMark(); + return textEmphasisCustomMark(); case TextEmphasisMarkDot: { - DEFINE_STATIC_LOCAL(AtomicString, filledDotString, (&bullet, 1)); - DEFINE_STATIC_LOCAL(AtomicString, openDotString, (&whiteBullet, 1)); - return textEmphasisFill() == TextEmphasisFillFilled ? filledDotString : openDotString; + DEFINE_STATIC_LOCAL(AtomicString, filledDotString, (&bullet, 1)); + DEFINE_STATIC_LOCAL(AtomicString, openDotString, (&whiteBullet, 1)); + return textEmphasisFill() == TextEmphasisFillFilled ? filledDotString + : openDotString; } case TextEmphasisMarkCircle: { - DEFINE_STATIC_LOCAL(AtomicString, filledCircleString, (&blackCircle, 1)); - DEFINE_STATIC_LOCAL(AtomicString, openCircleString, (&whiteCircle, 1)); - return textEmphasisFill() == TextEmphasisFillFilled ? filledCircleString : openCircleString; + DEFINE_STATIC_LOCAL(AtomicString, filledCircleString, (&blackCircle, 1)); + DEFINE_STATIC_LOCAL(AtomicString, openCircleString, (&whiteCircle, 1)); + return textEmphasisFill() == TextEmphasisFillFilled ? filledCircleString + : openCircleString; } case TextEmphasisMarkDoubleCircle: { - DEFINE_STATIC_LOCAL(AtomicString, filledDoubleCircleString, (&fisheye, 1)); - DEFINE_STATIC_LOCAL(AtomicString, openDoubleCircleString, (&bullseye, 1)); - return textEmphasisFill() == TextEmphasisFillFilled ? filledDoubleCircleString : openDoubleCircleString; + DEFINE_STATIC_LOCAL(AtomicString, filledDoubleCircleString, + (&fisheye, 1)); + DEFINE_STATIC_LOCAL(AtomicString, openDoubleCircleString, (&bullseye, 1)); + return textEmphasisFill() == TextEmphasisFillFilled + ? filledDoubleCircleString + : openDoubleCircleString; } case TextEmphasisMarkTriangle: { - DEFINE_STATIC_LOCAL(AtomicString, filledTriangleString, (&blackUpPointingTriangle, 1)); - DEFINE_STATIC_LOCAL(AtomicString, openTriangleString, (&whiteUpPointingTriangle, 1)); - return textEmphasisFill() == TextEmphasisFillFilled ? filledTriangleString : openTriangleString; + DEFINE_STATIC_LOCAL(AtomicString, filledTriangleString, + (&blackUpPointingTriangle, 1)); + DEFINE_STATIC_LOCAL(AtomicString, openTriangleString, + (&whiteUpPointingTriangle, 1)); + return textEmphasisFill() == TextEmphasisFillFilled ? filledTriangleString + : openTriangleString; } case TextEmphasisMarkSesame: { - DEFINE_STATIC_LOCAL(AtomicString, filledSesameString, (&sesameDot, 1)); - DEFINE_STATIC_LOCAL(AtomicString, openSesameString, (&whiteSesameDot, 1)); - return textEmphasisFill() == TextEmphasisFillFilled ? filledSesameString : openSesameString; + DEFINE_STATIC_LOCAL(AtomicString, filledSesameString, (&sesameDot, 1)); + DEFINE_STATIC_LOCAL(AtomicString, openSesameString, (&whiteSesameDot, 1)); + return textEmphasisFill() == TextEmphasisFillFilled ? filledSesameString + : openSesameString; } case TextEmphasisMarkAuto: - ASSERT_NOT_REACHED(); - return nullAtom; - } + ASSERT_NOT_REACHED(); + return nullAtom; + } - ASSERT_NOT_REACHED(); - return nullAtom; + ASSERT_NOT_REACHED(); + return nullAtom; } -const Font& RenderStyle::font() const { return inherited->font; } -const FontMetrics& RenderStyle::fontMetrics() const { return inherited->font.fontMetrics(); } -const FontDescription& RenderStyle::fontDescription() const { return inherited->font.fontDescription(); } -float RenderStyle::specifiedFontSize() const { return fontDescription().specifiedSize(); } -float RenderStyle::computedFontSize() const { return fontDescription().computedSize(); } -int RenderStyle::fontSize() const { return fontDescription().computedPixelSize(); } -FontWeight RenderStyle::fontWeight() const { return fontDescription().weight(); } -FontStretch RenderStyle::fontStretch() const { return fontDescription().stretch(); } +const Font& RenderStyle::font() const { + return inherited->font; +} +const FontMetrics& RenderStyle::fontMetrics() const { + return inherited->font.fontMetrics(); +} +const FontDescription& RenderStyle::fontDescription() const { + return inherited->font.fontDescription(); +} +float RenderStyle::specifiedFontSize() const { + return fontDescription().specifiedSize(); +} +float RenderStyle::computedFontSize() const { + return fontDescription().computedSize(); +} +int RenderStyle::fontSize() const { + return fontDescription().computedPixelSize(); +} +FontWeight RenderStyle::fontWeight() const { + return fontDescription().weight(); +} +FontStretch RenderStyle::fontStretch() const { + return fontDescription().stretch(); +} -TextDecoration RenderStyle::textDecorationsInEffect() const -{ - int decorations = 0; +TextDecoration RenderStyle::textDecorationsInEffect() const { + int decorations = 0; - const Vector& applied = appliedTextDecorations(); + const Vector& applied = appliedTextDecorations(); - for (size_t i = 0; i < applied.size(); ++i) - decorations |= applied[i].line(); + for (size_t i = 0; i < applied.size(); ++i) + decorations |= applied[i].line(); - return static_cast(decorations); + return static_cast(decorations); } -const Vector& RenderStyle::appliedTextDecorations() const -{ - if (!inherited_flags.m_textUnderline && !rareInheritedData->appliedTextDecorations) { - DEFINE_STATIC_LOCAL(Vector, empty, ()); - return empty; - } - if (inherited_flags.m_textUnderline) { - DEFINE_STATIC_LOCAL(Vector, underline, (1, AppliedTextDecoration(TextDecorationUnderline))); - return underline; - } +const Vector& RenderStyle::appliedTextDecorations() + const { + if (!inherited_flags.m_textUnderline && + !rareInheritedData->appliedTextDecorations) { + DEFINE_STATIC_LOCAL(Vector, empty, ()); + return empty; + } + if (inherited_flags.m_textUnderline) { + DEFINE_STATIC_LOCAL(Vector, underline, + (1, AppliedTextDecoration(TextDecorationUnderline))); + return underline; + } - return rareInheritedData->appliedTextDecorations->vector(); + return rareInheritedData->appliedTextDecorations->vector(); } -float RenderStyle::wordSpacing() const { return fontDescription().wordSpacing(); } -float RenderStyle::letterSpacing() const { return fontDescription().letterSpacing(); } +float RenderStyle::wordSpacing() const { + return fontDescription().wordSpacing(); +} +float RenderStyle::letterSpacing() const { + return fontDescription().letterSpacing(); +} -bool RenderStyle::setFontDescription(const FontDescription& v) -{ - if (inherited->font.fontDescription() != v) { - inherited.access()->font = Font(v); - return true; - } - return false; +bool RenderStyle::setFontDescription(const FontDescription& v) { + if (inherited->font.fontDescription() != v) { + inherited.access()->font = Font(v); + return true; + } + return false; } -const Length& RenderStyle::specifiedLineHeight() const { return inherited->line_height; } -Length RenderStyle::lineHeight() const -{ - return inherited->line_height; +const Length& RenderStyle::specifiedLineHeight() const { + return inherited->line_height; +} +Length RenderStyle::lineHeight() const { + return inherited->line_height; } -void RenderStyle::setLineHeight(const Length& specifiedLineHeight) { SET_VAR(inherited, line_height, specifiedLineHeight); } +void RenderStyle::setLineHeight(const Length& specifiedLineHeight) { + SET_VAR(inherited, line_height, specifiedLineHeight); +} -int RenderStyle::computedLineHeight() const -{ - const Length& lh = lineHeight(); +int RenderStyle::computedLineHeight() const { + const Length& lh = lineHeight(); - // Negative value means the line height is not set. Use the font's built-in spacing. - if (lh.isNegative()) - return fontMetrics().lineSpacing(); + // Negative value means the line height is not set. Use the font's built-in + // spacing. + if (lh.isNegative()) + return fontMetrics().lineSpacing(); - if (lh.isPercent()) - return minimumValueForLength(lh, fontSize()); + if (lh.isPercent()) + return minimumValueForLength(lh, fontSize()); - return lh.value(); + return lh.value(); } -void RenderStyle::setWordSpacing(float wordSpacing) -{ - FontSelector* currentFontSelector = font().fontSelector(); - FontDescription desc(fontDescription()); - desc.setWordSpacing(wordSpacing); - setFontDescription(desc); - font().update(currentFontSelector); +void RenderStyle::setWordSpacing(float wordSpacing) { + FontSelector* currentFontSelector = font().fontSelector(); + FontDescription desc(fontDescription()); + desc.setWordSpacing(wordSpacing); + setFontDescription(desc); + font().update(currentFontSelector); } -void RenderStyle::setLetterSpacing(float letterSpacing) -{ - FontSelector* currentFontSelector = font().fontSelector(); - FontDescription desc(fontDescription()); - desc.setLetterSpacing(letterSpacing); - setFontDescription(desc); - font().update(currentFontSelector); +void RenderStyle::setLetterSpacing(float letterSpacing) { + FontSelector* currentFontSelector = font().fontSelector(); + FontDescription desc(fontDescription()); + desc.setLetterSpacing(letterSpacing); + setFontDescription(desc); + font().update(currentFontSelector); } -void RenderStyle::setFontSize(float size) -{ - ASSERT(std::isfinite(size)); - if (!std::isfinite(size) || size < 0) - size = 0; +void RenderStyle::setFontSize(float size) { + ASSERT(std::isfinite(size)); + if (!std::isfinite(size) || size < 0) + size = 0; - FontSelector* currentFontSelector = font().fontSelector(); - FontDescription desc(fontDescription()); - desc.setSpecifiedSize(size); - desc.setComputedSize(size); + FontSelector* currentFontSelector = font().fontSelector(); + FontDescription desc(fontDescription()); + desc.setSpecifiedSize(size); + desc.setComputedSize(size); - setFontDescription(desc); - font().update(currentFontSelector); + setFontDescription(desc); + font().update(currentFontSelector); } -void RenderStyle::setFontWeight(FontWeight weight) -{ - FontSelector* currentFontSelector = font().fontSelector(); - FontDescription desc(fontDescription()); - desc.setWeight(weight); - setFontDescription(desc); - font().update(currentFontSelector); +void RenderStyle::setFontWeight(FontWeight weight) { + FontSelector* currentFontSelector = font().fontSelector(); + FontDescription desc(fontDescription()); + desc.setWeight(weight); + setFontDescription(desc); + font().update(currentFontSelector); } -void RenderStyle::addAppliedTextDecoration(const AppliedTextDecoration& decoration) -{ - RefPtr& list = rareInheritedData.access()->appliedTextDecorations; +void RenderStyle::addAppliedTextDecoration( + const AppliedTextDecoration& decoration) { + RefPtr& list = + rareInheritedData.access()->appliedTextDecorations; - if (!list) - list = AppliedTextDecorationList::create(); - else if (!list->hasOneRef()) - list = list->copy(); + if (!list) + list = AppliedTextDecorationList::create(); + else if (!list->hasOneRef()) + list = list->copy(); - if (inherited_flags.m_textUnderline) { - inherited_flags.m_textUnderline = false; - list->append(AppliedTextDecoration(TextDecorationUnderline)); - } + if (inherited_flags.m_textUnderline) { + inherited_flags.m_textUnderline = false; + list->append(AppliedTextDecoration(TextDecorationUnderline)); + } - list->append(decoration); + list->append(decoration); } -void RenderStyle::applyTextDecorations() -{ - if (textDecoration() == TextDecorationNone) - return; - - TextDecorationStyle style = textDecorationStyle(); - StyleColor styleColor = decorationStyleColor(); +void RenderStyle::applyTextDecorations() { + if (textDecoration() == TextDecorationNone) + return; - int decorations = textDecoration(); + TextDecorationStyle style = textDecorationStyle(); + StyleColor styleColor = decorationStyleColor(); - if (decorations & TextDecorationUnderline) { - // To save memory, we don't use AppliedTextDecoration objects in the - // common case of a single simple underline. - AppliedTextDecoration underline(TextDecorationUnderline, style, styleColor); + int decorations = textDecoration(); - if (!rareInheritedData->appliedTextDecorations && underline.isSimpleUnderline()) - inherited_flags.m_textUnderline = true; - else - addAppliedTextDecoration(underline); - } - if (decorations & TextDecorationOverline) - addAppliedTextDecoration(AppliedTextDecoration(TextDecorationOverline, style, styleColor)); - if (decorations & TextDecorationLineThrough) - addAppliedTextDecoration(AppliedTextDecoration(TextDecorationLineThrough, style, styleColor)); -} + if (decorations & TextDecorationUnderline) { + // To save memory, we don't use AppliedTextDecoration objects in the + // common case of a single simple underline. + AppliedTextDecoration underline(TextDecorationUnderline, style, styleColor); -void RenderStyle::clearAppliedTextDecorations() -{ - inherited_flags.m_textUnderline = false; + if (!rareInheritedData->appliedTextDecorations && + underline.isSimpleUnderline()) + inherited_flags.m_textUnderline = true; + else + addAppliedTextDecoration(underline); + } + if (decorations & TextDecorationOverline) + addAppliedTextDecoration( + AppliedTextDecoration(TextDecorationOverline, style, styleColor)); + if (decorations & TextDecorationLineThrough) + addAppliedTextDecoration( + AppliedTextDecoration(TextDecorationLineThrough, style, styleColor)); +} + +void RenderStyle::clearAppliedTextDecorations() { + inherited_flags.m_textUnderline = false; + + if (rareInheritedData->appliedTextDecorations) + rareInheritedData.access()->appliedTextDecorations = nullptr; +} + +void RenderStyle::setFontStretch(FontStretch stretch) { + FontSelector* currentFontSelector = font().fontSelector(); + FontDescription desc(fontDescription()); + desc.setStretch(stretch); + setFontDescription(desc); + font().update(currentFontSelector); +} + +void RenderStyle::getShadowExtent(const ShadowList* shadowList, + LayoutUnit& top, + LayoutUnit& right, + LayoutUnit& bottom, + LayoutUnit& left) const { + top = 0; + right = 0; + bottom = 0; + left = 0; + + size_t shadowCount = shadowList ? shadowList->shadows().size() : 0; + for (size_t i = 0; i < shadowCount; ++i) { + const ShadowData& shadow = shadowList->shadows()[i]; + if (shadow.style() == Inset) + continue; + float blurAndSpread = shadow.blur() + shadow.spread(); - if (rareInheritedData->appliedTextDecorations) - rareInheritedData.access()->appliedTextDecorations = nullptr; -} - -void RenderStyle::setFontStretch(FontStretch stretch) -{ - FontSelector* currentFontSelector = font().fontSelector(); - FontDescription desc(fontDescription()); - desc.setStretch(stretch); - setFontDescription(desc); - font().update(currentFontSelector); -} - -void RenderStyle::getShadowExtent(const ShadowList* shadowList, LayoutUnit &top, LayoutUnit &right, LayoutUnit &bottom, LayoutUnit &left) const -{ - top = 0; - right = 0; - bottom = 0; - left = 0; - - size_t shadowCount = shadowList ? shadowList->shadows().size() : 0; - for (size_t i = 0; i < shadowCount; ++i) { - const ShadowData& shadow = shadowList->shadows()[i]; - if (shadow.style() == Inset) - continue; - float blurAndSpread = shadow.blur() + shadow.spread(); - - top = std::min(top, shadow.y() - blurAndSpread); - right = std::max(right, shadow.x() + blurAndSpread); - bottom = std::max(bottom, shadow.y() + blurAndSpread); - left = std::min(left, shadow.x() - blurAndSpread); - } + top = std::min(top, shadow.y() - blurAndSpread); + right = std::max(right, shadow.x() + blurAndSpread); + bottom = std::max(bottom, shadow.y() + blurAndSpread); + left = std::min(left, shadow.x() - blurAndSpread); + } } -LayoutBoxExtent RenderStyle::getShadowInsetExtent(const ShadowList* shadowList) const -{ - LayoutUnit top = 0; - LayoutUnit right = 0; - LayoutUnit bottom = 0; - LayoutUnit left = 0; - - size_t shadowCount = shadowList ? shadowList->shadows().size() : 0; - for (size_t i = 0; i < shadowCount; ++i) { - const ShadowData& shadow = shadowList->shadows()[i]; - if (shadow.style() == Normal) - continue; - float blurAndSpread = shadow.blur() + shadow.spread(); - top = std::max(top, shadow.y() + blurAndSpread); - right = std::min(right, shadow.x() - blurAndSpread); - bottom = std::min(bottom, shadow.y() - blurAndSpread); - left = std::max(left, shadow.x() + blurAndSpread); - } +LayoutBoxExtent RenderStyle::getShadowInsetExtent( + const ShadowList* shadowList) const { + LayoutUnit top = 0; + LayoutUnit right = 0; + LayoutUnit bottom = 0; + LayoutUnit left = 0; + + size_t shadowCount = shadowList ? shadowList->shadows().size() : 0; + for (size_t i = 0; i < shadowCount; ++i) { + const ShadowData& shadow = shadowList->shadows()[i]; + if (shadow.style() == Normal) + continue; + float blurAndSpread = shadow.blur() + shadow.spread(); + top = std::max(top, shadow.y() + blurAndSpread); + right = std::min(right, shadow.x() - blurAndSpread); + bottom = std::min(bottom, shadow.y() - blurAndSpread); + left = std::max(left, shadow.x() + blurAndSpread); + } - return LayoutBoxExtent(top, right, bottom, left); + return LayoutBoxExtent(top, right, bottom, left); } -void RenderStyle::getShadowHorizontalExtent(const ShadowList* shadowList, LayoutUnit &left, LayoutUnit &right) const -{ - left = 0; - right = 0; +void RenderStyle::getShadowHorizontalExtent(const ShadowList* shadowList, + LayoutUnit& left, + LayoutUnit& right) const { + left = 0; + right = 0; - size_t shadowCount = shadowList ? shadowList->shadows().size() : 0; - for (size_t i = 0; i < shadowCount; ++i) { - const ShadowData& shadow = shadowList->shadows()[i]; - if (shadow.style() == Inset) - continue; - float blurAndSpread = shadow.blur() + shadow.spread(); + size_t shadowCount = shadowList ? shadowList->shadows().size() : 0; + for (size_t i = 0; i < shadowCount; ++i) { + const ShadowData& shadow = shadowList->shadows()[i]; + if (shadow.style() == Inset) + continue; + float blurAndSpread = shadow.blur() + shadow.spread(); - left = std::min(left, shadow.x() - blurAndSpread); - right = std::max(right, shadow.x() + blurAndSpread); - } + left = std::min(left, shadow.x() - blurAndSpread); + right = std::max(right, shadow.x() + blurAndSpread); + } } -void RenderStyle::getShadowVerticalExtent(const ShadowList* shadowList, LayoutUnit &top, LayoutUnit &bottom) const -{ - top = 0; - bottom = 0; +void RenderStyle::getShadowVerticalExtent(const ShadowList* shadowList, + LayoutUnit& top, + LayoutUnit& bottom) const { + top = 0; + bottom = 0; - size_t shadowCount = shadowList ? shadowList->shadows().size() : 0; - for (size_t i = 0; i < shadowCount; ++i) { - const ShadowData& shadow = shadowList->shadows()[i]; - if (shadow.style() == Inset) - continue; - float blurAndSpread = shadow.blur() + shadow.spread(); + size_t shadowCount = shadowList ? shadowList->shadows().size() : 0; + for (size_t i = 0; i < shadowCount; ++i) { + const ShadowData& shadow = shadowList->shadows()[i]; + if (shadow.style() == Inset) + continue; + float blurAndSpread = shadow.blur() + shadow.spread(); - top = std::min(top, shadow.y() - blurAndSpread); - bottom = std::max(bottom, shadow.y() + blurAndSpread); - } + top = std::min(top, shadow.y() - blurAndSpread); + bottom = std::max(bottom, shadow.y() + blurAndSpread); + } } -StyleColor RenderStyle::decorationStyleColor() const -{ - StyleColor styleColor = textDecorationColor(); +StyleColor RenderStyle::decorationStyleColor() const { + StyleColor styleColor = textDecorationColor(); - if (!styleColor.isCurrentColor()) - return styleColor; + if (!styleColor.isCurrentColor()) + return styleColor; - if (textStrokeWidth()) { - // Prefer stroke color if possible, but not if it's fully transparent. - StyleColor textStrokeStyleColor = textStrokeColor(); - if (!textStrokeStyleColor.isCurrentColor() && textStrokeStyleColor.color().alpha()) - return textStrokeStyleColor; - } + if (textStrokeWidth()) { + // Prefer stroke color if possible, but not if it's fully transparent. + StyleColor textStrokeStyleColor = textStrokeColor(); + if (!textStrokeStyleColor.isCurrentColor() && + textStrokeStyleColor.color().alpha()) + return textStrokeStyleColor; + } - return textFillColor(); + return textFillColor(); } -Color RenderStyle::decorationColor() const -{ - return decorationStyleColor().resolve(color()); +Color RenderStyle::decorationColor() const { + return decorationStyleColor().resolve(color()); } -const BorderValue& RenderStyle::borderBefore() const -{ - // FIXME(sky): Remove - return borderTop(); +const BorderValue& RenderStyle::borderBefore() const { + // FIXME(sky): Remove + return borderTop(); } -const BorderValue& RenderStyle::borderAfter() const -{ - // FIXME(sky): Remove - return borderBottom(); +const BorderValue& RenderStyle::borderAfter() const { + // FIXME(sky): Remove + return borderBottom(); } -const BorderValue& RenderStyle::borderStart() const -{ - return isLeftToRightDirection() ? borderLeft() : borderRight(); +const BorderValue& RenderStyle::borderStart() const { + return isLeftToRightDirection() ? borderLeft() : borderRight(); } -const BorderValue& RenderStyle::borderEnd() const -{ - return isLeftToRightDirection() ? borderRight() : borderLeft(); +const BorderValue& RenderStyle::borderEnd() const { + return isLeftToRightDirection() ? borderRight() : borderLeft(); } -unsigned short RenderStyle::borderBeforeWidth() const -{ - // FIXME(sky): Remove - return borderTopWidth(); +unsigned short RenderStyle::borderBeforeWidth() const { + // FIXME(sky): Remove + return borderTopWidth(); } -unsigned short RenderStyle::borderAfterWidth() const -{ - // FIXME(sky): Remove - return borderBottomWidth(); +unsigned short RenderStyle::borderAfterWidth() const { + // FIXME(sky): Remove + return borderBottomWidth(); } -unsigned short RenderStyle::borderStartWidth() const -{ - return isLeftToRightDirection() ? borderLeftWidth() : borderRightWidth(); +unsigned short RenderStyle::borderStartWidth() const { + return isLeftToRightDirection() ? borderLeftWidth() : borderRightWidth(); } -unsigned short RenderStyle::borderEndWidth() const -{ - return isLeftToRightDirection() ? borderRightWidth() : borderLeftWidth(); +unsigned short RenderStyle::borderEndWidth() const { + return isLeftToRightDirection() ? borderRightWidth() : borderLeftWidth(); } -void RenderStyle::setMarginStart(const Length& margin) -{ - if (isLeftToRightDirection()) - setMarginLeft(margin); - else - setMarginRight(margin); +void RenderStyle::setMarginStart(const Length& margin) { + if (isLeftToRightDirection()) + setMarginLeft(margin); + else + setMarginRight(margin); } -void RenderStyle::setMarginEnd(const Length& margin) -{ - if (isLeftToRightDirection()) - setMarginRight(margin); - else - setMarginLeft(margin); +void RenderStyle::setMarginEnd(const Length& margin) { + if (isLeftToRightDirection()) + setMarginRight(margin); + else + setMarginLeft(margin); } -TextEmphasisMark RenderStyle::textEmphasisMark() const -{ - TextEmphasisMark mark = static_cast(rareInheritedData->textEmphasisMark); - if (mark != TextEmphasisMarkAuto) - return mark; - return TextEmphasisMarkDot; +TextEmphasisMark RenderStyle::textEmphasisMark() const { + TextEmphasisMark mark = + static_cast(rareInheritedData->textEmphasisMark); + if (mark != TextEmphasisMarkAuto) + return mark; + return TextEmphasisMarkDot; } -Color RenderStyle::initialTapHighlightColor() -{ - return RenderTheme::tapHighlightColor(); +Color RenderStyle::initialTapHighlightColor() { + return RenderTheme::tapHighlightColor(); } -float calcBorderRadiiConstraintScaleFor(const FloatRect& rect, const FloatRoundedRect::Radii& radii) -{ - // Constrain corner radii using CSS3 rules: - // http://www.w3.org/TR/css3-background/#the-border-radius +float calcBorderRadiiConstraintScaleFor(const FloatRect& rect, + const FloatRoundedRect::Radii& radii) { + // Constrain corner radii using CSS3 rules: + // http://www.w3.org/TR/css3-background/#the-border-radius - float factor = 1; - float radiiSum; + float factor = 1; + float radiiSum; - // top - radiiSum = radii.topLeft().width() + radii.topRight().width(); // Casts to avoid integer overflow. - if (radiiSum > rect.width()) - factor = std::min(rect.width() / radiiSum, factor); + // top + radiiSum = radii.topLeft().width() + + radii.topRight().width(); // Casts to avoid integer overflow. + if (radiiSum > rect.width()) + factor = std::min(rect.width() / radiiSum, factor); - // bottom - radiiSum = radii.bottomLeft().width() + radii.bottomRight().width(); - if (radiiSum > rect.width()) - factor = std::min(rect.width() / radiiSum, factor); + // bottom + radiiSum = radii.bottomLeft().width() + radii.bottomRight().width(); + if (radiiSum > rect.width()) + factor = std::min(rect.width() / radiiSum, factor); - // left - radiiSum = radii.topLeft().height() + radii.bottomLeft().height(); - if (radiiSum > rect.height()) - factor = std::min(rect.height() / radiiSum, factor); + // left + radiiSum = radii.topLeft().height() + radii.bottomLeft().height(); + if (radiiSum > rect.height()) + factor = std::min(rect.height() / radiiSum, factor); - // right - radiiSum = radii.topRight().height() + radii.bottomRight().height(); - if (radiiSum > rect.height()) - factor = std::min(rect.height() / radiiSum, factor); + // right + radiiSum = radii.topRight().height() + radii.bottomRight().height(); + if (radiiSum > rect.height()) + factor = std::min(rect.height() / radiiSum, factor); - ASSERT(factor <= 1); - return factor; + ASSERT(factor <= 1); + return factor; } -} // namespace blink +} // namespace blink diff --git a/sky/engine/core/rendering/style/RenderStyle.h b/sky/engine/core/rendering/style/RenderStyle.h index e583b86ee39e2..782edd1338396 100644 --- a/sky/engine/core/rendering/style/RenderStyle.h +++ b/sky/engine/core/rendering/style/RenderStyle.h @@ -2,7 +2,8 @@ * Copyright (C) 2000 Lars Knoll (knoll@kde.org) * (C) 2000 Antti Koivisto (koivisto@kde.org) * (C) 2000 Dirk Mueller (mueller@kde.org) - * Copyright (C) 2003, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All + * rights reserved. * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.com) * * This library is free software; you can redistribute it and/or @@ -61,19 +62,22 @@ #include "flutter/sky/engine/wtf/StdLibExtras.h" #include "flutter/sky/engine/wtf/Vector.h" -template inline bool compareEqual(const T& t, const U& u) { return t == static_cast(u); } +template +inline bool compareEqual(const T& t, const U& u) { + return t == static_cast(u); +} -#define SET_VAR(group, variable, value) \ - if (!compareEqual(group->variable, value)) \ - group.access()->variable = value +#define SET_VAR(group, variable, value) \ + if (!compareEqual(group->variable, value)) \ + group.access()->variable = value -#define SET_VAR_WITH_SETTER(group, getter, setter, value) \ - if (!compareEqual(group->getter(), value)) \ - group.access()->setter(value) +#define SET_VAR_WITH_SETTER(group, getter, setter, value) \ + if (!compareEqual(group->getter(), value)) \ + group.access()->setter(value) #define SET_BORDERVALUE_COLOR(group, variable, value) \ - if (!compareEqual(group->variable.color(), value)) \ - group.access()->variable.setColor(value) + if (!compareEqual(group->variable.color(), value)) \ + group.access()->variable.setColor(value) namespace blink { @@ -89,1054 +93,1653 @@ class StyleInheritedData; class StyleResolver; class TransformationMatrix; -class RenderStyle: public RefCounted { - friend class EditingStyle; // Editing has to only reveal unvisited info. - friend class CSSComputedStyleDeclaration; // Ignores visited styles, so needs to be able to see unvisited info. - friend class StyleBuilderFunctions; // Sets color styles - - // FIXME: When we stop resolving currentColor at style time, these can be removed. - friend class CSSToStyleMap; - friend class FilterOperationResolver; - friend class StyleBuilderConverter; - friend class StyleResolverState; - friend class StyleResolver; -protected: - - // non-inherited attributes - DataRef m_box; - DataRef visual; - DataRef m_background; - DataRef surround; - DataRef rareNonInheritedData; - - // inherited attributes - DataRef rareInheritedData; - DataRef inherited; - -// !START SYNC!: Keep this in sync with the copy constructor in RenderStyle.cpp and implicitlyInherited() in StyleResolver.cpp - - // inherit - struct InheritedFlags { - bool operator==(const InheritedFlags& other) const - { - return (_visibility == other._visibility) - && (_text_align == other._text_align) - && (m_textUnderline == other.m_textUnderline) - && (_direction == other._direction) - && (_white_space == other._white_space) - && (m_rtlOrdering == other.m_rtlOrdering) - && (_pointerEvents == other._pointerEvents); - } - - bool operator!=(const InheritedFlags& other) const { return !(*this == other); } - - unsigned _visibility : 2; // EVisibility - unsigned _text_align : 4; // ETextAlign - unsigned m_textUnderline : 1; - unsigned _direction : 1; // TextDirection - unsigned _white_space : 3; // EWhiteSpace - - // non CSS2 inherited - unsigned m_rtlOrdering : 1; // Order - unsigned _pointerEvents : 4; // EPointerEvents - - // 19 bits - } inherited_flags; - -// don't inherit - struct NonInheritedFlags { - bool operator==(const NonInheritedFlags& other) const - { - return effectiveDisplay == other.effectiveDisplay - && originalDisplay == other.originalDisplay - && overflowX == other.overflowX - && overflowY == other.overflowY - && verticalAlign == other.verticalAlign - && position == other.position - && styleType == other.styleType - && affectedByFocus == other.affectedByFocus - && affectedByHover == other.affectedByHover - && affectedByActive == other.affectedByActive - && unicodeBidi == other.unicodeBidi - && explicitInheritance == other.explicitInheritance - && currentColor == other.currentColor - && unique == other.unique - && emptyState == other.emptyState - && firstChildState == other.firstChildState - && lastChildState == other.lastChildState - && isLink == other.isLink; - } - - bool operator!=(const NonInheritedFlags& other) const { return !(*this == other); } - - unsigned effectiveDisplay : 5; // EDisplay - unsigned originalDisplay : 5; // EDisplay - unsigned overflowX : 3; // EOverflow - unsigned overflowY : 3; // EOverflow - unsigned verticalAlign : 4; // EVerticalAlign - unsigned position : 1; // EPosition - unsigned unicodeBidi : 3; // EUnicodeBidi - - // This is set if we used viewport units when resolving a length. - // It is mutable so we can pass around const RenderStyles to resolve lengths. - mutable unsigned hasViewportUnits : 1; - - // 32 bits - - unsigned styleType : 6; // PseudoId - unsigned explicitInheritance : 1; // Explicitly inherits a non-inherited property - unsigned currentColor : 1; // At least one color has the value 'currentColor' - unsigned unique : 1; // Style can not be shared. - - unsigned emptyState : 1; - unsigned firstChildState : 1; - unsigned lastChildState : 1; - - unsigned affectedByFocus : 1; - unsigned affectedByHover : 1; - unsigned affectedByActive : 1; - - unsigned isLink : 1; - // If you add more style bits here, you will also need to update RenderStyle::copyNonInheritedFrom() - // 63 bits - } noninherited_flags; - -// !END SYNC! - -protected: - void setBitDefaults() - { - inherited_flags._visibility = initialVisibility(); - inherited_flags._text_align = initialTextAlign(); - inherited_flags.m_textUnderline = false; - inherited_flags._direction = initialDirection(); - inherited_flags._white_space = initialWhiteSpace(); - inherited_flags.m_rtlOrdering = initialRTLOrdering(); - inherited_flags._pointerEvents = initialPointerEvents(); - - noninherited_flags.effectiveDisplay = noninherited_flags.originalDisplay = initialDisplay(); - noninherited_flags.overflowX = initialOverflowX(); - noninherited_flags.overflowY = initialOverflowY(); - noninherited_flags.verticalAlign = initialVerticalAlign(); - noninherited_flags.position = initialPosition(); - noninherited_flags.unicodeBidi = initialUnicodeBidi(); - noninherited_flags.explicitInheritance = false; - noninherited_flags.currentColor = false; - noninherited_flags.unique = false; - noninherited_flags.emptyState = false; - noninherited_flags.firstChildState = false; - noninherited_flags.lastChildState = false; - noninherited_flags.hasViewportUnits = false; - noninherited_flags.affectedByFocus = false; - noninherited_flags.affectedByHover = false; - noninherited_flags.affectedByActive = false; - noninherited_flags.isLink = false; - } - -private: - ALWAYS_INLINE RenderStyle(); - - enum DefaultStyleTag { - DefaultStyle - }; - ALWAYS_INLINE explicit RenderStyle(DefaultStyleTag); - ALWAYS_INLINE RenderStyle(const RenderStyle&); - -public: - static PassRefPtr create(); - static PassRefPtr createDefaultStyle(); - static PassRefPtr clone(const RenderStyle*); - - // Computes how the style change should be propagated down the tree. - static StyleRecalcChange stylePropagationDiff(const RenderStyle* oldStyle, const RenderStyle* newStyle); - - StyleDifference visualInvalidationDiff(const RenderStyle&) const; - - void inheritFrom(const RenderStyle* inheritParent); - void copyNonInheritedFrom(const RenderStyle*); - - void setHasViewportUnits(bool hasViewportUnits = true) const { noninherited_flags.hasViewportUnits = hasViewportUnits; } - bool hasViewportUnits() const { return noninherited_flags.hasViewportUnits; } - - bool affectedByFocus() const { return noninherited_flags.affectedByFocus; } - bool affectedByHover() const { return noninherited_flags.affectedByHover; } - bool affectedByActive() const { return noninherited_flags.affectedByActive; } - - void setAffectedByFocus() { noninherited_flags.affectedByFocus = true; } - void setAffectedByHover() { noninherited_flags.affectedByHover = true; } - void setAffectedByActive() { noninherited_flags.affectedByActive = true; } - - bool operator==(const RenderStyle& other) const; - bool operator!=(const RenderStyle& other) const { return !(*this == other); } - bool hasMargin() const { return surround->margin.nonZero(); } - bool hasBorder() const { return surround->border.hasBorder(); } - bool hasPadding() const { return surround->padding.nonZero(); } - bool hasOffset() const { return surround->offset.nonZero(); } - bool hasMarginBeforeQuirk() const { return marginBefore().quirk(); } - bool hasMarginAfterQuirk() const { return marginAfter().quirk(); } - - bool hasBackgroundImage() const { return m_background->background().hasImage(); } - bool hasFixedBackgroundImage() const { return m_background->background().hasFixedImage(); } - - bool hasEntirelyFixedBackground() const; - - bool hasBackground() const - { - Color color = resolveColor(backgroundColor()); - if (color.alpha()) - return true; - return hasBackgroundImage(); - } - - Order rtlOrdering() const { return static_cast(inherited_flags.m_rtlOrdering); } - void setRTLOrdering(Order o) { inherited_flags.m_rtlOrdering = o; } - - // attribute getter methods - - EDisplay display() const { return static_cast(noninherited_flags.effectiveDisplay); } - EDisplay originalDisplay() const { return static_cast(noninherited_flags.originalDisplay); } - - const Length& left() const { return surround->offset.left(); } - const Length& right() const { return surround->offset.right(); } - const Length& top() const { return surround->offset.top(); } - const Length& bottom() const { return surround->offset.bottom(); } - - // Accessors for positioned object edges that take into account writing mode. - const Length& logicalLeft() const { return surround->offset.logicalLeft(); } - const Length& logicalRight() const { return surround->offset.logicalRight(); } - const Length& logicalTop() const { return surround->offset.before(); } - const Length& logicalBottom() const { return surround->offset.after(); } - - // Whether or not a positioned element requires normal flow x/y to be computed - // to determine its position. - bool hasAutoLeftAndRight() const { return left().isAuto() && right().isAuto(); } - bool hasAutoTopAndBottom() const { return top().isAuto() && bottom().isAuto(); } - - EPosition position() const { return static_cast(noninherited_flags.position); } - bool hasOutOfFlowPosition() const { return position() == AbsolutePosition; } - // FIXME(sky): Remove - bool hasInFlowPosition() const { return false; } - - const Length& width() const { return m_box->width(); } - const Length& height() const { return m_box->height(); } - const Length& minWidth() const { return m_box->minWidth(); } - const Length& maxWidth() const { return m_box->maxWidth(); } - const Length& minHeight() const { return m_box->minHeight(); } - const Length& maxHeight() const { return m_box->maxHeight(); } - - const Length& logicalWidth() const { return width(); } - const Length& logicalHeight() const { return height(); } - const Length& logicalMinWidth() const { return minWidth(); } - const Length& logicalMaxWidth() const { return maxWidth(); } - const Length& logicalMinHeight() const { return minHeight(); } - const Length& logicalMaxHeight() const { return maxHeight(); } - - const BorderData& border() const { return surround->border; } - const BorderValue& borderLeft() const { return surround->border.left(); } - const BorderValue& borderRight() const { return surround->border.right(); } - const BorderValue& borderTop() const { return surround->border.top(); } - const BorderValue& borderBottom() const { return surround->border.bottom(); } - - const BorderValue& borderBefore() const; - const BorderValue& borderAfter() const; - const BorderValue& borderStart() const; - const BorderValue& borderEnd() const; - - const LengthSize& borderTopLeftRadius() const { return surround->border.topLeft(); } - const LengthSize& borderTopRightRadius() const { return surround->border.topRight(); } - const LengthSize& borderBottomLeftRadius() const { return surround->border.bottomLeft(); } - const LengthSize& borderBottomRightRadius() const { return surround->border.bottomRight(); } - bool hasBorderRadius() const { return surround->border.hasBorderRadius(); } - - unsigned borderLeftWidth() const { return surround->border.borderLeftWidth(); } - EBorderStyle borderLeftStyle() const { return surround->border.left().style(); } - bool borderLeftIsTransparent() const { return surround->border.left().isTransparent(); } - unsigned borderRightWidth() const { return surround->border.borderRightWidth(); } - EBorderStyle borderRightStyle() const { return surround->border.right().style(); } - bool borderRightIsTransparent() const { return surround->border.right().isTransparent(); } - unsigned borderTopWidth() const { return surround->border.borderTopWidth(); } - EBorderStyle borderTopStyle() const { return surround->border.top().style(); } - bool borderTopIsTransparent() const { return surround->border.top().isTransparent(); } - unsigned borderBottomWidth() const { return surround->border.borderBottomWidth(); } - EBorderStyle borderBottomStyle() const { return surround->border.bottom().style(); } - bool borderBottomIsTransparent() const { return surround->border.bottom().isTransparent(); } - - unsigned short borderBeforeWidth() const; - unsigned short borderAfterWidth() const; - unsigned short borderStartWidth() const; - unsigned short borderEndWidth() const; - - unsigned short outlineSize() const { return max(0, outlineWidth() + outlineOffset()); } - unsigned short outlineWidth() const - { - if (m_background->outline().style() == BNONE) - return 0; - return m_background->outline().width(); - } - bool hasOutline() const { return outlineWidth() > 0 && outlineStyle() > BHIDDEN; } - EBorderStyle outlineStyle() const { return m_background->outline().style(); } - OutlineIsAuto outlineStyleIsAuto() const { return static_cast(m_background->outline().isAuto()); } - - EOverflow overflowX() const { return static_cast(noninherited_flags.overflowX); } - EOverflow overflowY() const { return static_cast(noninherited_flags.overflowY); } - // It's sufficient to just check one direction, since it's illegal to have visible on only one overflow value. - bool isOverflowVisible() const { ASSERT(overflowX() != OVISIBLE || overflowX() == overflowY()); return overflowX() == OVISIBLE; } - bool isOverflowPaged() const { return overflowY() == OPAGEDX || overflowY() == OPAGEDY; } - - EVerticalAlign verticalAlign() const { return static_cast(noninherited_flags.verticalAlign); } - const Length& verticalAlignLength() const { return m_box->verticalAlign(); } - - const Length& clipLeft() const { return visual->clip.left(); } - const Length& clipRight() const { return visual->clip.right(); } - const Length& clipTop() const { return visual->clip.top(); } - const Length& clipBottom() const { return visual->clip.bottom(); } - const LengthBox& clip() const { return visual->clip; } - bool hasAutoClip() const { return visual->hasAutoClip; } - - EUnicodeBidi unicodeBidi() const { return static_cast(noninherited_flags.unicodeBidi); } - - const Font& font() const; - const FontMetrics& fontMetrics() const; - const FontDescription& fontDescription() const; - float specifiedFontSize() const; - float computedFontSize() const; - int fontSize() const; - FontWeight fontWeight() const; - FontStretch fontStretch() const; - - const Length& textIndent() const { return rareInheritedData->indent; } - TextIndentLine textIndentLine() const { return static_cast(rareInheritedData->m_textIndentLine); } - TextIndentType textIndentType() const { return static_cast(rareInheritedData->m_textIndentType); } - ETextAlign textAlign() const { return static_cast(inherited_flags._text_align); } - TextAlignLast textAlignLast() const { return static_cast(rareInheritedData->m_textAlignLast); } - TextJustify textJustify() const { return static_cast(rareInheritedData->m_textJustify); } - TextDecoration textDecorationsInEffect() const; - const Vector& appliedTextDecorations() const; - TextDecoration textDecoration() const { return static_cast(visual->textDecoration); } - TextUnderlinePosition textUnderlinePosition() const { return static_cast(rareInheritedData->m_textUnderlinePosition); } - TextDecorationStyle textDecorationStyle() const { return static_cast(rareNonInheritedData->m_textDecorationStyle); } - float wordSpacing() const; - float letterSpacing() const; - - TextDirection direction() const { return static_cast(inherited_flags._direction); } - bool isLeftToRightDirection() const { return direction() == LTR; } - - const Length& specifiedLineHeight() const; - Length lineHeight() const; - int computedLineHeight() const; - - EWhiteSpace whiteSpace() const { return static_cast(inherited_flags._white_space); } - static bool autoWrap(EWhiteSpace ws) - { - // Nowrap and pre don't automatically wrap. - return ws != NOWRAP && ws != PRE; - } - - bool autoWrap() const - { - return autoWrap(whiteSpace()); - } - - static bool preserveNewline(EWhiteSpace ws) - { - // Normal and nowrap do not preserve newlines. - return ws != NORMAL && ws != NOWRAP; - } - - bool preserveNewline() const - { - return preserveNewline(whiteSpace()); - } - - static bool collapseWhiteSpace(EWhiteSpace ws) - { - // Pre and prewrap do not collapse whitespace. - return ws != PRE && ws != PRE_WRAP; - } - - bool collapseWhiteSpace() const - { - return collapseWhiteSpace(whiteSpace()); - } - - bool isCollapsibleWhiteSpace(UChar c) const - { - switch (c) { - case ' ': - case '\t': - return collapseWhiteSpace(); - case '\n': - return !preserveNewline(); - } - return false; - } - - bool breakOnlyAfterWhiteSpace() const - { - return whiteSpace() == PRE_WRAP || lineBreak() == LineBreakAfterWhiteSpace; - } - - bool breakWords() const - { - return wordBreak() == BreakWordBreak || overflowWrap() == BreakOverflowWrap; - } - - EFillRepeat backgroundRepeatX() const { return static_cast(m_background->background().repeatX()); } - EFillRepeat backgroundRepeatY() const { return static_cast(m_background->background().repeatY()); } - CompositeOperator backgroundComposite() const { return static_cast(m_background->background().composite()); } - EFillAttachment backgroundAttachment() const { return static_cast(m_background->background().attachment()); } - EFillBox backgroundClip() const { return static_cast(m_background->background().clip()); } - EFillBox backgroundOrigin() const { return static_cast(m_background->background().origin()); } - const Length& backgroundXPosition() const { return m_background->background().xPosition(); } - const Length& backgroundYPosition() const { return m_background->background().yPosition(); } - EFillSizeType backgroundSizeType() const { return m_background->background().sizeType(); } - const LengthSize& backgroundSizeLength() const { return m_background->background().sizeLength(); } - FillLayer& accessBackgroundLayers() { return m_background.access()->m_background; } - const FillLayer& backgroundLayers() const { return m_background->background(); } - - short horizontalBorderSpacing() const; - short verticalBorderSpacing() const; - - const Length& marginTop() const { return surround->margin.top(); } - const Length& marginBottom() const { return surround->margin.bottom(); } - const Length& marginLeft() const { return surround->margin.left(); } - const Length& marginRight() const { return surround->margin.right(); } - const Length& marginBefore() const { return surround->margin.before(); } - const Length& marginAfter() const { return surround->margin.after(); } - const Length& marginStart() const { return surround->margin.start(direction()); } - const Length& marginEnd() const { return surround->margin.end(direction()); } - const Length& marginStartUsing(const RenderStyle* otherStyle) const { return surround->margin.start(otherStyle->direction()); } - const Length& marginEndUsing(const RenderStyle* otherStyle) const { return surround->margin.end(otherStyle->direction()); } - const Length& marginBeforeUsing(const RenderStyle* otherStyle) const { return surround->margin.before(); } - const Length& marginAfterUsing(const RenderStyle* otherStyle) const { return surround->margin.after(); } - - const LengthBox& paddingBox() const { return surround->padding; } - const Length& paddingTop() const { return surround->padding.top(); } - const Length& paddingBottom() const { return surround->padding.bottom(); } - const Length& paddingLeft() const { return surround->padding.left(); } - const Length& paddingRight() const { return surround->padding.right(); } - const Length& paddingBefore() const { return surround->padding.before(); } - const Length& paddingAfter() const { return surround->padding.after(); } - const Length& paddingStart() const { return surround->padding.start(direction()); } - const Length& paddingEnd() const { return surround->padding.end(direction()); } - - bool isLink() const { return noninherited_flags.isLink; } - - // CSS3 Getter Methods - - int outlineOffset() const - { - if (m_background->outline().style() == BNONE) - return 0; - return m_background->outline().offset(); - } - - ShadowList* textShadow() const { return rareInheritedData->textShadow.get(); } - void getTextShadowExtent(LayoutUnit& top, LayoutUnit& right, LayoutUnit& bottom, LayoutUnit& left) const { getShadowExtent(textShadow(), top, right, bottom, left); } - void getTextShadowHorizontalExtent(LayoutUnit& left, LayoutUnit& right) const { getShadowHorizontalExtent(textShadow(), left, right); } - void getTextShadowVerticalExtent(LayoutUnit& top, LayoutUnit& bottom) const { getShadowVerticalExtent(textShadow(), top, bottom); } - void getTextShadowInlineDirectionExtent(LayoutUnit& logicalLeft, LayoutUnit& logicalRight) { getShadowInlineDirectionExtent(textShadow(), logicalLeft, logicalRight); } - void getTextShadowBlockDirectionExtent(LayoutUnit& logicalTop, LayoutUnit& logicalBottom) { getShadowBlockDirectionExtent(textShadow(), logicalTop, logicalBottom); } - - float textStrokeWidth() const { return rareInheritedData->textStrokeWidth; } - float opacity() const { return rareNonInheritedData->opacity; } - bool hasOpacity() const { return opacity() < 1.0f; } - // aspect ratio convenience method - bool hasAspectRatio() const { return rareNonInheritedData->m_hasAspectRatio; } - float aspectRatio() const { return aspectRatioNumerator() / aspectRatioDenominator(); } - float aspectRatioDenominator() const { return rareNonInheritedData->m_aspectRatioDenominator; } - float aspectRatioNumerator() const { return rareNonInheritedData->m_aspectRatioNumerator; } - - int order() const { return rareNonInheritedData->m_order; } - float flexGrow() const { return rareNonInheritedData->m_flexibleBox->m_flexGrow; } - float flexShrink() const { return rareNonInheritedData->m_flexibleBox->m_flexShrink; } - const Length& flexBasis() const { return rareNonInheritedData->m_flexibleBox->m_flexBasis; } - EAlignContent alignContent() const { return static_cast(rareNonInheritedData->m_alignContent); } - ItemPosition alignItems() const { return static_cast(rareNonInheritedData->m_alignItems); } - OverflowAlignment alignItemsOverflowAlignment() const { return static_cast(rareNonInheritedData->m_alignItemsOverflowAlignment); } - ItemPosition alignSelf() const { return static_cast(rareNonInheritedData->m_alignSelf); } - OverflowAlignment alignSelfOverflowAlignment() const { return static_cast(rareNonInheritedData->m_alignSelfOverflowAlignment); } - EFlexDirection flexDirection() const { return static_cast(rareNonInheritedData->m_flexibleBox->m_flexDirection); } - bool isColumnFlexDirection() const { return flexDirection() == FlowColumn || flexDirection() == FlowColumnReverse; } - bool isReverseFlexDirection() const { return flexDirection() == FlowRowReverse || flexDirection() == FlowColumnReverse; } - EFlexWrap flexWrap() const { return static_cast(rareNonInheritedData->m_flexibleBox->m_flexWrap); } - EJustifyContent justifyContent() const { return static_cast(rareNonInheritedData->m_justifyContent); } - ItemPosition justifyItems() const { return static_cast(rareNonInheritedData->m_justifyItems); } - OverflowAlignment justifyItemsOverflowAlignment() const { return static_cast(rareNonInheritedData->m_justifyItemsOverflowAlignment); } - ItemPositionType justifyItemsPositionType() const { return static_cast(rareNonInheritedData->m_justifyItemsPositionType); } - ItemPosition justifySelf() const { return static_cast(rareNonInheritedData->m_justifySelf); } - OverflowAlignment justifySelfOverflowAlignment() const { return static_cast(rareNonInheritedData->m_justifySelfOverflowAlignment); } - - ShadowList* boxShadow() const { return rareNonInheritedData->m_boxShadow.get(); } - void getBoxShadowExtent(LayoutUnit& top, LayoutUnit& right, LayoutUnit& bottom, LayoutUnit& left) const { getShadowExtent(boxShadow(), top, right, bottom, left); } - LayoutBoxExtent getBoxShadowInsetExtent() const { return getShadowInsetExtent(boxShadow()); } - void getBoxShadowHorizontalExtent(LayoutUnit& left, LayoutUnit& right) const { getShadowHorizontalExtent(boxShadow(), left, right); } - void getBoxShadowVerticalExtent(LayoutUnit& top, LayoutUnit& bottom) const { getShadowVerticalExtent(boxShadow(), top, bottom); } - void getBoxShadowInlineDirectionExtent(LayoutUnit& logicalLeft, LayoutUnit& logicalRight) { getShadowInlineDirectionExtent(boxShadow(), logicalLeft, logicalRight); } - void getBoxShadowBlockDirectionExtent(LayoutUnit& logicalTop, LayoutUnit& logicalBottom) { getShadowBlockDirectionExtent(boxShadow(), logicalTop, logicalBottom); } - - EBoxDecorationBreak boxDecorationBreak() const { return m_box->boxDecorationBreak(); } - - // FIXME: reflections should belong to this helper function but they are currently handled - // through their self-painting layers. So the rendering code doesn't account for them. - bool hasVisualOverflowingEffect() const { return boxShadow() || hasOutline(); } - - EBoxSizing boxSizing() const { return m_box->boxSizing(); } - EUserModify userModify() const { return static_cast(rareInheritedData->userModify); } - EUserSelect userSelect() const { return static_cast(rareInheritedData->userSelect); } - TextOverflow textOverflow() const { return static_cast(rareNonInheritedData->textOverflow); } - EWordBreak wordBreak() const { return static_cast(rareInheritedData->wordBreak); } - EOverflowWrap overflowWrap() const { return static_cast(rareInheritedData->overflowWrap); } - LineBreak lineBreak() const { return static_cast(rareInheritedData->lineBreak); } - const AtomicString& highlight() const { return rareInheritedData->highlight; } - const AtomicString& hyphenationString() const { return rareInheritedData->hyphenationString; } - const AtomicString& locale() const { return rareInheritedData->locale; } - const TransformOperations& transform() const { return rareNonInheritedData->m_transform->m_operations; } - const Length& transformOriginX() const { return rareNonInheritedData->m_transform->m_x; } - const Length& transformOriginY() const { return rareNonInheritedData->m_transform->m_y; } - float transformOriginZ() const { return rareNonInheritedData->m_transform->m_z; } - bool hasTransform() const { return !rareNonInheritedData->m_transform->m_operations.operations().isEmpty(); } - bool transformDataEquivalent(const RenderStyle& otherStyle) const { return rareNonInheritedData->m_transform == otherStyle.rareNonInheritedData->m_transform; } - - TextEmphasisFill textEmphasisFill() const { return static_cast(rareInheritedData->textEmphasisFill); } - TextEmphasisMark textEmphasisMark() const; - const AtomicString& textEmphasisCustomMark() const { return rareInheritedData->textEmphasisCustomMark; } - TextEmphasisPosition textEmphasisPosition() const { return static_cast(rareInheritedData->textEmphasisPosition); } - const AtomicString& textEmphasisMarkString() const; - - TextOrientation textOrientation() const { return static_cast(rareInheritedData->m_textOrientation); } - - ObjectFit objectFit() const { return static_cast(rareNonInheritedData->m_objectFit); } - LengthPoint objectPosition() const { return rareNonInheritedData->m_objectPosition; } - - // Return true if any transform related property (currently transform, transformStyle3D or perspective) - // indicates that we are transforming - bool hasTransformRelatedProperty() const { return hasTransform() || preserves3D() || hasPerspective(); } - - enum ApplyTransformOrigin { IncludeTransformOrigin, ExcludeTransformOrigin }; - void applyTransform(TransformationMatrix&, const LayoutSize& borderBoxSize, ApplyTransformOrigin = IncludeTransformOrigin) const; - void applyTransform(TransformationMatrix&, const FloatRect& boundingBox, ApplyTransformOrigin = IncludeTransformOrigin) const; - - unsigned tabSize() const { return rareInheritedData->m_tabSize; } - - // End CSS3 Getters - - WrapFlow wrapFlow() const { return static_cast(rareNonInheritedData->m_wrapFlow); } - WrapThrough wrapThrough() const { return static_cast(rareNonInheritedData->m_wrapThrough); } - - // Apple-specific property getter methods - EPointerEvents pointerEvents() const { return static_cast(inherited_flags._pointerEvents); } - - ETransformStyle3D transformStyle3D() const { return static_cast(rareNonInheritedData->m_transformStyle3D); } - bool preserves3D() const { return rareNonInheritedData->m_transformStyle3D == TransformStyle3DPreserve3D; } - - float perspective() const { return rareNonInheritedData->m_perspective; } - bool hasPerspective() const { return rareNonInheritedData->m_perspective > 0; } - const Length& perspectiveOriginX() const { return rareNonInheritedData->m_perspectiveOriginX; } - const Length& perspectiveOriginY() const { return rareNonInheritedData->m_perspectiveOriginY; } - - LineBoxContain lineBoxContain() const { return rareInheritedData->m_lineBoxContain; } - Color tapHighlightColor() const { return rareInheritedData->tapHighlightColor; } - - EImageRendering imageRendering() const { return static_cast(rareInheritedData->m_imageRendering); } - - TouchAction touchAction() const { return static_cast(rareNonInheritedData->m_touchAction); } - TouchActionDelay touchActionDelay() const { return static_cast(rareInheritedData->m_touchActionDelay); } - - // Flutter property getters - const AtomicString& ellipsis() const { return rareNonInheritedData->m_ellipsis; } - int maxLines() const { return rareNonInheritedData->m_maxLines; } - -// attribute setter methods - - void setDisplay(EDisplay v) { noninherited_flags.effectiveDisplay = v; } - void setOriginalDisplay(EDisplay v) { noninherited_flags.originalDisplay = v; } - void setPosition(EPosition v) { noninherited_flags.position = v; } - - void setLeft(const Length& v) { SET_VAR(surround, offset.m_left, v); } - void setRight(const Length& v) { SET_VAR(surround, offset.m_right, v); } - void setTop(const Length& v) { SET_VAR(surround, offset.m_top, v); } - void setBottom(const Length& v) { SET_VAR(surround, offset.m_bottom, v); } - - void setWidth(const Length& v) { SET_VAR(m_box, m_width, v); } - void setHeight(const Length& v) { SET_VAR(m_box, m_height, v); } - - void setLogicalWidth(const Length& v) - { - // FIXME(sky): Remove - SET_VAR(m_box, m_width, v); +class RenderStyle : public RefCounted { + friend class EditingStyle; // Editing has to only reveal unvisited info. + friend class CSSComputedStyleDeclaration; // Ignores visited styles, so needs + // to be able to see unvisited + // info. + friend class StyleBuilderFunctions; // Sets color styles + + // FIXME: When we stop resolving currentColor at style time, these can be + // removed. + friend class CSSToStyleMap; + friend class FilterOperationResolver; + friend class StyleBuilderConverter; + friend class StyleResolverState; + friend class StyleResolver; + + protected: + // non-inherited attributes + DataRef m_box; + DataRef visual; + DataRef m_background; + DataRef surround; + DataRef rareNonInheritedData; + + // inherited attributes + DataRef rareInheritedData; + DataRef inherited; + + // !START SYNC!: Keep this in sync with the copy constructor in + // RenderStyle.cpp and implicitlyInherited() in StyleResolver.cpp + + // inherit + struct InheritedFlags { + bool operator==(const InheritedFlags& other) const { + return (_visibility == other._visibility) && + (_text_align == other._text_align) && + (m_textUnderline == other.m_textUnderline) && + (_direction == other._direction) && + (_white_space == other._white_space) && + (m_rtlOrdering == other.m_rtlOrdering) && + (_pointerEvents == other._pointerEvents); } - void setLogicalHeight(const Length& v) - { - // FIXME(sky): Remove - SET_VAR(m_box, m_height, v); + bool operator!=(const InheritedFlags& other) const { + return !(*this == other); } - void setMinWidth(const Length& v) { SET_VAR(m_box, m_minWidth, v); } - void setMaxWidth(const Length& v) { SET_VAR(m_box, m_maxWidth, v); } - void setMinHeight(const Length& v) { SET_VAR(m_box, m_minHeight, v); } - void setMaxHeight(const Length& v) { SET_VAR(m_box, m_maxHeight, v); } - - void resetBorder() - { - resetBorderTop(); - resetBorderRight(); - resetBorderBottom(); - resetBorderLeft(); - resetBorderTopLeftRadius(); - resetBorderTopRightRadius(); - resetBorderBottomLeftRadius(); - resetBorderBottomRightRadius(); - } - void resetBorderTop() { SET_VAR(surround, border.m_top, BorderValue()); } - void resetBorderRight() { SET_VAR(surround, border.m_right, BorderValue()); } - void resetBorderBottom() { SET_VAR(surround, border.m_bottom, BorderValue()); } - void resetBorderLeft() { SET_VAR(surround, border.m_left, BorderValue()); } - void resetBorderTopLeftRadius() { SET_VAR(surround, border.m_topLeft, initialBorderRadius()); } - void resetBorderTopRightRadius() { SET_VAR(surround, border.m_topRight, initialBorderRadius()); } - void resetBorderBottomLeftRadius() { SET_VAR(surround, border.m_bottomLeft, initialBorderRadius()); } - void resetBorderBottomRightRadius() { SET_VAR(surround, border.m_bottomRight, initialBorderRadius()); } - - void setBackgroundColor(const StyleColor& v) { SET_VAR(m_background, m_color, v); } - - void setBackgroundXPosition(const Length& length) { SET_VAR(m_background, m_background.m_xPosition, length); } - void setBackgroundYPosition(const Length& length) { SET_VAR(m_background, m_background.m_yPosition, length); } - void setBackgroundSize(EFillSizeType b) { SET_VAR(m_background, m_background.m_sizeType, b); } - void setBackgroundSizeLength(const LengthSize& s) { SET_VAR(m_background, m_background.m_sizeLength, s); } - - void setBorderTopLeftRadius(const LengthSize& s) { SET_VAR(surround, border.m_topLeft, s); } - void setBorderTopRightRadius(const LengthSize& s) { SET_VAR(surround, border.m_topRight, s); } - void setBorderBottomLeftRadius(const LengthSize& s) { SET_VAR(surround, border.m_bottomLeft, s); } - void setBorderBottomRightRadius(const LengthSize& s) { SET_VAR(surround, border.m_bottomRight, s); } - - void setBorderRadius(const LengthSize& s) - { - setBorderTopLeftRadius(s); - setBorderTopRightRadius(s); - setBorderBottomLeftRadius(s); - setBorderBottomRightRadius(s); - } - void setBorderRadius(const IntSize& s) - { - setBorderRadius(LengthSize(Length(s.width(), Fixed), Length(s.height(), Fixed))); - } - - RoundedRect getRoundedBorderFor(const LayoutRect& borderRect, bool includeLogicalLeftEdge = true, bool includeLogicalRightEdge = true) const; - RoundedRect getRoundedInnerBorderFor(const LayoutRect& borderRect, bool includeLogicalLeftEdge = true, bool includeLogicalRightEdge = true) const; - - RoundedRect getRoundedInnerBorderFor(const LayoutRect& borderRect, - int topWidth, int bottomWidth, int leftWidth, int rightWidth, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) const; - - void setBorderLeftWidth(unsigned v) { SET_VAR(surround, border.m_left.m_width, v); } - void setBorderLeftStyle(EBorderStyle v) { SET_VAR(surround, border.m_left.m_style, v); } - void setBorderLeftColor(const StyleColor& v) { SET_BORDERVALUE_COLOR(surround, border.m_left, v); } - void setBorderRightWidth(unsigned v) { SET_VAR(surround, border.m_right.m_width, v); } - void setBorderRightStyle(EBorderStyle v) { SET_VAR(surround, border.m_right.m_style, v); } - void setBorderRightColor(const StyleColor& v) { SET_BORDERVALUE_COLOR(surround, border.m_right, v); } - void setBorderTopWidth(unsigned v) { SET_VAR(surround, border.m_top.m_width, v); } - void setBorderTopStyle(EBorderStyle v) { SET_VAR(surround, border.m_top.m_style, v); } - void setBorderTopColor(const StyleColor& v) { SET_BORDERVALUE_COLOR(surround, border.m_top, v); } - void setBorderBottomWidth(unsigned v) { SET_VAR(surround, border.m_bottom.m_width, v); } - void setBorderBottomStyle(EBorderStyle v) { SET_VAR(surround, border.m_bottom.m_style, v); } - void setBorderBottomColor(const StyleColor& v) { SET_BORDERVALUE_COLOR(surround, border.m_bottom, v); } - - void setOutlineWidth(unsigned short v) { SET_VAR(m_background, m_outline.m_width, v); } - void setOutlineStyleIsAuto(OutlineIsAuto isAuto) { SET_VAR(m_background, m_outline.m_isAuto, isAuto); } - void setOutlineStyle(EBorderStyle v) { SET_VAR(m_background, m_outline.m_style, v); } - void setOutlineColor(const StyleColor& v) { SET_BORDERVALUE_COLOR(m_background, m_outline, v); } - - void setOverflowX(EOverflow v) { noninherited_flags.overflowX = v; } - void setOverflowY(EOverflow v) { noninherited_flags.overflowY = v; } - void setVisibility(EVisibility v) { inherited_flags._visibility = v; } - void setVerticalAlign(EVerticalAlign v) { noninherited_flags.verticalAlign = v; } - void setVerticalAlignLength(const Length& length) { setVerticalAlign(LENGTH); SET_VAR(m_box, m_verticalAlign, length); } - - void setHasAutoClip() { SET_VAR(visual, hasAutoClip, true); SET_VAR(visual, clip, RenderStyle::initialClip()); } - void setClip(const LengthBox& box) { SET_VAR(visual, hasAutoClip, false); SET_VAR(visual, clip, box); } - - void setUnicodeBidi(EUnicodeBidi b) { noninherited_flags.unicodeBidi = b; } - - bool setFontDescription(const FontDescription&); - // Only used for text autosizing. - void setFontSize(float); - void setFontStretch(FontStretch); - void setFontWeight(FontWeight); - - void setColor(const Color&); - void setTextIndent(const Length& v) { SET_VAR(rareInheritedData, indent, v); } - void setTextIndentLine(TextIndentLine v) { SET_VAR(rareInheritedData, m_textIndentLine, v); } - void setTextIndentType(TextIndentType v) { SET_VAR(rareInheritedData, m_textIndentType, v); } - void setTextAlign(ETextAlign v) { inherited_flags._text_align = v; } - void setTextAlignLast(TextAlignLast v) { SET_VAR(rareInheritedData, m_textAlignLast, v); } - void setTextJustify(TextJustify v) { SET_VAR(rareInheritedData, m_textJustify, v); } - void applyTextDecorations(); - void clearAppliedTextDecorations(); - void setTextDecoration(TextDecoration v) { SET_VAR(visual, textDecoration, v); } - void setTextUnderlinePosition(TextUnderlinePosition v) { SET_VAR(rareInheritedData, m_textUnderlinePosition, v); } - void setTextDecorationStyle(TextDecorationStyle v) { SET_VAR(rareNonInheritedData, m_textDecorationStyle, v); } - void setDirection(TextDirection v) { inherited_flags._direction = v; } - void setLineHeight(const Length& specifiedLineHeight); - - void setImageRendering(EImageRendering v) { SET_VAR(rareInheritedData, m_imageRendering, v); } - - void setWhiteSpace(EWhiteSpace v) { inherited_flags._white_space = v; } - - // FIXME: Remove these two and replace them with respective FontBuilder calls. - void setWordSpacing(float); - void setLetterSpacing(float); - - void adjustBackgroundLayers() - { - if (backgroundLayers().next()) { - accessBackgroundLayers().cullEmptyLayers(); - accessBackgroundLayers().fillUnsetProperties(); - } + unsigned _visibility : 2; // EVisibility + unsigned _text_align : 4; // ETextAlign + unsigned m_textUnderline : 1; + unsigned _direction : 1; // TextDirection + unsigned _white_space : 3; // EWhiteSpace + + // non CSS2 inherited + unsigned m_rtlOrdering : 1; // Order + unsigned _pointerEvents : 4; // EPointerEvents + + // 19 bits + } inherited_flags; + + // don't inherit + struct NonInheritedFlags { + bool operator==(const NonInheritedFlags& other) const { + return effectiveDisplay == other.effectiveDisplay && + originalDisplay == other.originalDisplay && + overflowX == other.overflowX && overflowY == other.overflowY && + verticalAlign == other.verticalAlign && + position == other.position && styleType == other.styleType && + affectedByFocus == other.affectedByFocus && + affectedByHover == other.affectedByHover && + affectedByActive == other.affectedByActive && + unicodeBidi == other.unicodeBidi && + explicitInheritance == other.explicitInheritance && + currentColor == other.currentColor && unique == other.unique && + emptyState == other.emptyState && + firstChildState == other.firstChildState && + lastChildState == other.lastChildState && isLink == other.isLink; } - void setHorizontalBorderSpacing(short); - void setVerticalBorderSpacing(short); - - void setHasAspectRatio(bool b) { SET_VAR(rareNonInheritedData, m_hasAspectRatio, b); } - void setAspectRatioDenominator(float v) { SET_VAR(rareNonInheritedData, m_aspectRatioDenominator, v); } - void setAspectRatioNumerator(float v) { SET_VAR(rareNonInheritedData, m_aspectRatioNumerator, v); } - - void setMarginTop(const Length& v) { SET_VAR(surround, margin.m_top, v); } - void setMarginBottom(const Length& v) { SET_VAR(surround, margin.m_bottom, v); } - void setMarginLeft(const Length& v) { SET_VAR(surround, margin.m_left, v); } - void setMarginRight(const Length& v) { SET_VAR(surround, margin.m_right, v); } - void setMarginStart(const Length&); - void setMarginEnd(const Length&); - - void resetPadding() { SET_VAR(surround, padding, LengthBox(Auto)); } - void setPaddingBox(const LengthBox& b) { SET_VAR(surround, padding, b); } - void setPaddingTop(const Length& v) { SET_VAR(surround, padding.m_top, v); } - void setPaddingBottom(const Length& v) { SET_VAR(surround, padding.m_bottom, v); } - void setPaddingLeft(const Length& v) { SET_VAR(surround, padding.m_left, v); } - void setPaddingRight(const Length& v) { SET_VAR(surround, padding.m_right, v); } - - void setIsLink(bool b) { noninherited_flags.isLink = b; } - - bool hasAutoZIndex() const { return m_box->hasAutoZIndex(); } - void setHasAutoZIndex() { SET_VAR(m_box, m_hasAutoZIndex, true); SET_VAR(m_box, m_zIndex, 0); } - unsigned zIndex() const { return m_box->zIndex(); } - void setZIndex(unsigned v) { SET_VAR(m_box, m_hasAutoZIndex, false); SET_VAR(m_box, m_zIndex, v); } - - // CSS3 Setters - void setOutlineOffset(int v) { SET_VAR(m_background, m_outline.m_offset, v); } - void setTextShadow(PassRefPtr); - void setTextStrokeColor(const StyleColor& c) { SET_VAR_WITH_SETTER(rareInheritedData, textStrokeColor, setTextStrokeColor, c); } - void setTextStrokeWidth(float w) { SET_VAR(rareInheritedData, textStrokeWidth, w); } - void setTextFillColor(const StyleColor& c) { SET_VAR_WITH_SETTER(rareInheritedData, textFillColor, setTextFillColor, c); } - void setOpacity(float f) { float v = clampTo(f, 0, 1); SET_VAR(rareNonInheritedData, opacity, v); } - // For valid values of box-align see http://www.w3.org/TR/2009/WD-css3-flexbox-20090723/#alignment - void setBoxDecorationBreak(EBoxDecorationBreak b) { SET_VAR(m_box, m_boxDecorationBreak, b); } - void setBoxShadow(PassRefPtr); - void setBoxSizing(EBoxSizing s) { SET_VAR(m_box, m_boxSizing, s); } - void setFlexGrow(float f) { SET_VAR(rareNonInheritedData.access()->m_flexibleBox, m_flexGrow, f); } - void setFlexShrink(float f) { SET_VAR(rareNonInheritedData.access()->m_flexibleBox, m_flexShrink, f); } - void setFlexBasis(const Length& length) { SET_VAR(rareNonInheritedData.access()->m_flexibleBox, m_flexBasis, length); } - // We restrict the smallest value to int min + 2 because we use int min and int min + 1 as special values in a hash set. - void setOrder(int o) { SET_VAR(rareNonInheritedData, m_order, max(std::numeric_limits::min() + 2, o)); } - void setAlignContent(EAlignContent p) { SET_VAR(rareNonInheritedData, m_alignContent, p); } - void setAlignItems(ItemPosition a) { SET_VAR(rareNonInheritedData, m_alignItems, a); } - void setAlignItemsOverflowAlignment(OverflowAlignment overflowAlignment) { SET_VAR(rareNonInheritedData, m_alignItemsOverflowAlignment, overflowAlignment); } - void setAlignSelf(ItemPosition a) { SET_VAR(rareNonInheritedData, m_alignSelf, a); } - void setAlignSelfOverflowAlignment(OverflowAlignment overflowAlignment) { SET_VAR(rareNonInheritedData, m_alignSelfOverflowAlignment, overflowAlignment); } - void setFlexDirection(EFlexDirection direction) { SET_VAR(rareNonInheritedData.access()->m_flexibleBox, m_flexDirection, direction); } - void setFlexWrap(EFlexWrap w) { SET_VAR(rareNonInheritedData.access()->m_flexibleBox, m_flexWrap, w); } - void setJustifyContent(EJustifyContent p) { SET_VAR(rareNonInheritedData, m_justifyContent, p); } - void setJustifyItems(ItemPosition justifyItems) { SET_VAR(rareNonInheritedData, m_justifyItems, justifyItems); } - void setJustifyItemsOverflowAlignment(OverflowAlignment overflowAlignment) { SET_VAR(rareNonInheritedData, m_justifyItemsOverflowAlignment, overflowAlignment); } - void setJustifyItemsPositionType(ItemPositionType positionType) { SET_VAR(rareNonInheritedData, m_justifyItemsPositionType, positionType); } - void setJustifySelf(ItemPosition justifySelf) { SET_VAR(rareNonInheritedData, m_justifySelf, justifySelf); } - void setJustifySelfOverflowAlignment(OverflowAlignment overflowAlignment) { SET_VAR(rareNonInheritedData, m_justifySelfOverflowAlignment, overflowAlignment); } - - void setUserModify(EUserModify u) { SET_VAR(rareInheritedData, userModify, u); } - void setUserSelect(EUserSelect s) { SET_VAR(rareInheritedData, userSelect, s); } - void setTextOverflow(TextOverflow overflow) { SET_VAR(rareNonInheritedData, textOverflow, overflow); } - void setWordBreak(EWordBreak b) { SET_VAR(rareInheritedData, wordBreak, b); } - void setOverflowWrap(EOverflowWrap b) { SET_VAR(rareInheritedData, overflowWrap, b); } - void setLineBreak(LineBreak b) { SET_VAR(rareInheritedData, lineBreak, b); } - void setHighlight(const AtomicString& h) { SET_VAR(rareInheritedData, highlight, h); } - void setHyphens(Hyphens h) { SET_VAR(rareInheritedData, hyphens, h); } - void setHyphenationString(const AtomicString& h) { SET_VAR(rareInheritedData, hyphenationString, h); } - void setLocale(const AtomicString& locale) { SET_VAR(rareInheritedData, locale, locale); } - void setTransform(const TransformOperations& ops) { SET_VAR(rareNonInheritedData.access()->m_transform, m_operations, ops); } - void setTransformOriginX(const Length& l) { SET_VAR(rareNonInheritedData.access()->m_transform, m_x, l); } - void setTransformOriginY(const Length& l) { SET_VAR(rareNonInheritedData.access()->m_transform, m_y, l); } - void setTransformOriginZ(float f) { SET_VAR(rareNonInheritedData.access()->m_transform, m_z, f); } - void setTextDecorationColor(const StyleColor& c) { SET_VAR(rareNonInheritedData, m_textDecorationColor, c); } - void setTextEmphasisColor(const StyleColor& c) { SET_VAR_WITH_SETTER(rareInheritedData, textEmphasisColor, setTextEmphasisColor, c); } - void setTextEmphasisFill(TextEmphasisFill fill) { SET_VAR(rareInheritedData, textEmphasisFill, fill); } - void setTextEmphasisMark(TextEmphasisMark mark) { SET_VAR(rareInheritedData, textEmphasisMark, mark); } - void setTextEmphasisCustomMark(const AtomicString& mark) { SET_VAR(rareInheritedData, textEmphasisCustomMark, mark); } - void setTextEmphasisPosition(TextEmphasisPosition position) { SET_VAR(rareInheritedData, textEmphasisPosition, position); } - bool setTextOrientation(TextOrientation); - - void setObjectFit(ObjectFit f) { SET_VAR(rareNonInheritedData, m_objectFit, f); } - void setObjectPosition(LengthPoint position) { SET_VAR(rareNonInheritedData, m_objectPosition, position); } - - void setTabSize(unsigned size) { SET_VAR(rareInheritedData, m_tabSize, size); } - - // End CSS3 Setters - - void setWrapFlow(WrapFlow wrapFlow) { SET_VAR(rareNonInheritedData, m_wrapFlow, wrapFlow); } - void setWrapThrough(WrapThrough wrapThrough) { SET_VAR(rareNonInheritedData, m_wrapThrough, wrapThrough); } - - // Apple-specific property setters - void setPointerEvents(EPointerEvents p) { inherited_flags._pointerEvents = p; } - - void setTransformStyle3D(ETransformStyle3D b) { SET_VAR(rareNonInheritedData, m_transformStyle3D, b); } - void setPerspective(float p) { SET_VAR(rareNonInheritedData, m_perspective, p); } - void setPerspectiveOriginX(const Length& l) { SET_VAR(rareNonInheritedData, m_perspectiveOriginX, l); } - void setPerspectiveOriginY(const Length& l) { SET_VAR(rareNonInheritedData, m_perspectiveOriginY, l); } - - void setLineBoxContain(LineBoxContain c) { SET_VAR(rareInheritedData, m_lineBoxContain, c); } - void setTapHighlightColor(const Color& c) { SET_VAR(rareInheritedData, tapHighlightColor, c); } - void setTouchAction(TouchAction t) { SET_VAR(rareNonInheritedData, m_touchAction, t); } - void setTouchActionDelay(TouchActionDelay t) { SET_VAR(rareInheritedData, m_touchActionDelay, t); } - - void setClipPath(PassRefPtr operation) - { - if (rareNonInheritedData->m_clipPath != operation) - rareNonInheritedData.access()->m_clipPath = operation; - } - ClipPathOperation* clipPath() const { return rareNonInheritedData->m_clipPath.get(); } - - // Flutter property setters - void setEllipsis(const AtomicString& e) { SET_VAR(rareNonInheritedData, m_ellipsis, e); } - void setMaxLines(int m) { SET_VAR(rareNonInheritedData, m_maxLines, m); } - - static ClipPathOperation* initialClipPath() { return 0; } - - const CounterDirectiveMap* counterDirectives() const; - CounterDirectiveMap& accessCounterDirectives(); - const CounterDirectives getCounterDirectives(const AtomicString& identifier) const; - - const AtomicString& hyphenString() const; - - bool inheritedNotEqual(const RenderStyle*) const; - bool inheritedDataShared(const RenderStyle*) const; - - bool requiresOnlyBlockChildren() const; - bool isDisplayReplacedType() const { return isDisplayReplacedType(display()); } - bool isDisplayInlineType() const { return isDisplayInlineType(display()); } - bool isOriginalDisplayInlineType() const { return isDisplayInlineType(originalDisplay()); } - bool isDisplayFlexibleBox() const { return isDisplayFlexibleBox(display()); } - - // A unique style is one that has matches something that makes it impossible to share. - bool unique() const { return noninherited_flags.unique; } - void setUnique() { noninherited_flags.unique = true; } - - bool isSharable() const; - - bool emptyState() const { return noninherited_flags.emptyState; } - void setEmptyState(bool b) { setUnique(); noninherited_flags.emptyState = b; } - bool firstChildState() const { return noninherited_flags.firstChildState; } - void setFirstChildState() { setUnique(); noninherited_flags.firstChildState = true; } - bool lastChildState() const { return noninherited_flags.lastChildState; } - void setLastChildState() { setUnique(); noninherited_flags.lastChildState = true; } - - StyleColor decorationStyleColor() const; - Color decorationColor() const; - - void setHasExplicitlyInheritedProperties() { noninherited_flags.explicitInheritance = true; } - bool hasExplicitlyInheritedProperties() const { return noninherited_flags.explicitInheritance; } - - void setHasCurrentColor() { noninherited_flags.currentColor = true; } - bool hasCurrentColor() const { return noninherited_flags.currentColor; } - - bool hasBoxDecorations() const { return hasBorder() || hasBorderRadius() || hasOutline() || boxShadow(); } - - // Initial values for all the properties - static EBorderStyle initialBorderStyle() { return BNONE; } - static OutlineIsAuto initialOutlineStyleIsAuto() { return AUTO_OFF; } - static LengthSize initialBorderRadius() { return LengthSize(Length(0, Fixed), Length(0, Fixed)); } - static LengthBox initialClip() { return LengthBox(); } - static TextDirection initialDirection() { return LTR; } - static TextOrientation initialTextOrientation() { return TextOrientationVerticalRight; } - static ObjectFit initialObjectFit() { return ObjectFitFill; } - static LengthPoint initialObjectPosition() { return LengthPoint(Length(50.0, Percent), Length(50.0, Percent)); } - static EDisplay initialDisplay() { return FLEX; } - static EOverflow initialOverflowX() { return OVISIBLE; } - static EOverflow initialOverflowY() { return OVISIBLE; } - static EPosition initialPosition() { return StaticPosition; } - static EUnicodeBidi initialUnicodeBidi() { return UBNormal; } - static EVisibility initialVisibility() { return VISIBLE; } - static EWhiteSpace initialWhiteSpace() { return PRE_WRAP; } - static short initialHorizontalBorderSpacing() { return 0; } - static short initialVerticalBorderSpacing() { return 0; } - static Color initialColor() { return Color::white; } - static unsigned initialBorderWidth() { return 3; } - static unsigned short initialColumnRuleWidth() { return 3; } - static unsigned short initialOutlineWidth() { return 3; } - static float initialLetterWordSpacing() { return 0.0f; } - static Length initialSize() { return Length(); } - static Length initialMinSize() { return Length(Fixed); } - static Length initialMaxSize() { return Length(MaxSizeNone); } - static Length initialOffset() { return Length(); } - static Length initialMargin() { return Length(Fixed); } - static Length initialPadding() { return Length(Fixed); } - static Length initialTextIndent() { return Length(Fixed); } - static TextIndentLine initialTextIndentLine() { return TextIndentFirstLine; } - static TextIndentType initialTextIndentType() { return TextIndentNormal; } - static EVerticalAlign initialVerticalAlign() { return BASELINE; } - static Length initialLineHeight() { return Length(-100.0, Percent); } - static ETextAlign initialTextAlign() { return TASTART; } - static TextAlignLast initialTextAlignLast() { return TextAlignLastAuto; } - static TextJustify initialTextJustify() { return TextJustifyAuto; } - static TextDecoration initialTextDecoration() { return TextDecorationNone; } - static TextUnderlinePosition initialTextUnderlinePosition() { return TextUnderlinePositionAuto; } - static TextDecorationStyle initialTextDecorationStyle() { return TextDecorationStyleSolid; } - static int initialOutlineOffset() { return 0; } - static float initialOpacity() { return 1.0f; } - static EBoxAlignment initialBoxAlign() { return BSTRETCH; } - static EBoxDecorationBreak initialBoxDecorationBreak() { return DSLICE; } - static EBoxDirection initialBoxDirection() { return BNORMAL; } - static EBoxLines initialBoxLines() { return SINGLE; } - static EBoxOrient initialBoxOrient() { return HORIZONTAL; } - static EBoxPack initialBoxPack() { return Start; } - static float initialBoxFlex() { return 0.0f; } - static unsigned initialBoxFlexGroup() { return 1; } - static unsigned initialBoxOrdinalGroup() { return 1; } - static EBoxSizing initialBoxSizing() { return CONTENT_BOX; } - static float initialFlexGrow() { return 0; } - static float initialFlexShrink() { return 0; } - static Length initialFlexBasis() { return Length(Auto); } - static int initialOrder() { return 0; } - static EAlignContent initialAlignContent() { return AlignContentStretch; } - static ItemPosition initialAlignItems() { return ItemPositionStretch; } - static OverflowAlignment initialAlignItemsOverflowAlignment() { return OverflowAlignmentDefault; } - static ItemPosition initialAlignSelf() { return ItemPositionAuto; } - static OverflowAlignment initialAlignSelfOverflowAlignment() { return OverflowAlignmentDefault; } - static EFlexDirection initialFlexDirection() { return FlowColumn; } - static EFlexWrap initialFlexWrap() { return FlexNoWrap; } - static EJustifyContent initialJustifyContent() { return JustifyFlexStart; } - static ItemPosition initialJustifyItems() { return ItemPositionAuto; } - static OverflowAlignment initialJustifyItemsOverflowAlignment() { return OverflowAlignmentDefault; } - static ItemPositionType initialJustifyItemsPositionType() { return NonLegacyPosition; } - static ItemPosition initialJustifySelf() { return ItemPositionAuto; } - static OverflowAlignment initialJustifySelfOverflowAlignment() { return OverflowAlignmentDefault; } - static EUserModify initialUserModify() { return READ_ONLY; } - static EUserSelect initialUserSelect() { return SELECT_TEXT; } - static TextOverflow initialTextOverflow() { return TextOverflowClip; } - static EWordBreak initialWordBreak() { return NormalWordBreak; } - static EOverflowWrap initialOverflowWrap() { return BreakOverflowWrap; } - static LineBreak initialLineBreak() { return LineBreakAuto; } - static const AtomicString& initialHighlight() { return nullAtom; } - static const AtomicString& initialHyphenationString() { return nullAtom; } - static const AtomicString& initialLocale() { return nullAtom; } - static bool initialHasAspectRatio() { return false; } - static float initialAspectRatioDenominator() { return 1; } - static float initialAspectRatioNumerator() { return 1; } - static Order initialRTLOrdering() { return LogicalOrder; } - static float initialTextStrokeWidth() { return 0; } - static unsigned short initialColumnCount() { return 1; } - static ColumnFill initialColumnFill() { return ColumnFillBalance; } - static const TransformOperations& initialTransform() { DEFINE_STATIC_LOCAL(TransformOperations, ops, ()); return ops; } - static Length initialTransformOriginX() { return Length(50.0, Percent); } - static Length initialTransformOriginY() { return Length(50.0, Percent); } - static EPointerEvents initialPointerEvents() { return PE_AUTO; } - static float initialTransformOriginZ() { return 0; } - static ETransformStyle3D initialTransformStyle3D() { return TransformStyle3DFlat; } - static float initialPerspective() { return 0; } - static Length initialPerspectiveOriginX() { return Length(50.0, Percent); } - static Length initialPerspectiveOriginY() { return Length(50.0, Percent); } - static Color initialBackgroundColor() { return Color::transparent; } - static TextEmphasisFill initialTextEmphasisFill() { return TextEmphasisFillFilled; } - static TextEmphasisMark initialTextEmphasisMark() { return TextEmphasisMarkNone; } - static const AtomicString& initialTextEmphasisCustomMark() { return nullAtom; } - static TextEmphasisPosition initialTextEmphasisPosition() { return TextEmphasisPositionOver; } - static LineBoxContain initialLineBoxContain() { return LineBoxContainBlock | LineBoxContainInline | LineBoxContainReplaced; } - static ImageOrientationEnum initialImageOrientation() { return OriginTopLeft; } - static EImageRendering initialImageRendering() { return ImageRenderingAuto; } - static ImageResolutionSource initialImageResolutionSource() { return ImageResolutionSpecified; } - static ImageResolutionSnap initialImageResolutionSnap() { return ImageResolutionNoSnap; } - static float initialImageResolution() { return 1; } - static TouchAction initialTouchAction() { return TouchActionAuto; } - static TouchActionDelay initialTouchActionDelay() { return TouchActionDelayScript; } - static ShadowList* initialBoxShadow() { return 0; } - static ShadowList* initialTextShadow() { return 0; } - - static unsigned initialTabSize() { return 8; } - - static WrapFlow initialWrapFlow() { return WrapFlowAuto; } - static WrapThrough initialWrapThrough() { return WrapThroughWrap; } - - // Keep these at the end. - // FIXME: Why? Seems these should all be one big sorted list. - static Color initialTapHighlightColor(); - - Color resolveColor(StyleColor unresolvedColor) const { - return unresolvedColor.resolve(color()); - } - - StyleColor borderLeftColor() const { return surround->border.left().color(); } - StyleColor borderRightColor() const { return surround->border.right().color(); } - StyleColor borderTopColor() const { return surround->border.top().color(); } - StyleColor borderBottomColor() const { return surround->border.bottom().color(); } - StyleColor backgroundColor() const { return m_background->color(); } - Color color() const; - StyleColor textEmphasisColor() const { return rareInheritedData->textEmphasisColor(); } - StyleColor textFillColor() const { return rareInheritedData->textFillColor(); } - StyleColor textStrokeColor() const { return rareInheritedData->textStrokeColor(); } - -private: - void inheritUnicodeBidiFrom(const RenderStyle* parent) { noninherited_flags.unicodeBidi = parent->noninherited_flags.unicodeBidi; } - void getShadowExtent(const ShadowList*, LayoutUnit& top, LayoutUnit& right, LayoutUnit& bottom, LayoutUnit& left) const; - LayoutBoxExtent getShadowInsetExtent(const ShadowList*) const; - void getShadowHorizontalExtent(const ShadowList*, LayoutUnit& left, LayoutUnit& right) const; - void getShadowVerticalExtent(const ShadowList*, LayoutUnit& top, LayoutUnit& bottom) const; - void getShadowInlineDirectionExtent(const ShadowList* shadow, LayoutUnit& logicalLeft, LayoutUnit& logicalRight) const - { - return getShadowHorizontalExtent(shadow, logicalLeft, logicalRight); - } - void getShadowBlockDirectionExtent(const ShadowList* shadow, LayoutUnit& logicalTop, LayoutUnit& logicalBottom) const - { - return getShadowVerticalExtent(shadow, logicalTop, logicalBottom); + bool operator!=(const NonInheritedFlags& other) const { + return !(*this == other); } - bool isDisplayFlexibleBox(EDisplay display) const - { - return display == FLEX || display == INLINE_FLEX; - } - - bool isDisplayReplacedType(EDisplay display) const - { - return display == INLINE_FLEX; + unsigned effectiveDisplay : 5; // EDisplay + unsigned originalDisplay : 5; // EDisplay + unsigned overflowX : 3; // EOverflow + unsigned overflowY : 3; // EOverflow + unsigned verticalAlign : 4; // EVerticalAlign + unsigned position : 1; // EPosition + unsigned unicodeBidi : 3; // EUnicodeBidi + + // This is set if we used viewport units when resolving a length. + // It is mutable so we can pass around const RenderStyles to resolve + // lengths. + mutable unsigned hasViewportUnits : 1; + + // 32 bits + + unsigned styleType : 6; // PseudoId + unsigned explicitInheritance : 1; // Explicitly inherits a non-inherited + // property + unsigned + currentColor : 1; // At least one color has the value 'currentColor' + unsigned unique : 1; // Style can not be shared. + + unsigned emptyState : 1; + unsigned firstChildState : 1; + unsigned lastChildState : 1; + + unsigned affectedByFocus : 1; + unsigned affectedByHover : 1; + unsigned affectedByActive : 1; + + unsigned isLink : 1; + // If you add more style bits here, you will also need to update + // RenderStyle::copyNonInheritedFrom() 63 bits + } noninherited_flags; + + // !END SYNC! + + protected: + void setBitDefaults() { + inherited_flags._visibility = initialVisibility(); + inherited_flags._text_align = initialTextAlign(); + inherited_flags.m_textUnderline = false; + inherited_flags._direction = initialDirection(); + inherited_flags._white_space = initialWhiteSpace(); + inherited_flags.m_rtlOrdering = initialRTLOrdering(); + inherited_flags._pointerEvents = initialPointerEvents(); + + noninherited_flags.effectiveDisplay = noninherited_flags.originalDisplay = + initialDisplay(); + noninherited_flags.overflowX = initialOverflowX(); + noninherited_flags.overflowY = initialOverflowY(); + noninherited_flags.verticalAlign = initialVerticalAlign(); + noninherited_flags.position = initialPosition(); + noninherited_flags.unicodeBidi = initialUnicodeBidi(); + noninherited_flags.explicitInheritance = false; + noninherited_flags.currentColor = false; + noninherited_flags.unique = false; + noninherited_flags.emptyState = false; + noninherited_flags.firstChildState = false; + noninherited_flags.lastChildState = false; + noninherited_flags.hasViewportUnits = false; + noninherited_flags.affectedByFocus = false; + noninherited_flags.affectedByHover = false; + noninherited_flags.affectedByActive = false; + noninherited_flags.isLink = false; + } + + private: + ALWAYS_INLINE RenderStyle(); + + enum DefaultStyleTag { DefaultStyle }; + ALWAYS_INLINE explicit RenderStyle(DefaultStyleTag); + ALWAYS_INLINE RenderStyle(const RenderStyle&); + + public: + static PassRefPtr create(); + static PassRefPtr createDefaultStyle(); + static PassRefPtr clone(const RenderStyle*); + + // Computes how the style change should be propagated down the tree. + static StyleRecalcChange stylePropagationDiff(const RenderStyle* oldStyle, + const RenderStyle* newStyle); + + StyleDifference visualInvalidationDiff(const RenderStyle&) const; + + void inheritFrom(const RenderStyle* inheritParent); + void copyNonInheritedFrom(const RenderStyle*); + + void setHasViewportUnits(bool hasViewportUnits = true) const { + noninherited_flags.hasViewportUnits = hasViewportUnits; + } + bool hasViewportUnits() const { return noninherited_flags.hasViewportUnits; } + + bool affectedByFocus() const { return noninherited_flags.affectedByFocus; } + bool affectedByHover() const { return noninherited_flags.affectedByHover; } + bool affectedByActive() const { return noninherited_flags.affectedByActive; } + + void setAffectedByFocus() { noninherited_flags.affectedByFocus = true; } + void setAffectedByHover() { noninherited_flags.affectedByHover = true; } + void setAffectedByActive() { noninherited_flags.affectedByActive = true; } + + bool operator==(const RenderStyle& other) const; + bool operator!=(const RenderStyle& other) const { return !(*this == other); } + bool hasMargin() const { return surround->margin.nonZero(); } + bool hasBorder() const { return surround->border.hasBorder(); } + bool hasPadding() const { return surround->padding.nonZero(); } + bool hasOffset() const { return surround->offset.nonZero(); } + bool hasMarginBeforeQuirk() const { return marginBefore().quirk(); } + bool hasMarginAfterQuirk() const { return marginAfter().quirk(); } + + bool hasBackgroundImage() const { + return m_background->background().hasImage(); + } + bool hasFixedBackgroundImage() const { + return m_background->background().hasFixedImage(); + } + + bool hasEntirelyFixedBackground() const; + + bool hasBackground() const { + Color color = resolveColor(backgroundColor()); + if (color.alpha()) + return true; + return hasBackgroundImage(); + } + + Order rtlOrdering() const { + return static_cast(inherited_flags.m_rtlOrdering); + } + void setRTLOrdering(Order o) { inherited_flags.m_rtlOrdering = o; } + + // attribute getter methods + + EDisplay display() const { + return static_cast(noninherited_flags.effectiveDisplay); + } + EDisplay originalDisplay() const { + return static_cast(noninherited_flags.originalDisplay); + } + + const Length& left() const { return surround->offset.left(); } + const Length& right() const { return surround->offset.right(); } + const Length& top() const { return surround->offset.top(); } + const Length& bottom() const { return surround->offset.bottom(); } + + // Accessors for positioned object edges that take into account writing mode. + const Length& logicalLeft() const { return surround->offset.logicalLeft(); } + const Length& logicalRight() const { return surround->offset.logicalRight(); } + const Length& logicalTop() const { return surround->offset.before(); } + const Length& logicalBottom() const { return surround->offset.after(); } + + // Whether or not a positioned element requires normal flow x/y to be computed + // to determine its position. + bool hasAutoLeftAndRight() const { + return left().isAuto() && right().isAuto(); + } + bool hasAutoTopAndBottom() const { + return top().isAuto() && bottom().isAuto(); + } + + EPosition position() const { + return static_cast(noninherited_flags.position); + } + bool hasOutOfFlowPosition() const { return position() == AbsolutePosition; } + // FIXME(sky): Remove + bool hasInFlowPosition() const { return false; } + + const Length& width() const { return m_box->width(); } + const Length& height() const { return m_box->height(); } + const Length& minWidth() const { return m_box->minWidth(); } + const Length& maxWidth() const { return m_box->maxWidth(); } + const Length& minHeight() const { return m_box->minHeight(); } + const Length& maxHeight() const { return m_box->maxHeight(); } + + const Length& logicalWidth() const { return width(); } + const Length& logicalHeight() const { return height(); } + const Length& logicalMinWidth() const { return minWidth(); } + const Length& logicalMaxWidth() const { return maxWidth(); } + const Length& logicalMinHeight() const { return minHeight(); } + const Length& logicalMaxHeight() const { return maxHeight(); } + + const BorderData& border() const { return surround->border; } + const BorderValue& borderLeft() const { return surround->border.left(); } + const BorderValue& borderRight() const { return surround->border.right(); } + const BorderValue& borderTop() const { return surround->border.top(); } + const BorderValue& borderBottom() const { return surround->border.bottom(); } + + const BorderValue& borderBefore() const; + const BorderValue& borderAfter() const; + const BorderValue& borderStart() const; + const BorderValue& borderEnd() const; + + const LengthSize& borderTopLeftRadius() const { + return surround->border.topLeft(); + } + const LengthSize& borderTopRightRadius() const { + return surround->border.topRight(); + } + const LengthSize& borderBottomLeftRadius() const { + return surround->border.bottomLeft(); + } + const LengthSize& borderBottomRightRadius() const { + return surround->border.bottomRight(); + } + bool hasBorderRadius() const { return surround->border.hasBorderRadius(); } + + unsigned borderLeftWidth() const { + return surround->border.borderLeftWidth(); + } + EBorderStyle borderLeftStyle() const { + return surround->border.left().style(); + } + bool borderLeftIsTransparent() const { + return surround->border.left().isTransparent(); + } + unsigned borderRightWidth() const { + return surround->border.borderRightWidth(); + } + EBorderStyle borderRightStyle() const { + return surround->border.right().style(); + } + bool borderRightIsTransparent() const { + return surround->border.right().isTransparent(); + } + unsigned borderTopWidth() const { return surround->border.borderTopWidth(); } + EBorderStyle borderTopStyle() const { return surround->border.top().style(); } + bool borderTopIsTransparent() const { + return surround->border.top().isTransparent(); + } + unsigned borderBottomWidth() const { + return surround->border.borderBottomWidth(); + } + EBorderStyle borderBottomStyle() const { + return surround->border.bottom().style(); + } + bool borderBottomIsTransparent() const { + return surround->border.bottom().isTransparent(); + } + + unsigned short borderBeforeWidth() const; + unsigned short borderAfterWidth() const; + unsigned short borderStartWidth() const; + unsigned short borderEndWidth() const; + + unsigned short outlineSize() const { + return max(0, outlineWidth() + outlineOffset()); + } + unsigned short outlineWidth() const { + if (m_background->outline().style() == BNONE) + return 0; + return m_background->outline().width(); + } + bool hasOutline() const { + return outlineWidth() > 0 && outlineStyle() > BHIDDEN; + } + EBorderStyle outlineStyle() const { return m_background->outline().style(); } + OutlineIsAuto outlineStyleIsAuto() const { + return static_cast(m_background->outline().isAuto()); + } + + EOverflow overflowX() const { + return static_cast(noninherited_flags.overflowX); + } + EOverflow overflowY() const { + return static_cast(noninherited_flags.overflowY); + } + // It's sufficient to just check one direction, since it's illegal to have + // visible on only one overflow value. + bool isOverflowVisible() const { + ASSERT(overflowX() != OVISIBLE || overflowX() == overflowY()); + return overflowX() == OVISIBLE; + } + bool isOverflowPaged() const { + return overflowY() == OPAGEDX || overflowY() == OPAGEDY; + } + + EVerticalAlign verticalAlign() const { + return static_cast(noninherited_flags.verticalAlign); + } + const Length& verticalAlignLength() const { return m_box->verticalAlign(); } + + const Length& clipLeft() const { return visual->clip.left(); } + const Length& clipRight() const { return visual->clip.right(); } + const Length& clipTop() const { return visual->clip.top(); } + const Length& clipBottom() const { return visual->clip.bottom(); } + const LengthBox& clip() const { return visual->clip; } + bool hasAutoClip() const { return visual->hasAutoClip; } + + EUnicodeBidi unicodeBidi() const { + return static_cast(noninherited_flags.unicodeBidi); + } + + const Font& font() const; + const FontMetrics& fontMetrics() const; + const FontDescription& fontDescription() const; + float specifiedFontSize() const; + float computedFontSize() const; + int fontSize() const; + FontWeight fontWeight() const; + FontStretch fontStretch() const; + + const Length& textIndent() const { return rareInheritedData->indent; } + TextIndentLine textIndentLine() const { + return static_cast(rareInheritedData->m_textIndentLine); + } + TextIndentType textIndentType() const { + return static_cast(rareInheritedData->m_textIndentType); + } + ETextAlign textAlign() const { + return static_cast(inherited_flags._text_align); + } + TextAlignLast textAlignLast() const { + return static_cast(rareInheritedData->m_textAlignLast); + } + TextJustify textJustify() const { + return static_cast(rareInheritedData->m_textJustify); + } + TextDecoration textDecorationsInEffect() const; + const Vector& appliedTextDecorations() const; + TextDecoration textDecoration() const { + return static_cast(visual->textDecoration); + } + TextUnderlinePosition textUnderlinePosition() const { + return static_cast( + rareInheritedData->m_textUnderlinePosition); + } + TextDecorationStyle textDecorationStyle() const { + return static_cast( + rareNonInheritedData->m_textDecorationStyle); + } + float wordSpacing() const; + float letterSpacing() const; + + TextDirection direction() const { + return static_cast(inherited_flags._direction); + } + bool isLeftToRightDirection() const { return direction() == LTR; } + + const Length& specifiedLineHeight() const; + Length lineHeight() const; + int computedLineHeight() const; + + EWhiteSpace whiteSpace() const { + return static_cast(inherited_flags._white_space); + } + static bool autoWrap(EWhiteSpace ws) { + // Nowrap and pre don't automatically wrap. + return ws != NOWRAP && ws != PRE; + } + + bool autoWrap() const { return autoWrap(whiteSpace()); } + + static bool preserveNewline(EWhiteSpace ws) { + // Normal and nowrap do not preserve newlines. + return ws != NORMAL && ws != NOWRAP; + } + + bool preserveNewline() const { return preserveNewline(whiteSpace()); } + + static bool collapseWhiteSpace(EWhiteSpace ws) { + // Pre and prewrap do not collapse whitespace. + return ws != PRE && ws != PRE_WRAP; + } + + bool collapseWhiteSpace() const { return collapseWhiteSpace(whiteSpace()); } + + bool isCollapsibleWhiteSpace(UChar c) const { + switch (c) { + case ' ': + case '\t': + return collapseWhiteSpace(); + case '\n': + return !preserveNewline(); } + return false; + } + + bool breakOnlyAfterWhiteSpace() const { + return whiteSpace() == PRE_WRAP || lineBreak() == LineBreakAfterWhiteSpace; + } + + bool breakWords() const { + return wordBreak() == BreakWordBreak || overflowWrap() == BreakOverflowWrap; + } + + EFillRepeat backgroundRepeatX() const { + return static_cast(m_background->background().repeatX()); + } + EFillRepeat backgroundRepeatY() const { + return static_cast(m_background->background().repeatY()); + } + CompositeOperator backgroundComposite() const { + return static_cast( + m_background->background().composite()); + } + EFillAttachment backgroundAttachment() const { + return static_cast( + m_background->background().attachment()); + } + EFillBox backgroundClip() const { + return static_cast(m_background->background().clip()); + } + EFillBox backgroundOrigin() const { + return static_cast(m_background->background().origin()); + } + const Length& backgroundXPosition() const { + return m_background->background().xPosition(); + } + const Length& backgroundYPosition() const { + return m_background->background().yPosition(); + } + EFillSizeType backgroundSizeType() const { + return m_background->background().sizeType(); + } + const LengthSize& backgroundSizeLength() const { + return m_background->background().sizeLength(); + } + FillLayer& accessBackgroundLayers() { + return m_background.access()->m_background; + } + const FillLayer& backgroundLayers() const { + return m_background->background(); + } + + short horizontalBorderSpacing() const; + short verticalBorderSpacing() const; + + const Length& marginTop() const { return surround->margin.top(); } + const Length& marginBottom() const { return surround->margin.bottom(); } + const Length& marginLeft() const { return surround->margin.left(); } + const Length& marginRight() const { return surround->margin.right(); } + const Length& marginBefore() const { return surround->margin.before(); } + const Length& marginAfter() const { return surround->margin.after(); } + const Length& marginStart() const { + return surround->margin.start(direction()); + } + const Length& marginEnd() const { return surround->margin.end(direction()); } + const Length& marginStartUsing(const RenderStyle* otherStyle) const { + return surround->margin.start(otherStyle->direction()); + } + const Length& marginEndUsing(const RenderStyle* otherStyle) const { + return surround->margin.end(otherStyle->direction()); + } + const Length& marginBeforeUsing(const RenderStyle* otherStyle) const { + return surround->margin.before(); + } + const Length& marginAfterUsing(const RenderStyle* otherStyle) const { + return surround->margin.after(); + } + + const LengthBox& paddingBox() const { return surround->padding; } + const Length& paddingTop() const { return surround->padding.top(); } + const Length& paddingBottom() const { return surround->padding.bottom(); } + const Length& paddingLeft() const { return surround->padding.left(); } + const Length& paddingRight() const { return surround->padding.right(); } + const Length& paddingBefore() const { return surround->padding.before(); } + const Length& paddingAfter() const { return surround->padding.after(); } + const Length& paddingStart() const { + return surround->padding.start(direction()); + } + const Length& paddingEnd() const { + return surround->padding.end(direction()); + } + + bool isLink() const { return noninherited_flags.isLink; } + + // CSS3 Getter Methods + + int outlineOffset() const { + if (m_background->outline().style() == BNONE) + return 0; + return m_background->outline().offset(); + } + + ShadowList* textShadow() const { return rareInheritedData->textShadow.get(); } + void getTextShadowExtent(LayoutUnit& top, + LayoutUnit& right, + LayoutUnit& bottom, + LayoutUnit& left) const { + getShadowExtent(textShadow(), top, right, bottom, left); + } + void getTextShadowHorizontalExtent(LayoutUnit& left, + LayoutUnit& right) const { + getShadowHorizontalExtent(textShadow(), left, right); + } + void getTextShadowVerticalExtent(LayoutUnit& top, LayoutUnit& bottom) const { + getShadowVerticalExtent(textShadow(), top, bottom); + } + void getTextShadowInlineDirectionExtent(LayoutUnit& logicalLeft, + LayoutUnit& logicalRight) { + getShadowInlineDirectionExtent(textShadow(), logicalLeft, logicalRight); + } + void getTextShadowBlockDirectionExtent(LayoutUnit& logicalTop, + LayoutUnit& logicalBottom) { + getShadowBlockDirectionExtent(textShadow(), logicalTop, logicalBottom); + } + + float textStrokeWidth() const { return rareInheritedData->textStrokeWidth; } + float opacity() const { return rareNonInheritedData->opacity; } + bool hasOpacity() const { return opacity() < 1.0f; } + // aspect ratio convenience method + bool hasAspectRatio() const { return rareNonInheritedData->m_hasAspectRatio; } + float aspectRatio() const { + return aspectRatioNumerator() / aspectRatioDenominator(); + } + float aspectRatioDenominator() const { + return rareNonInheritedData->m_aspectRatioDenominator; + } + float aspectRatioNumerator() const { + return rareNonInheritedData->m_aspectRatioNumerator; + } + + int order() const { return rareNonInheritedData->m_order; } + float flexGrow() const { + return rareNonInheritedData->m_flexibleBox->m_flexGrow; + } + float flexShrink() const { + return rareNonInheritedData->m_flexibleBox->m_flexShrink; + } + const Length& flexBasis() const { + return rareNonInheritedData->m_flexibleBox->m_flexBasis; + } + EAlignContent alignContent() const { + return static_cast(rareNonInheritedData->m_alignContent); + } + ItemPosition alignItems() const { + return static_cast(rareNonInheritedData->m_alignItems); + } + OverflowAlignment alignItemsOverflowAlignment() const { + return static_cast( + rareNonInheritedData->m_alignItemsOverflowAlignment); + } + ItemPosition alignSelf() const { + return static_cast(rareNonInheritedData->m_alignSelf); + } + OverflowAlignment alignSelfOverflowAlignment() const { + return static_cast( + rareNonInheritedData->m_alignSelfOverflowAlignment); + } + EFlexDirection flexDirection() const { + return static_cast( + rareNonInheritedData->m_flexibleBox->m_flexDirection); + } + bool isColumnFlexDirection() const { + return flexDirection() == FlowColumn || + flexDirection() == FlowColumnReverse; + } + bool isReverseFlexDirection() const { + return flexDirection() == FlowRowReverse || + flexDirection() == FlowColumnReverse; + } + EFlexWrap flexWrap() const { + return static_cast( + rareNonInheritedData->m_flexibleBox->m_flexWrap); + } + EJustifyContent justifyContent() const { + return static_cast(rareNonInheritedData->m_justifyContent); + } + ItemPosition justifyItems() const { + return static_cast(rareNonInheritedData->m_justifyItems); + } + OverflowAlignment justifyItemsOverflowAlignment() const { + return static_cast( + rareNonInheritedData->m_justifyItemsOverflowAlignment); + } + ItemPositionType justifyItemsPositionType() const { + return static_cast( + rareNonInheritedData->m_justifyItemsPositionType); + } + ItemPosition justifySelf() const { + return static_cast(rareNonInheritedData->m_justifySelf); + } + OverflowAlignment justifySelfOverflowAlignment() const { + return static_cast( + rareNonInheritedData->m_justifySelfOverflowAlignment); + } + + ShadowList* boxShadow() const { + return rareNonInheritedData->m_boxShadow.get(); + } + void getBoxShadowExtent(LayoutUnit& top, + LayoutUnit& right, + LayoutUnit& bottom, + LayoutUnit& left) const { + getShadowExtent(boxShadow(), top, right, bottom, left); + } + LayoutBoxExtent getBoxShadowInsetExtent() const { + return getShadowInsetExtent(boxShadow()); + } + void getBoxShadowHorizontalExtent(LayoutUnit& left, LayoutUnit& right) const { + getShadowHorizontalExtent(boxShadow(), left, right); + } + void getBoxShadowVerticalExtent(LayoutUnit& top, LayoutUnit& bottom) const { + getShadowVerticalExtent(boxShadow(), top, bottom); + } + void getBoxShadowInlineDirectionExtent(LayoutUnit& logicalLeft, + LayoutUnit& logicalRight) { + getShadowInlineDirectionExtent(boxShadow(), logicalLeft, logicalRight); + } + void getBoxShadowBlockDirectionExtent(LayoutUnit& logicalTop, + LayoutUnit& logicalBottom) { + getShadowBlockDirectionExtent(boxShadow(), logicalTop, logicalBottom); + } + + EBoxDecorationBreak boxDecorationBreak() const { + return m_box->boxDecorationBreak(); + } + + // FIXME: reflections should belong to this helper function but they are + // currently handled through their self-painting layers. So the rendering code + // doesn't account for them. + bool hasVisualOverflowingEffect() const { + return boxShadow() || hasOutline(); + } + + EBoxSizing boxSizing() const { return m_box->boxSizing(); } + EUserModify userModify() const { + return static_cast(rareInheritedData->userModify); + } + EUserSelect userSelect() const { + return static_cast(rareInheritedData->userSelect); + } + TextOverflow textOverflow() const { + return static_cast(rareNonInheritedData->textOverflow); + } + EWordBreak wordBreak() const { + return static_cast(rareInheritedData->wordBreak); + } + EOverflowWrap overflowWrap() const { + return static_cast(rareInheritedData->overflowWrap); + } + LineBreak lineBreak() const { + return static_cast(rareInheritedData->lineBreak); + } + const AtomicString& highlight() const { return rareInheritedData->highlight; } + const AtomicString& hyphenationString() const { + return rareInheritedData->hyphenationString; + } + const AtomicString& locale() const { return rareInheritedData->locale; } + const TransformOperations& transform() const { + return rareNonInheritedData->m_transform->m_operations; + } + const Length& transformOriginX() const { + return rareNonInheritedData->m_transform->m_x; + } + const Length& transformOriginY() const { + return rareNonInheritedData->m_transform->m_y; + } + float transformOriginZ() const { + return rareNonInheritedData->m_transform->m_z; + } + bool hasTransform() const { + return !rareNonInheritedData->m_transform->m_operations.operations() + .isEmpty(); + } + bool transformDataEquivalent(const RenderStyle& otherStyle) const { + return rareNonInheritedData->m_transform == + otherStyle.rareNonInheritedData->m_transform; + } + + TextEmphasisFill textEmphasisFill() const { + return static_cast(rareInheritedData->textEmphasisFill); + } + TextEmphasisMark textEmphasisMark() const; + const AtomicString& textEmphasisCustomMark() const { + return rareInheritedData->textEmphasisCustomMark; + } + TextEmphasisPosition textEmphasisPosition() const { + return static_cast( + rareInheritedData->textEmphasisPosition); + } + const AtomicString& textEmphasisMarkString() const; + + TextOrientation textOrientation() const { + return static_cast(rareInheritedData->m_textOrientation); + } + + ObjectFit objectFit() const { + return static_cast(rareNonInheritedData->m_objectFit); + } + LengthPoint objectPosition() const { + return rareNonInheritedData->m_objectPosition; + } + + // Return true if any transform related property (currently transform, + // transformStyle3D or perspective) indicates that we are transforming + bool hasTransformRelatedProperty() const { + return hasTransform() || preserves3D() || hasPerspective(); + } + + enum ApplyTransformOrigin { IncludeTransformOrigin, ExcludeTransformOrigin }; + void applyTransform(TransformationMatrix&, + const LayoutSize& borderBoxSize, + ApplyTransformOrigin = IncludeTransformOrigin) const; + void applyTransform(TransformationMatrix&, + const FloatRect& boundingBox, + ApplyTransformOrigin = IncludeTransformOrigin) const; + + unsigned tabSize() const { return rareInheritedData->m_tabSize; } + + // End CSS3 Getters + + WrapFlow wrapFlow() const { + return static_cast(rareNonInheritedData->m_wrapFlow); + } + WrapThrough wrapThrough() const { + return static_cast(rareNonInheritedData->m_wrapThrough); + } + + // Apple-specific property getter methods + EPointerEvents pointerEvents() const { + return static_cast(inherited_flags._pointerEvents); + } + + ETransformStyle3D transformStyle3D() const { + return static_cast( + rareNonInheritedData->m_transformStyle3D); + } + bool preserves3D() const { + return rareNonInheritedData->m_transformStyle3D == + TransformStyle3DPreserve3D; + } + + float perspective() const { return rareNonInheritedData->m_perspective; } + bool hasPerspective() const { + return rareNonInheritedData->m_perspective > 0; + } + const Length& perspectiveOriginX() const { + return rareNonInheritedData->m_perspectiveOriginX; + } + const Length& perspectiveOriginY() const { + return rareNonInheritedData->m_perspectiveOriginY; + } + + LineBoxContain lineBoxContain() const { + return rareInheritedData->m_lineBoxContain; + } + Color tapHighlightColor() const { + return rareInheritedData->tapHighlightColor; + } + + EImageRendering imageRendering() const { + return static_cast(rareInheritedData->m_imageRendering); + } + + TouchAction touchAction() const { + return static_cast(rareNonInheritedData->m_touchAction); + } + TouchActionDelay touchActionDelay() const { + return static_cast(rareInheritedData->m_touchActionDelay); + } + + // Flutter property getters + const AtomicString& ellipsis() const { + return rareNonInheritedData->m_ellipsis; + } + int maxLines() const { return rareNonInheritedData->m_maxLines; } + + // attribute setter methods + + void setDisplay(EDisplay v) { noninherited_flags.effectiveDisplay = v; } + void setOriginalDisplay(EDisplay v) { + noninherited_flags.originalDisplay = v; + } + void setPosition(EPosition v) { noninherited_flags.position = v; } + + void setLeft(const Length& v) { SET_VAR(surround, offset.m_left, v); } + void setRight(const Length& v) { SET_VAR(surround, offset.m_right, v); } + void setTop(const Length& v) { SET_VAR(surround, offset.m_top, v); } + void setBottom(const Length& v) { SET_VAR(surround, offset.m_bottom, v); } + + void setWidth(const Length& v) { SET_VAR(m_box, m_width, v); } + void setHeight(const Length& v) { SET_VAR(m_box, m_height, v); } + + void setLogicalWidth(const Length& v) { + // FIXME(sky): Remove + SET_VAR(m_box, m_width, v); + } - bool isDisplayInlineType(EDisplay display) const - { - return display == INLINE || isDisplayReplacedType(display); + void setLogicalHeight(const Length& v) { + // FIXME(sky): Remove + SET_VAR(m_box, m_height, v); + } + + void setMinWidth(const Length& v) { SET_VAR(m_box, m_minWidth, v); } + void setMaxWidth(const Length& v) { SET_VAR(m_box, m_maxWidth, v); } + void setMinHeight(const Length& v) { SET_VAR(m_box, m_minHeight, v); } + void setMaxHeight(const Length& v) { SET_VAR(m_box, m_maxHeight, v); } + + void resetBorder() { + resetBorderTop(); + resetBorderRight(); + resetBorderBottom(); + resetBorderLeft(); + resetBorderTopLeftRadius(); + resetBorderTopRightRadius(); + resetBorderBottomLeftRadius(); + resetBorderBottomRightRadius(); + } + void resetBorderTop() { SET_VAR(surround, border.m_top, BorderValue()); } + void resetBorderRight() { SET_VAR(surround, border.m_right, BorderValue()); } + void resetBorderBottom() { + SET_VAR(surround, border.m_bottom, BorderValue()); + } + void resetBorderLeft() { SET_VAR(surround, border.m_left, BorderValue()); } + void resetBorderTopLeftRadius() { + SET_VAR(surround, border.m_topLeft, initialBorderRadius()); + } + void resetBorderTopRightRadius() { + SET_VAR(surround, border.m_topRight, initialBorderRadius()); + } + void resetBorderBottomLeftRadius() { + SET_VAR(surround, border.m_bottomLeft, initialBorderRadius()); + } + void resetBorderBottomRightRadius() { + SET_VAR(surround, border.m_bottomRight, initialBorderRadius()); + } + + void setBackgroundColor(const StyleColor& v) { + SET_VAR(m_background, m_color, v); + } + + void setBackgroundXPosition(const Length& length) { + SET_VAR(m_background, m_background.m_xPosition, length); + } + void setBackgroundYPosition(const Length& length) { + SET_VAR(m_background, m_background.m_yPosition, length); + } + void setBackgroundSize(EFillSizeType b) { + SET_VAR(m_background, m_background.m_sizeType, b); + } + void setBackgroundSizeLength(const LengthSize& s) { + SET_VAR(m_background, m_background.m_sizeLength, s); + } + + void setBorderTopLeftRadius(const LengthSize& s) { + SET_VAR(surround, border.m_topLeft, s); + } + void setBorderTopRightRadius(const LengthSize& s) { + SET_VAR(surround, border.m_topRight, s); + } + void setBorderBottomLeftRadius(const LengthSize& s) { + SET_VAR(surround, border.m_bottomLeft, s); + } + void setBorderBottomRightRadius(const LengthSize& s) { + SET_VAR(surround, border.m_bottomRight, s); + } + + void setBorderRadius(const LengthSize& s) { + setBorderTopLeftRadius(s); + setBorderTopRightRadius(s); + setBorderBottomLeftRadius(s); + setBorderBottomRightRadius(s); + } + void setBorderRadius(const IntSize& s) { + setBorderRadius( + LengthSize(Length(s.width(), Fixed), Length(s.height(), Fixed))); + } + + RoundedRect getRoundedBorderFor(const LayoutRect& borderRect, + bool includeLogicalLeftEdge = true, + bool includeLogicalRightEdge = true) const; + RoundedRect getRoundedInnerBorderFor( + const LayoutRect& borderRect, + bool includeLogicalLeftEdge = true, + bool includeLogicalRightEdge = true) const; + + RoundedRect getRoundedInnerBorderFor(const LayoutRect& borderRect, + int topWidth, + int bottomWidth, + int leftWidth, + int rightWidth, + bool includeLogicalLeftEdge, + bool includeLogicalRightEdge) const; + + void setBorderLeftWidth(unsigned v) { + SET_VAR(surround, border.m_left.m_width, v); + } + void setBorderLeftStyle(EBorderStyle v) { + SET_VAR(surround, border.m_left.m_style, v); + } + void setBorderLeftColor(const StyleColor& v) { + SET_BORDERVALUE_COLOR(surround, border.m_left, v); + } + void setBorderRightWidth(unsigned v) { + SET_VAR(surround, border.m_right.m_width, v); + } + void setBorderRightStyle(EBorderStyle v) { + SET_VAR(surround, border.m_right.m_style, v); + } + void setBorderRightColor(const StyleColor& v) { + SET_BORDERVALUE_COLOR(surround, border.m_right, v); + } + void setBorderTopWidth(unsigned v) { + SET_VAR(surround, border.m_top.m_width, v); + } + void setBorderTopStyle(EBorderStyle v) { + SET_VAR(surround, border.m_top.m_style, v); + } + void setBorderTopColor(const StyleColor& v) { + SET_BORDERVALUE_COLOR(surround, border.m_top, v); + } + void setBorderBottomWidth(unsigned v) { + SET_VAR(surround, border.m_bottom.m_width, v); + } + void setBorderBottomStyle(EBorderStyle v) { + SET_VAR(surround, border.m_bottom.m_style, v); + } + void setBorderBottomColor(const StyleColor& v) { + SET_BORDERVALUE_COLOR(surround, border.m_bottom, v); + } + + void setOutlineWidth(unsigned short v) { + SET_VAR(m_background, m_outline.m_width, v); + } + void setOutlineStyleIsAuto(OutlineIsAuto isAuto) { + SET_VAR(m_background, m_outline.m_isAuto, isAuto); + } + void setOutlineStyle(EBorderStyle v) { + SET_VAR(m_background, m_outline.m_style, v); + } + void setOutlineColor(const StyleColor& v) { + SET_BORDERVALUE_COLOR(m_background, m_outline, v); + } + + void setOverflowX(EOverflow v) { noninherited_flags.overflowX = v; } + void setOverflowY(EOverflow v) { noninherited_flags.overflowY = v; } + void setVisibility(EVisibility v) { inherited_flags._visibility = v; } + void setVerticalAlign(EVerticalAlign v) { + noninherited_flags.verticalAlign = v; + } + void setVerticalAlignLength(const Length& length) { + setVerticalAlign(LENGTH); + SET_VAR(m_box, m_verticalAlign, length); + } + + void setHasAutoClip() { + SET_VAR(visual, hasAutoClip, true); + SET_VAR(visual, clip, RenderStyle::initialClip()); + } + void setClip(const LengthBox& box) { + SET_VAR(visual, hasAutoClip, false); + SET_VAR(visual, clip, box); + } + + void setUnicodeBidi(EUnicodeBidi b) { noninherited_flags.unicodeBidi = b; } + + bool setFontDescription(const FontDescription&); + // Only used for text autosizing. + void setFontSize(float); + void setFontStretch(FontStretch); + void setFontWeight(FontWeight); + + void setColor(const Color&); + void setTextIndent(const Length& v) { SET_VAR(rareInheritedData, indent, v); } + void setTextIndentLine(TextIndentLine v) { + SET_VAR(rareInheritedData, m_textIndentLine, v); + } + void setTextIndentType(TextIndentType v) { + SET_VAR(rareInheritedData, m_textIndentType, v); + } + void setTextAlign(ETextAlign v) { inherited_flags._text_align = v; } + void setTextAlignLast(TextAlignLast v) { + SET_VAR(rareInheritedData, m_textAlignLast, v); + } + void setTextJustify(TextJustify v) { + SET_VAR(rareInheritedData, m_textJustify, v); + } + void applyTextDecorations(); + void clearAppliedTextDecorations(); + void setTextDecoration(TextDecoration v) { + SET_VAR(visual, textDecoration, v); + } + void setTextUnderlinePosition(TextUnderlinePosition v) { + SET_VAR(rareInheritedData, m_textUnderlinePosition, v); + } + void setTextDecorationStyle(TextDecorationStyle v) { + SET_VAR(rareNonInheritedData, m_textDecorationStyle, v); + } + void setDirection(TextDirection v) { inherited_flags._direction = v; } + void setLineHeight(const Length& specifiedLineHeight); + + void setImageRendering(EImageRendering v) { + SET_VAR(rareInheritedData, m_imageRendering, v); + } + + void setWhiteSpace(EWhiteSpace v) { inherited_flags._white_space = v; } + + // FIXME: Remove these two and replace them with respective FontBuilder calls. + void setWordSpacing(float); + void setLetterSpacing(float); + + void adjustBackgroundLayers() { + if (backgroundLayers().next()) { + accessBackgroundLayers().cullEmptyLayers(); + accessBackgroundLayers().fillUnsetProperties(); } - - StyleColor textDecorationColor() const { return rareNonInheritedData->m_textDecorationColor; } - - void addAppliedTextDecoration(const AppliedTextDecoration&); - - bool diffNeedsFullLayout(const RenderStyle& other) const; - bool diffNeedsRecompositeLayer(const RenderStyle& other) const; - void updatePropertySpecificDifferences(const RenderStyle& other, StyleDifference&) const; + } + + void setHorizontalBorderSpacing(short); + void setVerticalBorderSpacing(short); + + void setHasAspectRatio(bool b) { + SET_VAR(rareNonInheritedData, m_hasAspectRatio, b); + } + void setAspectRatioDenominator(float v) { + SET_VAR(rareNonInheritedData, m_aspectRatioDenominator, v); + } + void setAspectRatioNumerator(float v) { + SET_VAR(rareNonInheritedData, m_aspectRatioNumerator, v); + } + + void setMarginTop(const Length& v) { SET_VAR(surround, margin.m_top, v); } + void setMarginBottom(const Length& v) { + SET_VAR(surround, margin.m_bottom, v); + } + void setMarginLeft(const Length& v) { SET_VAR(surround, margin.m_left, v); } + void setMarginRight(const Length& v) { SET_VAR(surround, margin.m_right, v); } + void setMarginStart(const Length&); + void setMarginEnd(const Length&); + + void resetPadding() { SET_VAR(surround, padding, LengthBox(Auto)); } + void setPaddingBox(const LengthBox& b) { SET_VAR(surround, padding, b); } + void setPaddingTop(const Length& v) { SET_VAR(surround, padding.m_top, v); } + void setPaddingBottom(const Length& v) { + SET_VAR(surround, padding.m_bottom, v); + } + void setPaddingLeft(const Length& v) { SET_VAR(surround, padding.m_left, v); } + void setPaddingRight(const Length& v) { + SET_VAR(surround, padding.m_right, v); + } + + void setIsLink(bool b) { noninherited_flags.isLink = b; } + + bool hasAutoZIndex() const { return m_box->hasAutoZIndex(); } + void setHasAutoZIndex() { + SET_VAR(m_box, m_hasAutoZIndex, true); + SET_VAR(m_box, m_zIndex, 0); + } + unsigned zIndex() const { return m_box->zIndex(); } + void setZIndex(unsigned v) { + SET_VAR(m_box, m_hasAutoZIndex, false); + SET_VAR(m_box, m_zIndex, v); + } + + // CSS3 Setters + void setOutlineOffset(int v) { SET_VAR(m_background, m_outline.m_offset, v); } + void setTextShadow(PassRefPtr); + void setTextStrokeColor(const StyleColor& c) { + SET_VAR_WITH_SETTER(rareInheritedData, textStrokeColor, setTextStrokeColor, + c); + } + void setTextStrokeWidth(float w) { + SET_VAR(rareInheritedData, textStrokeWidth, w); + } + void setTextFillColor(const StyleColor& c) { + SET_VAR_WITH_SETTER(rareInheritedData, textFillColor, setTextFillColor, c); + } + void setOpacity(float f) { + float v = clampTo(f, 0, 1); + SET_VAR(rareNonInheritedData, opacity, v); + } + // For valid values of box-align see + // http://www.w3.org/TR/2009/WD-css3-flexbox-20090723/#alignment + void setBoxDecorationBreak(EBoxDecorationBreak b) { + SET_VAR(m_box, m_boxDecorationBreak, b); + } + void setBoxShadow(PassRefPtr); + void setBoxSizing(EBoxSizing s) { SET_VAR(m_box, m_boxSizing, s); } + void setFlexGrow(float f) { + SET_VAR(rareNonInheritedData.access()->m_flexibleBox, m_flexGrow, f); + } + void setFlexShrink(float f) { + SET_VAR(rareNonInheritedData.access()->m_flexibleBox, m_flexShrink, f); + } + void setFlexBasis(const Length& length) { + SET_VAR(rareNonInheritedData.access()->m_flexibleBox, m_flexBasis, length); + } + // We restrict the smallest value to int min + 2 because we use int min and + // int min + 1 as special values in a hash set. + void setOrder(int o) { + SET_VAR(rareNonInheritedData, m_order, + max(std::numeric_limits::min() + 2, o)); + } + void setAlignContent(EAlignContent p) { + SET_VAR(rareNonInheritedData, m_alignContent, p); + } + void setAlignItems(ItemPosition a) { + SET_VAR(rareNonInheritedData, m_alignItems, a); + } + void setAlignItemsOverflowAlignment(OverflowAlignment overflowAlignment) { + SET_VAR(rareNonInheritedData, m_alignItemsOverflowAlignment, + overflowAlignment); + } + void setAlignSelf(ItemPosition a) { + SET_VAR(rareNonInheritedData, m_alignSelf, a); + } + void setAlignSelfOverflowAlignment(OverflowAlignment overflowAlignment) { + SET_VAR(rareNonInheritedData, m_alignSelfOverflowAlignment, + overflowAlignment); + } + void setFlexDirection(EFlexDirection direction) { + SET_VAR(rareNonInheritedData.access()->m_flexibleBox, m_flexDirection, + direction); + } + void setFlexWrap(EFlexWrap w) { + SET_VAR(rareNonInheritedData.access()->m_flexibleBox, m_flexWrap, w); + } + void setJustifyContent(EJustifyContent p) { + SET_VAR(rareNonInheritedData, m_justifyContent, p); + } + void setJustifyItems(ItemPosition justifyItems) { + SET_VAR(rareNonInheritedData, m_justifyItems, justifyItems); + } + void setJustifyItemsOverflowAlignment(OverflowAlignment overflowAlignment) { + SET_VAR(rareNonInheritedData, m_justifyItemsOverflowAlignment, + overflowAlignment); + } + void setJustifyItemsPositionType(ItemPositionType positionType) { + SET_VAR(rareNonInheritedData, m_justifyItemsPositionType, positionType); + } + void setJustifySelf(ItemPosition justifySelf) { + SET_VAR(rareNonInheritedData, m_justifySelf, justifySelf); + } + void setJustifySelfOverflowAlignment(OverflowAlignment overflowAlignment) { + SET_VAR(rareNonInheritedData, m_justifySelfOverflowAlignment, + overflowAlignment); + } + + void setUserModify(EUserModify u) { + SET_VAR(rareInheritedData, userModify, u); + } + void setUserSelect(EUserSelect s) { + SET_VAR(rareInheritedData, userSelect, s); + } + void setTextOverflow(TextOverflow overflow) { + SET_VAR(rareNonInheritedData, textOverflow, overflow); + } + void setWordBreak(EWordBreak b) { SET_VAR(rareInheritedData, wordBreak, b); } + void setOverflowWrap(EOverflowWrap b) { + SET_VAR(rareInheritedData, overflowWrap, b); + } + void setLineBreak(LineBreak b) { SET_VAR(rareInheritedData, lineBreak, b); } + void setHighlight(const AtomicString& h) { + SET_VAR(rareInheritedData, highlight, h); + } + void setHyphens(Hyphens h) { SET_VAR(rareInheritedData, hyphens, h); } + void setHyphenationString(const AtomicString& h) { + SET_VAR(rareInheritedData, hyphenationString, h); + } + void setLocale(const AtomicString& locale) { + SET_VAR(rareInheritedData, locale, locale); + } + void setTransform(const TransformOperations& ops) { + SET_VAR(rareNonInheritedData.access()->m_transform, m_operations, ops); + } + void setTransformOriginX(const Length& l) { + SET_VAR(rareNonInheritedData.access()->m_transform, m_x, l); + } + void setTransformOriginY(const Length& l) { + SET_VAR(rareNonInheritedData.access()->m_transform, m_y, l); + } + void setTransformOriginZ(float f) { + SET_VAR(rareNonInheritedData.access()->m_transform, m_z, f); + } + void setTextDecorationColor(const StyleColor& c) { + SET_VAR(rareNonInheritedData, m_textDecorationColor, c); + } + void setTextEmphasisColor(const StyleColor& c) { + SET_VAR_WITH_SETTER(rareInheritedData, textEmphasisColor, + setTextEmphasisColor, c); + } + void setTextEmphasisFill(TextEmphasisFill fill) { + SET_VAR(rareInheritedData, textEmphasisFill, fill); + } + void setTextEmphasisMark(TextEmphasisMark mark) { + SET_VAR(rareInheritedData, textEmphasisMark, mark); + } + void setTextEmphasisCustomMark(const AtomicString& mark) { + SET_VAR(rareInheritedData, textEmphasisCustomMark, mark); + } + void setTextEmphasisPosition(TextEmphasisPosition position) { + SET_VAR(rareInheritedData, textEmphasisPosition, position); + } + bool setTextOrientation(TextOrientation); + + void setObjectFit(ObjectFit f) { + SET_VAR(rareNonInheritedData, m_objectFit, f); + } + void setObjectPosition(LengthPoint position) { + SET_VAR(rareNonInheritedData, m_objectPosition, position); + } + + void setTabSize(unsigned size) { + SET_VAR(rareInheritedData, m_tabSize, size); + } + + // End CSS3 Setters + + void setWrapFlow(WrapFlow wrapFlow) { + SET_VAR(rareNonInheritedData, m_wrapFlow, wrapFlow); + } + void setWrapThrough(WrapThrough wrapThrough) { + SET_VAR(rareNonInheritedData, m_wrapThrough, wrapThrough); + } + + // Apple-specific property setters + void setPointerEvents(EPointerEvents p) { + inherited_flags._pointerEvents = p; + } + + void setTransformStyle3D(ETransformStyle3D b) { + SET_VAR(rareNonInheritedData, m_transformStyle3D, b); + } + void setPerspective(float p) { + SET_VAR(rareNonInheritedData, m_perspective, p); + } + void setPerspectiveOriginX(const Length& l) { + SET_VAR(rareNonInheritedData, m_perspectiveOriginX, l); + } + void setPerspectiveOriginY(const Length& l) { + SET_VAR(rareNonInheritedData, m_perspectiveOriginY, l); + } + + void setLineBoxContain(LineBoxContain c) { + SET_VAR(rareInheritedData, m_lineBoxContain, c); + } + void setTapHighlightColor(const Color& c) { + SET_VAR(rareInheritedData, tapHighlightColor, c); + } + void setTouchAction(TouchAction t) { + SET_VAR(rareNonInheritedData, m_touchAction, t); + } + void setTouchActionDelay(TouchActionDelay t) { + SET_VAR(rareInheritedData, m_touchActionDelay, t); + } + + void setClipPath(PassRefPtr operation) { + if (rareNonInheritedData->m_clipPath != operation) + rareNonInheritedData.access()->m_clipPath = operation; + } + ClipPathOperation* clipPath() const { + return rareNonInheritedData->m_clipPath.get(); + } + + // Flutter property setters + void setEllipsis(const AtomicString& e) { + SET_VAR(rareNonInheritedData, m_ellipsis, e); + } + void setMaxLines(int m) { SET_VAR(rareNonInheritedData, m_maxLines, m); } + + static ClipPathOperation* initialClipPath() { return 0; } + + const CounterDirectiveMap* counterDirectives() const; + CounterDirectiveMap& accessCounterDirectives(); + const CounterDirectives getCounterDirectives( + const AtomicString& identifier) const; + + const AtomicString& hyphenString() const; + + bool inheritedNotEqual(const RenderStyle*) const; + bool inheritedDataShared(const RenderStyle*) const; + + bool requiresOnlyBlockChildren() const; + bool isDisplayReplacedType() const { + return isDisplayReplacedType(display()); + } + bool isDisplayInlineType() const { return isDisplayInlineType(display()); } + bool isOriginalDisplayInlineType() const { + return isDisplayInlineType(originalDisplay()); + } + bool isDisplayFlexibleBox() const { return isDisplayFlexibleBox(display()); } + + // A unique style is one that has matches something that makes it impossible + // to share. + bool unique() const { return noninherited_flags.unique; } + void setUnique() { noninherited_flags.unique = true; } + + bool isSharable() const; + + bool emptyState() const { return noninherited_flags.emptyState; } + void setEmptyState(bool b) { + setUnique(); + noninherited_flags.emptyState = b; + } + bool firstChildState() const { return noninherited_flags.firstChildState; } + void setFirstChildState() { + setUnique(); + noninherited_flags.firstChildState = true; + } + bool lastChildState() const { return noninherited_flags.lastChildState; } + void setLastChildState() { + setUnique(); + noninherited_flags.lastChildState = true; + } + + StyleColor decorationStyleColor() const; + Color decorationColor() const; + + void setHasExplicitlyInheritedProperties() { + noninherited_flags.explicitInheritance = true; + } + bool hasExplicitlyInheritedProperties() const { + return noninherited_flags.explicitInheritance; + } + + void setHasCurrentColor() { noninherited_flags.currentColor = true; } + bool hasCurrentColor() const { return noninherited_flags.currentColor; } + + bool hasBoxDecorations() const { + return hasBorder() || hasBorderRadius() || hasOutline() || boxShadow(); + } + + // Initial values for all the properties + static EBorderStyle initialBorderStyle() { return BNONE; } + static OutlineIsAuto initialOutlineStyleIsAuto() { return AUTO_OFF; } + static LengthSize initialBorderRadius() { + return LengthSize(Length(0, Fixed), Length(0, Fixed)); + } + static LengthBox initialClip() { return LengthBox(); } + static TextDirection initialDirection() { return LTR; } + static TextOrientation initialTextOrientation() { + return TextOrientationVerticalRight; + } + static ObjectFit initialObjectFit() { return ObjectFitFill; } + static LengthPoint initialObjectPosition() { + return LengthPoint(Length(50.0, Percent), Length(50.0, Percent)); + } + static EDisplay initialDisplay() { return FLEX; } + static EOverflow initialOverflowX() { return OVISIBLE; } + static EOverflow initialOverflowY() { return OVISIBLE; } + static EPosition initialPosition() { return StaticPosition; } + static EUnicodeBidi initialUnicodeBidi() { return UBNormal; } + static EVisibility initialVisibility() { return VISIBLE; } + static EWhiteSpace initialWhiteSpace() { return PRE_WRAP; } + static short initialHorizontalBorderSpacing() { return 0; } + static short initialVerticalBorderSpacing() { return 0; } + static Color initialColor() { return Color::white; } + static unsigned initialBorderWidth() { return 3; } + static unsigned short initialColumnRuleWidth() { return 3; } + static unsigned short initialOutlineWidth() { return 3; } + static float initialLetterWordSpacing() { return 0.0f; } + static Length initialSize() { return Length(); } + static Length initialMinSize() { return Length(Fixed); } + static Length initialMaxSize() { return Length(MaxSizeNone); } + static Length initialOffset() { return Length(); } + static Length initialMargin() { return Length(Fixed); } + static Length initialPadding() { return Length(Fixed); } + static Length initialTextIndent() { return Length(Fixed); } + static TextIndentLine initialTextIndentLine() { return TextIndentFirstLine; } + static TextIndentType initialTextIndentType() { return TextIndentNormal; } + static EVerticalAlign initialVerticalAlign() { return BASELINE; } + static Length initialLineHeight() { return Length(-100.0, Percent); } + static ETextAlign initialTextAlign() { return TASTART; } + static TextAlignLast initialTextAlignLast() { return TextAlignLastAuto; } + static TextJustify initialTextJustify() { return TextJustifyAuto; } + static TextDecoration initialTextDecoration() { return TextDecorationNone; } + static TextUnderlinePosition initialTextUnderlinePosition() { + return TextUnderlinePositionAuto; + } + static TextDecorationStyle initialTextDecorationStyle() { + return TextDecorationStyleSolid; + } + static int initialOutlineOffset() { return 0; } + static float initialOpacity() { return 1.0f; } + static EBoxAlignment initialBoxAlign() { return BSTRETCH; } + static EBoxDecorationBreak initialBoxDecorationBreak() { return DSLICE; } + static EBoxDirection initialBoxDirection() { return BNORMAL; } + static EBoxLines initialBoxLines() { return SINGLE; } + static EBoxOrient initialBoxOrient() { return HORIZONTAL; } + static EBoxPack initialBoxPack() { return Start; } + static float initialBoxFlex() { return 0.0f; } + static unsigned initialBoxFlexGroup() { return 1; } + static unsigned initialBoxOrdinalGroup() { return 1; } + static EBoxSizing initialBoxSizing() { return CONTENT_BOX; } + static float initialFlexGrow() { return 0; } + static float initialFlexShrink() { return 0; } + static Length initialFlexBasis() { return Length(Auto); } + static int initialOrder() { return 0; } + static EAlignContent initialAlignContent() { return AlignContentStretch; } + static ItemPosition initialAlignItems() { return ItemPositionStretch; } + static OverflowAlignment initialAlignItemsOverflowAlignment() { + return OverflowAlignmentDefault; + } + static ItemPosition initialAlignSelf() { return ItemPositionAuto; } + static OverflowAlignment initialAlignSelfOverflowAlignment() { + return OverflowAlignmentDefault; + } + static EFlexDirection initialFlexDirection() { return FlowColumn; } + static EFlexWrap initialFlexWrap() { return FlexNoWrap; } + static EJustifyContent initialJustifyContent() { return JustifyFlexStart; } + static ItemPosition initialJustifyItems() { return ItemPositionAuto; } + static OverflowAlignment initialJustifyItemsOverflowAlignment() { + return OverflowAlignmentDefault; + } + static ItemPositionType initialJustifyItemsPositionType() { + return NonLegacyPosition; + } + static ItemPosition initialJustifySelf() { return ItemPositionAuto; } + static OverflowAlignment initialJustifySelfOverflowAlignment() { + return OverflowAlignmentDefault; + } + static EUserModify initialUserModify() { return READ_ONLY; } + static EUserSelect initialUserSelect() { return SELECT_TEXT; } + static TextOverflow initialTextOverflow() { return TextOverflowClip; } + static EWordBreak initialWordBreak() { return NormalWordBreak; } + static EOverflowWrap initialOverflowWrap() { return BreakOverflowWrap; } + static LineBreak initialLineBreak() { return LineBreakAuto; } + static const AtomicString& initialHighlight() { return nullAtom; } + static const AtomicString& initialHyphenationString() { return nullAtom; } + static const AtomicString& initialLocale() { return nullAtom; } + static bool initialHasAspectRatio() { return false; } + static float initialAspectRatioDenominator() { return 1; } + static float initialAspectRatioNumerator() { return 1; } + static Order initialRTLOrdering() { return LogicalOrder; } + static float initialTextStrokeWidth() { return 0; } + static unsigned short initialColumnCount() { return 1; } + static ColumnFill initialColumnFill() { return ColumnFillBalance; } + static const TransformOperations& initialTransform() { + DEFINE_STATIC_LOCAL(TransformOperations, ops, ()); + return ops; + } + static Length initialTransformOriginX() { return Length(50.0, Percent); } + static Length initialTransformOriginY() { return Length(50.0, Percent); } + static EPointerEvents initialPointerEvents() { return PE_AUTO; } + static float initialTransformOriginZ() { return 0; } + static ETransformStyle3D initialTransformStyle3D() { + return TransformStyle3DFlat; + } + static float initialPerspective() { return 0; } + static Length initialPerspectiveOriginX() { return Length(50.0, Percent); } + static Length initialPerspectiveOriginY() { return Length(50.0, Percent); } + static Color initialBackgroundColor() { return Color::transparent; } + static TextEmphasisFill initialTextEmphasisFill() { + return TextEmphasisFillFilled; + } + static TextEmphasisMark initialTextEmphasisMark() { + return TextEmphasisMarkNone; + } + static const AtomicString& initialTextEmphasisCustomMark() { + return nullAtom; + } + static TextEmphasisPosition initialTextEmphasisPosition() { + return TextEmphasisPositionOver; + } + static LineBoxContain initialLineBoxContain() { + return LineBoxContainBlock | LineBoxContainInline | LineBoxContainReplaced; + } + static ImageOrientationEnum initialImageOrientation() { + return OriginTopLeft; + } + static EImageRendering initialImageRendering() { return ImageRenderingAuto; } + static ImageResolutionSource initialImageResolutionSource() { + return ImageResolutionSpecified; + } + static ImageResolutionSnap initialImageResolutionSnap() { + return ImageResolutionNoSnap; + } + static float initialImageResolution() { return 1; } + static TouchAction initialTouchAction() { return TouchActionAuto; } + static TouchActionDelay initialTouchActionDelay() { + return TouchActionDelayScript; + } + static ShadowList* initialBoxShadow() { return 0; } + static ShadowList* initialTextShadow() { return 0; } + + static unsigned initialTabSize() { return 8; } + + static WrapFlow initialWrapFlow() { return WrapFlowAuto; } + static WrapThrough initialWrapThrough() { return WrapThroughWrap; } + + // Keep these at the end. + // FIXME: Why? Seems these should all be one big sorted list. + static Color initialTapHighlightColor(); + + Color resolveColor(StyleColor unresolvedColor) const { + return unresolvedColor.resolve(color()); + } + + StyleColor borderLeftColor() const { return surround->border.left().color(); } + StyleColor borderRightColor() const { + return surround->border.right().color(); + } + StyleColor borderTopColor() const { return surround->border.top().color(); } + StyleColor borderBottomColor() const { + return surround->border.bottom().color(); + } + StyleColor backgroundColor() const { return m_background->color(); } + Color color() const; + StyleColor textEmphasisColor() const { + return rareInheritedData->textEmphasisColor(); + } + StyleColor textFillColor() const { + return rareInheritedData->textFillColor(); + } + StyleColor textStrokeColor() const { + return rareInheritedData->textStrokeColor(); + } + + private: + void inheritUnicodeBidiFrom(const RenderStyle* parent) { + noninherited_flags.unicodeBidi = parent->noninherited_flags.unicodeBidi; + } + void getShadowExtent(const ShadowList*, + LayoutUnit& top, + LayoutUnit& right, + LayoutUnit& bottom, + LayoutUnit& left) const; + LayoutBoxExtent getShadowInsetExtent(const ShadowList*) const; + void getShadowHorizontalExtent(const ShadowList*, + LayoutUnit& left, + LayoutUnit& right) const; + void getShadowVerticalExtent(const ShadowList*, + LayoutUnit& top, + LayoutUnit& bottom) const; + void getShadowInlineDirectionExtent(const ShadowList* shadow, + LayoutUnit& logicalLeft, + LayoutUnit& logicalRight) const { + return getShadowHorizontalExtent(shadow, logicalLeft, logicalRight); + } + void getShadowBlockDirectionExtent(const ShadowList* shadow, + LayoutUnit& logicalTop, + LayoutUnit& logicalBottom) const { + return getShadowVerticalExtent(shadow, logicalTop, logicalBottom); + } + + bool isDisplayFlexibleBox(EDisplay display) const { + return display == FLEX || display == INLINE_FLEX; + } + + bool isDisplayReplacedType(EDisplay display) const { + return display == INLINE_FLEX; + } + + bool isDisplayInlineType(EDisplay display) const { + return display == INLINE || isDisplayReplacedType(display); + } + + StyleColor textDecorationColor() const { + return rareNonInheritedData->m_textDecorationColor; + } + + void addAppliedTextDecoration(const AppliedTextDecoration&); + + bool diffNeedsFullLayout(const RenderStyle& other) const; + bool diffNeedsRecompositeLayer(const RenderStyle& other) const; + void updatePropertySpecificDifferences(const RenderStyle& other, + StyleDifference&) const; }; -inline bool RenderStyle::isSharable() const -{ - return !unique(); +inline bool RenderStyle::isSharable() const { + return !unique(); } -inline bool RenderStyle::setTextOrientation(TextOrientation textOrientation) -{ - if (compareEqual(rareInheritedData->m_textOrientation, textOrientation)) - return false; +inline bool RenderStyle::setTextOrientation(TextOrientation textOrientation) { + if (compareEqual(rareInheritedData->m_textOrientation, textOrientation)) + return false; - rareInheritedData.access()->m_textOrientation = textOrientation; - return true; + rareInheritedData.access()->m_textOrientation = textOrientation; + return true; } -float calcBorderRadiiConstraintScaleFor(const FloatRect&, const FloatRoundedRect::Radii&); +float calcBorderRadiiConstraintScaleFor(const FloatRect&, + const FloatRoundedRect::Radii&); -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_STYLE_RENDERSTYLE_H_ diff --git a/sky/engine/core/rendering/style/RenderStyleConstants.h b/sky/engine/core/rendering/style/RenderStyleConstants.h index 3d57288a9b275..636e9cd6e1aea 100644 --- a/sky/engine/core/rendering/style/RenderStyleConstants.h +++ b/sky/engine/core/rendering/style/RenderStyleConstants.h @@ -2,9 +2,11 @@ * Copyright (C) 2000 Lars Knoll (knoll@kde.org) * (C) 2000 Antti Koivisto (koivisto@kde.org) * (C) 2000 Dirk Mueller (mueller@kde.org) - * Copyright (C) 2003, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. + * All rights reserved. * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.com) - * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) + * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. + * (http://www.torchmobile.com/) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -31,27 +33,47 @@ namespace blink { enum StyleRecalcChange { - NoChange, - NoInherit, - Inherit, - Force, - Reattach, - ReattachNoRenderer + NoChange, + NoInherit, + Inherit, + Force, + Reattach, + ReattachNoRenderer }; enum ColumnFill { ColumnFillBalance, ColumnFillAuto }; -// These have been defined in the order of their precedence for border-collapsing. Do -// not change this order! This order also must match the order in CSSValueKeywords.in. -enum EBorderStyle { BNONE, BHIDDEN, INSET, GROOVE, OUTSET, RIDGE, DOTTED, DASHED, SOLID, DOUBLE }; +// These have been defined in the order of their precedence for +// border-collapsing. Do not change this order! This order also must match the +// order in CSSValueKeywords.in. +enum EBorderStyle { + BNONE, + BHIDDEN, + INSET, + GROOVE, + OUTSET, + RIDGE, + DOTTED, + DASHED, + SOLID, + DOUBLE +}; -enum EBorderPrecedence { BOFF, BTABLE, BCOLGROUP, BCOL, BROWGROUP, BROW, BCELL }; +enum EBorderPrecedence { + BOFF, + BTABLE, + BCOLGROUP, + BCOL, + BROWGROUP, + BROW, + BCELL +}; enum OutlineIsAuto { AUTO_OFF = 0, AUTO_ON }; enum EPosition { - StaticPosition = 0, - AbsolutePosition = 1, + StaticPosition = 0, + AbsolutePosition = 1, }; // Box decoration attributes. Not inherited. @@ -64,31 +86,29 @@ enum EBoxSizing { CONTENT_BOX, BORDER_BOX }; // Random visual rendering model attributes. Not inherited. -enum EOverflow { - OVISIBLE, OHIDDEN, OAUTO, OOVERLAY, OPAGEDX, OPAGEDY -}; +enum EOverflow { OVISIBLE, OHIDDEN, OAUTO, OOVERLAY, OPAGEDX, OPAGEDY }; enum EVerticalAlign { - BASELINE, MIDDLE, SUB, SUPER, TEXT_TOP, - TEXT_BOTTOM, TOP, BOTTOM, BASELINE_MIDDLE, LENGTH + BASELINE, + MIDDLE, + SUB, + SUPER, + TEXT_TOP, + TEXT_BOTTOM, + TOP, + BOTTOM, + BASELINE_MIDDLE, + LENGTH }; -enum EFillAttachment { - LocalBackgroundAttachment, FixedBackgroundAttachment -}; +enum EFillAttachment { LocalBackgroundAttachment, FixedBackgroundAttachment }; -enum EFillBox { - BorderFillBox, PaddingFillBox, ContentFillBox -}; +enum EFillBox { BorderFillBox, PaddingFillBox, ContentFillBox }; -enum EFillRepeat { - RepeatFill, NoRepeatFill, RoundFill, SpaceFill -}; +enum EFillRepeat { RepeatFill, NoRepeatFill, RoundFill, SpaceFill }; // FIXME(sky): Remove this enum. -enum EFillLayerType { - BackgroundFillLayer -}; +enum EFillLayerType { BackgroundFillLayer }; // CSS3 Background Values enum EFillSizeType { Contain, Cover, SizeLength, SizeNone }; @@ -106,191 +126,277 @@ enum EBoxDirection { BNORMAL, BREVERSE }; // CSS3 Flexbox Properties -enum EAlignContent { AlignContentFlexStart, AlignContentFlexEnd, AlignContentCenter, AlignContentSpaceBetween, AlignContentSpaceAround, AlignContentStretch }; +enum EAlignContent { + AlignContentFlexStart, + AlignContentFlexEnd, + AlignContentCenter, + AlignContentSpaceBetween, + AlignContentSpaceAround, + AlignContentStretch +}; enum EFlexDirection { FlowRow, FlowRowReverse, FlowColumn, FlowColumnReverse }; enum EFlexWrap { FlexNoWrap, FlexWrap, FlexWrapReverse }; -enum EJustifyContent { JustifyFlexStart, JustifyFlexEnd, JustifyCenter, JustifySpaceBetween, JustifySpaceAround }; +enum EJustifyContent { + JustifyFlexStart, + JustifyFlexEnd, + JustifyCenter, + JustifySpaceBetween, + JustifySpaceAround +}; // CSS3 User Modify Properties -enum EUserModify { - READ_ONLY, READ_WRITE, READ_WRITE_PLAINTEXT_ONLY -}; +enum EUserModify { READ_ONLY, READ_WRITE, READ_WRITE_PLAINTEXT_ONLY }; // CSS3 User Select Values -enum EUserSelect { - SELECT_NONE, SELECT_TEXT, SELECT_ALL -}; +enum EUserSelect { SELECT_NONE, SELECT_TEXT, SELECT_ALL }; // CSS3 Image Values -enum ObjectFit { ObjectFitFill, ObjectFitContain, ObjectFitCover, ObjectFitNone, ObjectFitScaleDown }; +enum ObjectFit { + ObjectFitFill, + ObjectFitContain, + ObjectFitCover, + ObjectFitNone, + ObjectFitScaleDown +}; // Word Break Values. Matches WinIE, rather than CSS3 -enum EWordBreak { - NormalWordBreak, BreakAllWordBreak, BreakWordBreak -}; +enum EWordBreak { NormalWordBreak, BreakAllWordBreak, BreakWordBreak }; -enum EOverflowWrap { - NormalOverflowWrap, BreakOverflowWrap -}; +enum EOverflowWrap { NormalOverflowWrap, BreakOverflowWrap }; enum LineBreak { - LineBreakAuto, LineBreakLoose, LineBreakNormal, LineBreakStrict, LineBreakAfterWhiteSpace + LineBreakAuto, + LineBreakLoose, + LineBreakNormal, + LineBreakStrict, + LineBreakAfterWhiteSpace }; -enum EAnimPlayState { - AnimPlayStatePlaying, - AnimPlayStatePaused -}; +enum EAnimPlayState { AnimPlayStatePlaying, AnimPlayStatePaused }; -enum EWhiteSpace { - NORMAL, PRE, PRE_WRAP, PRE_LINE, NOWRAP, KHTML_NOWRAP -}; +enum EWhiteSpace { NORMAL, PRE, PRE_WRAP, PRE_LINE, NOWRAP, KHTML_NOWRAP }; -// The order of this enum must match the order of the values in dart:ui's TextAlign. +// The order of this enum must match the order of the values in dart:ui's +// TextAlign. enum ETextAlign { - LEFT, RIGHT, CENTER, JUSTIFY, TASTART, TAEND, + LEFT, + RIGHT, + CENTER, + JUSTIFY, + TASTART, + TAEND, }; static const size_t TextDecorationBits = 4; enum TextDecoration { - TextDecorationNone = 0x0, - TextDecorationUnderline = 0x1, - TextDecorationOverline = 0x2, - TextDecorationLineThrough = 0x4, - TextDecorationBlink = 0x8 + TextDecorationNone = 0x0, + TextDecorationUnderline = 0x1, + TextDecorationOverline = 0x2, + TextDecorationLineThrough = 0x4, + TextDecorationBlink = 0x8 }; -inline TextDecoration operator| (TextDecoration a, TextDecoration b) { return TextDecoration(int(a) | int(b)); } -inline TextDecoration& operator|= (TextDecoration& a, TextDecoration b) { return a = a | b; } +inline TextDecoration operator|(TextDecoration a, TextDecoration b) { + return TextDecoration(int(a) | int(b)); +} +inline TextDecoration& operator|=(TextDecoration& a, TextDecoration b) { + return a = a | b; +} enum TextDecorationStyle { - TextDecorationStyleSolid, - TextDecorationStyleDouble, - TextDecorationStyleDotted, - TextDecorationStyleDashed, - TextDecorationStyleWavy + TextDecorationStyleSolid, + TextDecorationStyleDouble, + TextDecorationStyleDotted, + TextDecorationStyleDashed, + TextDecorationStyleWavy }; enum TextAlignLast { - TextAlignLastAuto, TextAlignLastStart, TextAlignLastEnd, TextAlignLastLeft, TextAlignLastRight, TextAlignLastCenter, TextAlignLastJustify + TextAlignLastAuto, + TextAlignLastStart, + TextAlignLastEnd, + TextAlignLastLeft, + TextAlignLastRight, + TextAlignLastCenter, + TextAlignLastJustify }; enum TextJustify { - TextJustifyAuto, TextJustifyNone, TextJustifyInterWord, TextJustifyDistribute + TextJustifyAuto, + TextJustifyNone, + TextJustifyInterWord, + TextJustifyDistribute }; enum TextUnderlinePosition { - // FIXME: Implement support for 'under left' and 'under right' values. - TextUnderlinePositionAuto, - TextUnderlinePositionUnder + // FIXME: Implement support for 'under left' and 'under right' values. + TextUnderlinePositionAuto, + TextUnderlinePositionUnder }; enum EVisibility { VISIBLE, HIDDEN, COLLAPSE }; -// The order of this enum must match the order of the display values in CSSValueKeywords.in. +// The order of this enum must match the order of the display values in +// CSSValueKeywords.in. enum EDisplay { - INLINE, PARAGRAPH, - FLEX, INLINE_FLEX, - NONE, + INLINE, + PARAGRAPH, + FLEX, + INLINE_FLEX, + NONE, }; enum EPointerEvents { - PE_NONE, PE_AUTO, PE_STROKE, PE_FILL, PE_PAINTED, PE_VISIBLE, - PE_VISIBLE_STROKE, PE_VISIBLE_FILL, PE_VISIBLE_PAINTED, PE_BOUNDINGBOX, - PE_ALL + PE_NONE, + PE_AUTO, + PE_STROKE, + PE_FILL, + PE_PAINTED, + PE_VISIBLE, + PE_VISIBLE_STROKE, + PE_VISIBLE_FILL, + PE_VISIBLE_PAINTED, + PE_BOUNDINGBOX, + PE_ALL }; -enum ETransformStyle3D { - TransformStyle3DFlat, TransformStyle3DPreserve3D -}; +enum ETransformStyle3D { TransformStyle3DFlat, TransformStyle3DPreserve3D }; enum Hyphens { HyphensNone, HyphensManual, HyphensAuto }; enum TextEmphasisFill { TextEmphasisFillFilled, TextEmphasisFillOpen }; -enum TextEmphasisMark { TextEmphasisMarkNone, TextEmphasisMarkAuto, TextEmphasisMarkDot, TextEmphasisMarkCircle, TextEmphasisMarkDoubleCircle, TextEmphasisMarkTriangle, TextEmphasisMarkSesame, TextEmphasisMarkCustom }; +enum TextEmphasisMark { + TextEmphasisMarkNone, + TextEmphasisMarkAuto, + TextEmphasisMarkDot, + TextEmphasisMarkCircle, + TextEmphasisMarkDoubleCircle, + TextEmphasisMarkTriangle, + TextEmphasisMarkSesame, + TextEmphasisMarkCustom +}; -enum TextEmphasisPosition { TextEmphasisPositionOver, TextEmphasisPositionUnder }; +enum TextEmphasisPosition { + TextEmphasisPositionOver, + TextEmphasisPositionUnder +}; -enum TextOrientation { TextOrientationVerticalRight, TextOrientationUpright, TextOrientationSideways, TextOrientationSidewaysRight }; +enum TextOrientation { + TextOrientationVerticalRight, + TextOrientationUpright, + TextOrientationSideways, + TextOrientationSidewaysRight +}; enum TextOverflow { TextOverflowClip = 0, TextOverflowEllipsis }; -enum EImageRendering { ImageRenderingAuto, ImageRenderingOptimizeSpeed, ImageRenderingOptimizeQuality, ImageRenderingOptimizeContrast, ImageRenderingPixelated }; +enum EImageRendering { + ImageRenderingAuto, + ImageRenderingOptimizeSpeed, + ImageRenderingOptimizeQuality, + ImageRenderingOptimizeContrast, + ImageRenderingPixelated +}; -enum ImageResolutionSource { ImageResolutionSpecified = 0, ImageResolutionFromImage }; +enum ImageResolutionSource { + ImageResolutionSpecified = 0, + ImageResolutionFromImage +}; -enum ImageResolutionSnap { ImageResolutionNoSnap = 0, ImageResolutionSnapPixels }; +enum ImageResolutionSnap { + ImageResolutionNoSnap = 0, + ImageResolutionSnapPixels +}; enum Order { LogicalOrder = 0, VisualOrder }; -enum WrapFlow { WrapFlowAuto, WrapFlowBoth, WrapFlowStart, WrapFlowEnd, WrapFlowMaximum, WrapFlowClear }; +enum WrapFlow { + WrapFlowAuto, + WrapFlowBoth, + WrapFlowStart, + WrapFlowEnd, + WrapFlowMaximum, + WrapFlowClear +}; enum WrapThrough { WrapThroughWrap, WrapThroughNone }; static const size_t TouchActionBits = 4; enum TouchAction { - TouchActionAuto = 0x0, - TouchActionNone = 0x1, - TouchActionPanX = 0x2, - TouchActionPanY = 0x4, - TouchActionPinchZoom = 0x8, + TouchActionAuto = 0x0, + TouchActionNone = 0x1, + TouchActionPanX = 0x2, + TouchActionPanY = 0x4, + TouchActionPinchZoom = 0x8, }; -inline TouchAction operator| (TouchAction a, TouchAction b) { return TouchAction(int(a) | int(b)); } -inline TouchAction& operator|= (TouchAction& a, TouchAction b) { return a = a | b; } -inline TouchAction operator& (TouchAction a, TouchAction b) { return TouchAction(int(a) & int(b)); } -inline TouchAction& operator&= (TouchAction& a, TouchAction b) { return a = a & b; } +inline TouchAction operator|(TouchAction a, TouchAction b) { + return TouchAction(int(a) | int(b)); +} +inline TouchAction& operator|=(TouchAction& a, TouchAction b) { + return a = a | b; +} +inline TouchAction operator&(TouchAction a, TouchAction b) { + return TouchAction(int(a) & int(b)); +} +inline TouchAction& operator&=(TouchAction& a, TouchAction b) { + return a = a & b; +} enum TouchActionDelay { TouchActionDelayNone, TouchActionDelayScript }; enum ItemPosition { - ItemPositionAuto, - ItemPositionStretch, - ItemPositionBaseline, - ItemPositionLastBaseline, - ItemPositionCenter, - ItemPositionStart, - ItemPositionEnd, - ItemPositionSelfStart, - ItemPositionSelfEnd, - ItemPositionFlexStart, - ItemPositionFlexEnd, - ItemPositionLeft, - ItemPositionRight + ItemPositionAuto, + ItemPositionStretch, + ItemPositionBaseline, + ItemPositionLastBaseline, + ItemPositionCenter, + ItemPositionStart, + ItemPositionEnd, + ItemPositionSelfStart, + ItemPositionSelfEnd, + ItemPositionFlexStart, + ItemPositionFlexEnd, + ItemPositionLeft, + ItemPositionRight }; enum OverflowAlignment { - OverflowAlignmentDefault, - OverflowAlignmentTrue, - OverflowAlignmentSafe + OverflowAlignmentDefault, + OverflowAlignmentTrue, + OverflowAlignmentSafe }; -enum ItemPositionType { - NonLegacyPosition, - LegacyPosition -}; +enum ItemPositionType { NonLegacyPosition, LegacyPosition }; -// Reasonable maximum to prevent insane font sizes from causing crashes on some platforms (such as Windows). +// Reasonable maximum to prevent insane font sizes from causing crashes on some +// platforms (such as Windows). static const float maximumAllowedFontSize = 1000000.0f; enum TextIndentLine { TextIndentFirstLine, TextIndentEachLine }; enum TextIndentType { TextIndentNormal, TextIndentHanging }; -enum CSSBoxType { BoxMissing = 0, MarginBox, BorderBox, PaddingBox, ContentBox }; +enum CSSBoxType { + BoxMissing = 0, + MarginBox, + BorderBox, + PaddingBox, + ContentBox +}; enum LineBoxContainFlags { - LineBoxContainNone = 0x0, - LineBoxContainBlock = 0x1, - LineBoxContainInline = 0x2, - LineBoxContainFont = 0x4, - LineBoxContainGlyphs = 0x8, - LineBoxContainReplaced = 0x10, - LineBoxContainInlineBox = 0x20 + LineBoxContainNone = 0x0, + LineBoxContainBlock = 0x1, + LineBoxContainInline = 0x2, + LineBoxContainFont = 0x4, + LineBoxContainGlyphs = 0x8, + LineBoxContainReplaced = 0x10, + LineBoxContainInlineBox = 0x20 }; typedef unsigned LineBoxContain; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_STYLE_RENDERSTYLECONSTANTS_H_ diff --git a/sky/engine/core/rendering/style/ShadowData.cpp b/sky/engine/core/rendering/style/ShadowData.cpp index 403f04f9495a1..2d4ec0d7aa752 100644 --- a/sky/engine/core/rendering/style/ShadowData.cpp +++ b/sky/engine/core/rendering/style/ShadowData.cpp @@ -1,6 +1,7 @@ /* * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org) - * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. + * All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -25,25 +26,19 @@ namespace blink { -bool ShadowData::operator==(const ShadowData& o) const -{ - return m_location == o.m_location - && m_blur == o.m_blur - && m_spread == o.m_spread - && m_style == o.m_style - && m_color == o.m_color; +bool ShadowData::operator==(const ShadowData& o) const { + return m_location == o.m_location && m_blur == o.m_blur && + m_spread == o.m_spread && m_style == o.m_style && m_color == o.m_color; } -ShadowData ShadowData::blend(const ShadowData& from, double progress) const -{ - if (style() != from.style()) - return *this; +ShadowData ShadowData::blend(const ShadowData& from, double progress) const { + if (style() != from.style()) + return *this; - return ShadowData(blink::blend(from.location(), location(), progress), - clampTo(blink::blend(from.blur(), blur(), progress), 0.0f), - blink::blend(from.spread(), spread(), progress), - style(), - blink::blend(from.color(), color(), progress)); + return ShadowData(blink::blend(from.location(), location(), progress), + clampTo(blink::blend(from.blur(), blur(), progress), 0.0f), + blink::blend(from.spread(), spread(), progress), style(), + blink::blend(from.color(), color(), progress)); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/core/rendering/style/ShadowData.h b/sky/engine/core/rendering/style/ShadowData.h index 86577d4dc74e5..f0298cfad87b4 100644 --- a/sky/engine/core/rendering/style/ShadowData.h +++ b/sky/engine/core/rendering/style/ShadowData.h @@ -2,7 +2,8 @@ * Copyright (C) 2000 Lars Knoll (knoll@kde.org) * (C) 2000 Antti Koivisto (koivisto@kde.org) * (C) 2000 Dirk Mueller (mueller@kde.org) - * Copyright (C) 2003, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2005, 2006, 2007, 2008, 2009 Apple Inc. + * All rights reserved. * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.com) * * This library is free software; you can redistribute it and/or @@ -32,41 +33,46 @@ namespace blink { enum ShadowStyle { Normal, Inset }; -// This class holds information about shadows for the text-shadow and box-shadow properties. +// This class holds information about shadows for the text-shadow and box-shadow +// properties. class ShadowData { - WTF_MAKE_FAST_ALLOCATED; -public: - ShadowData(const FloatPoint& location, float blur, float spread, ShadowStyle style, const Color& color) - : m_location(location) - , m_blur(blur) - , m_spread(spread) - , m_color(color) - , m_style(style) - { - } + WTF_MAKE_FAST_ALLOCATED; - bool operator==(const ShadowData&) const; - bool operator!=(const ShadowData& o) const { return !(*this == o); } + public: + ShadowData(const FloatPoint& location, + float blur, + float spread, + ShadowStyle style, + const Color& color) + : m_location(location), + m_blur(blur), + m_spread(spread), + m_color(color), + m_style(style) {} - ShadowData blend(const ShadowData& from, double progress) const; + bool operator==(const ShadowData&) const; + bool operator!=(const ShadowData& o) const { return !(*this == o); } - float x() const { return m_location.x(); } - float y() const { return m_location.y(); } - FloatPoint location() const { return m_location; } - float blur() const { return m_blur; } - float spread() const { return m_spread; } - ShadowStyle style() const { return m_style; } - const Color& color() const { return m_color; } + ShadowData blend(const ShadowData& from, double progress) const; -private: - FloatPoint m_location; - float m_blur; - float m_spread; - // FIXME: We should use StyleColor here to allow currentColor to work correctly with visited links - Color m_color; - ShadowStyle m_style; + float x() const { return m_location.x(); } + float y() const { return m_location.y(); } + FloatPoint location() const { return m_location; } + float blur() const { return m_blur; } + float spread() const { return m_spread; } + ShadowStyle style() const { return m_style; } + const Color& color() const { return m_color; } + + private: + FloatPoint m_location; + float m_blur; + float m_spread; + // FIXME: We should use StyleColor here to allow currentColor to work + // correctly with visited links + Color m_color; + ShadowStyle m_style; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_STYLE_SHADOWDATA_H_ diff --git a/sky/engine/core/rendering/style/ShadowList.cpp b/sky/engine/core/rendering/style/ShadowList.cpp index dc2e81c4ac8d6..703f0b0f549b9 100644 --- a/sky/engine/core/rendering/style/ShadowList.cpp +++ b/sky/engine/core/rendering/style/ShadowList.cpp @@ -35,78 +35,85 @@ namespace blink { -static inline void calculateShadowExtent(const ShadowList* shadowList, float& shadowLeft, float& shadowRight, float& shadowTop, float& shadowBottom) -{ - ASSERT(shadowList); - size_t shadowCount = shadowList->shadows().size(); - for (size_t i = 0; i < shadowCount; ++i) { - const ShadowData& shadow = shadowList->shadows()[i]; - if (shadow.style() == Inset) - continue; - float blurAndSpread = shadow.blur() + shadow.spread(); - shadowLeft = std::min(shadow.x() - blurAndSpread, shadowLeft); - shadowRight = std::max(shadow.x() + blurAndSpread, shadowRight); - shadowTop = std::min(shadow.y() - blurAndSpread, shadowTop); - shadowBottom = std::max(shadow.y() + blurAndSpread, shadowBottom); - } +static inline void calculateShadowExtent(const ShadowList* shadowList, + float& shadowLeft, + float& shadowRight, + float& shadowTop, + float& shadowBottom) { + ASSERT(shadowList); + size_t shadowCount = shadowList->shadows().size(); + for (size_t i = 0; i < shadowCount; ++i) { + const ShadowData& shadow = shadowList->shadows()[i]; + if (shadow.style() == Inset) + continue; + float blurAndSpread = shadow.blur() + shadow.spread(); + shadowLeft = std::min(shadow.x() - blurAndSpread, shadowLeft); + shadowRight = std::max(shadow.x() + blurAndSpread, shadowRight); + shadowTop = std::min(shadow.y() - blurAndSpread, shadowTop); + shadowBottom = std::max(shadow.y() + blurAndSpread, shadowBottom); + } } -void ShadowList::adjustRectForShadow(LayoutRect& rect) const -{ - FloatRect floatRect(rect); - adjustRectForShadow(floatRect); - rect = LayoutRect(floatRect); +void ShadowList::adjustRectForShadow(LayoutRect& rect) const { + FloatRect floatRect(rect); + adjustRectForShadow(floatRect); + rect = LayoutRect(floatRect); } -void ShadowList::adjustRectForShadow(FloatRect& rect) const -{ - float shadowLeft = 0; - float shadowRight = 0; - float shadowTop = 0; - float shadowBottom = 0; - calculateShadowExtent(this, shadowLeft, shadowRight, shadowTop, shadowBottom); +void ShadowList::adjustRectForShadow(FloatRect& rect) const { + float shadowLeft = 0; + float shadowRight = 0; + float shadowTop = 0; + float shadowBottom = 0; + calculateShadowExtent(this, shadowLeft, shadowRight, shadowTop, shadowBottom); - rect.move(shadowLeft, shadowTop); - rect.setWidth(rect.width() - shadowLeft + shadowRight); - rect.setHeight(rect.height() - shadowTop + shadowBottom); + rect.move(shadowLeft, shadowTop); + rect.setWidth(rect.width() - shadowLeft + shadowRight); + rect.setHeight(rect.height() - shadowTop + shadowBottom); } -PassRefPtr ShadowList::blend(const ShadowList* from, const ShadowList* to, double progress) -{ - size_t fromLength = from ? from->shadows().size() : 0; - size_t toLength = to ? to->shadows().size() : 0; - if (!fromLength && !toLength) - return nullptr; +PassRefPtr ShadowList::blend(const ShadowList* from, + const ShadowList* to, + double progress) { + size_t fromLength = from ? from->shadows().size() : 0; + size_t toLength = to ? to->shadows().size() : 0; + if (!fromLength && !toLength) + return nullptr; - ShadowDataVector shadows; + ShadowDataVector shadows; - DEFINE_STATIC_LOCAL(ShadowData, defaultShadowData, (FloatPoint(), 0, 0, Normal, Color::transparent)); - DEFINE_STATIC_LOCAL(ShadowData, defaultInsetShadowData, (FloatPoint(), 0, 0, Inset, Color::transparent)); + DEFINE_STATIC_LOCAL(ShadowData, defaultShadowData, + (FloatPoint(), 0, 0, Normal, Color::transparent)); + DEFINE_STATIC_LOCAL(ShadowData, defaultInsetShadowData, + (FloatPoint(), 0, 0, Inset, Color::transparent)); - size_t maxLength = std::max(fromLength, toLength); - for (size_t i = 0; i < maxLength; ++i) { - const ShadowData* fromShadow = i < fromLength ? &from->shadows()[i] : 0; - const ShadowData* toShadow = i < toLength ? &to->shadows()[i] : 0; - if (!fromShadow) - fromShadow = toShadow->style() == Inset ? &defaultInsetShadowData : &defaultShadowData; - else if (!toShadow) - toShadow = fromShadow->style() == Inset ? &defaultInsetShadowData : &defaultShadowData; - shadows.append(toShadow->blend(*fromShadow, progress)); - } + size_t maxLength = std::max(fromLength, toLength); + for (size_t i = 0; i < maxLength; ++i) { + const ShadowData* fromShadow = i < fromLength ? &from->shadows()[i] : 0; + const ShadowData* toShadow = i < toLength ? &to->shadows()[i] : 0; + if (!fromShadow) + fromShadow = toShadow->style() == Inset ? &defaultInsetShadowData + : &defaultShadowData; + else if (!toShadow) + toShadow = fromShadow->style() == Inset ? &defaultInsetShadowData + : &defaultShadowData; + shadows.append(toShadow->blend(*fromShadow, progress)); + } - return ShadowList::adopt(shadows); + return ShadowList::adopt(shadows); } -PassOwnPtr ShadowList::createDrawLooper(DrawLooperBuilder::ShadowAlphaMode alphaMode) const -{ - OwnPtr drawLooperBuilder = DrawLooperBuilder::create(); - for (size_t i = shadows().size(); i--; ) { - const ShadowData& shadow = shadows()[i]; - drawLooperBuilder->addShadow(FloatSize(shadow.x(), shadow.y()), shadow.blur(), shadow.color(), - DrawLooperBuilder::ShadowRespectsTransforms, alphaMode); - } - drawLooperBuilder->addUnmodifiedContent(); - return drawLooperBuilder.release(); +PassOwnPtr ShadowList::createDrawLooper( + DrawLooperBuilder::ShadowAlphaMode alphaMode) const { + OwnPtr drawLooperBuilder = DrawLooperBuilder::create(); + for (size_t i = shadows().size(); i--;) { + const ShadowData& shadow = shadows()[i]; + drawLooperBuilder->addShadow( + FloatSize(shadow.x(), shadow.y()), shadow.blur(), shadow.color(), + DrawLooperBuilder::ShadowRespectsTransforms, alphaMode); + } + drawLooperBuilder->addUnmodifiedContent(); + return drawLooperBuilder.release(); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/core/rendering/style/ShadowList.h b/sky/engine/core/rendering/style/ShadowList.h index 368d96206beb3..9f7637e070a2d 100644 --- a/sky/engine/core/rendering/style/ShadowList.h +++ b/sky/engine/core/rendering/style/ShadowList.h @@ -48,34 +48,37 @@ typedef Vector ShadowDataVector; // These are used to store shadows in specified order, but we usually want to // iterate over them backwards as the first-specified shadow is painted on top. class ShadowList : public RefCounted { -public: - // This consumes passed in vector. - static PassRefPtr adopt(ShadowDataVector& shadows) - { - return adoptRef(new ShadowList(shadows)); - } - const ShadowDataVector& shadows() const { return m_shadows; } - bool operator==(const ShadowList& o) const { return m_shadows == o.m_shadows; } - bool operator!=(const ShadowList& o) const { return !(*this == o); } + public: + // This consumes passed in vector. + static PassRefPtr adopt(ShadowDataVector& shadows) { + return adoptRef(new ShadowList(shadows)); + } + const ShadowDataVector& shadows() const { return m_shadows; } + bool operator==(const ShadowList& o) const { + return m_shadows == o.m_shadows; + } + bool operator!=(const ShadowList& o) const { return !(*this == o); } - static PassRefPtr blend(const ShadowList* from, const ShadowList* to, double progress); + static PassRefPtr blend(const ShadowList* from, + const ShadowList* to, + double progress); - void adjustRectForShadow(LayoutRect&) const; - void adjustRectForShadow(FloatRect&) const; + void adjustRectForShadow(LayoutRect&) const; + void adjustRectForShadow(FloatRect&) const; - PassOwnPtr createDrawLooper(DrawLooperBuilder::ShadowAlphaMode) const; + PassOwnPtr createDrawLooper( + DrawLooperBuilder::ShadowAlphaMode) const; -private: - ShadowList(ShadowDataVector& shadows) - { - // If we have no shadows, we use a null ShadowList - ASSERT(!shadows.isEmpty()); - m_shadows.swap(shadows); - m_shadows.shrinkToFit(); - } - ShadowDataVector m_shadows; + private: + ShadowList(ShadowDataVector& shadows) { + // If we have no shadows, we use a null ShadowList + ASSERT(!shadows.isEmpty()); + m_shadows.swap(shadows); + m_shadows.shrinkToFit(); + } + ShadowDataVector m_shadows; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_STYLE_SHADOWLIST_H_ diff --git a/sky/engine/core/rendering/style/ShapeValue.h b/sky/engine/core/rendering/style/ShapeValue.h index 5121c89e62306..6a0f37e4ed56e 100644 --- a/sky/engine/core/rendering/style/ShapeValue.h +++ b/sky/engine/core/rendering/style/ShapeValue.h @@ -37,81 +37,64 @@ namespace blink { class ShapeValue : public RefCounted { -public: - enum ShapeValueType { - // The Auto value is defined by a null ShapeValue* - Box, - Image - }; - - static PassRefPtr createBoxShapeValue(CSSBoxType cssBox) - { - return adoptRef(new ShapeValue(cssBox)); - } - - static PassRefPtr createImageValue(PassRefPtr image) - { - return adoptRef(new ShapeValue(image)); - } - - ShapeValueType type() const { return m_type; } - - StyleImage* image() const { return m_image.get(); } - bool isImageValid() const - { - if (!image()) - return false; - return image()->isGeneratedImage(); - } - void setImage(PassRefPtr image) - { - ASSERT(type() == Image); - if (m_image != image) - m_image = image; - } - CSSBoxType cssBox() const { return m_cssBox; } - - bool operator==(const ShapeValue& other) const; - -private: - ShapeValue(ShapeValueType type) - : m_type(type) - , m_cssBox(BoxMissing) - { - } - ShapeValue(PassRefPtr image) - : m_type(Image) - , m_image(image) - , m_cssBox(ContentBox) - { - } - ShapeValue(CSSBoxType cssBox) - : m_type(Box) - , m_cssBox(cssBox) - { - } - - ShapeValueType m_type; - RefPtr m_image; - CSSBoxType m_cssBox; + public: + enum ShapeValueType { + // The Auto value is defined by a null ShapeValue* + Box, + Image + }; + + static PassRefPtr createBoxShapeValue(CSSBoxType cssBox) { + return adoptRef(new ShapeValue(cssBox)); + } + + static PassRefPtr createImageValue(PassRefPtr image) { + return adoptRef(new ShapeValue(image)); + } + + ShapeValueType type() const { return m_type; } + + StyleImage* image() const { return m_image.get(); } + bool isImageValid() const { + if (!image()) + return false; + return image()->isGeneratedImage(); + } + void setImage(PassRefPtr image) { + ASSERT(type() == Image); + if (m_image != image) + m_image = image; + } + CSSBoxType cssBox() const { return m_cssBox; } + + bool operator==(const ShapeValue& other) const; + + private: + ShapeValue(ShapeValueType type) : m_type(type), m_cssBox(BoxMissing) {} + ShapeValue(PassRefPtr image) + : m_type(Image), m_image(image), m_cssBox(ContentBox) {} + ShapeValue(CSSBoxType cssBox) : m_type(Box), m_cssBox(cssBox) {} + + ShapeValueType m_type; + RefPtr m_image; + CSSBoxType m_cssBox; }; -inline bool ShapeValue::operator==(const ShapeValue& other) const -{ - if (type() != other.type()) - return false; +inline bool ShapeValue::operator==(const ShapeValue& other) const { + if (type() != other.type()) + return false; - switch (type()) { + switch (type()) { case Box: - return cssBox() == other.cssBox(); + return cssBox() == other.cssBox(); case Image: - return image() == other.image(); - } + return image() == other.image(); + } - ASSERT_NOT_REACHED(); - return false; + ASSERT_NOT_REACHED(); + return false; } -} +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_STYLE_SHAPEVALUE_H_ diff --git a/sky/engine/core/rendering/style/StyleBackgroundData.cpp b/sky/engine/core/rendering/style/StyleBackgroundData.cpp index 162449063c1fa..38bc262c153c7 100644 --- a/sky/engine/core/rendering/style/StyleBackgroundData.cpp +++ b/sky/engine/core/rendering/style/StyleBackgroundData.cpp @@ -27,27 +27,23 @@ namespace blink { StyleBackgroundData::StyleBackgroundData() - : m_background(BackgroundFillLayer, true) - , m_color(RenderStyle::initialBackgroundColor()) -{ -} + : m_background(BackgroundFillLayer, true), + m_color(RenderStyle::initialBackgroundColor()) {} StyleBackgroundData::StyleBackgroundData(const StyleBackgroundData& o) - : RefCounted() - , m_background(o.m_background) - , m_color(o.m_color) - , m_outline(o.m_outline) -{ -} - -bool StyleBackgroundData::operator==(const StyleBackgroundData& o) const -{ - return m_background == o.m_background && m_color == o.m_color && m_outline == o.m_outline; + : RefCounted(), + m_background(o.m_background), + m_color(o.m_color), + m_outline(o.m_outline) {} + +bool StyleBackgroundData::operator==(const StyleBackgroundData& o) const { + return m_background == o.m_background && m_color == o.m_color && + m_outline == o.m_outline; } -bool StyleBackgroundData::visuallyEqual(const StyleBackgroundData& o) const -{ - return m_background == o.m_background && m_color == o.m_color && m_outline.visuallyEqual(o.m_outline); +bool StyleBackgroundData::visuallyEqual(const StyleBackgroundData& o) const { + return m_background == o.m_background && m_color == o.m_color && + m_outline.visuallyEqual(o.m_outline); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/core/rendering/style/StyleBackgroundData.h b/sky/engine/core/rendering/style/StyleBackgroundData.h index 4cd8f853187e4..ba8739d721f78 100644 --- a/sky/engine/core/rendering/style/StyleBackgroundData.h +++ b/sky/engine/core/rendering/style/StyleBackgroundData.h @@ -34,34 +34,35 @@ namespace blink { class StyleBackgroundData : public RefCounted { -public: - static PassRefPtr create() { return adoptRef(new StyleBackgroundData); } - PassRefPtr copy() const { return adoptRef(new StyleBackgroundData(*this)); } - ~StyleBackgroundData() { } + public: + static PassRefPtr create() { + return adoptRef(new StyleBackgroundData); + } + PassRefPtr copy() const { + return adoptRef(new StyleBackgroundData(*this)); + } + ~StyleBackgroundData() {} - bool operator==(const StyleBackgroundData& o) const; - bool operator!=(const StyleBackgroundData& o) const - { - return !(*this == o); - } + bool operator==(const StyleBackgroundData& o) const; + bool operator!=(const StyleBackgroundData& o) const { return !(*this == o); } - bool visuallyEqual(const StyleBackgroundData&) const; + bool visuallyEqual(const StyleBackgroundData&) const; - const FillLayer& background() const { return m_background; } - const StyleColor& color() const { return m_color; } - const OutlineValue& outline() const { return m_outline; } + const FillLayer& background() const { return m_background; } + const StyleColor& color() const { return m_color; } + const OutlineValue& outline() const { return m_outline; } -private: - friend class RenderStyle; + private: + friend class RenderStyle; - StyleBackgroundData(); - StyleBackgroundData(const StyleBackgroundData&); + StyleBackgroundData(); + StyleBackgroundData(const StyleBackgroundData&); - FillLayer m_background; - StyleColor m_color; - OutlineValue m_outline; + FillLayer m_background; + StyleColor m_color; + OutlineValue m_outline; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_STYLE_STYLEBACKGROUNDDATA_H_ diff --git a/sky/engine/core/rendering/style/StyleBoxData.cpp b/sky/engine/core/rendering/style/StyleBoxData.cpp index dbac9fec56109..92433305cd320 100644 --- a/sky/engine/core/rendering/style/StyleBoxData.cpp +++ b/sky/engine/core/rendering/style/StyleBoxData.cpp @@ -26,55 +26,45 @@ namespace blink { struct SameSizeAsStyleBoxData : public RefCounted { - Length length[7]; - unsigned m_zIndex; - uint32_t bitfields; + Length length[7]; + unsigned m_zIndex; + uint32_t bitfields; }; -COMPILE_ASSERT(sizeof(StyleBoxData) == sizeof(SameSizeAsStyleBoxData), StyleBoxData_should_not_grow); +COMPILE_ASSERT(sizeof(StyleBoxData) == sizeof(SameSizeAsStyleBoxData), + StyleBoxData_should_not_grow); StyleBoxData::StyleBoxData() - : m_minWidth(RenderStyle::initialMinSize()) - , m_maxWidth(RenderStyle::initialMaxSize()) - , m_minHeight(RenderStyle::initialMinSize()) - , m_maxHeight(RenderStyle::initialMaxSize()) - , m_zIndex(0) - , m_hasAutoZIndex(true) - , m_boxSizing(RenderStyle::initialBoxSizing()) - , m_boxDecorationBreak(DSLICE) -{ -} + : m_minWidth(RenderStyle::initialMinSize()), + m_maxWidth(RenderStyle::initialMaxSize()), + m_minHeight(RenderStyle::initialMinSize()), + m_maxHeight(RenderStyle::initialMaxSize()), + m_zIndex(0), + m_hasAutoZIndex(true), + m_boxSizing(RenderStyle::initialBoxSizing()), + m_boxDecorationBreak(DSLICE) {} StyleBoxData::StyleBoxData(const StyleBoxData& o) - : RefCounted() - , m_width(o.m_width) - , m_height(o.m_height) - , m_minWidth(o.m_minWidth) - , m_maxWidth(o.m_maxWidth) - , m_minHeight(o.m_minHeight) - , m_maxHeight(o.m_maxHeight) - , m_verticalAlign(o.m_verticalAlign) - , m_zIndex(o.m_zIndex) - , m_hasAutoZIndex(o.m_hasAutoZIndex) - , m_boxSizing(o.m_boxSizing) - , m_boxDecorationBreak(o.m_boxDecorationBreak) -{ -} + : RefCounted(), + m_width(o.m_width), + m_height(o.m_height), + m_minWidth(o.m_minWidth), + m_maxWidth(o.m_maxWidth), + m_minHeight(o.m_minHeight), + m_maxHeight(o.m_maxHeight), + m_verticalAlign(o.m_verticalAlign), + m_zIndex(o.m_zIndex), + m_hasAutoZIndex(o.m_hasAutoZIndex), + m_boxSizing(o.m_boxSizing), + m_boxDecorationBreak(o.m_boxDecorationBreak) {} -bool StyleBoxData::operator==(const StyleBoxData& o) const -{ - return m_width == o.m_width - && m_height == o.m_height - && m_minWidth == o.m_minWidth - && m_maxWidth == o.m_maxWidth - && m_minHeight == o.m_minHeight - && m_maxHeight == o.m_maxHeight - && m_verticalAlign == o.m_verticalAlign - && m_zIndex == o.m_zIndex - && m_hasAutoZIndex == o.m_hasAutoZIndex - && m_boxSizing == o.m_boxSizing - && m_boxDecorationBreak == o.m_boxDecorationBreak - ; +bool StyleBoxData::operator==(const StyleBoxData& o) const { + return m_width == o.m_width && m_height == o.m_height && + m_minWidth == o.m_minWidth && m_maxWidth == o.m_maxWidth && + m_minHeight == o.m_minHeight && m_maxHeight == o.m_maxHeight && + m_verticalAlign == o.m_verticalAlign && m_zIndex == o.m_zIndex && + m_hasAutoZIndex == o.m_hasAutoZIndex && m_boxSizing == o.m_boxSizing && + m_boxDecorationBreak == o.m_boxDecorationBreak; } -} // namespace blink +} // namespace blink diff --git a/sky/engine/core/rendering/style/StyleBoxData.h b/sky/engine/core/rendering/style/StyleBoxData.h index 03f7d8a57ca53..1ef4d4b99f3a7 100644 --- a/sky/engine/core/rendering/style/StyleBoxData.h +++ b/sky/engine/core/rendering/style/StyleBoxData.h @@ -33,56 +33,59 @@ namespace blink { class StyleBoxData : public RefCounted { -public: - static PassRefPtr create() { return adoptRef(new StyleBoxData); } - PassRefPtr copy() const { return adoptRef(new StyleBoxData(*this)); } + public: + static PassRefPtr create() { + return adoptRef(new StyleBoxData); + } + PassRefPtr copy() const { + return adoptRef(new StyleBoxData(*this)); + } - bool operator==(const StyleBoxData& o) const; - bool operator!=(const StyleBoxData& o) const - { - return !(*this == o); - } + bool operator==(const StyleBoxData& o) const; + bool operator!=(const StyleBoxData& o) const { return !(*this == o); } - const Length& width() const { return m_width; } - const Length& height() const { return m_height; } + const Length& width() const { return m_width; } + const Length& height() const { return m_height; } - const Length& minWidth() const { return m_minWidth; } - const Length& minHeight() const { return m_minHeight; } + const Length& minWidth() const { return m_minWidth; } + const Length& minHeight() const { return m_minHeight; } - const Length& maxWidth() const { return m_maxWidth; } - const Length& maxHeight() const { return m_maxHeight; } + const Length& maxWidth() const { return m_maxWidth; } + const Length& maxHeight() const { return m_maxHeight; } - const Length& verticalAlign() const { return m_verticalAlign; } + const Length& verticalAlign() const { return m_verticalAlign; } - unsigned zIndex() const { return m_zIndex; } - bool hasAutoZIndex() const { return m_hasAutoZIndex; } + unsigned zIndex() const { return m_zIndex; } + bool hasAutoZIndex() const { return m_hasAutoZIndex; } - EBoxSizing boxSizing() const { return static_cast(m_boxSizing); } - EBoxDecorationBreak boxDecorationBreak() const { return static_cast(m_boxDecorationBreak); } + EBoxSizing boxSizing() const { return static_cast(m_boxSizing); } + EBoxDecorationBreak boxDecorationBreak() const { + return static_cast(m_boxDecorationBreak); + } -private: - friend class RenderStyle; + private: + friend class RenderStyle; - StyleBoxData(); - StyleBoxData(const StyleBoxData&); + StyleBoxData(); + StyleBoxData(const StyleBoxData&); - Length m_width; - Length m_height; + Length m_width; + Length m_height; - Length m_minWidth; - Length m_maxWidth; + Length m_minWidth; + Length m_maxWidth; - Length m_minHeight; - Length m_maxHeight; + Length m_minHeight; + Length m_maxHeight; - Length m_verticalAlign; + Length m_verticalAlign; - unsigned m_zIndex; - unsigned m_hasAutoZIndex : 1; - unsigned m_boxSizing : 1; // EBoxSizing - unsigned m_boxDecorationBreak : 1; // EBoxDecorationBreak + unsigned m_zIndex; + unsigned m_hasAutoZIndex : 1; + unsigned m_boxSizing : 1; // EBoxSizing + unsigned m_boxDecorationBreak : 1; // EBoxDecorationBreak }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_STYLE_STYLEBOXDATA_H_ diff --git a/sky/engine/core/rendering/style/StyleColor.h b/sky/engine/core/rendering/style/StyleColor.h index ce090339277d1..97b2498723c3f 100644 --- a/sky/engine/core/rendering/style/StyleColor.h +++ b/sky/engine/core/rendering/style/StyleColor.h @@ -36,34 +36,36 @@ namespace blink { class StyleColor { -public: - StyleColor(Color color) : m_color(color), m_currentColor(false) { } - static StyleColor currentColor() { return StyleColor(); } + public: + StyleColor(Color color) : m_color(color), m_currentColor(false) {} + static StyleColor currentColor() { return StyleColor(); } - bool isCurrentColor() const { return m_currentColor; } - Color color() const { ASSERT(!isCurrentColor()); return m_color; } + bool isCurrentColor() const { return m_currentColor; } + Color color() const { + ASSERT(!isCurrentColor()); + return m_color; + } - Color resolve(Color currentColor) const { return m_currentColor ? currentColor : m_color; } + Color resolve(Color currentColor) const { + return m_currentColor ? currentColor : m_color; + } -private: - StyleColor() : m_currentColor(true) { } - Color m_color; - bool m_currentColor; + private: + StyleColor() : m_currentColor(true) {} + Color m_color; + bool m_currentColor; }; -inline bool operator==(const StyleColor& a, const StyleColor& b) -{ - if (a.isCurrentColor() || b.isCurrentColor()) - return a.isCurrentColor() && b.isCurrentColor(); - return a.color() == b.color(); +inline bool operator==(const StyleColor& a, const StyleColor& b) { + if (a.isCurrentColor() || b.isCurrentColor()) + return a.isCurrentColor() && b.isCurrentColor(); + return a.color() == b.color(); } -inline bool operator!=(const StyleColor& a, const StyleColor& b) -{ - return !(a == b); +inline bool operator!=(const StyleColor& a, const StyleColor& b) { + return !(a == b); } - -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_STYLE_STYLECOLOR_H_ diff --git a/sky/engine/core/rendering/style/StyleDifference.h b/sky/engine/core/rendering/style/StyleDifference.h index 09ffc26a74692..b442403abbd95 100644 --- a/sky/engine/core/rendering/style/StyleDifference.h +++ b/sky/engine/core/rendering/style/StyleDifference.h @@ -10,56 +10,61 @@ namespace blink { class StyleDifference { -public: - enum PropertyDifference { - TransformChanged = 1 << 0, - OpacityChanged = 1 << 1, - ZIndexChanged = 1 << 2, - FilterChanged = 1 << 3, - }; - - StyleDifference() - : m_layoutType(NoLayout) - , m_propertySpecificDifferences(0) - { } - - bool needsLayout() const { return m_layoutType != NoLayout; } - void clearNeedsLayout() { m_layoutType = NoLayout; } - - // The offset of this positioned object has been updated. - bool needsPositionedMovementLayout() const { return m_layoutType == PositionedMovement; } - void setNeedsPositionedMovementLayout() - { - ASSERT(!needsFullLayout()); - m_layoutType = PositionedMovement; - } - - bool needsFullLayout() const { return m_layoutType == FullLayout; } - void setNeedsFullLayout() { m_layoutType = FullLayout; } - - bool transformChanged() const { return m_propertySpecificDifferences & TransformChanged; } - void setTransformChanged() { m_propertySpecificDifferences |= TransformChanged; } - - bool opacityChanged() const { return m_propertySpecificDifferences & OpacityChanged; } - void setOpacityChanged() { m_propertySpecificDifferences |= OpacityChanged; } - - bool zIndexChanged() const { return m_propertySpecificDifferences & ZIndexChanged; } - void setZIndexChanged() { m_propertySpecificDifferences |= ZIndexChanged; } - - bool filterChanged() const { return m_propertySpecificDifferences & FilterChanged; } - void setFilterChanged() { m_propertySpecificDifferences |= FilterChanged; } - -private: - enum LayoutType { - NoLayout = 0, - PositionedMovement, - FullLayout - }; - unsigned m_layoutType : 2; - - unsigned m_propertySpecificDifferences : 5; + public: + enum PropertyDifference { + TransformChanged = 1 << 0, + OpacityChanged = 1 << 1, + ZIndexChanged = 1 << 2, + FilterChanged = 1 << 3, + }; + + StyleDifference() + : m_layoutType(NoLayout), m_propertySpecificDifferences(0) {} + + bool needsLayout() const { return m_layoutType != NoLayout; } + void clearNeedsLayout() { m_layoutType = NoLayout; } + + // The offset of this positioned object has been updated. + bool needsPositionedMovementLayout() const { + return m_layoutType == PositionedMovement; + } + void setNeedsPositionedMovementLayout() { + ASSERT(!needsFullLayout()); + m_layoutType = PositionedMovement; + } + + bool needsFullLayout() const { return m_layoutType == FullLayout; } + void setNeedsFullLayout() { m_layoutType = FullLayout; } + + bool transformChanged() const { + return m_propertySpecificDifferences & TransformChanged; + } + void setTransformChanged() { + m_propertySpecificDifferences |= TransformChanged; + } + + bool opacityChanged() const { + return m_propertySpecificDifferences & OpacityChanged; + } + void setOpacityChanged() { m_propertySpecificDifferences |= OpacityChanged; } + + bool zIndexChanged() const { + return m_propertySpecificDifferences & ZIndexChanged; + } + void setZIndexChanged() { m_propertySpecificDifferences |= ZIndexChanged; } + + bool filterChanged() const { + return m_propertySpecificDifferences & FilterChanged; + } + void setFilterChanged() { m_propertySpecificDifferences |= FilterChanged; } + + private: + enum LayoutType { NoLayout = 0, PositionedMovement, FullLayout }; + unsigned m_layoutType : 2; + + unsigned m_propertySpecificDifferences : 5; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_STYLE_STYLEDIFFERENCE_H_ diff --git a/sky/engine/core/rendering/style/StyleFilterData.cpp b/sky/engine/core/rendering/style/StyleFilterData.cpp index 3b9ab6d7e5c31..3e2bc4c952c8e 100644 --- a/sky/engine/core/rendering/style/StyleFilterData.cpp +++ b/sky/engine/core/rendering/style/StyleFilterData.cpp @@ -27,18 +27,12 @@ namespace blink { -StyleFilterData::StyleFilterData() -{ -} +StyleFilterData::StyleFilterData() {} -StyleFilterData::StyleFilterData(const StyleFilterData& o) -{ -} +StyleFilterData::StyleFilterData(const StyleFilterData& o) {} -bool StyleFilterData::operator==(const StyleFilterData& o) const -{ - return true; +bool StyleFilterData::operator==(const StyleFilterData& o) const { + return true; } -} // namespace blink - +} // namespace blink diff --git a/sky/engine/core/rendering/style/StyleFilterData.h b/sky/engine/core/rendering/style/StyleFilterData.h index a2ac4218948b3..efaadb52a3c38 100644 --- a/sky/engine/core/rendering/style/StyleFilterData.h +++ b/sky/engine/core/rendering/style/StyleFilterData.h @@ -32,22 +32,22 @@ namespace blink { class StyleFilterData : public RefCounted { -public: - static PassRefPtr create() { return adoptRef(new StyleFilterData); } - PassRefPtr copy() const { return adoptRef(new StyleFilterData(*this)); } - - bool operator==(const StyleFilterData&) const; - bool operator!=(const StyleFilterData& o) const - { - return !(*this == o); - } - -private: - StyleFilterData(); - StyleFilterData(const StyleFilterData&); + public: + static PassRefPtr create() { + return adoptRef(new StyleFilterData); + } + PassRefPtr copy() const { + return adoptRef(new StyleFilterData(*this)); + } + + bool operator==(const StyleFilterData&) const; + bool operator!=(const StyleFilterData& o) const { return !(*this == o); } + + private: + StyleFilterData(); + StyleFilterData(const StyleFilterData&); }; -} // namespace blink - +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_STYLE_STYLEFILTERDATA_H_ diff --git a/sky/engine/core/rendering/style/StyleFlexibleBoxData.cpp b/sky/engine/core/rendering/style/StyleFlexibleBoxData.cpp index 58f31faa96329..d2117d1fce58b 100644 --- a/sky/engine/core/rendering/style/StyleFlexibleBoxData.cpp +++ b/sky/engine/core/rendering/style/StyleFlexibleBoxData.cpp @@ -10,16 +10,17 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. * */ @@ -30,28 +31,24 @@ namespace blink { StyleFlexibleBoxData::StyleFlexibleBoxData() - : m_flexGrow(RenderStyle::initialFlexGrow()) - , m_flexShrink(RenderStyle::initialFlexShrink()) - , m_flexBasis(RenderStyle::initialFlexBasis()) - , m_flexDirection(RenderStyle::initialFlexDirection()) - , m_flexWrap(RenderStyle::initialFlexWrap()) -{ -} + : m_flexGrow(RenderStyle::initialFlexGrow()), + m_flexShrink(RenderStyle::initialFlexShrink()), + m_flexBasis(RenderStyle::initialFlexBasis()), + m_flexDirection(RenderStyle::initialFlexDirection()), + m_flexWrap(RenderStyle::initialFlexWrap()) {} StyleFlexibleBoxData::StyleFlexibleBoxData(const StyleFlexibleBoxData& o) - : RefCounted() - , m_flexGrow(o.m_flexGrow) - , m_flexShrink(o.m_flexShrink) - , m_flexBasis(o.m_flexBasis) - , m_flexDirection(o.m_flexDirection) - , m_flexWrap(o.m_flexWrap) -{ -} + : RefCounted(), + m_flexGrow(o.m_flexGrow), + m_flexShrink(o.m_flexShrink), + m_flexBasis(o.m_flexBasis), + m_flexDirection(o.m_flexDirection), + m_flexWrap(o.m_flexWrap) {} -bool StyleFlexibleBoxData::operator==(const StyleFlexibleBoxData& o) const -{ - return m_flexGrow == o.m_flexGrow && m_flexShrink == o.m_flexShrink && m_flexBasis == o.m_flexBasis - && m_flexDirection == o.m_flexDirection && m_flexWrap == o.m_flexWrap; +bool StyleFlexibleBoxData::operator==(const StyleFlexibleBoxData& o) const { + return m_flexGrow == o.m_flexGrow && m_flexShrink == o.m_flexShrink && + m_flexBasis == o.m_flexBasis && m_flexDirection == o.m_flexDirection && + m_flexWrap == o.m_flexWrap; } -} +} // namespace blink diff --git a/sky/engine/core/rendering/style/StyleFlexibleBoxData.h b/sky/engine/core/rendering/style/StyleFlexibleBoxData.h index 2bb32e6d11eba..e6058befae749 100644 --- a/sky/engine/core/rendering/style/StyleFlexibleBoxData.h +++ b/sky/engine/core/rendering/style/StyleFlexibleBoxData.h @@ -10,16 +10,17 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. * */ @@ -34,28 +35,29 @@ namespace blink { class StyleFlexibleBoxData : public RefCounted { -public: - static PassRefPtr create() { return adoptRef(new StyleFlexibleBoxData); } - PassRefPtr copy() const { return adoptRef(new StyleFlexibleBoxData(*this)); } - - bool operator==(const StyleFlexibleBoxData&) const; - bool operator!=(const StyleFlexibleBoxData& o) const - { - return !(*this == o); - } - - float m_flexGrow; - float m_flexShrink; - Length m_flexBasis; - - unsigned m_flexDirection : 2; // EFlexDirection - unsigned m_flexWrap : 2; // EFlexWrap - -private: - StyleFlexibleBoxData(); - StyleFlexibleBoxData(const StyleFlexibleBoxData&); + public: + static PassRefPtr create() { + return adoptRef(new StyleFlexibleBoxData); + } + PassRefPtr copy() const { + return adoptRef(new StyleFlexibleBoxData(*this)); + } + + bool operator==(const StyleFlexibleBoxData&) const; + bool operator!=(const StyleFlexibleBoxData& o) const { return !(*this == o); } + + float m_flexGrow; + float m_flexShrink; + Length m_flexBasis; + + unsigned m_flexDirection : 2; // EFlexDirection + unsigned m_flexWrap : 2; // EFlexWrap + + private: + StyleFlexibleBoxData(); + StyleFlexibleBoxData(const StyleFlexibleBoxData&); }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_STYLE_STYLEFLEXIBLEBOXDATA_H_ diff --git a/sky/engine/core/rendering/style/StyleImage.h b/sky/engine/core/rendering/style/StyleImage.h index e074db98948be..0cec9e265a292 100644 --- a/sky/engine/core/rendering/style/StyleImage.h +++ b/sky/engine/core/rendering/style/StyleImage.h @@ -38,47 +38,49 @@ class RenderObject; typedef void* WrappedImagePtr; class StyleImage : public RefCounted { -public: - virtual ~StyleImage() { } + public: + virtual ~StyleImage() {} - bool operator==(const StyleImage& other) const - { - return data() == other.data(); - } + bool operator==(const StyleImage& other) const { + return data() == other.data(); + } - virtual bool canRender(const RenderObject&) const { return true; } - virtual bool isLoaded() const { return true; } - virtual bool errorOccurred() const { return false; } - virtual LayoutSize imageSize(const RenderObject*) const = 0; - virtual void computeIntrinsicDimensions(const RenderObject*, Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio) = 0; - virtual bool imageHasRelativeWidth() const = 0; - virtual bool imageHasRelativeHeight() const = 0; - virtual bool usesImageContainerSize() const = 0; - virtual void setContainerSizeForRenderer(const RenderObject*, const IntSize&) = 0; - virtual void addClient(RenderObject*) = 0; - virtual void removeClient(RenderObject*) = 0; - virtual PassRefPtr image(RenderObject*, const IntSize&) const = 0; - virtual WrappedImagePtr data() const = 0; - virtual float imageScaleFactor() const { return 1; } - virtual bool knownToBeOpaque(const RenderObject*) const = 0; + virtual bool canRender(const RenderObject&) const { return true; } + virtual bool isLoaded() const { return true; } + virtual bool errorOccurred() const { return false; } + virtual LayoutSize imageSize(const RenderObject*) const = 0; + virtual void computeIntrinsicDimensions(const RenderObject*, + Length& intrinsicWidth, + Length& intrinsicHeight, + FloatSize& intrinsicRatio) = 0; + virtual bool imageHasRelativeWidth() const = 0; + virtual bool imageHasRelativeHeight() const = 0; + virtual bool usesImageContainerSize() const = 0; + virtual void setContainerSizeForRenderer(const RenderObject*, + const IntSize&) = 0; + virtual void addClient(RenderObject*) = 0; + virtual void removeClient(RenderObject*) = 0; + virtual PassRefPtr image(RenderObject*, const IntSize&) const = 0; + virtual WrappedImagePtr data() const = 0; + virtual float imageScaleFactor() const { return 1; } + virtual bool knownToBeOpaque(const RenderObject*) const = 0; - ALWAYS_INLINE bool isPendingImage() const { return m_isPendingImage; } - ALWAYS_INLINE bool isGeneratedImage() const { return m_isGeneratedImage; } + ALWAYS_INLINE bool isPendingImage() const { return m_isPendingImage; } + ALWAYS_INLINE bool isGeneratedImage() const { return m_isGeneratedImage; } -protected: - StyleImage() - : m_isPendingImage(false) - , m_isGeneratedImage(false) - { - } - bool m_isPendingImage:1; - bool m_isGeneratedImage:1; + protected: + StyleImage() : m_isPendingImage(false), m_isGeneratedImage(false) {} + bool m_isPendingImage : 1; + bool m_isGeneratedImage : 1; }; -#define DEFINE_STYLE_IMAGE_TYPE_CASTS(thisType, function) \ - DEFINE_TYPE_CASTS(thisType, StyleImage, styleImage, styleImage->function, styleImage.function); \ - inline thisType* to##thisType(const RefPtr& styleImage) { return to##thisType(styleImage.get()); } \ - typedef int NeedsSemiColonAfterDefineStyleImageTypeCasts +#define DEFINE_STYLE_IMAGE_TYPE_CASTS(thisType, function) \ + DEFINE_TYPE_CASTS(thisType, StyleImage, styleImage, styleImage->function, \ + styleImage.function); \ + inline thisType* to##thisType(const RefPtr& styleImage) { \ + return to##thisType(styleImage.get()); \ + } \ + typedef int NeedsSemiColonAfterDefineStyleImageTypeCasts -} +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_STYLE_STYLEIMAGE_H_ diff --git a/sky/engine/core/rendering/style/StyleInheritedData.cpp b/sky/engine/core/rendering/style/StyleInheritedData.cpp index 8076bd8b53aa5..97163776a596f 100644 --- a/sky/engine/core/rendering/style/StyleInheritedData.cpp +++ b/sky/engine/core/rendering/style/StyleInheritedData.cpp @@ -26,34 +26,25 @@ namespace blink { StyleInheritedData::StyleInheritedData() - : horizontal_border_spacing(RenderStyle::initialHorizontalBorderSpacing()) - , vertical_border_spacing(RenderStyle::initialVerticalBorderSpacing()) - , line_height(RenderStyle::initialLineHeight()) - , color(RenderStyle::initialColor()) -{ -} + : horizontal_border_spacing(RenderStyle::initialHorizontalBorderSpacing()), + vertical_border_spacing(RenderStyle::initialVerticalBorderSpacing()), + line_height(RenderStyle::initialLineHeight()), + color(RenderStyle::initialColor()) {} -StyleInheritedData::~StyleInheritedData() -{ -} +StyleInheritedData::~StyleInheritedData() {} StyleInheritedData::StyleInheritedData(const StyleInheritedData& o) - : RefCounted() - , horizontal_border_spacing(o.horizontal_border_spacing) - , vertical_border_spacing(o.vertical_border_spacing) - , line_height(o.line_height) - , font(o.font) - , color(o.color) -{ -} - -bool StyleInheritedData::operator==(const StyleInheritedData& o) const -{ - return line_height == o.line_height - && font == o.font - && color == o.color - && horizontal_border_spacing == o.horizontal_border_spacing - && vertical_border_spacing == o.vertical_border_spacing; + : RefCounted(), + horizontal_border_spacing(o.horizontal_border_spacing), + vertical_border_spacing(o.vertical_border_spacing), + line_height(o.line_height), + font(o.font), + color(o.color) {} + +bool StyleInheritedData::operator==(const StyleInheritedData& o) const { + return line_height == o.line_height && font == o.font && color == o.color && + horizontal_border_spacing == o.horizontal_border_spacing && + vertical_border_spacing == o.vertical_border_spacing; } -} // namespace blink +} // namespace blink diff --git a/sky/engine/core/rendering/style/StyleInheritedData.h b/sky/engine/core/rendering/style/StyleInheritedData.h index c128cac8b1151..a849f21eda334 100644 --- a/sky/engine/core/rendering/style/StyleInheritedData.h +++ b/sky/engine/core/rendering/style/StyleInheritedData.h @@ -35,32 +35,33 @@ namespace blink { class StyleInheritedData : public RefCounted { -public: - static PassRefPtr create() { return adoptRef(new StyleInheritedData); } - PassRefPtr copy() const { return adoptRef(new StyleInheritedData(*this)); } - ~StyleInheritedData(); + public: + static PassRefPtr create() { + return adoptRef(new StyleInheritedData); + } + PassRefPtr copy() const { + return adoptRef(new StyleInheritedData(*this)); + } + ~StyleInheritedData(); - bool operator==(const StyleInheritedData& o) const; - bool operator!=(const StyleInheritedData& o) const - { - return !(*this == o); - } + bool operator==(const StyleInheritedData& o) const; + bool operator!=(const StyleInheritedData& o) const { return !(*this == o); } - short horizontal_border_spacing; - short vertical_border_spacing; + short horizontal_border_spacing; + short vertical_border_spacing; - // could be packed in a short but doesn't - // make a difference currently because of padding - Length line_height; + // could be packed in a short but doesn't + // make a difference currently because of padding + Length line_height; - Font font; - Color color; + Font font; + Color color; -private: - StyleInheritedData(); - StyleInheritedData(const StyleInheritedData&); + private: + StyleInheritedData(); + StyleInheritedData(const StyleInheritedData&); }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_STYLE_STYLEINHERITEDDATA_H_ diff --git a/sky/engine/core/rendering/style/StyleRareInheritedData.cpp b/sky/engine/core/rendering/style/StyleRareInheritedData.cpp index 19379c8a4e009..d60b15a44ebe7 100644 --- a/sky/engine/core/rendering/style/StyleRareInheritedData.cpp +++ b/sky/engine/core/rendering/style/StyleRareInheritedData.cpp @@ -1,6 +1,7 @@ /* * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org) - * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. + * All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -31,130 +32,117 @@ namespace blink { StyleRareInheritedData::StyleRareInheritedData() - : textStrokeWidth(RenderStyle::initialTextStrokeWidth()) - , indent(RenderStyle::initialTextIndent()) - , m_textStrokeColorIsCurrentColor(true) - , m_textFillColorIsCurrentColor(true) - , m_textEmphasisColorIsCurrentColor(true) - , userModify(READ_ONLY) - , wordBreak(RenderStyle::initialWordBreak()) - , overflowWrap(RenderStyle::initialOverflowWrap()) - , lineBreak(LineBreakAuto) - , userSelect(RenderStyle::initialUserSelect()) - , hyphens(HyphensManual) - , textEmphasisFill(TextEmphasisFillFilled) - , textEmphasisMark(TextEmphasisMarkNone) - , textEmphasisPosition(TextEmphasisPositionOver) - , m_textAlignLast(RenderStyle::initialTextAlignLast()) - , m_textJustify(RenderStyle::initialTextJustify()) - , m_textOrientation(TextOrientationVerticalRight) - , m_textIndentLine(RenderStyle::initialTextIndentLine()) - , m_textIndentType(RenderStyle::initialTextIndentLine()) - , m_lineBoxContain(RenderStyle::initialLineBoxContain()) - , m_imageRendering(RenderStyle::initialImageRendering()) - , m_textUnderlinePosition(RenderStyle::initialTextUnderlinePosition()) - , m_touchActionDelay(RenderStyle::initialTouchActionDelay()) - , m_subtreeWillChangeContents(false) - , hyphenationLimitBefore(-1) - , hyphenationLimitAfter(-1) - , hyphenationLimitLines(-1) - , m_tabSize(RenderStyle::initialTabSize()) - , tapHighlightColor(RenderStyle::initialTapHighlightColor()) -{ -} + : textStrokeWidth(RenderStyle::initialTextStrokeWidth()), + indent(RenderStyle::initialTextIndent()), + m_textStrokeColorIsCurrentColor(true), + m_textFillColorIsCurrentColor(true), + m_textEmphasisColorIsCurrentColor(true), + userModify(READ_ONLY), + wordBreak(RenderStyle::initialWordBreak()), + overflowWrap(RenderStyle::initialOverflowWrap()), + lineBreak(LineBreakAuto), + userSelect(RenderStyle::initialUserSelect()), + hyphens(HyphensManual), + textEmphasisFill(TextEmphasisFillFilled), + textEmphasisMark(TextEmphasisMarkNone), + textEmphasisPosition(TextEmphasisPositionOver), + m_textAlignLast(RenderStyle::initialTextAlignLast()), + m_textJustify(RenderStyle::initialTextJustify()), + m_textOrientation(TextOrientationVerticalRight), + m_textIndentLine(RenderStyle::initialTextIndentLine()), + m_textIndentType(RenderStyle::initialTextIndentLine()), + m_lineBoxContain(RenderStyle::initialLineBoxContain()), + m_imageRendering(RenderStyle::initialImageRendering()), + m_textUnderlinePosition(RenderStyle::initialTextUnderlinePosition()), + m_touchActionDelay(RenderStyle::initialTouchActionDelay()), + m_subtreeWillChangeContents(false), + hyphenationLimitBefore(-1), + hyphenationLimitAfter(-1), + hyphenationLimitLines(-1), + m_tabSize(RenderStyle::initialTabSize()), + tapHighlightColor(RenderStyle::initialTapHighlightColor()) {} StyleRareInheritedData::StyleRareInheritedData(const StyleRareInheritedData& o) - : RefCounted() - , m_textStrokeColor(o.m_textStrokeColor) - , textStrokeWidth(o.textStrokeWidth) - , m_textFillColor(o.m_textFillColor) - , m_textEmphasisColor(o.m_textEmphasisColor) - , textShadow(o.textShadow) - , highlight(o.highlight) - , indent(o.indent) - , m_textStrokeColorIsCurrentColor(o.m_textStrokeColorIsCurrentColor) - , m_textFillColorIsCurrentColor(o.m_textFillColorIsCurrentColor) - , m_textEmphasisColorIsCurrentColor(o.m_textEmphasisColorIsCurrentColor) - , userModify(o.userModify) - , wordBreak(o.wordBreak) - , overflowWrap(o.overflowWrap) - , lineBreak(o.lineBreak) - , userSelect(o.userSelect) - , hyphens(o.hyphens) - , textEmphasisFill(o.textEmphasisFill) - , textEmphasisMark(o.textEmphasisMark) - , textEmphasisPosition(o.textEmphasisPosition) - , m_textAlignLast(o.m_textAlignLast) - , m_textJustify(o.m_textJustify) - , m_textOrientation(o.m_textOrientation) - , m_textIndentLine(o.m_textIndentLine) - , m_textIndentType(o.m_textIndentType) - , m_lineBoxContain(o.m_lineBoxContain) - , m_imageRendering(o.m_imageRendering) - , m_textUnderlinePosition(o.m_textUnderlinePosition) - , m_touchActionDelay(o.m_touchActionDelay) - , m_subtreeWillChangeContents(o.m_subtreeWillChangeContents) - , hyphenationLimitBefore(o.hyphenationLimitBefore) - , hyphenationLimitAfter(o.hyphenationLimitAfter) - , hyphenationLimitLines(o.hyphenationLimitLines) - , hyphenationString(o.hyphenationString) - , locale(o.locale) - , textEmphasisCustomMark(o.textEmphasisCustomMark) - , m_tabSize(o.m_tabSize) - , tapHighlightColor(o.tapHighlightColor) - , appliedTextDecorations(o.appliedTextDecorations) -{ -} + : RefCounted(), + m_textStrokeColor(o.m_textStrokeColor), + textStrokeWidth(o.textStrokeWidth), + m_textFillColor(o.m_textFillColor), + m_textEmphasisColor(o.m_textEmphasisColor), + textShadow(o.textShadow), + highlight(o.highlight), + indent(o.indent), + m_textStrokeColorIsCurrentColor(o.m_textStrokeColorIsCurrentColor), + m_textFillColorIsCurrentColor(o.m_textFillColorIsCurrentColor), + m_textEmphasisColorIsCurrentColor(o.m_textEmphasisColorIsCurrentColor), + userModify(o.userModify), + wordBreak(o.wordBreak), + overflowWrap(o.overflowWrap), + lineBreak(o.lineBreak), + userSelect(o.userSelect), + hyphens(o.hyphens), + textEmphasisFill(o.textEmphasisFill), + textEmphasisMark(o.textEmphasisMark), + textEmphasisPosition(o.textEmphasisPosition), + m_textAlignLast(o.m_textAlignLast), + m_textJustify(o.m_textJustify), + m_textOrientation(o.m_textOrientation), + m_textIndentLine(o.m_textIndentLine), + m_textIndentType(o.m_textIndentType), + m_lineBoxContain(o.m_lineBoxContain), + m_imageRendering(o.m_imageRendering), + m_textUnderlinePosition(o.m_textUnderlinePosition), + m_touchActionDelay(o.m_touchActionDelay), + m_subtreeWillChangeContents(o.m_subtreeWillChangeContents), + hyphenationLimitBefore(o.hyphenationLimitBefore), + hyphenationLimitAfter(o.hyphenationLimitAfter), + hyphenationLimitLines(o.hyphenationLimitLines), + hyphenationString(o.hyphenationString), + locale(o.locale), + textEmphasisCustomMark(o.textEmphasisCustomMark), + m_tabSize(o.m_tabSize), + tapHighlightColor(o.tapHighlightColor), + appliedTextDecorations(o.appliedTextDecorations) {} -StyleRareInheritedData::~StyleRareInheritedData() -{ -} +StyleRareInheritedData::~StyleRareInheritedData() {} -bool StyleRareInheritedData::operator==(const StyleRareInheritedData& o) const -{ - return m_textStrokeColor == o.m_textStrokeColor - && textStrokeWidth == o.textStrokeWidth - && m_textFillColor == o.m_textFillColor - && m_textEmphasisColor == o.m_textEmphasisColor - && tapHighlightColor == o.tapHighlightColor - && shadowDataEquivalent(o) - && highlight == o.highlight - && indent == o.indent - && m_textStrokeColorIsCurrentColor == o.m_textStrokeColorIsCurrentColor - && m_textFillColorIsCurrentColor == o.m_textFillColorIsCurrentColor - && m_textEmphasisColorIsCurrentColor == o.m_textEmphasisColorIsCurrentColor - && userModify == o.userModify - && wordBreak == o.wordBreak - && overflowWrap == o.overflowWrap - && lineBreak == o.lineBreak - && userSelect == o.userSelect - && hyphens == o.hyphens - && hyphenationLimitBefore == o.hyphenationLimitBefore - && hyphenationLimitAfter == o.hyphenationLimitAfter - && hyphenationLimitLines == o.hyphenationLimitLines - && textEmphasisFill == o.textEmphasisFill - && textEmphasisMark == o.textEmphasisMark - && textEmphasisPosition == o.textEmphasisPosition - && m_touchActionDelay == o.m_touchActionDelay - && m_textAlignLast == o.m_textAlignLast - && m_textJustify == o.m_textJustify - && m_textOrientation == o.m_textOrientation - && m_textIndentLine == o.m_textIndentLine - && m_textIndentType == o.m_textIndentType - && m_lineBoxContain == o.m_lineBoxContain - && m_subtreeWillChangeContents == o.m_subtreeWillChangeContents - && hyphenationString == o.hyphenationString - && locale == o.locale - && textEmphasisCustomMark == o.textEmphasisCustomMark - && m_tabSize == o.m_tabSize - && m_imageRendering == o.m_imageRendering - && m_textUnderlinePosition == o.m_textUnderlinePosition - && dataEquivalent(appliedTextDecorations, o.appliedTextDecorations); +bool StyleRareInheritedData::operator==(const StyleRareInheritedData& o) const { + return m_textStrokeColor == o.m_textStrokeColor && + textStrokeWidth == o.textStrokeWidth && + m_textFillColor == o.m_textFillColor && + m_textEmphasisColor == o.m_textEmphasisColor && + tapHighlightColor == o.tapHighlightColor && shadowDataEquivalent(o) && + highlight == o.highlight && indent == o.indent && + m_textStrokeColorIsCurrentColor == o.m_textStrokeColorIsCurrentColor && + m_textFillColorIsCurrentColor == o.m_textFillColorIsCurrentColor && + m_textEmphasisColorIsCurrentColor == + o.m_textEmphasisColorIsCurrentColor && + userModify == o.userModify && wordBreak == o.wordBreak && + overflowWrap == o.overflowWrap && lineBreak == o.lineBreak && + userSelect == o.userSelect && hyphens == o.hyphens && + hyphenationLimitBefore == o.hyphenationLimitBefore && + hyphenationLimitAfter == o.hyphenationLimitAfter && + hyphenationLimitLines == o.hyphenationLimitLines && + textEmphasisFill == o.textEmphasisFill && + textEmphasisMark == o.textEmphasisMark && + textEmphasisPosition == o.textEmphasisPosition && + m_touchActionDelay == o.m_touchActionDelay && + m_textAlignLast == o.m_textAlignLast && + m_textJustify == o.m_textJustify && + m_textOrientation == o.m_textOrientation && + m_textIndentLine == o.m_textIndentLine && + m_textIndentType == o.m_textIndentType && + m_lineBoxContain == o.m_lineBoxContain && + m_subtreeWillChangeContents == o.m_subtreeWillChangeContents && + hyphenationString == o.hyphenationString && locale == o.locale && + textEmphasisCustomMark == o.textEmphasisCustomMark && + m_tabSize == o.m_tabSize && m_imageRendering == o.m_imageRendering && + m_textUnderlinePosition == o.m_textUnderlinePosition && + dataEquivalent(appliedTextDecorations, o.appliedTextDecorations); } -bool StyleRareInheritedData::shadowDataEquivalent(const StyleRareInheritedData& o) const -{ - return dataEquivalent(textShadow.get(), o.textShadow.get()); +bool StyleRareInheritedData::shadowDataEquivalent( + const StyleRareInheritedData& o) const { + return dataEquivalent(textShadow.get(), o.textShadow.get()); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/core/rendering/style/StyleRareInheritedData.h b/sky/engine/core/rendering/style/StyleRareInheritedData.h index 8ca69f2177a75..1b11f14488274 100644 --- a/sky/engine/core/rendering/style/StyleRareInheritedData.h +++ b/sky/engine/core/rendering/style/StyleRareInheritedData.h @@ -42,88 +42,111 @@ class StyleImage; typedef RefVector AppliedTextDecorationList; -// This struct is for rarely used inherited CSS3, CSS2, and WebKit-specific properties. -// By grouping them together, we save space, and only allocate this object when someone -// actually uses one of these properties. +// This struct is for rarely used inherited CSS3, CSS2, and WebKit-specific +// properties. By grouping them together, we save space, and only allocate this +// object when someone actually uses one of these properties. class StyleRareInheritedData : public RefCounted { -public: - static PassRefPtr create() { return adoptRef(new StyleRareInheritedData); } - PassRefPtr copy() const { return adoptRef(new StyleRareInheritedData(*this)); } - ~StyleRareInheritedData(); - - bool operator==(const StyleRareInheritedData& o) const; - bool operator!=(const StyleRareInheritedData& o) const - { - return !(*this == o); - } - bool shadowDataEquivalent(const StyleRareInheritedData&) const; - - StyleColor textStrokeColor() const { return m_textStrokeColorIsCurrentColor ? StyleColor::currentColor() : StyleColor(m_textStrokeColor); } - StyleColor textFillColor() const { return m_textFillColorIsCurrentColor ? StyleColor::currentColor() : StyleColor(m_textFillColor); } - StyleColor textEmphasisColor() const { return m_textEmphasisColorIsCurrentColor ? StyleColor::currentColor() : StyleColor(m_textEmphasisColor); } - - void setTextStrokeColor(const StyleColor& color) { m_textStrokeColor = color.resolve(Color()); m_textStrokeColorIsCurrentColor = color.isCurrentColor(); } - void setTextFillColor(const StyleColor& color) { m_textFillColor = color.resolve(Color()); m_textFillColorIsCurrentColor = color.isCurrentColor(); } - void setTextEmphasisColor(const StyleColor& color) { m_textEmphasisColor = color.resolve(Color()); m_textEmphasisColorIsCurrentColor = color.isCurrentColor(); } - - Color m_textStrokeColor; - float textStrokeWidth; - Color m_textFillColor; - Color m_textEmphasisColor; - - RefPtr textShadow; // Our text shadow information for shadowed text drawing. - AtomicString highlight; // Apple-specific extension for custom highlight rendering. - - Length indent; - - unsigned m_textStrokeColorIsCurrentColor : 1; - unsigned m_textFillColorIsCurrentColor : 1; - unsigned m_textEmphasisColorIsCurrentColor : 1; - - unsigned userModify : 2; // EUserModify (editing) - unsigned wordBreak : 2; // EWordBreak - unsigned overflowWrap : 1; // EOverflowWrap - unsigned lineBreak : 3; // LineBreak - unsigned userSelect : 2; // EUserSelect - unsigned hyphens : 2; // Hyphens - unsigned textEmphasisFill : 1; // TextEmphasisFill - unsigned textEmphasisMark : 3; // TextEmphasisMark - unsigned textEmphasisPosition : 1; // TextEmphasisPosition - unsigned m_textAlignLast : 3; // TextAlignLast - unsigned m_textJustify : 2; // TextJustify - unsigned m_textOrientation : 2; // TextOrientation - unsigned m_textIndentLine : 1; // TextIndentEachLine - unsigned m_textIndentType : 1; // TextIndentHanging - unsigned m_lineBoxContain: 7; // LineBoxContain - // CSS Image Values Level 3 - unsigned m_imageRendering : 3; // EImageRendering - unsigned m_textUnderlinePosition : 1; // TextUnderlinePosition - unsigned m_touchActionDelay : 1; // TouchActionDelay - - // Though will-change is not itself an inherited property, the intent - // expressed by 'will-change: contents' includes descendants. - unsigned m_subtreeWillChangeContents : 1; - - short hyphenationLimitBefore; - short hyphenationLimitAfter; - short hyphenationLimitLines; - AtomicString hyphenationString; - - AtomicString locale; - - AtomicString textEmphasisCustomMark; - - unsigned m_tabSize; - - Color tapHighlightColor; - - RefPtr appliedTextDecorations; - -private: - StyleRareInheritedData(); - StyleRareInheritedData(const StyleRareInheritedData&); + public: + static PassRefPtr create() { + return adoptRef(new StyleRareInheritedData); + } + PassRefPtr copy() const { + return adoptRef(new StyleRareInheritedData(*this)); + } + ~StyleRareInheritedData(); + + bool operator==(const StyleRareInheritedData& o) const; + bool operator!=(const StyleRareInheritedData& o) const { + return !(*this == o); + } + bool shadowDataEquivalent(const StyleRareInheritedData&) const; + + StyleColor textStrokeColor() const { + return m_textStrokeColorIsCurrentColor ? StyleColor::currentColor() + : StyleColor(m_textStrokeColor); + } + StyleColor textFillColor() const { + return m_textFillColorIsCurrentColor ? StyleColor::currentColor() + : StyleColor(m_textFillColor); + } + StyleColor textEmphasisColor() const { + return m_textEmphasisColorIsCurrentColor ? StyleColor::currentColor() + : StyleColor(m_textEmphasisColor); + } + + void setTextStrokeColor(const StyleColor& color) { + m_textStrokeColor = color.resolve(Color()); + m_textStrokeColorIsCurrentColor = color.isCurrentColor(); + } + void setTextFillColor(const StyleColor& color) { + m_textFillColor = color.resolve(Color()); + m_textFillColorIsCurrentColor = color.isCurrentColor(); + } + void setTextEmphasisColor(const StyleColor& color) { + m_textEmphasisColor = color.resolve(Color()); + m_textEmphasisColorIsCurrentColor = color.isCurrentColor(); + } + + Color m_textStrokeColor; + float textStrokeWidth; + Color m_textFillColor; + Color m_textEmphasisColor; + + RefPtr + textShadow; // Our text shadow information for shadowed text drawing. + AtomicString + highlight; // Apple-specific extension for custom highlight rendering. + + Length indent; + + unsigned m_textStrokeColorIsCurrentColor : 1; + unsigned m_textFillColorIsCurrentColor : 1; + unsigned m_textEmphasisColorIsCurrentColor : 1; + + unsigned userModify : 2; // EUserModify (editing) + unsigned wordBreak : 2; // EWordBreak + unsigned overflowWrap : 1; // EOverflowWrap + unsigned lineBreak : 3; // LineBreak + unsigned userSelect : 2; // EUserSelect + unsigned hyphens : 2; // Hyphens + unsigned textEmphasisFill : 1; // TextEmphasisFill + unsigned textEmphasisMark : 3; // TextEmphasisMark + unsigned textEmphasisPosition : 1; // TextEmphasisPosition + unsigned m_textAlignLast : 3; // TextAlignLast + unsigned m_textJustify : 2; // TextJustify + unsigned m_textOrientation : 2; // TextOrientation + unsigned m_textIndentLine : 1; // TextIndentEachLine + unsigned m_textIndentType : 1; // TextIndentHanging + unsigned m_lineBoxContain : 7; // LineBoxContain + // CSS Image Values Level 3 + unsigned m_imageRendering : 3; // EImageRendering + unsigned m_textUnderlinePosition : 1; // TextUnderlinePosition + unsigned m_touchActionDelay : 1; // TouchActionDelay + + // Though will-change is not itself an inherited property, the intent + // expressed by 'will-change: contents' includes descendants. + unsigned m_subtreeWillChangeContents : 1; + + short hyphenationLimitBefore; + short hyphenationLimitAfter; + short hyphenationLimitLines; + AtomicString hyphenationString; + + AtomicString locale; + + AtomicString textEmphasisCustomMark; + + unsigned m_tabSize; + + Color tapHighlightColor; + + RefPtr appliedTextDecorations; + + private: + StyleRareInheritedData(); + StyleRareInheritedData(const StyleRareInheritedData&); }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_STYLE_STYLERAREINHERITEDDATA_H_ diff --git a/sky/engine/core/rendering/style/StyleRareNonInheritedData.cpp b/sky/engine/core/rendering/style/StyleRareNonInheritedData.cpp index 7d775642ae60f..5eff2a086da5d 100644 --- a/sky/engine/core/rendering/style/StyleRareNonInheritedData.cpp +++ b/sky/engine/core/rendering/style/StyleRareNonInheritedData.cpp @@ -30,130 +30,124 @@ namespace blink { StyleRareNonInheritedData::StyleRareNonInheritedData() - : opacity(RenderStyle::initialOpacity()) - , m_aspectRatioDenominator(RenderStyle::initialAspectRatioDenominator()) - , m_aspectRatioNumerator(RenderStyle::initialAspectRatioNumerator()) - , m_perspective(RenderStyle::initialPerspective()) - , m_perspectiveOriginX(RenderStyle::initialPerspectiveOriginX()) - , m_perspectiveOriginY(RenderStyle::initialPerspectiveOriginY()) - , m_clipPath(RenderStyle::initialClipPath()) - , m_textDecorationColor(StyleColor::currentColor()) - , m_order(RenderStyle::initialOrder()) - , m_objectPosition(RenderStyle::initialObjectPosition()) - , m_maxLines(INT_MAX) - , m_transformStyle3D(RenderStyle::initialTransformStyle3D()) - , m_alignContent(RenderStyle::initialAlignContent()) - , m_alignItems(RenderStyle::initialAlignItems()) - , m_alignItemsOverflowAlignment(RenderStyle::initialAlignItemsOverflowAlignment()) - , m_alignSelf(RenderStyle::initialAlignSelf()) - , m_alignSelfOverflowAlignment(RenderStyle::initialAlignSelfOverflowAlignment()) - , m_justifyContent(RenderStyle::initialJustifyContent()) - , textOverflow(RenderStyle::initialTextOverflow()) - , m_textDecorationStyle(RenderStyle::initialTextDecorationStyle()) - , m_wrapFlow(RenderStyle::initialWrapFlow()) - , m_wrapThrough(RenderStyle::initialWrapThrough()) - , m_hasAspectRatio(false) - , m_touchAction(RenderStyle::initialTouchAction()) - , m_objectFit(RenderStyle::initialObjectFit()) - , m_justifyItems(RenderStyle::initialJustifyItems()) - , m_justifyItemsOverflowAlignment(RenderStyle::initialJustifyItemsOverflowAlignment()) - , m_justifyItemsPositionType(RenderStyle::initialJustifyItemsPositionType()) - , m_justifySelf(RenderStyle::initialJustifySelf()) - , m_justifySelfOverflowAlignment(RenderStyle::initialJustifySelfOverflowAlignment()) -{ -} + : opacity(RenderStyle::initialOpacity()), + m_aspectRatioDenominator(RenderStyle::initialAspectRatioDenominator()), + m_aspectRatioNumerator(RenderStyle::initialAspectRatioNumerator()), + m_perspective(RenderStyle::initialPerspective()), + m_perspectiveOriginX(RenderStyle::initialPerspectiveOriginX()), + m_perspectiveOriginY(RenderStyle::initialPerspectiveOriginY()), + m_clipPath(RenderStyle::initialClipPath()), + m_textDecorationColor(StyleColor::currentColor()), + m_order(RenderStyle::initialOrder()), + m_objectPosition(RenderStyle::initialObjectPosition()), + m_maxLines(INT_MAX), + m_transformStyle3D(RenderStyle::initialTransformStyle3D()), + m_alignContent(RenderStyle::initialAlignContent()), + m_alignItems(RenderStyle::initialAlignItems()), + m_alignItemsOverflowAlignment( + RenderStyle::initialAlignItemsOverflowAlignment()), + m_alignSelf(RenderStyle::initialAlignSelf()), + m_alignSelfOverflowAlignment( + RenderStyle::initialAlignSelfOverflowAlignment()), + m_justifyContent(RenderStyle::initialJustifyContent()), + textOverflow(RenderStyle::initialTextOverflow()), + m_textDecorationStyle(RenderStyle::initialTextDecorationStyle()), + m_wrapFlow(RenderStyle::initialWrapFlow()), + m_wrapThrough(RenderStyle::initialWrapThrough()), + m_hasAspectRatio(false), + m_touchAction(RenderStyle::initialTouchAction()), + m_objectFit(RenderStyle::initialObjectFit()), + m_justifyItems(RenderStyle::initialJustifyItems()), + m_justifyItemsOverflowAlignment( + RenderStyle::initialJustifyItemsOverflowAlignment()), + m_justifyItemsPositionType( + RenderStyle::initialJustifyItemsPositionType()), + m_justifySelf(RenderStyle::initialJustifySelf()), + m_justifySelfOverflowAlignment( + RenderStyle::initialJustifySelfOverflowAlignment()) {} -StyleRareNonInheritedData::StyleRareNonInheritedData(const StyleRareNonInheritedData& o) - : RefCounted() - , opacity(o.opacity) - , m_aspectRatioDenominator(o.m_aspectRatioDenominator) - , m_aspectRatioNumerator(o.m_aspectRatioNumerator) - , m_perspective(o.m_perspective) - , m_perspectiveOriginX(o.m_perspectiveOriginX) - , m_perspectiveOriginY(o.m_perspectiveOriginY) - , m_flexibleBox(o.m_flexibleBox) - , m_transform(o.m_transform) - , m_filter(o.m_filter) - , m_counterDirectives(o.m_counterDirectives ? clone(*o.m_counterDirectives) : nullptr) - , m_boxShadow(o.m_boxShadow) - , m_clipPath(o.m_clipPath) - , m_textDecorationColor(o.m_textDecorationColor) - , m_order(o.m_order) - , m_objectPosition(o.m_objectPosition) - , m_maxLines(o.m_maxLines) - , m_transformStyle3D(o.m_transformStyle3D) - , m_alignContent(o.m_alignContent) - , m_alignItems(o.m_alignItems) - , m_alignItemsOverflowAlignment(o.m_alignItemsOverflowAlignment) - , m_alignSelf(o.m_alignSelf) - , m_alignSelfOverflowAlignment(o.m_alignSelfOverflowAlignment) - , m_justifyContent(o.m_justifyContent) - , textOverflow(o.textOverflow) - , m_textDecorationStyle(o.m_textDecorationStyle) - , m_wrapFlow(o.m_wrapFlow) - , m_wrapThrough(o.m_wrapThrough) - , m_hasAspectRatio(o.m_hasAspectRatio) - , m_touchAction(o.m_touchAction) - , m_objectFit(o.m_objectFit) - , m_justifyItems(o.m_justifyItems) - , m_justifyItemsOverflowAlignment(o.m_justifyItemsOverflowAlignment) - , m_justifyItemsPositionType(o.m_justifyItemsPositionType) - , m_justifySelf(o.m_justifySelf) - , m_justifySelfOverflowAlignment(o.m_justifySelfOverflowAlignment) -{ -} +StyleRareNonInheritedData::StyleRareNonInheritedData( + const StyleRareNonInheritedData& o) + : RefCounted(), + opacity(o.opacity), + m_aspectRatioDenominator(o.m_aspectRatioDenominator), + m_aspectRatioNumerator(o.m_aspectRatioNumerator), + m_perspective(o.m_perspective), + m_perspectiveOriginX(o.m_perspectiveOriginX), + m_perspectiveOriginY(o.m_perspectiveOriginY), + m_flexibleBox(o.m_flexibleBox), + m_transform(o.m_transform), + m_filter(o.m_filter), + m_counterDirectives(o.m_counterDirectives ? clone(*o.m_counterDirectives) + : nullptr), + m_boxShadow(o.m_boxShadow), + m_clipPath(o.m_clipPath), + m_textDecorationColor(o.m_textDecorationColor), + m_order(o.m_order), + m_objectPosition(o.m_objectPosition), + m_maxLines(o.m_maxLines), + m_transformStyle3D(o.m_transformStyle3D), + m_alignContent(o.m_alignContent), + m_alignItems(o.m_alignItems), + m_alignItemsOverflowAlignment(o.m_alignItemsOverflowAlignment), + m_alignSelf(o.m_alignSelf), + m_alignSelfOverflowAlignment(o.m_alignSelfOverflowAlignment), + m_justifyContent(o.m_justifyContent), + textOverflow(o.textOverflow), + m_textDecorationStyle(o.m_textDecorationStyle), + m_wrapFlow(o.m_wrapFlow), + m_wrapThrough(o.m_wrapThrough), + m_hasAspectRatio(o.m_hasAspectRatio), + m_touchAction(o.m_touchAction), + m_objectFit(o.m_objectFit), + m_justifyItems(o.m_justifyItems), + m_justifyItemsOverflowAlignment(o.m_justifyItemsOverflowAlignment), + m_justifyItemsPositionType(o.m_justifyItemsPositionType), + m_justifySelf(o.m_justifySelf), + m_justifySelfOverflowAlignment(o.m_justifySelfOverflowAlignment) {} -StyleRareNonInheritedData::~StyleRareNonInheritedData() -{ -} +StyleRareNonInheritedData::~StyleRareNonInheritedData() {} -bool StyleRareNonInheritedData::operator==(const StyleRareNonInheritedData& o) const -{ - return opacity == o.opacity - && m_aspectRatioDenominator == o.m_aspectRatioDenominator - && m_aspectRatioNumerator == o.m_aspectRatioNumerator - && m_perspective == o.m_perspective - && m_perspectiveOriginX == o.m_perspectiveOriginX - && m_perspectiveOriginY == o.m_perspectiveOriginY - && m_flexibleBox == o.m_flexibleBox - && m_transform == o.m_transform - && m_filter == o.m_filter - && counterDataEquivalent(o) - && shadowDataEquivalent(o) - && m_clipPath == o.m_clipPath - && m_textDecorationColor == o.m_textDecorationColor - && m_order == o.m_order - && m_objectPosition == o.m_objectPosition - && m_maxLines == o.m_maxLines - && m_transformStyle3D == o.m_transformStyle3D - && m_alignContent == o.m_alignContent - && m_alignItems == o.m_alignItems - && m_alignItemsOverflowAlignment == o.m_alignItemsOverflowAlignment - && m_alignSelf == o.m_alignSelf - && m_alignSelfOverflowAlignment == o.m_alignSelfOverflowAlignment - && m_justifyContent == o.m_justifyContent - && textOverflow == o.textOverflow - && m_textDecorationStyle == o.m_textDecorationStyle - && m_wrapFlow == o.m_wrapFlow - && m_wrapThrough == o.m_wrapThrough - && m_hasAspectRatio == o.m_hasAspectRatio - && m_touchAction == o.m_touchAction - && m_objectFit == o.m_objectFit - && m_justifyItems == o.m_justifyItems - && m_justifyItemsOverflowAlignment == o.m_justifyItemsOverflowAlignment - && m_justifyItemsPositionType == o.m_justifyItemsPositionType - && m_justifySelf == o.m_justifySelf - && m_justifySelfOverflowAlignment == o.m_justifySelfOverflowAlignment; +bool StyleRareNonInheritedData::operator==( + const StyleRareNonInheritedData& o) const { + return opacity == o.opacity && + m_aspectRatioDenominator == o.m_aspectRatioDenominator && + m_aspectRatioNumerator == o.m_aspectRatioNumerator && + m_perspective == o.m_perspective && + m_perspectiveOriginX == o.m_perspectiveOriginX && + m_perspectiveOriginY == o.m_perspectiveOriginY && + m_flexibleBox == o.m_flexibleBox && m_transform == o.m_transform && + m_filter == o.m_filter && counterDataEquivalent(o) && + shadowDataEquivalent(o) && m_clipPath == o.m_clipPath && + m_textDecorationColor == o.m_textDecorationColor && + m_order == o.m_order && m_objectPosition == o.m_objectPosition && + m_maxLines == o.m_maxLines && + m_transformStyle3D == o.m_transformStyle3D && + m_alignContent == o.m_alignContent && m_alignItems == o.m_alignItems && + m_alignItemsOverflowAlignment == o.m_alignItemsOverflowAlignment && + m_alignSelf == o.m_alignSelf && + m_alignSelfOverflowAlignment == o.m_alignSelfOverflowAlignment && + m_justifyContent == o.m_justifyContent && + textOverflow == o.textOverflow && + m_textDecorationStyle == o.m_textDecorationStyle && + m_wrapFlow == o.m_wrapFlow && m_wrapThrough == o.m_wrapThrough && + m_hasAspectRatio == o.m_hasAspectRatio && + m_touchAction == o.m_touchAction && m_objectFit == o.m_objectFit && + m_justifyItems == o.m_justifyItems && + m_justifyItemsOverflowAlignment == o.m_justifyItemsOverflowAlignment && + m_justifyItemsPositionType == o.m_justifyItemsPositionType && + m_justifySelf == o.m_justifySelf && + m_justifySelfOverflowAlignment == o.m_justifySelfOverflowAlignment; } -bool StyleRareNonInheritedData::counterDataEquivalent(const StyleRareNonInheritedData& o) const -{ - return dataEquivalent(m_counterDirectives, o.m_counterDirectives); +bool StyleRareNonInheritedData::counterDataEquivalent( + const StyleRareNonInheritedData& o) const { + return dataEquivalent(m_counterDirectives, o.m_counterDirectives); } -bool StyleRareNonInheritedData::shadowDataEquivalent(const StyleRareNonInheritedData& o) const -{ - return dataEquivalent(m_boxShadow, o.m_boxShadow); +bool StyleRareNonInheritedData::shadowDataEquivalent( + const StyleRareNonInheritedData& o) const { + return dataEquivalent(m_boxShadow, o.m_boxShadow); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/core/rendering/style/StyleRareNonInheritedData.h b/sky/engine/core/rendering/style/StyleRareNonInheritedData.h index 15f7f11be807b..e96b4a7349dc8 100644 --- a/sky/engine/core/rendering/style/StyleRareNonInheritedData.h +++ b/sky/engine/core/rendering/style/StyleRareNonInheritedData.h @@ -45,86 +45,97 @@ class StyleFilterData; class StyleFlexibleBoxData; class StyleTransformData; -// This struct is for rarely used non-inherited CSS3, CSS2, and WebKit-specific properties. -// By grouping them together, we save space, and only allocate this object when someone -// actually uses one of these properties. +// This struct is for rarely used non-inherited CSS3, CSS2, and WebKit-specific +// properties. By grouping them together, we save space, and only allocate this +// object when someone actually uses one of these properties. class StyleRareNonInheritedData : public RefCounted { -public: - static PassRefPtr create() { return adoptRef(new StyleRareNonInheritedData); } - PassRefPtr copy() const { return adoptRef(new StyleRareNonInheritedData(*this)); } - ~StyleRareNonInheritedData(); + public: + static PassRefPtr create() { + return adoptRef(new StyleRareNonInheritedData); + } + PassRefPtr copy() const { + return adoptRef(new StyleRareNonInheritedData(*this)); + } + ~StyleRareNonInheritedData(); - bool operator==(const StyleRareNonInheritedData&) const; - bool operator!=(const StyleRareNonInheritedData& o) const { return !(*this == o); } + bool operator==(const StyleRareNonInheritedData&) const; + bool operator!=(const StyleRareNonInheritedData& o) const { + return !(*this == o); + } - bool counterDataEquivalent(const StyleRareNonInheritedData&) const; - bool shadowDataEquivalent(const StyleRareNonInheritedData&) const; - bool hasOpacity() const { return opacity < 1; } + bool counterDataEquivalent(const StyleRareNonInheritedData&) const; + bool shadowDataEquivalent(const StyleRareNonInheritedData&) const; + bool hasOpacity() const { return opacity < 1; } - float opacity; // Whether or not we're transparent. + float opacity; // Whether or not we're transparent. - float m_aspectRatioDenominator; - float m_aspectRatioNumerator; + float m_aspectRatioDenominator; + float m_aspectRatioNumerator; - float m_perspective; - Length m_perspectiveOriginX; - Length m_perspectiveOriginY; + float m_perspective; + Length m_perspectiveOriginX; + Length m_perspectiveOriginY; - DataRef m_flexibleBox; - DataRef m_transform; // Transform properties (rotate, scale, skew, etc.) + DataRef m_flexibleBox; + DataRef + m_transform; // Transform properties (rotate, scale, skew, etc.) - DataRef m_filter; // Filter operations (url, sepia, blur, etc.) + DataRef + m_filter; // Filter operations (url, sepia, blur, etc.) - OwnPtr m_counterDirectives; + OwnPtr m_counterDirectives; - RefPtr m_boxShadow; + RefPtr m_boxShadow; - RefPtr m_clipPath; + RefPtr m_clipPath; - StyleColor m_textDecorationColor; + StyleColor m_textDecorationColor; - int m_order; + int m_order; - LengthPoint m_objectPosition; + LengthPoint m_objectPosition; - AtomicString m_ellipsis; - int m_maxLines; + AtomicString m_ellipsis; + int m_maxLines; - unsigned m_transformStyle3D : 1; // ETransformStyle3D + unsigned m_transformStyle3D : 1; // ETransformStyle3D - unsigned m_alignContent : 3; // EAlignContent - unsigned m_alignItems : 4; // ItemPosition - unsigned m_alignItemsOverflowAlignment : 2; // OverflowAlignment - unsigned m_alignSelf : 4; // ItemPosition - unsigned m_alignSelfOverflowAlignment : 2; // OverflowAlignment - unsigned m_justifyContent : 3; // EJustifyContent + unsigned m_alignContent : 3; // EAlignContent + unsigned m_alignItems : 4; // ItemPosition + unsigned m_alignItemsOverflowAlignment : 2; // OverflowAlignment + unsigned m_alignSelf : 4; // ItemPosition + unsigned m_alignSelfOverflowAlignment : 2; // OverflowAlignment + unsigned m_justifyContent : 3; // EJustifyContent - unsigned textOverflow : 1; // Whether or not lines that spill out should be truncated with "..." + unsigned textOverflow : 1; // Whether or not lines that spill out should be + // truncated with "..." - unsigned m_textDecorationStyle : 3; // TextDecorationStyle - unsigned m_wrapFlow: 3; // WrapFlow - unsigned m_wrapThrough: 1; // WrapThrough + unsigned m_textDecorationStyle : 3; // TextDecorationStyle + unsigned m_wrapFlow : 3; // WrapFlow + unsigned m_wrapThrough : 1; // WrapThrough - unsigned m_hasAspectRatio : 1; // Whether or not an aspect ratio has been specified. + unsigned m_hasAspectRatio : 1; // Whether or not an aspect ratio has been + // specified. - unsigned m_touchAction : TouchActionBits; // TouchAction + unsigned m_touchAction : TouchActionBits; // TouchAction - unsigned m_objectFit : 3; // ObjectFit + unsigned m_objectFit : 3; // ObjectFit - unsigned m_isolation : 1; // Isolation + unsigned m_isolation : 1; // Isolation - unsigned m_justifyItems : 4; // ItemPosition - unsigned m_justifyItemsOverflowAlignment : 2; // OverflowAlignment - unsigned m_justifyItemsPositionType: 1; // Whether or not alignment uses the 'legacy' keyword. + unsigned m_justifyItems : 4; // ItemPosition + unsigned m_justifyItemsOverflowAlignment : 2; // OverflowAlignment + unsigned m_justifyItemsPositionType : 1; // Whether or not alignment uses the + // 'legacy' keyword. - unsigned m_justifySelf : 4; // ItemPosition - unsigned m_justifySelfOverflowAlignment : 2; // OverflowAlignment + unsigned m_justifySelf : 4; // ItemPosition + unsigned m_justifySelfOverflowAlignment : 2; // OverflowAlignment -private: - StyleRareNonInheritedData(); - StyleRareNonInheritedData(const StyleRareNonInheritedData&); + private: + StyleRareNonInheritedData(); + StyleRareNonInheritedData(const StyleRareNonInheritedData&); }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_STYLE_STYLERARENONINHERITEDDATA_H_ diff --git a/sky/engine/core/rendering/style/StyleSurroundData.cpp b/sky/engine/core/rendering/style/StyleSurroundData.cpp index fe6504698ef32..3b4b045fdb100 100644 --- a/sky/engine/core/rendering/style/StyleSurroundData.cpp +++ b/sky/engine/core/rendering/style/StyleSurroundData.cpp @@ -23,24 +23,18 @@ namespace blink { -StyleSurroundData::StyleSurroundData() - : margin(Fixed) - , padding(Fixed) -{ -} +StyleSurroundData::StyleSurroundData() : margin(Fixed), padding(Fixed) {} StyleSurroundData::StyleSurroundData(const StyleSurroundData& o) - : RefCounted() - , offset(o.offset) - , margin(o.margin) - , padding(o.padding) - , border(o.border) -{ -} + : RefCounted(), + offset(o.offset), + margin(o.margin), + padding(o.padding), + border(o.border) {} -bool StyleSurroundData::operator==(const StyleSurroundData& o) const -{ - return offset == o.offset && margin == o.margin && padding == o.padding && border == o.border; +bool StyleSurroundData::operator==(const StyleSurroundData& o) const { + return offset == o.offset && margin == o.margin && padding == o.padding && + border == o.border; } -} // namespace blink +} // namespace blink diff --git a/sky/engine/core/rendering/style/StyleSurroundData.h b/sky/engine/core/rendering/style/StyleSurroundData.h index ad3826af61107..7b36179784cf7 100644 --- a/sky/engine/core/rendering/style/StyleSurroundData.h +++ b/sky/engine/core/rendering/style/StyleSurroundData.h @@ -33,26 +33,27 @@ namespace blink { class StyleSurroundData : public RefCounted { -public: - static PassRefPtr create() { return adoptRef(new StyleSurroundData); } - PassRefPtr copy() const { return adoptRef(new StyleSurroundData(*this)); } - - bool operator==(const StyleSurroundData& o) const; - bool operator!=(const StyleSurroundData& o) const - { - return !(*this == o); - } - - LengthBox offset; - LengthBox margin; - LengthBox padding; - BorderData border; - -private: - StyleSurroundData(); - StyleSurroundData(const StyleSurroundData&); + public: + static PassRefPtr create() { + return adoptRef(new StyleSurroundData); + } + PassRefPtr copy() const { + return adoptRef(new StyleSurroundData(*this)); + } + + bool operator==(const StyleSurroundData& o) const; + bool operator!=(const StyleSurroundData& o) const { return !(*this == o); } + + LengthBox offset; + LengthBox margin; + LengthBox padding; + BorderData border; + + private: + StyleSurroundData(); + StyleSurroundData(const StyleSurroundData&); }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_STYLE_STYLESURROUNDDATA_H_ diff --git a/sky/engine/core/rendering/style/StyleTransformData.cpp b/sky/engine/core/rendering/style/StyleTransformData.cpp index 13c5f39e8f9fe..4038803515519 100644 --- a/sky/engine/core/rendering/style/StyleTransformData.cpp +++ b/sky/engine/core/rendering/style/StyleTransformData.cpp @@ -26,25 +26,21 @@ namespace blink { StyleTransformData::StyleTransformData() - : m_operations(RenderStyle::initialTransform()) - , m_x(RenderStyle::initialTransformOriginX()) - , m_y(RenderStyle::initialTransformOriginY()) - , m_z(RenderStyle::initialTransformOriginZ()) -{ -} + : m_operations(RenderStyle::initialTransform()), + m_x(RenderStyle::initialTransformOriginX()), + m_y(RenderStyle::initialTransformOriginY()), + m_z(RenderStyle::initialTransformOriginZ()) {} StyleTransformData::StyleTransformData(const StyleTransformData& o) - : RefCounted() - , m_operations(o.m_operations) - , m_x(o.m_x) - , m_y(o.m_y) - , m_z(o.m_z) -{ -} + : RefCounted(), + m_operations(o.m_operations), + m_x(o.m_x), + m_y(o.m_y), + m_z(o.m_z) {} -bool StyleTransformData::operator==(const StyleTransformData& o) const -{ - return m_x == o.m_x && m_y == o.m_y && m_z == o.m_z && m_operations == o.m_operations; +bool StyleTransformData::operator==(const StyleTransformData& o) const { + return m_x == o.m_x && m_y == o.m_y && m_z == o.m_z && + m_operations == o.m_operations; } -} // namespace blink +} // namespace blink diff --git a/sky/engine/core/rendering/style/StyleTransformData.h b/sky/engine/core/rendering/style/StyleTransformData.h index 085deb10207e5..e9b5e4647b9ca 100644 --- a/sky/engine/core/rendering/style/StyleTransformData.h +++ b/sky/engine/core/rendering/style/StyleTransformData.h @@ -33,26 +33,27 @@ namespace blink { class StyleTransformData : public RefCounted { -public: - static PassRefPtr create() { return adoptRef(new StyleTransformData); } - PassRefPtr copy() const { return adoptRef(new StyleTransformData(*this)); } - - bool operator==(const StyleTransformData& o) const; - bool operator!=(const StyleTransformData& o) const - { - return !(*this == o); - } - - TransformOperations m_operations; - Length m_x; - Length m_y; - float m_z; - -private: - StyleTransformData(); - StyleTransformData(const StyleTransformData&); + public: + static PassRefPtr create() { + return adoptRef(new StyleTransformData); + } + PassRefPtr copy() const { + return adoptRef(new StyleTransformData(*this)); + } + + bool operator==(const StyleTransformData& o) const; + bool operator!=(const StyleTransformData& o) const { return !(*this == o); } + + TransformOperations m_operations; + Length m_x; + Length m_y; + float m_z; + + private: + StyleTransformData(); + StyleTransformData(const StyleTransformData&); }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_STYLE_STYLETRANSFORMDATA_H_ diff --git a/sky/engine/core/rendering/style/StyleVisualData.cpp b/sky/engine/core/rendering/style/StyleVisualData.cpp index 632a173de36c8..b63967bb6f5a6 100644 --- a/sky/engine/core/rendering/style/StyleVisualData.cpp +++ b/sky/engine/core/rendering/style/StyleVisualData.cpp @@ -26,21 +26,14 @@ namespace blink { StyleVisualData::StyleVisualData() - : hasAutoClip(true) - , textDecoration(RenderStyle::initialTextDecoration()) -{ -} + : hasAutoClip(true), textDecoration(RenderStyle::initialTextDecoration()) {} -StyleVisualData::~StyleVisualData() -{ -} +StyleVisualData::~StyleVisualData() {} StyleVisualData::StyleVisualData(const StyleVisualData& o) - : RefCounted() - , clip(o.clip) - , hasAutoClip(o.hasAutoClip) - , textDecoration(o.textDecoration) -{ -} + : RefCounted(), + clip(o.clip), + hasAutoClip(o.hasAutoClip), + textDecoration(o.textDecoration) {} -} // namespace blink +} // namespace blink diff --git a/sky/engine/core/rendering/style/StyleVisualData.h b/sky/engine/core/rendering/style/StyleVisualData.h index c5e9e81cbec2f..85061ae74f9cf 100644 --- a/sky/engine/core/rendering/style/StyleVisualData.h +++ b/sky/engine/core/rendering/style/StyleVisualData.h @@ -33,29 +33,31 @@ namespace blink { class StyleVisualData : public RefCounted { -public: - static PassRefPtr create() { return adoptRef(new StyleVisualData); } - PassRefPtr copy() const { return adoptRef(new StyleVisualData(*this)); } - ~StyleVisualData(); - - bool operator==(const StyleVisualData& o) const - { - return clip == o.clip - && hasAutoClip == o.hasAutoClip - && textDecoration == o.textDecoration; - } - bool operator!=(const StyleVisualData& o) const { return !(*this == o); } - - LengthBox clip; - bool hasAutoClip : 1; - unsigned textDecoration : TextDecorationBits; // Text decorations defined *only* by this element. - - -private: - StyleVisualData(); - StyleVisualData(const StyleVisualData&); + public: + static PassRefPtr create() { + return adoptRef(new StyleVisualData); + } + PassRefPtr copy() const { + return adoptRef(new StyleVisualData(*this)); + } + ~StyleVisualData(); + + bool operator==(const StyleVisualData& o) const { + return clip == o.clip && hasAutoClip == o.hasAutoClip && + textDecoration == o.textDecoration; + } + bool operator!=(const StyleVisualData& o) const { return !(*this == o); } + + LengthBox clip; + bool hasAutoClip : 1; + unsigned textDecoration + : TextDecorationBits; // Text decorations defined *only* by this element. + + private: + StyleVisualData(); + StyleVisualData(const StyleVisualData&); }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_CORE_RENDERING_STYLE_STYLEVISUALDATA_H_ diff --git a/sky/engine/platform/CalculationValue.h b/sky/engine/platform/CalculationValue.h index 549b1ed1bae23..229eea4b9c479 100644 --- a/sky/engine/platform/CalculationValue.h +++ b/sky/engine/platform/CalculationValue.h @@ -40,30 +40,31 @@ namespace blink { class PLATFORM_EXPORT CalculationValue : public RefCounted { -public: - static PassRefPtr create(PixelsAndPercent value, ValueRange range) - { - return adoptRef(new CalculationValue(value, range)); - } + public: + static PassRefPtr create(PixelsAndPercent value, + ValueRange range) { + return adoptRef(new CalculationValue(value, range)); + } - float evaluate(float maxValue) const { return pixels() + percent() / 100 * maxValue; } - bool operator==(const CalculationValue& o) const { return pixels() == o.pixels() && percent() == o.percent(); } - bool isNonNegative() const { return m_isNonNegative; } - float pixels() const { return m_value.pixels; } - float percent() const { return m_value.percent; } - PixelsAndPercent pixelsAndPercent() const { return m_value; } + float evaluate(float maxValue) const { + return pixels() + percent() / 100 * maxValue; + } + bool operator==(const CalculationValue& o) const { + return pixels() == o.pixels() && percent() == o.percent(); + } + bool isNonNegative() const { return m_isNonNegative; } + float pixels() const { return m_value.pixels; } + float percent() const { return m_value.percent; } + PixelsAndPercent pixelsAndPercent() const { return m_value; } -private: - CalculationValue(PixelsAndPercent value, ValueRange range) - : m_value(value) - , m_isNonNegative(range == ValueRangeNonNegative) - { - } + private: + CalculationValue(PixelsAndPercent value, ValueRange range) + : m_value(value), m_isNonNegative(range == ValueRangeNonNegative) {} - PixelsAndPercent m_value; - bool m_isNonNegative; + PixelsAndPercent m_value; + bool m_isNonNegative; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_CALCULATIONVALUE_H_ diff --git a/sky/engine/platform/Decimal.cpp b/sky/engine/platform/Decimal.cpp index ba78d2b23aabb..62bd3a8a18520 100644 --- a/sky/engine/platform/Decimal.cpp +++ b/sky/engine/platform/Decimal.cpp @@ -45,981 +45,953 @@ static int const ExponentMax = 1023; static int const ExponentMin = -1023; static int const Precision = 18; -static const uint64_t MaxCoefficient = UINT64_C(0xDE0B6B3A763FFFF); // 999999999999999999 == 18 9's +static const uint64_t MaxCoefficient = + UINT64_C(0xDE0B6B3A763FFFF); // 999999999999999999 == 18 9's // This class handles Decimal special values. class SpecialValueHandler { - WTF_MAKE_NONCOPYABLE(SpecialValueHandler); -public: - enum HandleResult { - BothFinite, - BothInfinity, - EitherNaN, - LHSIsInfinity, - RHSIsInfinity, - }; - - SpecialValueHandler(const Decimal& lhs, const Decimal& rhs); - HandleResult handle(); - Decimal value() const; - -private: - enum Result { - ResultIsLHS, - ResultIsRHS, - ResultIsUnknown, - }; - - const Decimal& m_lhs; - const Decimal& m_rhs; - Result m_result; + WTF_MAKE_NONCOPYABLE(SpecialValueHandler); + + public: + enum HandleResult { + BothFinite, + BothInfinity, + EitherNaN, + LHSIsInfinity, + RHSIsInfinity, + }; + + SpecialValueHandler(const Decimal& lhs, const Decimal& rhs); + HandleResult handle(); + Decimal value() const; + + private: + enum Result { + ResultIsLHS, + ResultIsRHS, + ResultIsUnknown, + }; + + const Decimal& m_lhs; + const Decimal& m_rhs; + Result m_result; }; SpecialValueHandler::SpecialValueHandler(const Decimal& lhs, const Decimal& rhs) - : m_lhs(lhs), m_rhs(rhs), m_result(ResultIsUnknown) -{ -} + : m_lhs(lhs), m_rhs(rhs), m_result(ResultIsUnknown) {} -SpecialValueHandler::HandleResult SpecialValueHandler::handle() -{ - if (m_lhs.isFinite() && m_rhs.isFinite()) - return BothFinite; +SpecialValueHandler::HandleResult SpecialValueHandler::handle() { + if (m_lhs.isFinite() && m_rhs.isFinite()) + return BothFinite; - const Decimal::EncodedData::FormatClass lhsClass = m_lhs.value().formatClass(); - const Decimal::EncodedData::FormatClass rhsClass = m_rhs.value().formatClass(); - if (lhsClass == Decimal::EncodedData::ClassNaN) { - m_result = ResultIsLHS; - return EitherNaN; - } + const Decimal::EncodedData::FormatClass lhsClass = + m_lhs.value().formatClass(); + const Decimal::EncodedData::FormatClass rhsClass = + m_rhs.value().formatClass(); + if (lhsClass == Decimal::EncodedData::ClassNaN) { + m_result = ResultIsLHS; + return EitherNaN; + } - if (rhsClass == Decimal::EncodedData::ClassNaN) { - m_result = ResultIsRHS; - return EitherNaN; - } + if (rhsClass == Decimal::EncodedData::ClassNaN) { + m_result = ResultIsRHS; + return EitherNaN; + } - if (lhsClass == Decimal::EncodedData::ClassInfinity) - return rhsClass == Decimal::EncodedData::ClassInfinity ? BothInfinity : LHSIsInfinity; + if (lhsClass == Decimal::EncodedData::ClassInfinity) + return rhsClass == Decimal::EncodedData::ClassInfinity ? BothInfinity + : LHSIsInfinity; - if (rhsClass == Decimal::EncodedData::ClassInfinity) - return RHSIsInfinity; + if (rhsClass == Decimal::EncodedData::ClassInfinity) + return RHSIsInfinity; - ASSERT_NOT_REACHED(); - return BothFinite; + ASSERT_NOT_REACHED(); + return BothFinite; } -Decimal SpecialValueHandler::value() const -{ - switch (m_result) { +Decimal SpecialValueHandler::value() const { + switch (m_result) { case ResultIsLHS: - return m_lhs; + return m_lhs; case ResultIsRHS: - return m_rhs; + return m_rhs; case ResultIsUnknown: default: - ASSERT_NOT_REACHED(); - return m_lhs; - } + ASSERT_NOT_REACHED(); + return m_lhs; + } } // This class is used for 128 bit unsigned integer arithmetic. class UInt128 { -public: - UInt128(uint64_t low, uint64_t high) - : m_high(high), m_low(low) - { - } + public: + UInt128(uint64_t low, uint64_t high) : m_high(high), m_low(low) {} - UInt128& operator/=(uint32_t); + UInt128& operator/=(uint32_t); - uint64_t high() const { return m_high; } - uint64_t low() const { return m_low; } + uint64_t high() const { return m_high; } + uint64_t low() const { return m_low; } - static UInt128 multiply(uint64_t u, uint64_t v) { return UInt128(u * v, multiplyHigh(u, v)); } + static UInt128 multiply(uint64_t u, uint64_t v) { + return UInt128(u * v, multiplyHigh(u, v)); + } -private: - static uint32_t highUInt32(uint64_t x) { return static_cast(x >> 32); } - static uint32_t lowUInt32(uint64_t x) { return static_cast(x & ((static_cast(1) << 32) - 1)); } - static uint64_t makeUInt64(uint32_t low, uint32_t high) { return low | (static_cast(high) << 32); } + private: + static uint32_t highUInt32(uint64_t x) { + return static_cast(x >> 32); + } + static uint32_t lowUInt32(uint64_t x) { + return static_cast(x & ((static_cast(1) << 32) - 1)); + } + static uint64_t makeUInt64(uint32_t low, uint32_t high) { + return low | (static_cast(high) << 32); + } - static uint64_t multiplyHigh(uint64_t, uint64_t); + static uint64_t multiplyHigh(uint64_t, uint64_t); - uint64_t m_high; - uint64_t m_low; + uint64_t m_high; + uint64_t m_low; }; -UInt128& UInt128::operator/=(const uint32_t divisor) -{ - ASSERT(divisor); +UInt128& UInt128::operator/=(const uint32_t divisor) { + ASSERT(divisor); - if (!m_high) { - m_low /= divisor; - return *this; - } - - uint32_t dividend[4]; - dividend[0] = lowUInt32(m_low); - dividend[1] = highUInt32(m_low); - dividend[2] = lowUInt32(m_high); - dividend[3] = highUInt32(m_high); - - uint32_t quotient[4]; - uint32_t remainder = 0; - for (int i = 3; i >= 0; --i) { - const uint64_t work = makeUInt64(dividend[i], remainder); - remainder = static_cast(work % divisor); - quotient[i] = static_cast(work / divisor); - } - m_low = makeUInt64(quotient[0], quotient[1]); - m_high = makeUInt64(quotient[2], quotient[3]); + if (!m_high) { + m_low /= divisor; return *this; + } + + uint32_t dividend[4]; + dividend[0] = lowUInt32(m_low); + dividend[1] = highUInt32(m_low); + dividend[2] = lowUInt32(m_high); + dividend[3] = highUInt32(m_high); + + uint32_t quotient[4]; + uint32_t remainder = 0; + for (int i = 3; i >= 0; --i) { + const uint64_t work = makeUInt64(dividend[i], remainder); + remainder = static_cast(work % divisor); + quotient[i] = static_cast(work / divisor); + } + m_low = makeUInt64(quotient[0], quotient[1]); + m_high = makeUInt64(quotient[2], quotient[3]); + return *this; } // Returns high 64bit of 128bit product. -uint64_t UInt128::multiplyHigh(uint64_t u, uint64_t v) -{ - const uint64_t uLow = lowUInt32(u); - const uint64_t uHigh = highUInt32(u); - const uint64_t vLow = lowUInt32(v); - const uint64_t vHigh = highUInt32(v); - const uint64_t partialProduct = uHigh * vLow + highUInt32(uLow * vLow); - return uHigh * vHigh + highUInt32(partialProduct) + highUInt32(uLow * vHigh + lowUInt32(partialProduct)); +uint64_t UInt128::multiplyHigh(uint64_t u, uint64_t v) { + const uint64_t uLow = lowUInt32(u); + const uint64_t uHigh = highUInt32(u); + const uint64_t vLow = lowUInt32(v); + const uint64_t vHigh = highUInt32(v); + const uint64_t partialProduct = uHigh * vLow + highUInt32(uLow * vLow); + return uHigh * vHigh + highUInt32(partialProduct) + + highUInt32(uLow * vHigh + lowUInt32(partialProduct)); } -static int countDigits(uint64_t x) -{ - int numberOfDigits = 0; - for (uint64_t powerOfTen = 1; x >= powerOfTen; powerOfTen *= 10) { - ++numberOfDigits; - if (powerOfTen >= std::numeric_limits::max() / 10) - break; - } - return numberOfDigits; +static int countDigits(uint64_t x) { + int numberOfDigits = 0; + for (uint64_t powerOfTen = 1; x >= powerOfTen; powerOfTen *= 10) { + ++numberOfDigits; + if (powerOfTen >= std::numeric_limits::max() / 10) + break; + } + return numberOfDigits; } -static uint64_t scaleDown(uint64_t x, int n) -{ - ASSERT(n >= 0); - while (n > 0 && x) { - x /= 10; - --n; - } - return x; +static uint64_t scaleDown(uint64_t x, int n) { + ASSERT(n >= 0); + while (n > 0 && x) { + x /= 10; + --n; + } + return x; } -static uint64_t scaleUp(uint64_t x, int n) -{ - ASSERT(n >= 0); - ASSERT(n < Precision); +static uint64_t scaleUp(uint64_t x, int n) { + ASSERT(n >= 0); + ASSERT(n < Precision); - uint64_t y = 1; - uint64_t z = 10; - for (;;) { - if (n & 1) - y = y * z; + uint64_t y = 1; + uint64_t z = 10; + for (;;) { + if (n & 1) + y = y * z; - n >>= 1; - if (!n) - return x * y; + n >>= 1; + if (!n) + return x * y; - z = z * z; - } + z = z * z; + } } -} // namespace DecimalPrivate +} // namespace DecimalPrivate using namespace DecimalPrivate; Decimal::EncodedData::EncodedData(Sign sign, FormatClass formatClass) - : m_coefficient(0) - , m_exponent(0) - , m_formatClass(formatClass) - , m_sign(sign) -{ -} + : m_coefficient(0), + m_exponent(0), + m_formatClass(formatClass), + m_sign(sign) {} Decimal::EncodedData::EncodedData(Sign sign, int exponent, uint64_t coefficient) - : m_formatClass(coefficient ? ClassNormal : ClassZero) - , m_sign(sign) -{ - if (exponent >= ExponentMin && exponent <= ExponentMax) { - while (coefficient > MaxCoefficient) { - coefficient /= 10; - ++exponent; - } + : m_formatClass(coefficient ? ClassNormal : ClassZero), m_sign(sign) { + if (exponent >= ExponentMin && exponent <= ExponentMax) { + while (coefficient > MaxCoefficient) { + coefficient /= 10; + ++exponent; } + } - if (exponent > ExponentMax) { - m_coefficient = 0; - m_exponent = 0; - m_formatClass = ClassInfinity; - return; - } + if (exponent > ExponentMax) { + m_coefficient = 0; + m_exponent = 0; + m_formatClass = ClassInfinity; + return; + } - if (exponent < ExponentMin) { - m_coefficient = 0; - m_exponent = 0; - m_formatClass = ClassZero; - return; - } + if (exponent < ExponentMin) { + m_coefficient = 0; + m_exponent = 0; + m_formatClass = ClassZero; + return; + } - m_coefficient = coefficient; - m_exponent = static_cast(exponent); + m_coefficient = coefficient; + m_exponent = static_cast(exponent); } -bool Decimal::EncodedData::operator==(const EncodedData& another) const -{ - return m_sign == another.m_sign - && m_formatClass == another.m_formatClass - && m_exponent == another.m_exponent - && m_coefficient == another.m_coefficient; +bool Decimal::EncodedData::operator==(const EncodedData& another) const { + return m_sign == another.m_sign && m_formatClass == another.m_formatClass && + m_exponent == another.m_exponent && + m_coefficient == another.m_coefficient; } Decimal::Decimal(int32_t i32) - : m_data(i32 < 0 ? Negative : Positive, 0, i32 < 0 ? static_cast(-static_cast(i32)) : static_cast(i32)) -{ -} + : m_data(i32 < 0 ? Negative : Positive, + 0, + i32 < 0 ? static_cast(-static_cast(i32)) + : static_cast(i32)) {} Decimal::Decimal(Sign sign, int exponent, uint64_t coefficient) - : m_data(sign, exponent, coefficient) -{ -} + : m_data(sign, exponent, coefficient) {} -Decimal::Decimal(const EncodedData& data) - : m_data(data) -{ -} +Decimal::Decimal(const EncodedData& data) : m_data(data) {} -Decimal::Decimal(const Decimal& other) - : m_data(other.m_data) -{ -} +Decimal::Decimal(const Decimal& other) : m_data(other.m_data) {} -Decimal& Decimal::operator=(const Decimal& other) -{ - m_data = other.m_data; - return *this; +Decimal& Decimal::operator=(const Decimal& other) { + m_data = other.m_data; + return *this; } -Decimal& Decimal::operator+=(const Decimal& other) -{ - m_data = (*this + other).m_data; - return *this; +Decimal& Decimal::operator+=(const Decimal& other) { + m_data = (*this + other).m_data; + return *this; } -Decimal& Decimal::operator-=(const Decimal& other) -{ - m_data = (*this - other).m_data; - return *this; +Decimal& Decimal::operator-=(const Decimal& other) { + m_data = (*this - other).m_data; + return *this; } -Decimal& Decimal::operator*=(const Decimal& other) -{ - m_data = (*this * other).m_data; - return *this; +Decimal& Decimal::operator*=(const Decimal& other) { + m_data = (*this * other).m_data; + return *this; } -Decimal& Decimal::operator/=(const Decimal& other) -{ - m_data = (*this / other).m_data; - return *this; +Decimal& Decimal::operator/=(const Decimal& other) { + m_data = (*this / other).m_data; + return *this; } -Decimal Decimal::operator-() const -{ - if (isNaN()) - return *this; +Decimal Decimal::operator-() const { + if (isNaN()) + return *this; - Decimal result(*this); - result.m_data.setSign(invertSign(m_data.sign())); - return result; + Decimal result(*this); + result.m_data.setSign(invertSign(m_data.sign())); + return result; } -Decimal Decimal::operator+(const Decimal& rhs) const -{ - const Decimal& lhs = *this; - const Sign lhsSign = lhs.sign(); - const Sign rhsSign = rhs.sign(); +Decimal Decimal::operator+(const Decimal& rhs) const { + const Decimal& lhs = *this; + const Sign lhsSign = lhs.sign(); + const Sign rhsSign = rhs.sign(); - SpecialValueHandler handler(lhs, rhs); - switch (handler.handle()) { + SpecialValueHandler handler(lhs, rhs); + switch (handler.handle()) { case SpecialValueHandler::BothFinite: - break; + break; case SpecialValueHandler::BothInfinity: - return lhsSign == rhsSign ? lhs : nan(); + return lhsSign == rhsSign ? lhs : nan(); case SpecialValueHandler::EitherNaN: - return handler.value(); + return handler.value(); case SpecialValueHandler::LHSIsInfinity: - return lhs; + return lhs; case SpecialValueHandler::RHSIsInfinity: - return rhs; - } + return rhs; + } - const AlignedOperands alignedOperands = alignOperands(lhs, rhs); + const AlignedOperands alignedOperands = alignOperands(lhs, rhs); - const uint64_t result = lhsSign == rhsSign - ? alignedOperands.lhsCoefficient + alignedOperands.rhsCoefficient - : alignedOperands.lhsCoefficient - alignedOperands.rhsCoefficient; + const uint64_t result = + lhsSign == rhsSign + ? alignedOperands.lhsCoefficient + alignedOperands.rhsCoefficient + : alignedOperands.lhsCoefficient - alignedOperands.rhsCoefficient; - if (lhsSign == Negative && rhsSign == Positive && !result) - return Decimal(Positive, alignedOperands.exponent, 0); + if (lhsSign == Negative && rhsSign == Positive && !result) + return Decimal(Positive, alignedOperands.exponent, 0); - return static_cast(result) >= 0 - ? Decimal(lhsSign, alignedOperands.exponent, result) - : Decimal(invertSign(lhsSign), alignedOperands.exponent, -static_cast(result)); + return static_cast(result) >= 0 + ? Decimal(lhsSign, alignedOperands.exponent, result) + : Decimal(invertSign(lhsSign), alignedOperands.exponent, + -static_cast(result)); } -Decimal Decimal::operator-(const Decimal& rhs) const -{ - const Decimal& lhs = *this; - const Sign lhsSign = lhs.sign(); - const Sign rhsSign = rhs.sign(); +Decimal Decimal::operator-(const Decimal& rhs) const { + const Decimal& lhs = *this; + const Sign lhsSign = lhs.sign(); + const Sign rhsSign = rhs.sign(); - SpecialValueHandler handler(lhs, rhs); - switch (handler.handle()) { + SpecialValueHandler handler(lhs, rhs); + switch (handler.handle()) { case SpecialValueHandler::BothFinite: - break; + break; case SpecialValueHandler::BothInfinity: - return lhsSign == rhsSign ? nan() : lhs; + return lhsSign == rhsSign ? nan() : lhs; case SpecialValueHandler::EitherNaN: - return handler.value(); + return handler.value(); case SpecialValueHandler::LHSIsInfinity: - return lhs; + return lhs; case SpecialValueHandler::RHSIsInfinity: - return infinity(invertSign(rhsSign)); - } + return infinity(invertSign(rhsSign)); + } - const AlignedOperands alignedOperands = alignOperands(lhs, rhs); + const AlignedOperands alignedOperands = alignOperands(lhs, rhs); - const uint64_t result = lhsSign == rhsSign - ? alignedOperands.lhsCoefficient - alignedOperands.rhsCoefficient - : alignedOperands.lhsCoefficient + alignedOperands.rhsCoefficient; + const uint64_t result = + lhsSign == rhsSign + ? alignedOperands.lhsCoefficient - alignedOperands.rhsCoefficient + : alignedOperands.lhsCoefficient + alignedOperands.rhsCoefficient; - if (lhsSign == Negative && rhsSign == Negative && !result) - return Decimal(Positive, alignedOperands.exponent, 0); + if (lhsSign == Negative && rhsSign == Negative && !result) + return Decimal(Positive, alignedOperands.exponent, 0); - return static_cast(result) >= 0 - ? Decimal(lhsSign, alignedOperands.exponent, result) - : Decimal(invertSign(lhsSign), alignedOperands.exponent, -static_cast(result)); + return static_cast(result) >= 0 + ? Decimal(lhsSign, alignedOperands.exponent, result) + : Decimal(invertSign(lhsSign), alignedOperands.exponent, + -static_cast(result)); } -Decimal Decimal::operator*(const Decimal& rhs) const -{ - const Decimal& lhs = *this; - const Sign lhsSign = lhs.sign(); - const Sign rhsSign = rhs.sign(); - const Sign resultSign = lhsSign == rhsSign ? Positive : Negative; +Decimal Decimal::operator*(const Decimal& rhs) const { + const Decimal& lhs = *this; + const Sign lhsSign = lhs.sign(); + const Sign rhsSign = rhs.sign(); + const Sign resultSign = lhsSign == rhsSign ? Positive : Negative; - SpecialValueHandler handler(lhs, rhs); - switch (handler.handle()) { + SpecialValueHandler handler(lhs, rhs); + switch (handler.handle()) { case SpecialValueHandler::BothFinite: { - const uint64_t lhsCoefficient = lhs.m_data.coefficient(); - const uint64_t rhsCoefficient = rhs.m_data.coefficient(); - int resultExponent = lhs.exponent() + rhs.exponent(); - UInt128 work(UInt128::multiply(lhsCoefficient, rhsCoefficient)); - while (work.high()) { - work /= 10; - ++resultExponent; - } - return Decimal(resultSign, resultExponent, work.low()); + const uint64_t lhsCoefficient = lhs.m_data.coefficient(); + const uint64_t rhsCoefficient = rhs.m_data.coefficient(); + int resultExponent = lhs.exponent() + rhs.exponent(); + UInt128 work(UInt128::multiply(lhsCoefficient, rhsCoefficient)); + while (work.high()) { + work /= 10; + ++resultExponent; + } + return Decimal(resultSign, resultExponent, work.low()); } case SpecialValueHandler::BothInfinity: - return infinity(resultSign); + return infinity(resultSign); case SpecialValueHandler::EitherNaN: - return handler.value(); + return handler.value(); case SpecialValueHandler::LHSIsInfinity: - return rhs.isZero() ? nan() : infinity(resultSign); + return rhs.isZero() ? nan() : infinity(resultSign); case SpecialValueHandler::RHSIsInfinity: - return lhs.isZero() ? nan() : infinity(resultSign); - } + return lhs.isZero() ? nan() : infinity(resultSign); + } - ASSERT_NOT_REACHED(); - return nan(); + ASSERT_NOT_REACHED(); + return nan(); } -Decimal Decimal::operator/(const Decimal& rhs) const -{ - const Decimal& lhs = *this; - const Sign lhsSign = lhs.sign(); - const Sign rhsSign = rhs.sign(); - const Sign resultSign = lhsSign == rhsSign ? Positive : Negative; +Decimal Decimal::operator/(const Decimal& rhs) const { + const Decimal& lhs = *this; + const Sign lhsSign = lhs.sign(); + const Sign rhsSign = rhs.sign(); + const Sign resultSign = lhsSign == rhsSign ? Positive : Negative; - SpecialValueHandler handler(lhs, rhs); - switch (handler.handle()) { + SpecialValueHandler handler(lhs, rhs); + switch (handler.handle()) { case SpecialValueHandler::BothFinite: - break; + break; case SpecialValueHandler::BothInfinity: - return nan(); + return nan(); case SpecialValueHandler::EitherNaN: - return handler.value(); + return handler.value(); case SpecialValueHandler::LHSIsInfinity: - return infinity(resultSign); + return infinity(resultSign); case SpecialValueHandler::RHSIsInfinity: - return zero(resultSign); - } + return zero(resultSign); + } - ASSERT(lhs.isFinite()); - ASSERT(rhs.isFinite()); + ASSERT(lhs.isFinite()); + ASSERT(rhs.isFinite()); - if (rhs.isZero()) - return lhs.isZero() ? nan() : infinity(resultSign); + if (rhs.isZero()) + return lhs.isZero() ? nan() : infinity(resultSign); - int resultExponent = lhs.exponent() - rhs.exponent(); + int resultExponent = lhs.exponent() - rhs.exponent(); - if (lhs.isZero()) - return Decimal(resultSign, resultExponent, 0); + if (lhs.isZero()) + return Decimal(resultSign, resultExponent, 0); - uint64_t remainder = lhs.m_data.coefficient(); - const uint64_t divisor = rhs.m_data.coefficient(); - uint64_t result = 0; - while (result < MaxCoefficient / 100) { - while (remainder < divisor) { - remainder *= 10; - result *= 10; - --resultExponent; - } - result += remainder / divisor; - remainder %= divisor; - if (!remainder) - break; + uint64_t remainder = lhs.m_data.coefficient(); + const uint64_t divisor = rhs.m_data.coefficient(); + uint64_t result = 0; + while (result < MaxCoefficient / 100) { + while (remainder < divisor) { + remainder *= 10; + result *= 10; + --resultExponent; + } + result += remainder / divisor; + remainder %= divisor; + if (!remainder) + break; + } + + if (remainder > divisor / 2) + ++result; + + return Decimal(resultSign, resultExponent, result); +} + +bool Decimal::operator==(const Decimal& rhs) const { + return m_data == rhs.m_data || compareTo(rhs).isZero(); +} + +bool Decimal::operator!=(const Decimal& rhs) const { + if (m_data == rhs.m_data) + return false; + const Decimal result = compareTo(rhs); + if (result.isNaN()) + return false; + return !result.isZero(); +} + +bool Decimal::operator<(const Decimal& rhs) const { + const Decimal result = compareTo(rhs); + if (result.isNaN()) + return false; + return !result.isZero() && result.isNegative(); +} + +bool Decimal::operator<=(const Decimal& rhs) const { + if (m_data == rhs.m_data) + return true; + const Decimal result = compareTo(rhs); + if (result.isNaN()) + return false; + return result.isZero() || result.isNegative(); +} + +bool Decimal::operator>(const Decimal& rhs) const { + const Decimal result = compareTo(rhs); + if (result.isNaN()) + return false; + return !result.isZero() && result.isPositive(); +} + +bool Decimal::operator>=(const Decimal& rhs) const { + if (m_data == rhs.m_data) + return true; + const Decimal result = compareTo(rhs); + if (result.isNaN()) + return false; + return result.isZero() || !result.isNegative(); +} + +Decimal Decimal::abs() const { + Decimal result(*this); + result.m_data.setSign(Positive); + return result; +} + +Decimal::AlignedOperands Decimal::alignOperands(const Decimal& lhs, + const Decimal& rhs) { + ASSERT(lhs.isFinite()); + ASSERT(rhs.isFinite()); + + const int lhsExponent = lhs.exponent(); + const int rhsExponent = rhs.exponent(); + int exponent = std::min(lhsExponent, rhsExponent); + uint64_t lhsCoefficient = lhs.m_data.coefficient(); + uint64_t rhsCoefficient = rhs.m_data.coefficient(); + + if (lhsExponent > rhsExponent) { + const int numberOfLHSDigits = countDigits(lhsCoefficient); + if (numberOfLHSDigits) { + const int lhsShiftAmount = lhsExponent - rhsExponent; + const int overflow = numberOfLHSDigits + lhsShiftAmount - Precision; + if (overflow <= 0) { + lhsCoefficient = scaleUp(lhsCoefficient, lhsShiftAmount); + } else { + lhsCoefficient = scaleUp(lhsCoefficient, lhsShiftAmount - overflow); + rhsCoefficient = scaleDown(rhsCoefficient, overflow); + exponent += overflow; + } } - if (remainder > divisor / 2) - ++result; + } else if (lhsExponent < rhsExponent) { + const int numberOfRHSDigits = countDigits(rhsCoefficient); + if (numberOfRHSDigits) { + const int rhsShiftAmount = rhsExponent - lhsExponent; + const int overflow = numberOfRHSDigits + rhsShiftAmount - Precision; + if (overflow <= 0) { + rhsCoefficient = scaleUp(rhsCoefficient, rhsShiftAmount); + } else { + rhsCoefficient = scaleUp(rhsCoefficient, rhsShiftAmount - overflow); + lhsCoefficient = scaleDown(lhsCoefficient, overflow); + exponent += overflow; + } + } + } - return Decimal(resultSign, resultExponent, result); + AlignedOperands alignedOperands; + alignedOperands.exponent = exponent; + alignedOperands.lhsCoefficient = lhsCoefficient; + alignedOperands.rhsCoefficient = rhsCoefficient; + return alignedOperands; } -bool Decimal::operator==(const Decimal& rhs) const -{ - return m_data == rhs.m_data || compareTo(rhs).isZero(); +static bool isMultiplePowersOfTen(uint64_t coefficient, int n) { + return !coefficient || !(coefficient % scaleUp(1, n)); } -bool Decimal::operator!=(const Decimal& rhs) const -{ - if (m_data == rhs.m_data) - return false; - const Decimal result = compareTo(rhs); - if (result.isNaN()) - return false; - return !result.isZero(); -} +// Round toward positive infinity. +// Note: Mac ports defines ceil(x) as wtf_ceil(x), so we can't use name "ceil" +// here. +Decimal Decimal::ceiling() const { + if (isSpecial()) + return *this; -bool Decimal::operator<(const Decimal& rhs) const -{ - const Decimal result = compareTo(rhs); - if (result.isNaN()) - return false; - return !result.isZero() && result.isNegative(); -} + if (exponent() >= 0) + return *this; -bool Decimal::operator<=(const Decimal& rhs) const -{ - if (m_data == rhs.m_data) - return true; - const Decimal result = compareTo(rhs); - if (result.isNaN()) - return false; - return result.isZero() || result.isNegative(); -} + uint64_t result = m_data.coefficient(); + const int numberOfDigits = countDigits(result); + const int numberOfDropDigits = -exponent(); + if (numberOfDigits < numberOfDropDigits) + return isPositive() ? Decimal(1) : zero(Positive); -bool Decimal::operator>(const Decimal& rhs) const -{ - const Decimal result = compareTo(rhs); - if (result.isNaN()) - return false; - return !result.isZero() && result.isPositive(); + result = scaleDown(result, numberOfDropDigits); + if (isPositive() && + !isMultiplePowersOfTen(m_data.coefficient(), numberOfDropDigits)) + ++result; + return Decimal(sign(), 0, result); } -bool Decimal::operator>=(const Decimal& rhs) const -{ - if (m_data == rhs.m_data) - return true; - const Decimal result = compareTo(rhs); - if (result.isNaN()) - return false; - return result.isZero() || !result.isNegative(); -} +Decimal Decimal::compareTo(const Decimal& rhs) const { + const Decimal result(*this - rhs); + switch (result.m_data.formatClass()) { + case EncodedData::ClassInfinity: + return result.isNegative() ? Decimal(-1) : Decimal(1); -Decimal Decimal::abs() const -{ - Decimal result(*this); - result.m_data.setSign(Positive); - return result; + case EncodedData::ClassNaN: + case EncodedData::ClassNormal: + return result; + + case EncodedData::ClassZero: + return zero(Positive); + + default: + ASSERT_NOT_REACHED(); + return nan(); + } } -Decimal::AlignedOperands Decimal::alignOperands(const Decimal& lhs, const Decimal& rhs) -{ - ASSERT(lhs.isFinite()); - ASSERT(rhs.isFinite()); - - const int lhsExponent = lhs.exponent(); - const int rhsExponent = rhs.exponent(); - int exponent = std::min(lhsExponent, rhsExponent); - uint64_t lhsCoefficient = lhs.m_data.coefficient(); - uint64_t rhsCoefficient = rhs.m_data.coefficient(); - - if (lhsExponent > rhsExponent) { - const int numberOfLHSDigits = countDigits(lhsCoefficient); - if (numberOfLHSDigits) { - const int lhsShiftAmount = lhsExponent - rhsExponent; - const int overflow = numberOfLHSDigits + lhsShiftAmount - Precision; - if (overflow <= 0) { - lhsCoefficient = scaleUp(lhsCoefficient, lhsShiftAmount); - } else { - lhsCoefficient = scaleUp(lhsCoefficient, lhsShiftAmount - overflow); - rhsCoefficient = scaleDown(rhsCoefficient, overflow); - exponent += overflow; - } - } +// Round toward negative infinity. +Decimal Decimal::floor() const { + if (isSpecial()) + return *this; - } else if (lhsExponent < rhsExponent) { - const int numberOfRHSDigits = countDigits(rhsCoefficient); - if (numberOfRHSDigits) { - const int rhsShiftAmount = rhsExponent - lhsExponent; - const int overflow = numberOfRHSDigits + rhsShiftAmount - Precision; - if (overflow <= 0) { - rhsCoefficient = scaleUp(rhsCoefficient, rhsShiftAmount); - } else { - rhsCoefficient = scaleUp(rhsCoefficient, rhsShiftAmount - overflow); - lhsCoefficient = scaleDown(lhsCoefficient, overflow); - exponent += overflow; - } + if (exponent() >= 0) + return *this; + + uint64_t result = m_data.coefficient(); + const int numberOfDigits = countDigits(result); + const int numberOfDropDigits = -exponent(); + if (numberOfDigits < numberOfDropDigits) + return isPositive() ? zero(Positive) : Decimal(-1); + + result = scaleDown(result, numberOfDropDigits); + if (isNegative() && + !isMultiplePowersOfTen(m_data.coefficient(), numberOfDropDigits)) + ++result; + return Decimal(sign(), 0, result); +} + +Decimal Decimal::fromDouble(double doubleValue) { + if (std::isfinite(doubleValue)) + return fromString(String::numberToStringECMAScript(doubleValue)); + + if (std::isinf(doubleValue)) + return infinity(doubleValue < 0 ? Negative : Positive); + + return nan(); +} + +Decimal Decimal::fromString(const String& str) { + int exponent = 0; + Sign exponentSign = Positive; + int numberOfDigits = 0; + int numberOfDigitsAfterDot = 0; + int numberOfExtraDigits = 0; + Sign sign = Positive; + + enum { + StateDigit, + StateDot, + StateDotDigit, + StateE, + StateEDigit, + StateESign, + StateSign, + StateStart, + StateZero, + } state = StateStart; + +#define HandleCharAndBreak(expected, nextState) \ + if (ch == expected) { \ + state = nextState; \ + break; \ + } + +#define HandleTwoCharsAndBreak(expected1, expected2, nextState) \ + if (ch == expected1 || ch == expected2) { \ + state = nextState; \ + break; \ + } + + uint64_t accumulator = 0; + for (unsigned index = 0; index < str.length(); ++index) { + const int ch = str[index]; + switch (state) { + case StateDigit: + if (ch >= '0' && ch <= '9') { + if (numberOfDigits < Precision) { + ++numberOfDigits; + accumulator *= 10; + accumulator += ch - '0'; + } else { + ++numberOfExtraDigits; + } + break; } - } - AlignedOperands alignedOperands; - alignedOperands.exponent = exponent; - alignedOperands.lhsCoefficient = lhsCoefficient; - alignedOperands.rhsCoefficient = rhsCoefficient; - return alignedOperands; -} + HandleCharAndBreak('.', StateDot); + HandleTwoCharsAndBreak('E', 'e', StateE); + return nan(); -static bool isMultiplePowersOfTen(uint64_t coefficient, int n) -{ - return !coefficient || !(coefficient % scaleUp(1, n)); -} + case StateDot: + case StateDotDigit: + if (ch >= '0' && ch <= '9') { + if (numberOfDigits < Precision) { + ++numberOfDigits; + ++numberOfDigitsAfterDot; + accumulator *= 10; + accumulator += ch - '0'; + } + state = StateDotDigit; + break; + } -// Round toward positive infinity. -// Note: Mac ports defines ceil(x) as wtf_ceil(x), so we can't use name "ceil" here. -Decimal Decimal::ceiling() const -{ - if (isSpecial()) - return *this; - - if (exponent() >= 0) - return *this; - - uint64_t result = m_data.coefficient(); - const int numberOfDigits = countDigits(result); - const int numberOfDropDigits = -exponent(); - if (numberOfDigits < numberOfDropDigits) - return isPositive() ? Decimal(1) : zero(Positive); - - result = scaleDown(result, numberOfDropDigits); - if (isPositive() && !isMultiplePowersOfTen(m_data.coefficient(), numberOfDropDigits)) - ++result; - return Decimal(sign(), 0, result); -} + HandleTwoCharsAndBreak('E', 'e', StateE); + return nan(); -Decimal Decimal::compareTo(const Decimal& rhs) const -{ - const Decimal result(*this - rhs); - switch (result.m_data.formatClass()) { - case EncodedData::ClassInfinity: - return result.isNegative() ? Decimal(-1) : Decimal(1); + case StateE: + if (ch == '+') { + exponentSign = Positive; + state = StateESign; + break; + } - case EncodedData::ClassNaN: - case EncodedData::ClassNormal: - return result; + if (ch == '-') { + exponentSign = Negative; + state = StateESign; + break; + } - case EncodedData::ClassZero: - return zero(Positive); + if (ch >= '0' && ch <= '9') { + exponent = ch - '0'; + state = StateEDigit; + break; + } - default: - ASSERT_NOT_REACHED(); return nan(); - } -} -// Round toward negative infinity. -Decimal Decimal::floor() const -{ - if (isSpecial()) - return *this; - - if (exponent() >= 0) - return *this; - - uint64_t result = m_data.coefficient(); - const int numberOfDigits = countDigits(result); - const int numberOfDropDigits = -exponent(); - if (numberOfDigits < numberOfDropDigits) - return isPositive() ? zero(Positive) : Decimal(-1); - - result = scaleDown(result, numberOfDropDigits); - if (isNegative() && !isMultiplePowersOfTen(m_data.coefficient(), numberOfDropDigits)) - ++result; - return Decimal(sign(), 0, result); -} + case StateEDigit: + if (ch >= '0' && ch <= '9') { + exponent *= 10; + exponent += ch - '0'; + if (exponent > ExponentMax + Precision) { + if (accumulator) + return exponentSign == Negative ? zero(Positive) : infinity(sign); + return zero(sign); + } + state = StateEDigit; + break; + } -Decimal Decimal::fromDouble(double doubleValue) -{ - if (std::isfinite(doubleValue)) - return fromString(String::numberToStringECMAScript(doubleValue)); + return nan(); - if (std::isinf(doubleValue)) - return infinity(doubleValue < 0 ? Negative : Positive); + case StateESign: + if (ch >= '0' && ch <= '9') { + exponent = ch - '0'; + state = StateEDigit; + break; + } - return nan(); -} + return nan(); -Decimal Decimal::fromString(const String& str) -{ - int exponent = 0; - Sign exponentSign = Positive; - int numberOfDigits = 0; - int numberOfDigitsAfterDot = 0; - int numberOfExtraDigits = 0; - Sign sign = Positive; - - enum { - StateDigit, - StateDot, - StateDotDigit, - StateE, - StateEDigit, - StateESign, - StateSign, - StateStart, - StateZero, - } state = StateStart; + case StateSign: + if (ch >= '1' && ch <= '9') { + accumulator = ch - '0'; + numberOfDigits = 1; + state = StateDigit; + break; + } -#define HandleCharAndBreak(expected, nextState) \ - if (ch == expected) { \ - state = nextState; \ - break; \ - } + HandleCharAndBreak('0', StateZero); + return nan(); -#define HandleTwoCharsAndBreak(expected1, expected2, nextState) \ - if (ch == expected1 || ch == expected2) { \ - state = nextState; \ - break; \ - } + case StateStart: + if (ch >= '1' && ch <= '9') { + accumulator = ch - '0'; + numberOfDigits = 1; + state = StateDigit; + break; + } - uint64_t accumulator = 0; - for (unsigned index = 0; index < str.length(); ++index) { - const int ch = str[index]; - switch (state) { - case StateDigit: - if (ch >= '0' && ch <= '9') { - if (numberOfDigits < Precision) { - ++numberOfDigits; - accumulator *= 10; - accumulator += ch - '0'; - } else { - ++numberOfExtraDigits; - } - break; - } - - HandleCharAndBreak('.', StateDot); - HandleTwoCharsAndBreak('E', 'e', StateE); - return nan(); - - case StateDot: - case StateDotDigit: - if (ch >= '0' && ch <= '9') { - if (numberOfDigits < Precision) { - ++numberOfDigits; - ++numberOfDigitsAfterDot; - accumulator *= 10; - accumulator += ch - '0'; - } - state = StateDotDigit; - break; - } - - HandleTwoCharsAndBreak('E', 'e', StateE); - return nan(); - - case StateE: - if (ch == '+') { - exponentSign = Positive; - state = StateESign; - break; - } - - if (ch == '-') { - exponentSign = Negative; - state = StateESign; - break; - } - - if (ch >= '0' && ch <= '9') { - exponent = ch - '0'; - state = StateEDigit; - break; - } - - return nan(); - - case StateEDigit: - if (ch >= '0' && ch <= '9') { - exponent *= 10; - exponent += ch - '0'; - if (exponent > ExponentMax + Precision) { - if (accumulator) - return exponentSign == Negative ? zero(Positive) : infinity(sign); - return zero(sign); - } - state = StateEDigit; - break; - } - - return nan(); - - case StateESign: - if (ch >= '0' && ch <= '9') { - exponent = ch - '0'; - state = StateEDigit; - break; - } - - return nan(); - - case StateSign: - if (ch >= '1' && ch <= '9') { - accumulator = ch - '0'; - numberOfDigits = 1; - state = StateDigit; - break; - } - - HandleCharAndBreak('0', StateZero); - return nan(); - - case StateStart: - if (ch >= '1' && ch <= '9') { - accumulator = ch - '0'; - numberOfDigits = 1; - state = StateDigit; - break; - } - - if (ch == '-') { - sign = Negative; - state = StateSign; - break; - } - - if (ch == '+') { - sign = Positive; - state = StateSign; - break; - } - - HandleCharAndBreak('0', StateZero); - HandleCharAndBreak('.', StateDot); - return nan(); - - case StateZero: - if (ch == '0') - break; - - if (ch >= '1' && ch <= '9') { - accumulator = ch - '0'; - numberOfDigits = 1; - state = StateDigit; - break; - } - - HandleCharAndBreak('.', StateDot); - HandleTwoCharsAndBreak('E', 'e', StateE); - return nan(); - - default: - ASSERT_NOT_REACHED(); - return nan(); + if (ch == '-') { + sign = Negative; + state = StateSign; + break; + } + + if (ch == '+') { + sign = Positive; + state = StateSign; + break; } - } - if (state == StateZero) - return zero(sign); + HandleCharAndBreak('0', StateZero); + HandleCharAndBreak('.', StateDot); + return nan(); - if (state == StateDigit || state == StateEDigit || state == StateDotDigit) { - int resultExponent = exponent * (exponentSign == Negative ? -1 : 1) - numberOfDigitsAfterDot + numberOfExtraDigits; - if (resultExponent < ExponentMin) - return zero(Positive); + case StateZero: + if (ch == '0') + break; - const int overflow = resultExponent - ExponentMax + 1; - if (overflow > 0) { - if (overflow + numberOfDigits - numberOfDigitsAfterDot > Precision) - return infinity(sign); - accumulator = scaleUp(accumulator, overflow); - resultExponent -= overflow; + if (ch >= '1' && ch <= '9') { + accumulator = ch - '0'; + numberOfDigits = 1; + state = StateDigit; + break; } - return Decimal(sign, resultExponent, accumulator); + HandleCharAndBreak('.', StateDot); + HandleTwoCharsAndBreak('E', 'e', StateE); + return nan(); + + default: + ASSERT_NOT_REACHED(); + return nan(); } + } + + if (state == StateZero) + return zero(sign); + + if (state == StateDigit || state == StateEDigit || state == StateDotDigit) { + int resultExponent = exponent * (exponentSign == Negative ? -1 : 1) - + numberOfDigitsAfterDot + numberOfExtraDigits; + if (resultExponent < ExponentMin) + return zero(Positive); + + const int overflow = resultExponent - ExponentMax + 1; + if (overflow > 0) { + if (overflow + numberOfDigits - numberOfDigitsAfterDot > Precision) + return infinity(sign); + accumulator = scaleUp(accumulator, overflow); + resultExponent -= overflow; + } + + return Decimal(sign, resultExponent, accumulator); + } - return nan(); + return nan(); } -Decimal Decimal::infinity(const Sign sign) -{ - return Decimal(EncodedData(sign, EncodedData::ClassInfinity)); +Decimal Decimal::infinity(const Sign sign) { + return Decimal(EncodedData(sign, EncodedData::ClassInfinity)); } -Decimal Decimal::nan() -{ - return Decimal(EncodedData(Positive, EncodedData::ClassNaN)); +Decimal Decimal::nan() { + return Decimal(EncodedData(Positive, EncodedData::ClassNaN)); } -Decimal Decimal::remainder(const Decimal& rhs) const -{ - const Decimal quotient = *this / rhs; - return quotient.isSpecial() ? quotient : *this - (quotient.isNegative() ? quotient.ceiling() : quotient.floor()) * rhs; +Decimal Decimal::remainder(const Decimal& rhs) const { + const Decimal quotient = *this / rhs; + return quotient.isSpecial() + ? quotient + : *this - (quotient.isNegative() ? quotient.ceiling() + : quotient.floor()) * + rhs; } -Decimal Decimal::round() const -{ - if (isSpecial()) - return *this; - - if (exponent() >= 0) - return *this; - - uint64_t result = m_data.coefficient(); - const int numberOfDigits = countDigits(result); - const int numberOfDropDigits = -exponent(); - if (numberOfDigits < numberOfDropDigits) - return zero(Positive); - - result = scaleDown(result, numberOfDropDigits - 1); - if (result % 10 >= 5) - result += 10; - result /= 10; - return Decimal(sign(), 0, result); +Decimal Decimal::round() const { + if (isSpecial()) + return *this; + + if (exponent() >= 0) + return *this; + + uint64_t result = m_data.coefficient(); + const int numberOfDigits = countDigits(result); + const int numberOfDropDigits = -exponent(); + if (numberOfDigits < numberOfDropDigits) + return zero(Positive); + + result = scaleDown(result, numberOfDropDigits - 1); + if (result % 10 >= 5) + result += 10; + result /= 10; + return Decimal(sign(), 0, result); } -double Decimal::toDouble() const -{ - if (isFinite()) { - bool valid; - const double doubleValue = toString().toDouble(&valid); - return valid ? doubleValue : std::numeric_limits::quiet_NaN(); - } +double Decimal::toDouble() const { + if (isFinite()) { + bool valid; + const double doubleValue = toString().toDouble(&valid); + return valid ? doubleValue : std::numeric_limits::quiet_NaN(); + } - if (isInfinity()) - return isNegative() ? -std::numeric_limits::infinity() : std::numeric_limits::infinity(); + if (isInfinity()) + return isNegative() ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); - return std::numeric_limits::quiet_NaN(); + return std::numeric_limits::quiet_NaN(); } -String Decimal::toString() const -{ - switch (m_data.formatClass()) { +String Decimal::toString() const { + switch (m_data.formatClass()) { case EncodedData::ClassInfinity: - return sign() ? "-Infinity" : "Infinity"; + return sign() ? "-Infinity" : "Infinity"; case EncodedData::ClassNaN: - return "NaN"; + return "NaN"; case EncodedData::ClassNormal: case EncodedData::ClassZero: - break; + break; default: - ASSERT_NOT_REACHED(); - return ""; + ASSERT_NOT_REACHED(); + return ""; + } + + StringBuilder builder; + if (sign()) + builder.append('-'); + + int originalExponent = exponent(); + uint64_t coefficient = m_data.coefficient(); + + if (originalExponent < 0) { + const int maxDigits = DBL_DIG; + uint64_t lastDigit = 0; + while (countDigits(coefficient) > maxDigits) { + lastDigit = coefficient % 10; + coefficient /= 10; + ++originalExponent; } - StringBuilder builder; - if (sign()) - builder.append('-'); - - int originalExponent = exponent(); - uint64_t coefficient = m_data.coefficient(); - - if (originalExponent < 0) { - const int maxDigits = DBL_DIG; - uint64_t lastDigit = 0; - while (countDigits(coefficient) > maxDigits) { - lastDigit = coefficient % 10; - coefficient /= 10; - ++originalExponent; - } - - if (lastDigit >= 5) - ++coefficient; + if (lastDigit >= 5) + ++coefficient; - while (originalExponent < 0 && coefficient && !(coefficient % 10)) { - coefficient /= 10; - ++originalExponent; - } + while (originalExponent < 0 && coefficient && !(coefficient % 10)) { + coefficient /= 10; + ++originalExponent; + } + } + + const String digits = String::number(coefficient); + int coefficientLength = static_cast(digits.length()); + const int adjustedExponent = originalExponent + coefficientLength - 1; + if (originalExponent <= 0 && adjustedExponent >= -6) { + if (!originalExponent) { + builder.append(digits); + return builder.toString(); } - const String digits = String::number(coefficient); - int coefficientLength = static_cast(digits.length()); - const int adjustedExponent = originalExponent + coefficientLength - 1; - if (originalExponent <= 0 && adjustedExponent >= -6) { - if (!originalExponent) { - builder.append(digits); - return builder.toString(); - } - - if (adjustedExponent >= 0) { - for (int i = 0; i < coefficientLength; ++i) { - builder.append(digits[i]); - if (i == adjustedExponent) - builder.append('.'); - } - return builder.toString(); - } + if (adjustedExponent >= 0) { + for (int i = 0; i < coefficientLength; ++i) { + builder.append(digits[i]); + if (i == adjustedExponent) + builder.append('.'); + } + return builder.toString(); + } - builder.appendLiteral("0."); - for (int i = adjustedExponent + 1; i < 0; ++i) - builder.append('0'); + builder.appendLiteral("0."); + for (int i = adjustedExponent + 1; i < 0; ++i) + builder.append('0'); - builder.append(digits); + builder.append(digits); - } else { - builder.append(digits[0]); - while (coefficientLength >= 2 && digits[coefficientLength - 1] == '0') - --coefficientLength; - if (coefficientLength >= 2) { - builder.append('.'); - for (int i = 1; i < coefficientLength; ++i) - builder.append(digits[i]); - } + } else { + builder.append(digits[0]); + while (coefficientLength >= 2 && digits[coefficientLength - 1] == '0') + --coefficientLength; + if (coefficientLength >= 2) { + builder.append('.'); + for (int i = 1; i < coefficientLength; ++i) + builder.append(digits[i]); + } - if (adjustedExponent) { - builder.append(adjustedExponent < 0 ? "e" : "e+"); - builder.appendNumber(adjustedExponent); - } + if (adjustedExponent) { + builder.append(adjustedExponent < 0 ? "e" : "e+"); + builder.appendNumber(adjustedExponent); } - return builder.toString(); + } + return builder.toString(); } -Decimal Decimal::zero(Sign sign) -{ - return Decimal(EncodedData(sign, EncodedData::ClassZero)); +Decimal Decimal::zero(Sign sign) { + return Decimal(EncodedData(sign, EncodedData::ClassZero)); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/Decimal.h b/sky/engine/platform/Decimal.h index f65480d63ebc8..6088487f9f2b4 100644 --- a/sky/engine/platform/Decimal.h +++ b/sky/engine/platform/Decimal.h @@ -46,137 +46,145 @@ class SpecialValueHandler; // // FIXME: Once all C++ compiler support decimal type, we should replace this // class to compiler supported one. See below URI for current status of decimal -// type for C++: // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1977.html +// type for C++: // +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1977.html class PLATFORM_EXPORT Decimal { - WTF_MAKE_FAST_ALLOCATED; -public: - enum Sign { - Positive, - Negative, - }; - - // You should not use EncodedData other than unit testing. - class EncodedData { - // For accessing FormatClass. - friend class Decimal; - friend class DecimalPrivate::SpecialValueHandler; - public: - EncodedData(Sign, int exponent, uint64_t coefficient); - - bool operator==(const EncodedData&) const; - bool operator!=(const EncodedData& another) const { return !operator==(another); } - - uint64_t coefficient() const { return m_coefficient; } - int countDigits() const; - int exponent() const { return m_exponent; } - bool isFinite() const { return !isSpecial(); } - bool isInfinity() const { return m_formatClass == ClassInfinity; } - bool isNaN() const { return m_formatClass == ClassNaN; } - bool isSpecial() const { return m_formatClass == ClassInfinity || m_formatClass == ClassNaN; } - bool isZero() const { return m_formatClass == ClassZero; } - Sign sign() const { return m_sign; } - void setSign(Sign sign) { m_sign = sign; } - - private: - enum FormatClass { - ClassInfinity, - ClassNormal, - ClassNaN, - ClassZero, - }; - - EncodedData(Sign, FormatClass); - FormatClass formatClass() const { return m_formatClass; } - - uint64_t m_coefficient; - int16_t m_exponent; - FormatClass m_formatClass; - Sign m_sign; - }; - - Decimal(int32_t = 0); - Decimal(Sign, int exponent, uint64_t coefficient); - Decimal(const Decimal&); - - Decimal& operator=(const Decimal&); - Decimal& operator+=(const Decimal&); - Decimal& operator-=(const Decimal&); - Decimal& operator*=(const Decimal&); - Decimal& operator/=(const Decimal&); - - Decimal operator-() const; - - bool operator==(const Decimal&) const; - bool operator!=(const Decimal&) const; - bool operator<(const Decimal&) const; - bool operator<=(const Decimal&) const; - bool operator>(const Decimal&) const; - bool operator>=(const Decimal&) const; - - Decimal operator+(const Decimal&) const; - Decimal operator-(const Decimal&) const; - Decimal operator*(const Decimal&) const; - Decimal operator/(const Decimal&) const; - - int exponent() const - { - ASSERT(isFinite()); - return m_data.exponent(); + WTF_MAKE_FAST_ALLOCATED; + + public: + enum Sign { + Positive, + Negative, + }; + + // You should not use EncodedData other than unit testing. + class EncodedData { + // For accessing FormatClass. + friend class Decimal; + friend class DecimalPrivate::SpecialValueHandler; + + public: + EncodedData(Sign, int exponent, uint64_t coefficient); + + bool operator==(const EncodedData&) const; + bool operator!=(const EncodedData& another) const { + return !operator==(another); } - bool isFinite() const { return m_data.isFinite(); } - bool isInfinity() const { return m_data.isInfinity(); } - bool isNaN() const { return m_data.isNaN(); } - bool isNegative() const { return sign() == Negative; } - bool isPositive() const { return sign() == Positive; } - bool isSpecial() const { return m_data.isSpecial(); } - bool isZero() const { return m_data.isZero(); } - - Decimal abs() const; - Decimal ceiling() const; - Decimal floor() const; - Decimal remainder(const Decimal&) const; - Decimal round() const; - - double toDouble() const; - // Note: toString method supports infinity and nan but fromString not. - String toString() const; - - static Decimal fromDouble(double); - // fromString supports following syntax EBNF: - // number ::= sign? digit+ ('.' digit*) (exponent-marker sign? digit+)? - // | sign? '.' digit+ (exponent-marker sign? digit+)? - // sign ::= '+' | '-' - // exponent-marker ::= 'e' | 'E' - // digit ::= '0' | '1' | ... | '9' - // Note: fromString doesn't support "infinity" and "nan". - static Decimal fromString(const String&); - static Decimal infinity(Sign); - static Decimal nan(); - static Decimal zero(Sign); - - // You should not use below methods. We expose them for unit testing. - explicit Decimal(const EncodedData&); - const EncodedData& value() const { return m_data; } - -private: - struct AlignedOperands { - uint64_t lhsCoefficient; - uint64_t rhsCoefficient; - int exponent; + uint64_t coefficient() const { return m_coefficient; } + int countDigits() const; + int exponent() const { return m_exponent; } + bool isFinite() const { return !isSpecial(); } + bool isInfinity() const { return m_formatClass == ClassInfinity; } + bool isNaN() const { return m_formatClass == ClassNaN; } + bool isSpecial() const { + return m_formatClass == ClassInfinity || m_formatClass == ClassNaN; + } + bool isZero() const { return m_formatClass == ClassZero; } + Sign sign() const { return m_sign; } + void setSign(Sign sign) { m_sign = sign; } + + private: + enum FormatClass { + ClassInfinity, + ClassNormal, + ClassNaN, + ClassZero, }; - Decimal(double); - Decimal compareTo(const Decimal&) const; - - static AlignedOperands alignOperands(const Decimal& lhs, const Decimal& rhs); - static inline Sign invertSign(Sign sign) { return sign == Negative ? Positive : Negative; } - - Sign sign() const { return m_data.sign(); } - - EncodedData m_data; + EncodedData(Sign, FormatClass); + FormatClass formatClass() const { return m_formatClass; } + + uint64_t m_coefficient; + int16_t m_exponent; + FormatClass m_formatClass; + Sign m_sign; + }; + + Decimal(int32_t = 0); + Decimal(Sign, int exponent, uint64_t coefficient); + Decimal(const Decimal&); + + Decimal& operator=(const Decimal&); + Decimal& operator+=(const Decimal&); + Decimal& operator-=(const Decimal&); + Decimal& operator*=(const Decimal&); + Decimal& operator/=(const Decimal&); + + Decimal operator-() const; + + bool operator==(const Decimal&) const; + bool operator!=(const Decimal&) const; + bool operator<(const Decimal&) const; + bool operator<=(const Decimal&) const; + bool operator>(const Decimal&) const; + bool operator>=(const Decimal&) const; + + Decimal operator+(const Decimal&) const; + Decimal operator-(const Decimal&) const; + Decimal operator*(const Decimal&)const; + Decimal operator/(const Decimal&) const; + + int exponent() const { + ASSERT(isFinite()); + return m_data.exponent(); + } + + bool isFinite() const { return m_data.isFinite(); } + bool isInfinity() const { return m_data.isInfinity(); } + bool isNaN() const { return m_data.isNaN(); } + bool isNegative() const { return sign() == Negative; } + bool isPositive() const { return sign() == Positive; } + bool isSpecial() const { return m_data.isSpecial(); } + bool isZero() const { return m_data.isZero(); } + + Decimal abs() const; + Decimal ceiling() const; + Decimal floor() const; + Decimal remainder(const Decimal&) const; + Decimal round() const; + + double toDouble() const; + // Note: toString method supports infinity and nan but fromString not. + String toString() const; + + static Decimal fromDouble(double); + // fromString supports following syntax EBNF: + // number ::= sign? digit+ ('.' digit*) (exponent-marker sign? digit+)? + // | sign? '.' digit+ (exponent-marker sign? digit+)? + // sign ::= '+' | '-' + // exponent-marker ::= 'e' | 'E' + // digit ::= '0' | '1' | ... | '9' + // Note: fromString doesn't support "infinity" and "nan". + static Decimal fromString(const String&); + static Decimal infinity(Sign); + static Decimal nan(); + static Decimal zero(Sign); + + // You should not use below methods. We expose them for unit testing. + explicit Decimal(const EncodedData&); + const EncodedData& value() const { return m_data; } + + private: + struct AlignedOperands { + uint64_t lhsCoefficient; + uint64_t rhsCoefficient; + int exponent; + }; + + Decimal(double); + Decimal compareTo(const Decimal&) const; + + static AlignedOperands alignOperands(const Decimal& lhs, const Decimal& rhs); + static inline Sign invertSign(Sign sign) { + return sign == Negative ? Positive : Negative; + } + + Sign sign() const { return m_data.sign(); } + + EncodedData m_data; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_DECIMAL_H_ diff --git a/sky/engine/platform/DecimalTest.cpp b/sky/engine/platform/DecimalTest.cpp index ea4fe4a683f0e..48abe09c60439 100644 --- a/sky/engine/platform/DecimalTest.cpp +++ b/sky/engine/platform/DecimalTest.cpp @@ -37,1086 +37,1130 @@ namespace blink { -std::ostream& operator<<(std::ostream& os, const Decimal& decimal) -{ - Decimal::EncodedData data = decimal.value(); - return os - << "encode(" << String::number(data.coefficient()).ascii().data() - << ", " << String::number(data.exponent()).ascii().data() - << ", " << (data.sign() == Decimal::Negative ? "Negative" : "Positive") - << ")=" << decimal.toString().ascii().data(); +std::ostream& operator<<(std::ostream& os, const Decimal& decimal) { + Decimal::EncodedData data = decimal.value(); + return os << "encode(" << String::number(data.coefficient()).ascii().data() + << ", " << String::number(data.exponent()).ascii().data() << ", " + << (data.sign() == Decimal::Negative ? "Negative" : "Positive") + << ")=" << decimal.toString().ascii().data(); } -} // namespace blink +} // namespace blink using namespace blink; // Simulate WebCore/html/StepRange class DecimalStepRange { -public: - Decimal maximum; - Decimal minimum; - Decimal step; - - DecimalStepRange(const Decimal& minimum, const Decimal& maximum, const Decimal& step) - : maximum(maximum) - , minimum(minimum) - , step(step) - { - } - - Decimal clampValue(Decimal value) const - { - const Decimal result = minimum + ((value - minimum) / step).round() * step; - ASSERT(result.isFinite()); - return result > maximum ? result - step : result; - } + public: + Decimal maximum; + Decimal minimum; + Decimal step; + + DecimalStepRange(const Decimal& minimum, + const Decimal& maximum, + const Decimal& step) + : maximum(maximum), minimum(minimum), step(step) {} + + Decimal clampValue(Decimal value) const { + const Decimal result = minimum + ((value - minimum) / step).round() * step; + ASSERT(result.isFinite()); + return result > maximum ? result - step : result; + } }; class DecimalTest : public ::testing::Test { -protected: - typedef Decimal::Sign Sign; - protected: static const Sign Positive = Decimal::Positive; - protected: static const Sign Negative = Decimal::Negative; - - Decimal encode(uint64_t coefficient, int exponent, Sign sign) - { - return Decimal(sign, exponent, coefficient); + protected: + typedef Decimal::Sign Sign; + + protected: + static const Sign Positive = Decimal::Positive; + + protected: + static const Sign Negative = Decimal::Negative; + + Decimal encode(uint64_t coefficient, int exponent, Sign sign) { + return Decimal(sign, exponent, coefficient); + } + + protected: + Decimal fromString(const String& string) { + return Decimal::fromString(string); + } + + protected: + Decimal stepDown(const String& minimum, + const String& maximum, + const String& step, + const String& valueString, + int numberOfStepTimes) { + DecimalStepRange stepRange(fromString(minimum), fromString(maximum), + fromString(step)); + Decimal value = fromString(valueString); + for (int i = 0; i < numberOfStepTimes; ++i) { + value -= stepRange.step; + value = stepRange.clampValue(value); } - - protected: Decimal fromString(const String& string) - { - return Decimal::fromString(string); - } - - protected: Decimal stepDown(const String& minimum, const String& maximum, const String& step, const String& valueString, int numberOfStepTimes) - { - DecimalStepRange stepRange(fromString(minimum), fromString(maximum), fromString(step)); - Decimal value = fromString(valueString); - for (int i = 0; i < numberOfStepTimes; ++i) { - value -= stepRange.step; - value = stepRange.clampValue(value); - } - return value; - } - - protected: Decimal stepUp(const String& minimum, const String& maximum, const String& step, const String& valueString, int numberOfStepTimes) - { - DecimalStepRange stepRange(fromString(minimum), fromString(maximum), fromString(step)); - Decimal value = fromString(valueString); - for (int i = 0; i < numberOfStepTimes; ++i) { - value += stepRange.step; - value = stepRange.clampValue(value); - } - return value; + return value; + } + + protected: + Decimal stepUp(const String& minimum, + const String& maximum, + const String& step, + const String& valueString, + int numberOfStepTimes) { + DecimalStepRange stepRange(fromString(minimum), fromString(maximum), + fromString(step)); + Decimal value = fromString(valueString); + for (int i = 0; i < numberOfStepTimes; ++i) { + value += stepRange.step; + value = stepRange.clampValue(value); } + return value; + } }; -// FIXME: We should use expectedSign without "Decimal::", however, g++ causes undefined references for DecimalTest::Positive and Negative. -#define EXPECT_DECIMAL_ENCODED_DATA_EQ(expectedCoefficient, expectedExponent, expectedSign, decimal) \ - EXPECT_EQ((expectedCoefficient), (decimal).value().coefficient()); \ - EXPECT_EQ((expectedExponent), (decimal).value().exponent()); \ - EXPECT_EQ(Decimal::expectedSign, (decimal).value().sign()); +// FIXME: We should use expectedSign without "Decimal::", however, g++ causes +// undefined references for DecimalTest::Positive and Negative. +#define EXPECT_DECIMAL_ENCODED_DATA_EQ(expectedCoefficient, expectedExponent, \ + expectedSign, decimal) \ + EXPECT_EQ((expectedCoefficient), (decimal).value().coefficient()); \ + EXPECT_EQ((expectedExponent), (decimal).value().exponent()); \ + EXPECT_EQ(Decimal::expectedSign, (decimal).value().sign()); -#define EXPECT_DECIMAL_STREQ(expected, decimal) EXPECT_STREQ((expected), (decimal).toString().ascii().data()) +#define EXPECT_DECIMAL_STREQ(expected, decimal) \ + EXPECT_STREQ((expected), (decimal).toString().ascii().data()) -TEST_F(DecimalTest, Abs) -{ - EXPECT_EQ(encode(0, 0, Positive), encode(0, 0, Positive).abs()); - EXPECT_EQ(encode(0, 0, Positive), encode(0, 0, Negative).abs()); +TEST_F(DecimalTest, Abs) { + EXPECT_EQ(encode(0, 0, Positive), encode(0, 0, Positive).abs()); + EXPECT_EQ(encode(0, 0, Positive), encode(0, 0, Negative).abs()); - EXPECT_EQ(encode(0, 10, Positive), encode(0, 10, Positive).abs()); - EXPECT_EQ(encode(0, 10, Positive), encode(0, 10, Negative).abs()); + EXPECT_EQ(encode(0, 10, Positive), encode(0, 10, Positive).abs()); + EXPECT_EQ(encode(0, 10, Positive), encode(0, 10, Negative).abs()); - EXPECT_EQ(encode(0, -10, Positive), encode(0, -10, Positive).abs()); - EXPECT_EQ(encode(0, -10, Positive), encode(0, -10, Negative).abs()); + EXPECT_EQ(encode(0, -10, Positive), encode(0, -10, Positive).abs()); + EXPECT_EQ(encode(0, -10, Positive), encode(0, -10, Negative).abs()); - EXPECT_EQ(encode(1, 0, Positive), encode(1, 0, Positive).abs()); - EXPECT_EQ(encode(1, 0, Positive), encode(1, 0, Negative).abs()); + EXPECT_EQ(encode(1, 0, Positive), encode(1, 0, Positive).abs()); + EXPECT_EQ(encode(1, 0, Positive), encode(1, 0, Negative).abs()); - EXPECT_EQ(encode(1, 10, Positive), encode(1, 10, Positive).abs()); - EXPECT_EQ(encode(1, 10, Positive), encode(1, 10, Negative).abs()); + EXPECT_EQ(encode(1, 10, Positive), encode(1, 10, Positive).abs()); + EXPECT_EQ(encode(1, 10, Positive), encode(1, 10, Negative).abs()); - EXPECT_EQ(encode(1, -10, Positive), encode(1, -10, Positive).abs()); - EXPECT_EQ(encode(1, -10, Positive), encode(1, -10, Negative).abs()); + EXPECT_EQ(encode(1, -10, Positive), encode(1, -10, Positive).abs()); + EXPECT_EQ(encode(1, -10, Positive), encode(1, -10, Negative).abs()); } -TEST_F(DecimalTest, AbsBigExponent) -{ - EXPECT_EQ(encode(1, 1000, Positive), encode(1, 1000, Positive).abs()); - EXPECT_EQ(encode(1, 1000, Positive), encode(1, 1000, Negative).abs()); +TEST_F(DecimalTest, AbsBigExponent) { + EXPECT_EQ(encode(1, 1000, Positive), encode(1, 1000, Positive).abs()); + EXPECT_EQ(encode(1, 1000, Positive), encode(1, 1000, Negative).abs()); } -TEST_F(DecimalTest, AbsSmallExponent) -{ - EXPECT_EQ(encode(1, -1000, Positive), encode(1, -1000, Positive).abs()); - EXPECT_EQ(encode(1, -1000, Positive), encode(1, -1000, Negative).abs()); +TEST_F(DecimalTest, AbsSmallExponent) { + EXPECT_EQ(encode(1, -1000, Positive), encode(1, -1000, Positive).abs()); + EXPECT_EQ(encode(1, -1000, Positive), encode(1, -1000, Negative).abs()); } -TEST_F(DecimalTest, AbsSpecialValues) -{ - EXPECT_EQ(Decimal::infinity(Positive), Decimal::infinity(Positive).abs()); - EXPECT_EQ(Decimal::infinity(Positive), Decimal::infinity(Negative).abs()); - EXPECT_EQ(Decimal::nan(), Decimal::nan().abs()); +TEST_F(DecimalTest, AbsSpecialValues) { + EXPECT_EQ(Decimal::infinity(Positive), Decimal::infinity(Positive).abs()); + EXPECT_EQ(Decimal::infinity(Positive), Decimal::infinity(Negative).abs()); + EXPECT_EQ(Decimal::nan(), Decimal::nan().abs()); } -TEST_F(DecimalTest, Add) -{ - EXPECT_EQ(encode(0, 0, Positive), Decimal(0) + Decimal(0)); - EXPECT_EQ(Decimal(1), Decimal(2) + Decimal(-1)); - EXPECT_EQ(Decimal(1), Decimal(-1) + Decimal(2)); - EXPECT_EQ(encode(100, 0, Positive), Decimal(99) + Decimal(1)); - EXPECT_EQ(encode(100, 0, Negative), Decimal(-50) + Decimal(-50)); - EXPECT_EQ(encode(UINT64_C(1000000000000000), 35, Positive), encode(1, 50, Positive) + Decimal(1)); - EXPECT_EQ(encode(UINT64_C(1000000000000000), 35, Positive), Decimal(1) + encode(1, 50, Positive)); - EXPECT_EQ(encode(UINT64_C(10000000001), 0, Positive), encode(1, 10, Positive) + Decimal(1)); - EXPECT_EQ(encode(UINT64_C(10000000001), 0, Positive), Decimal(1) + encode(1, 10, Positive)); - EXPECT_EQ(encode(1, 0, Positive), encode(1, -1022, Positive) + encode(1, 0, Positive)); - EXPECT_EQ(encode(2, -1022, Positive), encode(1, -1022, Positive) + encode(1, -1022, Positive)); +TEST_F(DecimalTest, Add) { + EXPECT_EQ(encode(0, 0, Positive), Decimal(0) + Decimal(0)); + EXPECT_EQ(Decimal(1), Decimal(2) + Decimal(-1)); + EXPECT_EQ(Decimal(1), Decimal(-1) + Decimal(2)); + EXPECT_EQ(encode(100, 0, Positive), Decimal(99) + Decimal(1)); + EXPECT_EQ(encode(100, 0, Negative), Decimal(-50) + Decimal(-50)); + EXPECT_EQ(encode(UINT64_C(1000000000000000), 35, Positive), + encode(1, 50, Positive) + Decimal(1)); + EXPECT_EQ(encode(UINT64_C(1000000000000000), 35, Positive), + Decimal(1) + encode(1, 50, Positive)); + EXPECT_EQ(encode(UINT64_C(10000000001), 0, Positive), + encode(1, 10, Positive) + Decimal(1)); + EXPECT_EQ(encode(UINT64_C(10000000001), 0, Positive), + Decimal(1) + encode(1, 10, Positive)); + EXPECT_EQ(encode(1, 0, Positive), + encode(1, -1022, Positive) + encode(1, 0, Positive)); + EXPECT_EQ(encode(2, -1022, Positive), + encode(1, -1022, Positive) + encode(1, -1022, Positive)); } -TEST_F(DecimalTest, AddBigExponent) -{ - EXPECT_EQ(encode(1, 1022, Positive), encode(1, 1022, Positive) + encode(1, 0, Positive)); - EXPECT_EQ(encode(2, 1022, Positive), encode(1, 1022, Positive) + encode(1, 1022, Positive)); - EXPECT_EQ(Decimal::infinity(Positive), encode(std::numeric_limits::max(), 1022, Positive) + encode(1, 0, Positive)); - EXPECT_EQ(encode(1, 1022, Positive), encode(1, 1022, Positive) + encode(1, -1000, Positive)); +TEST_F(DecimalTest, AddBigExponent) { + EXPECT_EQ(encode(1, 1022, Positive), + encode(1, 1022, Positive) + encode(1, 0, Positive)); + EXPECT_EQ(encode(2, 1022, Positive), + encode(1, 1022, Positive) + encode(1, 1022, Positive)); + EXPECT_EQ(Decimal::infinity(Positive), + encode(std::numeric_limits::max(), 1022, Positive) + + encode(1, 0, Positive)); + EXPECT_EQ(encode(1, 1022, Positive), + encode(1, 1022, Positive) + encode(1, -1000, Positive)); } -TEST_F(DecimalTest, AddSmallExponent) -{ - EXPECT_EQ(encode(1, 0, Positive), encode(1, -1022, Positive) + encode(1, 0, Positive)); - EXPECT_EQ(encode(2, -1022, Positive), encode(1, -1022, Positive) + encode(1, -1022, Positive)); +TEST_F(DecimalTest, AddSmallExponent) { + EXPECT_EQ(encode(1, 0, Positive), + encode(1, -1022, Positive) + encode(1, 0, Positive)); + EXPECT_EQ(encode(2, -1022, Positive), + encode(1, -1022, Positive) + encode(1, -1022, Positive)); } -TEST_F(DecimalTest, AddSpecialValues) -{ - const Decimal Infinity(Decimal::infinity(Positive)); - const Decimal MinusInfinity(Decimal::infinity(Negative)); - const Decimal NaN(Decimal::nan()); - const Decimal Ten(10); - - EXPECT_EQ(Infinity, Infinity + Infinity); - EXPECT_EQ(NaN, Infinity + MinusInfinity); - EXPECT_EQ(NaN, MinusInfinity + Infinity); - EXPECT_EQ(MinusInfinity, MinusInfinity + MinusInfinity); - - EXPECT_EQ(Infinity, Infinity + Ten); - EXPECT_EQ(Infinity, Ten + Infinity); - EXPECT_EQ(MinusInfinity, MinusInfinity + Ten); - EXPECT_EQ(MinusInfinity, Ten + MinusInfinity); - - EXPECT_EQ(NaN, NaN + NaN); - EXPECT_EQ(NaN, NaN + Ten); - EXPECT_EQ(NaN, Ten + NaN); - - EXPECT_EQ(NaN, NaN - Infinity); - EXPECT_EQ(NaN, NaN - MinusInfinity); - EXPECT_EQ(NaN, Infinity - NaN); - EXPECT_EQ(NaN, MinusInfinity - NaN); +TEST_F(DecimalTest, AddSpecialValues) { + const Decimal Infinity(Decimal::infinity(Positive)); + const Decimal MinusInfinity(Decimal::infinity(Negative)); + const Decimal NaN(Decimal::nan()); + const Decimal Ten(10); + + EXPECT_EQ(Infinity, Infinity + Infinity); + EXPECT_EQ(NaN, Infinity + MinusInfinity); + EXPECT_EQ(NaN, MinusInfinity + Infinity); + EXPECT_EQ(MinusInfinity, MinusInfinity + MinusInfinity); + + EXPECT_EQ(Infinity, Infinity + Ten); + EXPECT_EQ(Infinity, Ten + Infinity); + EXPECT_EQ(MinusInfinity, MinusInfinity + Ten); + EXPECT_EQ(MinusInfinity, Ten + MinusInfinity); + + EXPECT_EQ(NaN, NaN + NaN); + EXPECT_EQ(NaN, NaN + Ten); + EXPECT_EQ(NaN, Ten + NaN); + + EXPECT_EQ(NaN, NaN - Infinity); + EXPECT_EQ(NaN, NaN - MinusInfinity); + EXPECT_EQ(NaN, Infinity - NaN); + EXPECT_EQ(NaN, MinusInfinity - NaN); } -TEST_F(DecimalTest, Ceiling) -{ - EXPECT_EQ(Decimal(1), Decimal(1).ceiling()); - EXPECT_EQ(Decimal(1), encode(1, -10, Positive).ceiling()); - EXPECT_EQ(Decimal(2), encode(11, -1, Positive).ceiling()); - EXPECT_EQ(Decimal(2), encode(13, -1, Positive).ceiling()); - EXPECT_EQ(Decimal(2), encode(15, -1, Positive).ceiling()); - EXPECT_EQ(Decimal(2), encode(19, -1, Positive).ceiling()); - EXPECT_EQ(Decimal(2), encode(151, -2, Positive).ceiling()); - EXPECT_EQ(Decimal(2), encode(101, -2, Positive).ceiling()); - EXPECT_EQ(Decimal(1), encode(199, -3, Positive).ceiling()); - EXPECT_EQ(Decimal(2), encode(199, -2, Positive).ceiling()); - EXPECT_EQ(Decimal(3), encode(209, -2, Positive).ceiling()); - - EXPECT_EQ(Decimal(-1), Decimal(-1).ceiling()); - EXPECT_EQ(Decimal(0), encode(1, -10, Negative).ceiling()); - EXPECT_EQ(Decimal(-1), encode(11, -1, Negative).ceiling()); - EXPECT_EQ(Decimal(-1), encode(13, -1, Negative).ceiling()); - EXPECT_EQ(Decimal(-1), encode(15, -1, Negative).ceiling()); - EXPECT_EQ(Decimal(-1), encode(19, -1, Negative).ceiling()); - EXPECT_EQ(Decimal(-1), encode(151, -2, Negative).ceiling()); - EXPECT_EQ(Decimal(-1), encode(101, -2, Negative).ceiling()); - EXPECT_EQ(Decimal(0), encode(199, -3, Negative).ceiling()); - EXPECT_EQ(Decimal(-1), encode(199, -2, Negative).ceiling()); - EXPECT_EQ(Decimal(-2), encode(209, -2, Negative).ceiling()); +TEST_F(DecimalTest, Ceiling) { + EXPECT_EQ(Decimal(1), Decimal(1).ceiling()); + EXPECT_EQ(Decimal(1), encode(1, -10, Positive).ceiling()); + EXPECT_EQ(Decimal(2), encode(11, -1, Positive).ceiling()); + EXPECT_EQ(Decimal(2), encode(13, -1, Positive).ceiling()); + EXPECT_EQ(Decimal(2), encode(15, -1, Positive).ceiling()); + EXPECT_EQ(Decimal(2), encode(19, -1, Positive).ceiling()); + EXPECT_EQ(Decimal(2), encode(151, -2, Positive).ceiling()); + EXPECT_EQ(Decimal(2), encode(101, -2, Positive).ceiling()); + EXPECT_EQ(Decimal(1), encode(199, -3, Positive).ceiling()); + EXPECT_EQ(Decimal(2), encode(199, -2, Positive).ceiling()); + EXPECT_EQ(Decimal(3), encode(209, -2, Positive).ceiling()); + + EXPECT_EQ(Decimal(-1), Decimal(-1).ceiling()); + EXPECT_EQ(Decimal(0), encode(1, -10, Negative).ceiling()); + EXPECT_EQ(Decimal(-1), encode(11, -1, Negative).ceiling()); + EXPECT_EQ(Decimal(-1), encode(13, -1, Negative).ceiling()); + EXPECT_EQ(Decimal(-1), encode(15, -1, Negative).ceiling()); + EXPECT_EQ(Decimal(-1), encode(19, -1, Negative).ceiling()); + EXPECT_EQ(Decimal(-1), encode(151, -2, Negative).ceiling()); + EXPECT_EQ(Decimal(-1), encode(101, -2, Negative).ceiling()); + EXPECT_EQ(Decimal(0), encode(199, -3, Negative).ceiling()); + EXPECT_EQ(Decimal(-1), encode(199, -2, Negative).ceiling()); + EXPECT_EQ(Decimal(-2), encode(209, -2, Negative).ceiling()); } -TEST_F(DecimalTest, CeilingBigExponent) -{ - EXPECT_EQ(encode(1, 1000, Positive), encode(1, 1000, Positive).ceiling()); - EXPECT_EQ(encode(1, 1000, Negative), encode(1, 1000, Negative).ceiling()); +TEST_F(DecimalTest, CeilingBigExponent) { + EXPECT_EQ(encode(1, 1000, Positive), encode(1, 1000, Positive).ceiling()); + EXPECT_EQ(encode(1, 1000, Negative), encode(1, 1000, Negative).ceiling()); } -TEST_F(DecimalTest, CeilingSmallExponent) -{ - EXPECT_EQ(encode(1, 0, Positive), encode(1, -1000, Positive).ceiling()); - EXPECT_EQ(encode(0, 0, Negative), encode(1, -1000, Negative).ceiling()); +TEST_F(DecimalTest, CeilingSmallExponent) { + EXPECT_EQ(encode(1, 0, Positive), encode(1, -1000, Positive).ceiling()); + EXPECT_EQ(encode(0, 0, Negative), encode(1, -1000, Negative).ceiling()); } -TEST_F(DecimalTest, CeilingSpecialValues) -{ - EXPECT_EQ(Decimal::infinity(Positive), Decimal::infinity(Positive).ceiling()); - EXPECT_EQ(Decimal::infinity(Negative), Decimal::infinity(Negative).ceiling()); - EXPECT_EQ(Decimal::nan(), Decimal::nan().ceiling()); +TEST_F(DecimalTest, CeilingSpecialValues) { + EXPECT_EQ(Decimal::infinity(Positive), Decimal::infinity(Positive).ceiling()); + EXPECT_EQ(Decimal::infinity(Negative), Decimal::infinity(Negative).ceiling()); + EXPECT_EQ(Decimal::nan(), Decimal::nan().ceiling()); } -TEST_F(DecimalTest, Compare) -{ - EXPECT_TRUE(Decimal(0) == Decimal(0)); - EXPECT_TRUE(Decimal(0) != Decimal(1)); - EXPECT_TRUE(Decimal(0) < Decimal(1)); - EXPECT_TRUE(Decimal(0) <= Decimal(0)); - EXPECT_TRUE(Decimal(0) > Decimal(-1)); - EXPECT_TRUE(Decimal(0) >= Decimal(0)); - - EXPECT_FALSE(Decimal(1) == Decimal(2)); - EXPECT_FALSE(Decimal(1) != Decimal(1)); - EXPECT_FALSE(Decimal(1) < Decimal(0)); - EXPECT_FALSE(Decimal(1) <= Decimal(0)); - EXPECT_FALSE(Decimal(1) > Decimal(2)); - EXPECT_FALSE(Decimal(1) >= Decimal(2)); +TEST_F(DecimalTest, Compare) { + EXPECT_TRUE(Decimal(0) == Decimal(0)); + EXPECT_TRUE(Decimal(0) != Decimal(1)); + EXPECT_TRUE(Decimal(0) < Decimal(1)); + EXPECT_TRUE(Decimal(0) <= Decimal(0)); + EXPECT_TRUE(Decimal(0) > Decimal(-1)); + EXPECT_TRUE(Decimal(0) >= Decimal(0)); + + EXPECT_FALSE(Decimal(1) == Decimal(2)); + EXPECT_FALSE(Decimal(1) != Decimal(1)); + EXPECT_FALSE(Decimal(1) < Decimal(0)); + EXPECT_FALSE(Decimal(1) <= Decimal(0)); + EXPECT_FALSE(Decimal(1) > Decimal(2)); + EXPECT_FALSE(Decimal(1) >= Decimal(2)); } -TEST_F(DecimalTest, CompareBigExponent) -{ - EXPECT_TRUE(encode(1, 1000, Positive) == encode(1, 1000, Positive)); - EXPECT_FALSE(encode(1, 1000, Positive) != encode(1, 1000, Positive)); - EXPECT_FALSE(encode(1, 1000, Positive) < encode(1, 1000, Positive)); - EXPECT_TRUE(encode(1, 1000, Positive) <= encode(1, 1000, Positive)); - EXPECT_FALSE(encode(1, 1000, Positive) > encode(1, 1000, Positive)); - EXPECT_TRUE(encode(1, 1000, Positive) >= encode(1, 1000, Positive)); - - EXPECT_TRUE(encode(1, 1000, Negative) == encode(1, 1000, Negative)); - EXPECT_FALSE(encode(1, 1000, Negative) != encode(1, 1000, Negative)); - EXPECT_FALSE(encode(1, 1000, Negative) < encode(1, 1000, Negative)); - EXPECT_TRUE(encode(1, 1000, Negative) <= encode(1, 1000, Negative)); - EXPECT_FALSE(encode(1, 1000, Negative) > encode(1, 1000, Negative)); - EXPECT_TRUE(encode(1, 1000, Negative) >= encode(1, 1000, Negative)); - - EXPECT_FALSE(encode(2, 1000, Positive) == encode(1, 1000, Positive)); - EXPECT_TRUE(encode(2, 1000, Positive) != encode(1, 1000, Positive)); - EXPECT_FALSE(encode(2, 1000, Positive) < encode(1, 1000, Positive)); - EXPECT_FALSE(encode(2, 1000, Positive) <= encode(1, 1000, Positive)); - EXPECT_TRUE(encode(2, 1000, Positive) > encode(1, 1000, Positive)); - EXPECT_TRUE(encode(2, 1000, Positive) >= encode(1, 1000, Positive)); - - EXPECT_FALSE(encode(2, 1000, Negative) == encode(1, 1000, Negative)); - EXPECT_TRUE(encode(2, 1000, Negative) != encode(1, 1000, Negative)); - EXPECT_TRUE(encode(2, 1000, Negative) < encode(1, 1000, Negative)); - EXPECT_TRUE(encode(2, 1000, Negative) <= encode(1, 1000, Negative)); - EXPECT_FALSE(encode(2, 1000, Negative) > encode(1, 1000, Negative)); - EXPECT_FALSE(encode(2, 1000, Negative) >= encode(1, 1000, Negative)); +TEST_F(DecimalTest, CompareBigExponent) { + EXPECT_TRUE(encode(1, 1000, Positive) == encode(1, 1000, Positive)); + EXPECT_FALSE(encode(1, 1000, Positive) != encode(1, 1000, Positive)); + EXPECT_FALSE(encode(1, 1000, Positive) < encode(1, 1000, Positive)); + EXPECT_TRUE(encode(1, 1000, Positive) <= encode(1, 1000, Positive)); + EXPECT_FALSE(encode(1, 1000, Positive) > encode(1, 1000, Positive)); + EXPECT_TRUE(encode(1, 1000, Positive) >= encode(1, 1000, Positive)); + + EXPECT_TRUE(encode(1, 1000, Negative) == encode(1, 1000, Negative)); + EXPECT_FALSE(encode(1, 1000, Negative) != encode(1, 1000, Negative)); + EXPECT_FALSE(encode(1, 1000, Negative) < encode(1, 1000, Negative)); + EXPECT_TRUE(encode(1, 1000, Negative) <= encode(1, 1000, Negative)); + EXPECT_FALSE(encode(1, 1000, Negative) > encode(1, 1000, Negative)); + EXPECT_TRUE(encode(1, 1000, Negative) >= encode(1, 1000, Negative)); + + EXPECT_FALSE(encode(2, 1000, Positive) == encode(1, 1000, Positive)); + EXPECT_TRUE(encode(2, 1000, Positive) != encode(1, 1000, Positive)); + EXPECT_FALSE(encode(2, 1000, Positive) < encode(1, 1000, Positive)); + EXPECT_FALSE(encode(2, 1000, Positive) <= encode(1, 1000, Positive)); + EXPECT_TRUE(encode(2, 1000, Positive) > encode(1, 1000, Positive)); + EXPECT_TRUE(encode(2, 1000, Positive) >= encode(1, 1000, Positive)); + + EXPECT_FALSE(encode(2, 1000, Negative) == encode(1, 1000, Negative)); + EXPECT_TRUE(encode(2, 1000, Negative) != encode(1, 1000, Negative)); + EXPECT_TRUE(encode(2, 1000, Negative) < encode(1, 1000, Negative)); + EXPECT_TRUE(encode(2, 1000, Negative) <= encode(1, 1000, Negative)); + EXPECT_FALSE(encode(2, 1000, Negative) > encode(1, 1000, Negative)); + EXPECT_FALSE(encode(2, 1000, Negative) >= encode(1, 1000, Negative)); } -TEST_F(DecimalTest, CompareSmallExponent) -{ - EXPECT_TRUE(encode(1, -1000, Positive) == encode(1, -1000, Positive)); - EXPECT_FALSE(encode(1, -1000, Positive) != encode(1, -1000, Positive)); - EXPECT_FALSE(encode(1, -1000, Positive) < encode(1, -1000, Positive)); - EXPECT_TRUE(encode(1, -1000, Positive) <= encode(1, -1000, Positive)); - EXPECT_FALSE(encode(1, -1000, Positive) > encode(1, -1000, Positive)); - EXPECT_TRUE(encode(1, -1000, Positive) >= encode(1, -1000, Positive)); - - EXPECT_TRUE(encode(1, -1000, Negative) == encode(1, -1000, Negative)); - EXPECT_FALSE(encode(1, -1000, Negative) != encode(1, -1000, Negative)); - EXPECT_FALSE(encode(1, -1000, Negative) < encode(1, -1000, Negative)); - EXPECT_TRUE(encode(1, -1000, Negative) <= encode(1, -1000, Negative)); - EXPECT_FALSE(encode(1, -1000, Negative) > encode(1, -1000, Negative)); - EXPECT_TRUE(encode(1, -1000, Negative) >= encode(1, -1000, Negative)); - - EXPECT_FALSE(encode(2, -1000, Positive) == encode(1, -1000, Positive)); - EXPECT_TRUE(encode(2, -1000, Positive) != encode(1, -1000, Positive)); - EXPECT_FALSE(encode(2, -1000, Positive) < encode(1, -1000, Positive)); - EXPECT_FALSE(encode(2, -1000, Positive) <= encode(1, -1000, Positive)); - EXPECT_TRUE(encode(2, -1000, Positive) > encode(1, -1000, Positive)); - EXPECT_TRUE(encode(2, -1000, Positive) >= encode(1, -1000, Positive)); - - EXPECT_FALSE(encode(2, -1000, Negative) == encode(1, -1000, Negative)); - EXPECT_TRUE(encode(2, -1000, Negative) != encode(1, -1000, Negative)); - EXPECT_TRUE(encode(2, -1000, Negative) < encode(1, -1000, Negative)); - EXPECT_TRUE(encode(2, -1000, Negative) <= encode(1, -1000, Negative)); - EXPECT_FALSE(encode(2, -1000, Negative) > encode(1, -1000, Negative)); - EXPECT_FALSE(encode(2, -1000, Negative) >= encode(1, -1000, Negative)); +TEST_F(DecimalTest, CompareSmallExponent) { + EXPECT_TRUE(encode(1, -1000, Positive) == encode(1, -1000, Positive)); + EXPECT_FALSE(encode(1, -1000, Positive) != encode(1, -1000, Positive)); + EXPECT_FALSE(encode(1, -1000, Positive) < encode(1, -1000, Positive)); + EXPECT_TRUE(encode(1, -1000, Positive) <= encode(1, -1000, Positive)); + EXPECT_FALSE(encode(1, -1000, Positive) > encode(1, -1000, Positive)); + EXPECT_TRUE(encode(1, -1000, Positive) >= encode(1, -1000, Positive)); + + EXPECT_TRUE(encode(1, -1000, Negative) == encode(1, -1000, Negative)); + EXPECT_FALSE(encode(1, -1000, Negative) != encode(1, -1000, Negative)); + EXPECT_FALSE(encode(1, -1000, Negative) < encode(1, -1000, Negative)); + EXPECT_TRUE(encode(1, -1000, Negative) <= encode(1, -1000, Negative)); + EXPECT_FALSE(encode(1, -1000, Negative) > encode(1, -1000, Negative)); + EXPECT_TRUE(encode(1, -1000, Negative) >= encode(1, -1000, Negative)); + + EXPECT_FALSE(encode(2, -1000, Positive) == encode(1, -1000, Positive)); + EXPECT_TRUE(encode(2, -1000, Positive) != encode(1, -1000, Positive)); + EXPECT_FALSE(encode(2, -1000, Positive) < encode(1, -1000, Positive)); + EXPECT_FALSE(encode(2, -1000, Positive) <= encode(1, -1000, Positive)); + EXPECT_TRUE(encode(2, -1000, Positive) > encode(1, -1000, Positive)); + EXPECT_TRUE(encode(2, -1000, Positive) >= encode(1, -1000, Positive)); + + EXPECT_FALSE(encode(2, -1000, Negative) == encode(1, -1000, Negative)); + EXPECT_TRUE(encode(2, -1000, Negative) != encode(1, -1000, Negative)); + EXPECT_TRUE(encode(2, -1000, Negative) < encode(1, -1000, Negative)); + EXPECT_TRUE(encode(2, -1000, Negative) <= encode(1, -1000, Negative)); + EXPECT_FALSE(encode(2, -1000, Negative) > encode(1, -1000, Negative)); + EXPECT_FALSE(encode(2, -1000, Negative) >= encode(1, -1000, Negative)); } -TEST_F(DecimalTest, CompareSpecialValues) -{ - const Decimal Infinity(Decimal::infinity(Positive)); - const Decimal MinusInfinity(Decimal::infinity(Negative)); - const Decimal NaN(Decimal::nan()); - const Decimal Zero(Decimal::zero(Positive)); - const Decimal MinusZero(Decimal::zero(Negative)); - const Decimal Ten(10); - - EXPECT_TRUE(Zero == Zero); - EXPECT_FALSE(Zero != Zero); - EXPECT_FALSE(Zero < Zero); - EXPECT_TRUE(Zero <= Zero); - EXPECT_FALSE(Zero > Zero); - EXPECT_TRUE(Zero >= Zero); - - EXPECT_TRUE(Zero == MinusZero); - EXPECT_FALSE(Zero != MinusZero); - EXPECT_FALSE(Zero < MinusZero); - EXPECT_TRUE(Zero <= MinusZero); - EXPECT_FALSE(Zero > MinusZero); - EXPECT_TRUE(Zero >= MinusZero); - - EXPECT_TRUE(MinusZero == Zero); - EXPECT_FALSE(MinusZero != Zero); - EXPECT_FALSE(MinusZero < Zero); - EXPECT_TRUE(MinusZero <= Zero); - EXPECT_FALSE(MinusZero > Zero); - EXPECT_TRUE(MinusZero >= Zero); - - EXPECT_TRUE(MinusZero == MinusZero); - EXPECT_FALSE(MinusZero != MinusZero); - EXPECT_FALSE(MinusZero < MinusZero); - EXPECT_TRUE(MinusZero <= MinusZero); - EXPECT_FALSE(MinusZero > MinusZero); - EXPECT_TRUE(MinusZero >= MinusZero); - - EXPECT_TRUE(Infinity == Infinity); - EXPECT_FALSE(Infinity != Infinity); - EXPECT_FALSE(Infinity < Infinity); - EXPECT_TRUE(Infinity <= Infinity); - EXPECT_FALSE(Infinity > Infinity); - EXPECT_TRUE(Infinity >= Infinity); - - EXPECT_FALSE(Infinity == Ten); - EXPECT_TRUE(Infinity != Ten); - EXPECT_FALSE(Infinity < Ten); - EXPECT_FALSE(Infinity <= Ten); - EXPECT_TRUE(Infinity > Ten); - EXPECT_TRUE(Infinity >= Ten); - - EXPECT_FALSE(Infinity == MinusInfinity); - EXPECT_TRUE(Infinity != MinusInfinity); - EXPECT_FALSE(Infinity < MinusInfinity); - EXPECT_FALSE(Infinity <= MinusInfinity); - EXPECT_TRUE(Infinity > MinusInfinity); - EXPECT_TRUE(Infinity >= MinusInfinity); - - EXPECT_FALSE(Infinity == NaN); - EXPECT_FALSE(Infinity != NaN); - EXPECT_FALSE(Infinity < NaN); - EXPECT_FALSE(Infinity <= NaN); - EXPECT_FALSE(Infinity > NaN); - EXPECT_FALSE(Infinity >= NaN); - - EXPECT_FALSE(MinusInfinity == Infinity); - EXPECT_TRUE(MinusInfinity != Infinity); - EXPECT_TRUE(MinusInfinity < Infinity); - EXPECT_TRUE(MinusInfinity <= Infinity); - EXPECT_FALSE(MinusInfinity > Infinity); - EXPECT_FALSE(MinusInfinity >= Infinity); - - EXPECT_FALSE(MinusInfinity == Ten); - EXPECT_TRUE(MinusInfinity != Ten); - EXPECT_TRUE(MinusInfinity < Ten); - EXPECT_TRUE(MinusInfinity <= Ten); - EXPECT_FALSE(MinusInfinity > Ten); - EXPECT_FALSE(MinusInfinity >= Ten); - - EXPECT_TRUE(MinusInfinity == MinusInfinity); - EXPECT_FALSE(MinusInfinity != MinusInfinity); - EXPECT_FALSE(MinusInfinity < MinusInfinity); - EXPECT_TRUE(MinusInfinity <= MinusInfinity); - EXPECT_FALSE(MinusInfinity > MinusInfinity); - EXPECT_TRUE(MinusInfinity >= MinusInfinity); - - EXPECT_FALSE(MinusInfinity == NaN); - EXPECT_FALSE(MinusInfinity != NaN); - EXPECT_FALSE(MinusInfinity < NaN); - EXPECT_FALSE(MinusInfinity <= NaN); - EXPECT_FALSE(MinusInfinity > NaN); - EXPECT_FALSE(MinusInfinity >= NaN); - - EXPECT_FALSE(NaN == Infinity); - EXPECT_FALSE(NaN != Infinity); - EXPECT_FALSE(NaN < Infinity); - EXPECT_FALSE(NaN <= Infinity); - EXPECT_FALSE(NaN > Infinity); - EXPECT_FALSE(NaN >= Infinity); - - EXPECT_FALSE(NaN == Ten); - EXPECT_FALSE(NaN != Ten); - EXPECT_FALSE(NaN < Ten); - EXPECT_FALSE(NaN <= Ten); - EXPECT_FALSE(NaN > Ten); - EXPECT_FALSE(NaN >= Ten); - - EXPECT_FALSE(NaN == MinusInfinity); - EXPECT_FALSE(NaN != MinusInfinity); - EXPECT_FALSE(NaN < MinusInfinity); - EXPECT_FALSE(NaN <= MinusInfinity); - EXPECT_FALSE(NaN > MinusInfinity); - EXPECT_FALSE(NaN >= MinusInfinity); - - EXPECT_TRUE(NaN == NaN); - EXPECT_FALSE(NaN != NaN); - EXPECT_FALSE(NaN < NaN); - EXPECT_TRUE(NaN <= NaN); - EXPECT_FALSE(NaN > NaN); - EXPECT_TRUE(NaN >= NaN); +TEST_F(DecimalTest, CompareSpecialValues) { + const Decimal Infinity(Decimal::infinity(Positive)); + const Decimal MinusInfinity(Decimal::infinity(Negative)); + const Decimal NaN(Decimal::nan()); + const Decimal Zero(Decimal::zero(Positive)); + const Decimal MinusZero(Decimal::zero(Negative)); + const Decimal Ten(10); + + EXPECT_TRUE(Zero == Zero); + EXPECT_FALSE(Zero != Zero); + EXPECT_FALSE(Zero < Zero); + EXPECT_TRUE(Zero <= Zero); + EXPECT_FALSE(Zero > Zero); + EXPECT_TRUE(Zero >= Zero); + + EXPECT_TRUE(Zero == MinusZero); + EXPECT_FALSE(Zero != MinusZero); + EXPECT_FALSE(Zero < MinusZero); + EXPECT_TRUE(Zero <= MinusZero); + EXPECT_FALSE(Zero > MinusZero); + EXPECT_TRUE(Zero >= MinusZero); + + EXPECT_TRUE(MinusZero == Zero); + EXPECT_FALSE(MinusZero != Zero); + EXPECT_FALSE(MinusZero < Zero); + EXPECT_TRUE(MinusZero <= Zero); + EXPECT_FALSE(MinusZero > Zero); + EXPECT_TRUE(MinusZero >= Zero); + + EXPECT_TRUE(MinusZero == MinusZero); + EXPECT_FALSE(MinusZero != MinusZero); + EXPECT_FALSE(MinusZero < MinusZero); + EXPECT_TRUE(MinusZero <= MinusZero); + EXPECT_FALSE(MinusZero > MinusZero); + EXPECT_TRUE(MinusZero >= MinusZero); + + EXPECT_TRUE(Infinity == Infinity); + EXPECT_FALSE(Infinity != Infinity); + EXPECT_FALSE(Infinity < Infinity); + EXPECT_TRUE(Infinity <= Infinity); + EXPECT_FALSE(Infinity > Infinity); + EXPECT_TRUE(Infinity >= Infinity); + + EXPECT_FALSE(Infinity == Ten); + EXPECT_TRUE(Infinity != Ten); + EXPECT_FALSE(Infinity < Ten); + EXPECT_FALSE(Infinity <= Ten); + EXPECT_TRUE(Infinity > Ten); + EXPECT_TRUE(Infinity >= Ten); + + EXPECT_FALSE(Infinity == MinusInfinity); + EXPECT_TRUE(Infinity != MinusInfinity); + EXPECT_FALSE(Infinity < MinusInfinity); + EXPECT_FALSE(Infinity <= MinusInfinity); + EXPECT_TRUE(Infinity > MinusInfinity); + EXPECT_TRUE(Infinity >= MinusInfinity); + + EXPECT_FALSE(Infinity == NaN); + EXPECT_FALSE(Infinity != NaN); + EXPECT_FALSE(Infinity < NaN); + EXPECT_FALSE(Infinity <= NaN); + EXPECT_FALSE(Infinity > NaN); + EXPECT_FALSE(Infinity >= NaN); + + EXPECT_FALSE(MinusInfinity == Infinity); + EXPECT_TRUE(MinusInfinity != Infinity); + EXPECT_TRUE(MinusInfinity < Infinity); + EXPECT_TRUE(MinusInfinity <= Infinity); + EXPECT_FALSE(MinusInfinity > Infinity); + EXPECT_FALSE(MinusInfinity >= Infinity); + + EXPECT_FALSE(MinusInfinity == Ten); + EXPECT_TRUE(MinusInfinity != Ten); + EXPECT_TRUE(MinusInfinity < Ten); + EXPECT_TRUE(MinusInfinity <= Ten); + EXPECT_FALSE(MinusInfinity > Ten); + EXPECT_FALSE(MinusInfinity >= Ten); + + EXPECT_TRUE(MinusInfinity == MinusInfinity); + EXPECT_FALSE(MinusInfinity != MinusInfinity); + EXPECT_FALSE(MinusInfinity < MinusInfinity); + EXPECT_TRUE(MinusInfinity <= MinusInfinity); + EXPECT_FALSE(MinusInfinity > MinusInfinity); + EXPECT_TRUE(MinusInfinity >= MinusInfinity); + + EXPECT_FALSE(MinusInfinity == NaN); + EXPECT_FALSE(MinusInfinity != NaN); + EXPECT_FALSE(MinusInfinity < NaN); + EXPECT_FALSE(MinusInfinity <= NaN); + EXPECT_FALSE(MinusInfinity > NaN); + EXPECT_FALSE(MinusInfinity >= NaN); + + EXPECT_FALSE(NaN == Infinity); + EXPECT_FALSE(NaN != Infinity); + EXPECT_FALSE(NaN < Infinity); + EXPECT_FALSE(NaN <= Infinity); + EXPECT_FALSE(NaN > Infinity); + EXPECT_FALSE(NaN >= Infinity); + + EXPECT_FALSE(NaN == Ten); + EXPECT_FALSE(NaN != Ten); + EXPECT_FALSE(NaN < Ten); + EXPECT_FALSE(NaN <= Ten); + EXPECT_FALSE(NaN > Ten); + EXPECT_FALSE(NaN >= Ten); + + EXPECT_FALSE(NaN == MinusInfinity); + EXPECT_FALSE(NaN != MinusInfinity); + EXPECT_FALSE(NaN < MinusInfinity); + EXPECT_FALSE(NaN <= MinusInfinity); + EXPECT_FALSE(NaN > MinusInfinity); + EXPECT_FALSE(NaN >= MinusInfinity); + + EXPECT_TRUE(NaN == NaN); + EXPECT_FALSE(NaN != NaN); + EXPECT_FALSE(NaN < NaN); + EXPECT_TRUE(NaN <= NaN); + EXPECT_FALSE(NaN > NaN); + EXPECT_TRUE(NaN >= NaN); } -TEST_F(DecimalTest, Constructor) -{ - EXPECT_DECIMAL_ENCODED_DATA_EQ(0u, 0, Positive, encode(0, 0, Positive)); - EXPECT_DECIMAL_ENCODED_DATA_EQ(0u, 0, Negative, encode(0, 0, Negative)); - EXPECT_DECIMAL_ENCODED_DATA_EQ(1u, 0, Positive, encode(1, 0, Positive)); - EXPECT_DECIMAL_ENCODED_DATA_EQ(1u, 0, Negative, encode(1, 0, Negative)); - EXPECT_DECIMAL_ENCODED_DATA_EQ(1u, 1022, Positive, encode(1, 1022, Positive)); - EXPECT_DECIMAL_ENCODED_DATA_EQ(1u, 1022, Negative, encode(1, 1022, Negative)); - EXPECT_DECIMAL_ENCODED_DATA_EQ(1u, 1023, Positive, encode(1, 1023, Positive)); - EXPECT_DECIMAL_ENCODED_DATA_EQ(1u, 1023, Negative, encode(1, 1023, Negative)); - EXPECT_TRUE(encode(1, 2000, Positive).isInfinity()); - EXPECT_TRUE(encode(1, 2000, Negative).isInfinity()); - EXPECT_DECIMAL_ENCODED_DATA_EQ(0u, 0, Positive, encode(1, -2000, Positive)); - EXPECT_DECIMAL_ENCODED_DATA_EQ(0u, 0, Negative, encode(1, -2000, Negative)); - EXPECT_DECIMAL_ENCODED_DATA_EQ(UINT64_C(99999999999999998), 0, Positive, encode(UINT64_C(99999999999999998), 0, Positive)); - EXPECT_DECIMAL_ENCODED_DATA_EQ(UINT64_C(99999999999999998), 0, Negative, encode(UINT64_C(99999999999999998), 0, Negative)); - EXPECT_DECIMAL_ENCODED_DATA_EQ(UINT64_C(99999999999999999), 0, Positive, encode(UINT64_C(99999999999999999), 0, Positive)); - EXPECT_DECIMAL_ENCODED_DATA_EQ(UINT64_C(99999999999999999), 0, Negative, encode(UINT64_C(99999999999999999), 0, Negative)); - EXPECT_DECIMAL_ENCODED_DATA_EQ(UINT64_C(100000000000000000), 0, Positive, encode(UINT64_C(100000000000000000), 0, Positive)); - EXPECT_DECIMAL_ENCODED_DATA_EQ(UINT64_C(100000000000000000), 0, Negative, encode(UINT64_C(100000000000000000), 0, Negative)); +TEST_F(DecimalTest, Constructor) { + EXPECT_DECIMAL_ENCODED_DATA_EQ(0u, 0, Positive, encode(0, 0, Positive)); + EXPECT_DECIMAL_ENCODED_DATA_EQ(0u, 0, Negative, encode(0, 0, Negative)); + EXPECT_DECIMAL_ENCODED_DATA_EQ(1u, 0, Positive, encode(1, 0, Positive)); + EXPECT_DECIMAL_ENCODED_DATA_EQ(1u, 0, Negative, encode(1, 0, Negative)); + EXPECT_DECIMAL_ENCODED_DATA_EQ(1u, 1022, Positive, encode(1, 1022, Positive)); + EXPECT_DECIMAL_ENCODED_DATA_EQ(1u, 1022, Negative, encode(1, 1022, Negative)); + EXPECT_DECIMAL_ENCODED_DATA_EQ(1u, 1023, Positive, encode(1, 1023, Positive)); + EXPECT_DECIMAL_ENCODED_DATA_EQ(1u, 1023, Negative, encode(1, 1023, Negative)); + EXPECT_TRUE(encode(1, 2000, Positive).isInfinity()); + EXPECT_TRUE(encode(1, 2000, Negative).isInfinity()); + EXPECT_DECIMAL_ENCODED_DATA_EQ(0u, 0, Positive, encode(1, -2000, Positive)); + EXPECT_DECIMAL_ENCODED_DATA_EQ(0u, 0, Negative, encode(1, -2000, Negative)); + EXPECT_DECIMAL_ENCODED_DATA_EQ( + UINT64_C(99999999999999998), 0, Positive, + encode(UINT64_C(99999999999999998), 0, Positive)); + EXPECT_DECIMAL_ENCODED_DATA_EQ( + UINT64_C(99999999999999998), 0, Negative, + encode(UINT64_C(99999999999999998), 0, Negative)); + EXPECT_DECIMAL_ENCODED_DATA_EQ( + UINT64_C(99999999999999999), 0, Positive, + encode(UINT64_C(99999999999999999), 0, Positive)); + EXPECT_DECIMAL_ENCODED_DATA_EQ( + UINT64_C(99999999999999999), 0, Negative, + encode(UINT64_C(99999999999999999), 0, Negative)); + EXPECT_DECIMAL_ENCODED_DATA_EQ( + UINT64_C(100000000000000000), 0, Positive, + encode(UINT64_C(100000000000000000), 0, Positive)); + EXPECT_DECIMAL_ENCODED_DATA_EQ( + UINT64_C(100000000000000000), 0, Negative, + encode(UINT64_C(100000000000000000), 0, Negative)); } -TEST_F(DecimalTest, Division) -{ - EXPECT_EQ(encode(0, 0, Positive), Decimal(0) / Decimal(1)); - EXPECT_EQ(encode(2, 0, Negative), Decimal(2) / Decimal(-1)); - EXPECT_EQ(encode(5, -1, Negative), Decimal(-1) / Decimal(2)); - EXPECT_EQ(encode(99, 0, Positive), Decimal(99) / Decimal(1)); - EXPECT_EQ(Decimal(1), Decimal(-50) / Decimal(-50)); - EXPECT_EQ(encode(UINT64_C(33333333333333333), -17, Positive), Decimal(1) / Decimal(3)); - EXPECT_EQ(encode(UINT64_C(12345678901234), -1, Positive), encode(UINT64_C(12345678901234), 0, Positive) / Decimal(10)); +TEST_F(DecimalTest, Division) { + EXPECT_EQ(encode(0, 0, Positive), Decimal(0) / Decimal(1)); + EXPECT_EQ(encode(2, 0, Negative), Decimal(2) / Decimal(-1)); + EXPECT_EQ(encode(5, -1, Negative), Decimal(-1) / Decimal(2)); + EXPECT_EQ(encode(99, 0, Positive), Decimal(99) / Decimal(1)); + EXPECT_EQ(Decimal(1), Decimal(-50) / Decimal(-50)); + EXPECT_EQ(encode(UINT64_C(33333333333333333), -17, Positive), + Decimal(1) / Decimal(3)); + EXPECT_EQ(encode(UINT64_C(12345678901234), -1, Positive), + encode(UINT64_C(12345678901234), 0, Positive) / Decimal(10)); } -TEST_F(DecimalTest, DivisionBigExponent) -{ - EXPECT_EQ(encode(1, 1022, Positive), encode(1, 1022, Positive) / encode(1, 0, Positive)); - EXPECT_EQ(encode(1, 0, Positive), encode(1, 1022, Positive) / encode(1, 1022, Positive)); - EXPECT_EQ(Decimal::infinity(Positive), encode(1, 1022, Positive) / encode(1, -1000, Positive)); +TEST_F(DecimalTest, DivisionBigExponent) { + EXPECT_EQ(encode(1, 1022, Positive), + encode(1, 1022, Positive) / encode(1, 0, Positive)); + EXPECT_EQ(encode(1, 0, Positive), + encode(1, 1022, Positive) / encode(1, 1022, Positive)); + EXPECT_EQ(Decimal::infinity(Positive), + encode(1, 1022, Positive) / encode(1, -1000, Positive)); } -TEST_F(DecimalTest, DivisionSmallExponent) -{ - EXPECT_EQ(encode(1, -1022, Positive), encode(1, -1022, Positive) / encode(1, 0, Positive)); - EXPECT_EQ(encode(1, 0, Positive), encode(1, -1022, Positive) / encode(1, -1022, Positive)); +TEST_F(DecimalTest, DivisionSmallExponent) { + EXPECT_EQ(encode(1, -1022, Positive), + encode(1, -1022, Positive) / encode(1, 0, Positive)); + EXPECT_EQ(encode(1, 0, Positive), + encode(1, -1022, Positive) / encode(1, -1022, Positive)); } -TEST_F(DecimalTest, DivisionSpecialValues) -{ - const Decimal Infinity(Decimal::infinity(Positive)); - const Decimal MinusInfinity(Decimal::infinity(Negative)); - const Decimal NaN(Decimal::nan()); - const Decimal Zero(Decimal::zero(Positive)); - const Decimal MinusZero(Decimal::zero(Negative)); - const Decimal Ten(10); - const Decimal MinusTen(-10); - - EXPECT_EQ(NaN, Zero / Zero); - EXPECT_EQ(NaN, Zero / MinusZero); - EXPECT_EQ(NaN, MinusZero / Zero); - EXPECT_EQ(NaN, MinusZero / MinusZero); - - EXPECT_EQ(Infinity, Ten / Zero); - EXPECT_EQ(MinusInfinity, Ten / MinusZero); - EXPECT_EQ(MinusInfinity, MinusTen / Zero); - EXPECT_EQ(Infinity, MinusTen / MinusZero); - - EXPECT_EQ(Infinity, Infinity / Zero); - EXPECT_EQ(MinusInfinity, Infinity / MinusZero); - EXPECT_EQ(MinusInfinity, MinusInfinity / Zero); - EXPECT_EQ(Infinity, MinusInfinity / MinusZero); - - EXPECT_EQ(NaN, Infinity / Infinity); - EXPECT_EQ(NaN, Infinity / MinusInfinity); - EXPECT_EQ(NaN, MinusInfinity / Infinity); - EXPECT_EQ(NaN, MinusInfinity / MinusInfinity); - - EXPECT_EQ(Zero, Ten / Infinity); - EXPECT_EQ(MinusZero, Ten / MinusInfinity); - EXPECT_EQ(MinusZero, MinusTen / Infinity); - EXPECT_EQ(Zero, MinusTen / MinusInfinity); - - EXPECT_EQ(NaN, NaN / NaN); - EXPECT_EQ(NaN, NaN / Ten); - EXPECT_EQ(NaN, Ten / NaN); - - EXPECT_EQ(NaN, NaN / Infinity); - EXPECT_EQ(NaN, NaN / MinusInfinity); - EXPECT_EQ(NaN, Infinity / NaN); - EXPECT_EQ(NaN, MinusInfinity / NaN); +TEST_F(DecimalTest, DivisionSpecialValues) { + const Decimal Infinity(Decimal::infinity(Positive)); + const Decimal MinusInfinity(Decimal::infinity(Negative)); + const Decimal NaN(Decimal::nan()); + const Decimal Zero(Decimal::zero(Positive)); + const Decimal MinusZero(Decimal::zero(Negative)); + const Decimal Ten(10); + const Decimal MinusTen(-10); + + EXPECT_EQ(NaN, Zero / Zero); + EXPECT_EQ(NaN, Zero / MinusZero); + EXPECT_EQ(NaN, MinusZero / Zero); + EXPECT_EQ(NaN, MinusZero / MinusZero); + + EXPECT_EQ(Infinity, Ten / Zero); + EXPECT_EQ(MinusInfinity, Ten / MinusZero); + EXPECT_EQ(MinusInfinity, MinusTen / Zero); + EXPECT_EQ(Infinity, MinusTen / MinusZero); + + EXPECT_EQ(Infinity, Infinity / Zero); + EXPECT_EQ(MinusInfinity, Infinity / MinusZero); + EXPECT_EQ(MinusInfinity, MinusInfinity / Zero); + EXPECT_EQ(Infinity, MinusInfinity / MinusZero); + + EXPECT_EQ(NaN, Infinity / Infinity); + EXPECT_EQ(NaN, Infinity / MinusInfinity); + EXPECT_EQ(NaN, MinusInfinity / Infinity); + EXPECT_EQ(NaN, MinusInfinity / MinusInfinity); + + EXPECT_EQ(Zero, Ten / Infinity); + EXPECT_EQ(MinusZero, Ten / MinusInfinity); + EXPECT_EQ(MinusZero, MinusTen / Infinity); + EXPECT_EQ(Zero, MinusTen / MinusInfinity); + + EXPECT_EQ(NaN, NaN / NaN); + EXPECT_EQ(NaN, NaN / Ten); + EXPECT_EQ(NaN, Ten / NaN); + + EXPECT_EQ(NaN, NaN / Infinity); + EXPECT_EQ(NaN, NaN / MinusInfinity); + EXPECT_EQ(NaN, Infinity / NaN); + EXPECT_EQ(NaN, MinusInfinity / NaN); } -TEST_F(DecimalTest, EncodedData) -{ - EXPECT_EQ(encode(0, 0, Positive), encode(0, 0, Positive)); - EXPECT_EQ(encode(0, 0, Negative), encode(0, 0, Negative)); - EXPECT_EQ(Decimal(1), Decimal(1)); - EXPECT_EQ(encode(1, 0, Negative), encode(1, 0, Negative)); - EXPECT_EQ(Decimal::infinity(Positive), encode(1, 2000, Positive)); - EXPECT_EQ(Decimal::zero(Positive), encode(1, -2000, Positive)); +TEST_F(DecimalTest, EncodedData) { + EXPECT_EQ(encode(0, 0, Positive), encode(0, 0, Positive)); + EXPECT_EQ(encode(0, 0, Negative), encode(0, 0, Negative)); + EXPECT_EQ(Decimal(1), Decimal(1)); + EXPECT_EQ(encode(1, 0, Negative), encode(1, 0, Negative)); + EXPECT_EQ(Decimal::infinity(Positive), encode(1, 2000, Positive)); + EXPECT_EQ(Decimal::zero(Positive), encode(1, -2000, Positive)); } -TEST_F(DecimalTest, Floor) -{ - EXPECT_EQ(Decimal(1), Decimal(1).floor()); - EXPECT_EQ(Decimal(0), encode(1, -10, Positive).floor()); - EXPECT_EQ(Decimal(1), encode(11, -1, Positive).floor()); - EXPECT_EQ(Decimal(1), encode(13, -1, Positive).floor()); - EXPECT_EQ(Decimal(1), encode(15, -1, Positive).floor()); - EXPECT_EQ(Decimal(1), encode(19, -1, Positive).floor()); - EXPECT_EQ(Decimal(1), encode(193332, -5, Positive).floor()); - EXPECT_EQ(Decimal(12), encode(12002, -3, Positive).floor()); - - EXPECT_EQ(Decimal(-1), Decimal(-1).floor()); - EXPECT_EQ(Decimal(-1), encode(1, -10, Negative).floor()); - EXPECT_EQ(Decimal(-2), encode(11, -1, Negative).floor()); - EXPECT_EQ(Decimal(-2), encode(13, -1, Negative).floor()); - EXPECT_EQ(Decimal(-2), encode(15, -1, Negative).floor()); - EXPECT_EQ(Decimal(-2), encode(19, -1, Negative).floor()); - EXPECT_EQ(Decimal(-2), encode(193332, -5, Negative).floor()); - EXPECT_EQ(Decimal(-13), encode(12002, -3, Negative).floor()); +TEST_F(DecimalTest, Floor) { + EXPECT_EQ(Decimal(1), Decimal(1).floor()); + EXPECT_EQ(Decimal(0), encode(1, -10, Positive).floor()); + EXPECT_EQ(Decimal(1), encode(11, -1, Positive).floor()); + EXPECT_EQ(Decimal(1), encode(13, -1, Positive).floor()); + EXPECT_EQ(Decimal(1), encode(15, -1, Positive).floor()); + EXPECT_EQ(Decimal(1), encode(19, -1, Positive).floor()); + EXPECT_EQ(Decimal(1), encode(193332, -5, Positive).floor()); + EXPECT_EQ(Decimal(12), encode(12002, -3, Positive).floor()); + + EXPECT_EQ(Decimal(-1), Decimal(-1).floor()); + EXPECT_EQ(Decimal(-1), encode(1, -10, Negative).floor()); + EXPECT_EQ(Decimal(-2), encode(11, -1, Negative).floor()); + EXPECT_EQ(Decimal(-2), encode(13, -1, Negative).floor()); + EXPECT_EQ(Decimal(-2), encode(15, -1, Negative).floor()); + EXPECT_EQ(Decimal(-2), encode(19, -1, Negative).floor()); + EXPECT_EQ(Decimal(-2), encode(193332, -5, Negative).floor()); + EXPECT_EQ(Decimal(-13), encode(12002, -3, Negative).floor()); } -TEST_F(DecimalTest, FloorBigExponent) -{ - EXPECT_EQ(encode(1, 1000, Positive), encode(1, 1000, Positive).floor()); - EXPECT_EQ(encode(1, 1000, Negative), encode(1, 1000, Negative).floor()); +TEST_F(DecimalTest, FloorBigExponent) { + EXPECT_EQ(encode(1, 1000, Positive), encode(1, 1000, Positive).floor()); + EXPECT_EQ(encode(1, 1000, Negative), encode(1, 1000, Negative).floor()); } -TEST_F(DecimalTest, FloorSmallExponent) -{ - EXPECT_EQ(encode(0, 0, Positive), encode(1, -1000, Positive).floor()); - EXPECT_EQ(encode(1, 0, Negative), encode(1, -1000, Negative).floor()); +TEST_F(DecimalTest, FloorSmallExponent) { + EXPECT_EQ(encode(0, 0, Positive), encode(1, -1000, Positive).floor()); + EXPECT_EQ(encode(1, 0, Negative), encode(1, -1000, Negative).floor()); } -TEST_F(DecimalTest, FloorSpecialValues) -{ - EXPECT_EQ(Decimal::infinity(Positive), Decimal::infinity(Positive).floor()); - EXPECT_EQ(Decimal::infinity(Negative), Decimal::infinity(Negative).floor()); - EXPECT_EQ(Decimal::nan(), Decimal::nan().floor()); +TEST_F(DecimalTest, FloorSpecialValues) { + EXPECT_EQ(Decimal::infinity(Positive), Decimal::infinity(Positive).floor()); + EXPECT_EQ(Decimal::infinity(Negative), Decimal::infinity(Negative).floor()); + EXPECT_EQ(Decimal::nan(), Decimal::nan().floor()); } -TEST_F(DecimalTest, FromDouble) -{ - EXPECT_EQ(encode(0, 0, Positive), Decimal::fromDouble(0.0)); - EXPECT_EQ(encode(0, 0, Negative), Decimal::fromDouble(-0.0)); - EXPECT_EQ(encode(1, 0, Positive), Decimal::fromDouble(1)); - EXPECT_EQ(encode(1, 0, Negative), Decimal::fromDouble(-1)); - EXPECT_EQ(encode(123, 0, Positive), Decimal::fromDouble(123)); - EXPECT_EQ(encode(123, 0, Negative), Decimal::fromDouble(-123)); - EXPECT_EQ(encode(1, -1, Positive), Decimal::fromDouble(0.1)); - EXPECT_EQ(encode(1, -1, Negative), Decimal::fromDouble(-0.1)); +TEST_F(DecimalTest, FromDouble) { + EXPECT_EQ(encode(0, 0, Positive), Decimal::fromDouble(0.0)); + EXPECT_EQ(encode(0, 0, Negative), Decimal::fromDouble(-0.0)); + EXPECT_EQ(encode(1, 0, Positive), Decimal::fromDouble(1)); + EXPECT_EQ(encode(1, 0, Negative), Decimal::fromDouble(-1)); + EXPECT_EQ(encode(123, 0, Positive), Decimal::fromDouble(123)); + EXPECT_EQ(encode(123, 0, Negative), Decimal::fromDouble(-123)); + EXPECT_EQ(encode(1, -1, Positive), Decimal::fromDouble(0.1)); + EXPECT_EQ(encode(1, -1, Negative), Decimal::fromDouble(-0.1)); } -TEST_F(DecimalTest, FromDoubleLimits) -{ - EXPECT_EQ(encode(UINT64_C(2220446049250313), -31, Positive), Decimal::fromDouble(std::numeric_limits::epsilon())); - EXPECT_EQ(encode(UINT64_C(2220446049250313), -31, Negative), Decimal::fromDouble(-std::numeric_limits::epsilon())); - EXPECT_EQ(encode(UINT64_C(17976931348623157), 292, Positive), Decimal::fromDouble(std::numeric_limits::max())); - EXPECT_EQ(encode(UINT64_C(17976931348623157), 292, Negative), Decimal::fromDouble(-std::numeric_limits::max())); - EXPECT_EQ(encode(UINT64_C(22250738585072014), -324, Positive), Decimal::fromDouble(std::numeric_limits::min())); - EXPECT_EQ(encode(UINT64_C(22250738585072014), -324, Negative), Decimal::fromDouble(-std::numeric_limits::min())); - EXPECT_TRUE(Decimal::fromDouble(std::numeric_limits::infinity()).isInfinity()); - EXPECT_TRUE(Decimal::fromDouble(-std::numeric_limits::infinity()).isInfinity()); - EXPECT_TRUE(Decimal::fromDouble(std::numeric_limits::quiet_NaN()).isNaN()); - EXPECT_TRUE(Decimal::fromDouble(-std::numeric_limits::quiet_NaN()).isNaN()); +TEST_F(DecimalTest, FromDoubleLimits) { + EXPECT_EQ(encode(UINT64_C(2220446049250313), -31, Positive), + Decimal::fromDouble(std::numeric_limits::epsilon())); + EXPECT_EQ(encode(UINT64_C(2220446049250313), -31, Negative), + Decimal::fromDouble(-std::numeric_limits::epsilon())); + EXPECT_EQ(encode(UINT64_C(17976931348623157), 292, Positive), + Decimal::fromDouble(std::numeric_limits::max())); + EXPECT_EQ(encode(UINT64_C(17976931348623157), 292, Negative), + Decimal::fromDouble(-std::numeric_limits::max())); + EXPECT_EQ(encode(UINT64_C(22250738585072014), -324, Positive), + Decimal::fromDouble(std::numeric_limits::min())); + EXPECT_EQ(encode(UINT64_C(22250738585072014), -324, Negative), + Decimal::fromDouble(-std::numeric_limits::min())); + EXPECT_TRUE(Decimal::fromDouble(std::numeric_limits::infinity()) + .isInfinity()); + EXPECT_TRUE(Decimal::fromDouble(-std::numeric_limits::infinity()) + .isInfinity()); + EXPECT_TRUE( + Decimal::fromDouble(std::numeric_limits::quiet_NaN()).isNaN()); + EXPECT_TRUE( + Decimal::fromDouble(-std::numeric_limits::quiet_NaN()).isNaN()); } -TEST_F(DecimalTest, FromInt32) -{ - EXPECT_EQ(encode(0, 0, Positive), Decimal(0)); - EXPECT_EQ(encode(1, 0, Positive), Decimal(1)); - EXPECT_EQ(encode(1, 0, Negative), Decimal(-1)); - EXPECT_EQ(encode(100, 0, Positive), Decimal(100)); - EXPECT_EQ(encode(100, 0, Negative), Decimal(-100)); - EXPECT_EQ(encode(0x7FFFFFFF, 0, Positive), Decimal(std::numeric_limits::max())); - EXPECT_EQ(encode(0x80000000u, 0, Negative), Decimal(std::numeric_limits::min())); +TEST_F(DecimalTest, FromInt32) { + EXPECT_EQ(encode(0, 0, Positive), Decimal(0)); + EXPECT_EQ(encode(1, 0, Positive), Decimal(1)); + EXPECT_EQ(encode(1, 0, Negative), Decimal(-1)); + EXPECT_EQ(encode(100, 0, Positive), Decimal(100)); + EXPECT_EQ(encode(100, 0, Negative), Decimal(-100)); + EXPECT_EQ(encode(0x7FFFFFFF, 0, Positive), + Decimal(std::numeric_limits::max())); + EXPECT_EQ(encode(0x80000000u, 0, Negative), + Decimal(std::numeric_limits::min())); } -TEST_F(DecimalTest, FromString) -{ - EXPECT_EQ(encode(0, 0, Positive), fromString("0")); - EXPECT_EQ(encode(0, 0, Negative), fromString("-0")); - EXPECT_EQ(Decimal(1), fromString("1")); - EXPECT_EQ(encode(1, 0, Negative), fromString("-1")); - EXPECT_EQ(Decimal(1), fromString("01")); - EXPECT_EQ(encode(3, 0, Positive), fromString("+3")); - EXPECT_EQ(encode(0, 3, Positive), fromString("0E3")); - EXPECT_EQ(encode(5, -1, Positive), fromString(".5")); - EXPECT_EQ(encode(100, 0, Positive), fromString("100")); - EXPECT_EQ(encode(100, 0, Negative), fromString("-100")); - EXPECT_EQ(encode(123, -2, Positive), fromString("1.23")); - EXPECT_EQ(encode(123, -2, Negative), fromString("-1.23")); - EXPECT_EQ(encode(123, 8, Positive), fromString("1.23E10")); - EXPECT_EQ(encode(123, 8, Negative), fromString("-1.23E10")); - EXPECT_EQ(encode(123, 8, Positive), fromString("1.23E+10")); - EXPECT_EQ(encode(123, 8, Negative), fromString("-1.23E+10")); - EXPECT_EQ(encode(123, -12, Positive), fromString("1.23E-10")); - EXPECT_EQ(encode(123, -12, Negative), fromString("-1.23E-10")); - EXPECT_EQ(encode(5, -7, Positive), fromString("0.0000005")); - EXPECT_EQ(encode(0, 0, Positive), fromString("0e9999")); - EXPECT_EQ(encode(123, -3, Positive), fromString("0.123")); - EXPECT_EQ(encode(0, -2, Positive), fromString("00.00")); - EXPECT_EQ(encode(1, 2, Positive), fromString("1E2")); - EXPECT_EQ(Decimal::infinity(Positive), fromString("1E20000")); - EXPECT_EQ(Decimal::zero(Positive), fromString("1E-20000")); - EXPECT_EQ(encode(1000, 1023, Positive), fromString("1E1026")); - EXPECT_EQ(Decimal::zero(Positive), fromString("1E-1026")); - EXPECT_EQ(Decimal::infinity(Positive), fromString("1234567890E1036")); - - // 2^1024 - const uint64_t leadingDigitsOf2PowerOf1024 = UINT64_C(17976931348623159); - EXPECT_EQ(encode(leadingDigitsOf2PowerOf1024, 292, Positive), fromString("179769313486231590772930519078902473361797697894230657273430081157732675805500963132708477322407536021120113879871393357658789768814416622492847430639474124377767893424865485276302219601246094119453082952085005768838150682342462881473913110540827237163350510684586298239947245938479716304835356329624224137216")); +TEST_F(DecimalTest, FromString) { + EXPECT_EQ(encode(0, 0, Positive), fromString("0")); + EXPECT_EQ(encode(0, 0, Negative), fromString("-0")); + EXPECT_EQ(Decimal(1), fromString("1")); + EXPECT_EQ(encode(1, 0, Negative), fromString("-1")); + EXPECT_EQ(Decimal(1), fromString("01")); + EXPECT_EQ(encode(3, 0, Positive), fromString("+3")); + EXPECT_EQ(encode(0, 3, Positive), fromString("0E3")); + EXPECT_EQ(encode(5, -1, Positive), fromString(".5")); + EXPECT_EQ(encode(100, 0, Positive), fromString("100")); + EXPECT_EQ(encode(100, 0, Negative), fromString("-100")); + EXPECT_EQ(encode(123, -2, Positive), fromString("1.23")); + EXPECT_EQ(encode(123, -2, Negative), fromString("-1.23")); + EXPECT_EQ(encode(123, 8, Positive), fromString("1.23E10")); + EXPECT_EQ(encode(123, 8, Negative), fromString("-1.23E10")); + EXPECT_EQ(encode(123, 8, Positive), fromString("1.23E+10")); + EXPECT_EQ(encode(123, 8, Negative), fromString("-1.23E+10")); + EXPECT_EQ(encode(123, -12, Positive), fromString("1.23E-10")); + EXPECT_EQ(encode(123, -12, Negative), fromString("-1.23E-10")); + EXPECT_EQ(encode(5, -7, Positive), fromString("0.0000005")); + EXPECT_EQ(encode(0, 0, Positive), fromString("0e9999")); + EXPECT_EQ(encode(123, -3, Positive), fromString("0.123")); + EXPECT_EQ(encode(0, -2, Positive), fromString("00.00")); + EXPECT_EQ(encode(1, 2, Positive), fromString("1E2")); + EXPECT_EQ(Decimal::infinity(Positive), fromString("1E20000")); + EXPECT_EQ(Decimal::zero(Positive), fromString("1E-20000")); + EXPECT_EQ(encode(1000, 1023, Positive), fromString("1E1026")); + EXPECT_EQ(Decimal::zero(Positive), fromString("1E-1026")); + EXPECT_EQ(Decimal::infinity(Positive), fromString("1234567890E1036")); + + // 2^1024 + const uint64_t leadingDigitsOf2PowerOf1024 = UINT64_C(17976931348623159); + EXPECT_EQ(encode(leadingDigitsOf2PowerOf1024, 292, Positive), + fromString("1797693134862315907729305190789024733617976978942306572" + "7343008115773267580550096313270847732240753602112011387" + "9871393357658789768814416622492847430639474124377767893" + "4248654852763022196012460941194530829520850057688381506" + "8234246288147391311054082723716335051068458629823994724" + "5938479716304835356329624224137216")); } // These strings are look like proper number, but we don't accept them. -TEST_F(DecimalTest, FromStringLikeNumber) -{ - EXPECT_EQ(Decimal::nan(), fromString(" 123 ")); - EXPECT_EQ(Decimal::nan(), fromString("1,234")); +TEST_F(DecimalTest, FromStringLikeNumber) { + EXPECT_EQ(Decimal::nan(), fromString(" 123 ")); + EXPECT_EQ(Decimal::nan(), fromString("1,234")); } // fromString doesn't support infinity and NaN. -TEST_F(DecimalTest, FromStringSpecialValues) -{ - EXPECT_EQ(Decimal::nan(), fromString("INF")); - EXPECT_EQ(Decimal::nan(), fromString("Infinity")); - EXPECT_EQ(Decimal::nan(), fromString("infinity")); - EXPECT_EQ(Decimal::nan(), fromString("+Infinity")); - EXPECT_EQ(Decimal::nan(), fromString("+infinity")); - EXPECT_EQ(Decimal::nan(), fromString("-Infinity")); - EXPECT_EQ(Decimal::nan(), fromString("-infinity")); - EXPECT_EQ(Decimal::nan(), fromString("NaN")); - EXPECT_EQ(Decimal::nan(), fromString("nan")); - EXPECT_EQ(Decimal::nan(), fromString("+NaN")); - EXPECT_EQ(Decimal::nan(), fromString("+nan")); - EXPECT_EQ(Decimal::nan(), fromString("-NaN")); - EXPECT_EQ(Decimal::nan(), fromString("-nan")); +TEST_F(DecimalTest, FromStringSpecialValues) { + EXPECT_EQ(Decimal::nan(), fromString("INF")); + EXPECT_EQ(Decimal::nan(), fromString("Infinity")); + EXPECT_EQ(Decimal::nan(), fromString("infinity")); + EXPECT_EQ(Decimal::nan(), fromString("+Infinity")); + EXPECT_EQ(Decimal::nan(), fromString("+infinity")); + EXPECT_EQ(Decimal::nan(), fromString("-Infinity")); + EXPECT_EQ(Decimal::nan(), fromString("-infinity")); + EXPECT_EQ(Decimal::nan(), fromString("NaN")); + EXPECT_EQ(Decimal::nan(), fromString("nan")); + EXPECT_EQ(Decimal::nan(), fromString("+NaN")); + EXPECT_EQ(Decimal::nan(), fromString("+nan")); + EXPECT_EQ(Decimal::nan(), fromString("-NaN")); + EXPECT_EQ(Decimal::nan(), fromString("-nan")); } -TEST_F(DecimalTest, fromStringTruncated) -{ - EXPECT_EQ(Decimal::nan(), fromString("x")); - EXPECT_EQ(Decimal::nan(), fromString("0.")); - EXPECT_EQ(Decimal::nan(), fromString("1x")); +TEST_F(DecimalTest, fromStringTruncated) { + EXPECT_EQ(Decimal::nan(), fromString("x")); + EXPECT_EQ(Decimal::nan(), fromString("0.")); + EXPECT_EQ(Decimal::nan(), fromString("1x")); - EXPECT_EQ(Decimal::nan(), fromString("1Ex")); - EXPECT_EQ(Decimal::nan(), fromString("1E2x")); - EXPECT_EQ(Decimal::nan(), fromString("1E+x")); + EXPECT_EQ(Decimal::nan(), fromString("1Ex")); + EXPECT_EQ(Decimal::nan(), fromString("1E2x")); + EXPECT_EQ(Decimal::nan(), fromString("1E+x")); } -TEST_F(DecimalTest, Multiplication) -{ - EXPECT_EQ(encode(0, 0, Positive), Decimal(0) * Decimal(0)); - EXPECT_EQ(encode(2, 0, Negative), Decimal(2) * Decimal(-1)); - EXPECT_EQ(encode(2, 0, Negative), Decimal(-1) * Decimal(2)); - EXPECT_EQ(encode(99, 0, Positive), Decimal(99) * Decimal(1)); - EXPECT_EQ(encode(2500, 0, Positive), Decimal(-50) * Decimal(-50)); - EXPECT_EQ(encode(1, 21, Positive), encode(UINT64_C(10000000000), 0, Positive) * encode(UINT64_C(100000000000), 0, Positive)); +TEST_F(DecimalTest, Multiplication) { + EXPECT_EQ(encode(0, 0, Positive), Decimal(0) * Decimal(0)); + EXPECT_EQ(encode(2, 0, Negative), Decimal(2) * Decimal(-1)); + EXPECT_EQ(encode(2, 0, Negative), Decimal(-1) * Decimal(2)); + EXPECT_EQ(encode(99, 0, Positive), Decimal(99) * Decimal(1)); + EXPECT_EQ(encode(2500, 0, Positive), Decimal(-50) * Decimal(-50)); + EXPECT_EQ(encode(1, 21, Positive), + encode(UINT64_C(10000000000), 0, Positive) * + encode(UINT64_C(100000000000), 0, Positive)); } -TEST_F(DecimalTest, MultiplicationBigExponent) -{ - EXPECT_EQ(encode(1, 1022, Positive), encode(1, 1022, Positive) * encode(1, 0, Positive)); - EXPECT_EQ(Decimal::infinity(Positive), encode(1, 1022, Positive) * encode(1, 1022, Positive)); - EXPECT_EQ(encode(1, 22, Positive), encode(1, 1022, Positive) * encode(1, -1000, Positive)); +TEST_F(DecimalTest, MultiplicationBigExponent) { + EXPECT_EQ(encode(1, 1022, Positive), + encode(1, 1022, Positive) * encode(1, 0, Positive)); + EXPECT_EQ(Decimal::infinity(Positive), + encode(1, 1022, Positive) * encode(1, 1022, Positive)); + EXPECT_EQ(encode(1, 22, Positive), + encode(1, 1022, Positive) * encode(1, -1000, Positive)); } -TEST_F(DecimalTest, MultiplicationSmallExponent) -{ - EXPECT_EQ(encode(1, -1022, Positive), encode(1, -1022, Positive) * encode(1, 0, Positive)); - EXPECT_EQ(encode(0, 0, Positive), encode(1, -1022, Positive) * encode(1, -1022, Positive)); +TEST_F(DecimalTest, MultiplicationSmallExponent) { + EXPECT_EQ(encode(1, -1022, Positive), + encode(1, -1022, Positive) * encode(1, 0, Positive)); + EXPECT_EQ(encode(0, 0, Positive), + encode(1, -1022, Positive) * encode(1, -1022, Positive)); } -TEST_F(DecimalTest, MultiplicationSpecialValues) -{ - const Decimal Infinity(Decimal::infinity(Positive)); - const Decimal MinusInfinity(Decimal::infinity(Negative)); - const Decimal NaN(Decimal::nan()); - const Decimal Ten(10); - const Decimal MinusTen(-10); - const Decimal Zero(Decimal::zero(Positive)); - const Decimal MinusZero(Decimal::zero(Negative)); - - EXPECT_EQ(Infinity, Infinity * Infinity); - EXPECT_EQ(MinusInfinity, Infinity * MinusInfinity); - EXPECT_EQ(MinusInfinity, MinusInfinity * Infinity); - EXPECT_EQ(Infinity, MinusInfinity * MinusInfinity); - - EXPECT_EQ(NaN, Infinity * Zero); - EXPECT_EQ(NaN, Zero * MinusInfinity); - EXPECT_EQ(NaN, MinusInfinity * Zero); - EXPECT_EQ(NaN, MinusInfinity * Zero); - - EXPECT_EQ(NaN, Infinity * MinusZero); - EXPECT_EQ(NaN, MinusZero * MinusInfinity); - EXPECT_EQ(NaN, MinusInfinity * MinusZero); - EXPECT_EQ(NaN, MinusInfinity * MinusZero); - - EXPECT_EQ(Infinity, Infinity * Ten); - EXPECT_EQ(Infinity, Ten * Infinity); - EXPECT_EQ(MinusInfinity, MinusInfinity * Ten); - EXPECT_EQ(MinusInfinity, Ten * MinusInfinity); - - EXPECT_EQ(MinusInfinity, Infinity * MinusTen); - EXPECT_EQ(MinusInfinity, MinusTen * Infinity); - EXPECT_EQ(Infinity, MinusInfinity * MinusTen); - EXPECT_EQ(Infinity, MinusTen * MinusInfinity); - - EXPECT_EQ(NaN, NaN * NaN); - EXPECT_EQ(NaN, NaN * Ten); - EXPECT_EQ(NaN, Ten * NaN); - - EXPECT_EQ(NaN, NaN * Infinity); - EXPECT_EQ(NaN, NaN * MinusInfinity); - EXPECT_EQ(NaN, Infinity * NaN); - EXPECT_EQ(NaN, MinusInfinity * NaN); +TEST_F(DecimalTest, MultiplicationSpecialValues) { + const Decimal Infinity(Decimal::infinity(Positive)); + const Decimal MinusInfinity(Decimal::infinity(Negative)); + const Decimal NaN(Decimal::nan()); + const Decimal Ten(10); + const Decimal MinusTen(-10); + const Decimal Zero(Decimal::zero(Positive)); + const Decimal MinusZero(Decimal::zero(Negative)); + + EXPECT_EQ(Infinity, Infinity * Infinity); + EXPECT_EQ(MinusInfinity, Infinity * MinusInfinity); + EXPECT_EQ(MinusInfinity, MinusInfinity * Infinity); + EXPECT_EQ(Infinity, MinusInfinity * MinusInfinity); + + EXPECT_EQ(NaN, Infinity * Zero); + EXPECT_EQ(NaN, Zero * MinusInfinity); + EXPECT_EQ(NaN, MinusInfinity * Zero); + EXPECT_EQ(NaN, MinusInfinity * Zero); + + EXPECT_EQ(NaN, Infinity * MinusZero); + EXPECT_EQ(NaN, MinusZero * MinusInfinity); + EXPECT_EQ(NaN, MinusInfinity * MinusZero); + EXPECT_EQ(NaN, MinusInfinity * MinusZero); + + EXPECT_EQ(Infinity, Infinity * Ten); + EXPECT_EQ(Infinity, Ten * Infinity); + EXPECT_EQ(MinusInfinity, MinusInfinity * Ten); + EXPECT_EQ(MinusInfinity, Ten * MinusInfinity); + + EXPECT_EQ(MinusInfinity, Infinity * MinusTen); + EXPECT_EQ(MinusInfinity, MinusTen * Infinity); + EXPECT_EQ(Infinity, MinusInfinity * MinusTen); + EXPECT_EQ(Infinity, MinusTen * MinusInfinity); + + EXPECT_EQ(NaN, NaN * NaN); + EXPECT_EQ(NaN, NaN * Ten); + EXPECT_EQ(NaN, Ten * NaN); + + EXPECT_EQ(NaN, NaN * Infinity); + EXPECT_EQ(NaN, NaN * MinusInfinity); + EXPECT_EQ(NaN, Infinity * NaN); + EXPECT_EQ(NaN, MinusInfinity * NaN); } -TEST_F(DecimalTest, Negate) -{ - EXPECT_EQ(encode(0, 0, Negative), -encode(0, 0, Positive)); - EXPECT_EQ(encode(0, 0, Positive), -encode(0, 0, Negative)); +TEST_F(DecimalTest, Negate) { + EXPECT_EQ(encode(0, 0, Negative), -encode(0, 0, Positive)); + EXPECT_EQ(encode(0, 0, Positive), -encode(0, 0, Negative)); - EXPECT_EQ(encode(0, 10, Negative), -encode(0, 10, Positive)); - EXPECT_EQ(encode(0, 10, Positive), -encode(0, 10, Negative)); + EXPECT_EQ(encode(0, 10, Negative), -encode(0, 10, Positive)); + EXPECT_EQ(encode(0, 10, Positive), -encode(0, 10, Negative)); - EXPECT_EQ(encode(0, -10, Negative), -encode(0, -10, Positive)); - EXPECT_EQ(encode(0, -10, Positive), -encode(0, -10, Negative)); + EXPECT_EQ(encode(0, -10, Negative), -encode(0, -10, Positive)); + EXPECT_EQ(encode(0, -10, Positive), -encode(0, -10, Negative)); - EXPECT_EQ(encode(1, 0, Negative), -encode(1, 0, Positive)); - EXPECT_EQ(encode(1, 0, Positive), -encode(1, 0, Negative)); + EXPECT_EQ(encode(1, 0, Negative), -encode(1, 0, Positive)); + EXPECT_EQ(encode(1, 0, Positive), -encode(1, 0, Negative)); - EXPECT_EQ(encode(1, 10, Negative), -encode(1, 10, Positive)); - EXPECT_EQ(encode(1, 10, Positive), -encode(1, 10, Negative)); + EXPECT_EQ(encode(1, 10, Negative), -encode(1, 10, Positive)); + EXPECT_EQ(encode(1, 10, Positive), -encode(1, 10, Negative)); - EXPECT_EQ(encode(1, -10, Negative), -encode(1, -10, Positive)); - EXPECT_EQ(encode(1, -10, Positive), -encode(1, -10, Negative)); + EXPECT_EQ(encode(1, -10, Negative), -encode(1, -10, Positive)); + EXPECT_EQ(encode(1, -10, Positive), -encode(1, -10, Negative)); } -TEST_F(DecimalTest, NegateBigExponent) -{ - EXPECT_EQ(encode(1, 1000, Negative), -encode(1, 1000, Positive)); - EXPECT_EQ(encode(1, 1000, Positive), -encode(1, 1000, Negative)); +TEST_F(DecimalTest, NegateBigExponent) { + EXPECT_EQ(encode(1, 1000, Negative), -encode(1, 1000, Positive)); + EXPECT_EQ(encode(1, 1000, Positive), -encode(1, 1000, Negative)); } -TEST_F(DecimalTest, NegateSmallExponent) -{ - EXPECT_EQ(encode(1, -1000, Negative), -encode(1, -1000, Positive)); - EXPECT_EQ(encode(1, -1000, Positive), -encode(1, -1000, Negative)); +TEST_F(DecimalTest, NegateSmallExponent) { + EXPECT_EQ(encode(1, -1000, Negative), -encode(1, -1000, Positive)); + EXPECT_EQ(encode(1, -1000, Positive), -encode(1, -1000, Negative)); } -TEST_F(DecimalTest, NegateSpecialValues) -{ - EXPECT_EQ(Decimal::infinity(Negative), -Decimal::infinity(Positive)); - EXPECT_EQ(Decimal::infinity(Positive), -Decimal::infinity(Negative)); - EXPECT_EQ(Decimal::nan(), -Decimal::nan()); +TEST_F(DecimalTest, NegateSpecialValues) { + EXPECT_EQ(Decimal::infinity(Negative), -Decimal::infinity(Positive)); + EXPECT_EQ(Decimal::infinity(Positive), -Decimal::infinity(Negative)); + EXPECT_EQ(Decimal::nan(), -Decimal::nan()); } -TEST_F(DecimalTest, Predicates) -{ - EXPECT_TRUE(Decimal::zero(Positive).isFinite()); - EXPECT_FALSE(Decimal::zero(Positive).isInfinity()); - EXPECT_FALSE(Decimal::zero(Positive).isNaN()); - EXPECT_TRUE(Decimal::zero(Positive).isPositive()); - EXPECT_FALSE(Decimal::zero(Positive).isNegative()); - EXPECT_FALSE(Decimal::zero(Positive).isSpecial()); - EXPECT_TRUE(Decimal::zero(Positive).isZero()); - - EXPECT_TRUE(Decimal::zero(Negative).isFinite()); - EXPECT_FALSE(Decimal::zero(Negative).isInfinity()); - EXPECT_FALSE(Decimal::zero(Negative).isNaN()); - EXPECT_FALSE(Decimal::zero(Negative).isPositive()); - EXPECT_TRUE(Decimal::zero(Negative).isNegative()); - EXPECT_FALSE(Decimal::zero(Negative).isSpecial()); - EXPECT_TRUE(Decimal::zero(Negative).isZero()); - - EXPECT_TRUE(Decimal(123).isFinite()); - EXPECT_FALSE(Decimal(123).isInfinity()); - EXPECT_FALSE(Decimal(123).isNaN()); - EXPECT_TRUE(Decimal(123).isPositive()); - EXPECT_FALSE(Decimal(123).isNegative()); - EXPECT_FALSE(Decimal(123).isSpecial()); - EXPECT_FALSE(Decimal(123).isZero()); - - EXPECT_TRUE(Decimal(-123).isFinite()); - EXPECT_FALSE(Decimal(-123).isInfinity()); - EXPECT_FALSE(Decimal(-123).isNaN()); - EXPECT_FALSE(Decimal(-123).isPositive()); - EXPECT_TRUE(Decimal(-123).isNegative()); - EXPECT_FALSE(Decimal(-123).isSpecial()); - EXPECT_FALSE(Decimal(-123).isZero()); +TEST_F(DecimalTest, Predicates) { + EXPECT_TRUE(Decimal::zero(Positive).isFinite()); + EXPECT_FALSE(Decimal::zero(Positive).isInfinity()); + EXPECT_FALSE(Decimal::zero(Positive).isNaN()); + EXPECT_TRUE(Decimal::zero(Positive).isPositive()); + EXPECT_FALSE(Decimal::zero(Positive).isNegative()); + EXPECT_FALSE(Decimal::zero(Positive).isSpecial()); + EXPECT_TRUE(Decimal::zero(Positive).isZero()); + + EXPECT_TRUE(Decimal::zero(Negative).isFinite()); + EXPECT_FALSE(Decimal::zero(Negative).isInfinity()); + EXPECT_FALSE(Decimal::zero(Negative).isNaN()); + EXPECT_FALSE(Decimal::zero(Negative).isPositive()); + EXPECT_TRUE(Decimal::zero(Negative).isNegative()); + EXPECT_FALSE(Decimal::zero(Negative).isSpecial()); + EXPECT_TRUE(Decimal::zero(Negative).isZero()); + + EXPECT_TRUE(Decimal(123).isFinite()); + EXPECT_FALSE(Decimal(123).isInfinity()); + EXPECT_FALSE(Decimal(123).isNaN()); + EXPECT_TRUE(Decimal(123).isPositive()); + EXPECT_FALSE(Decimal(123).isNegative()); + EXPECT_FALSE(Decimal(123).isSpecial()); + EXPECT_FALSE(Decimal(123).isZero()); + + EXPECT_TRUE(Decimal(-123).isFinite()); + EXPECT_FALSE(Decimal(-123).isInfinity()); + EXPECT_FALSE(Decimal(-123).isNaN()); + EXPECT_FALSE(Decimal(-123).isPositive()); + EXPECT_TRUE(Decimal(-123).isNegative()); + EXPECT_FALSE(Decimal(-123).isSpecial()); + EXPECT_FALSE(Decimal(-123).isZero()); } -TEST_F(DecimalTest, PredicatesSpecialValues) -{ - EXPECT_FALSE(Decimal::infinity(Positive).isFinite()); - EXPECT_TRUE(Decimal::infinity(Positive).isInfinity()); - EXPECT_FALSE(Decimal::infinity(Positive).isNaN()); - EXPECT_TRUE(Decimal::infinity(Positive).isPositive()); - EXPECT_FALSE(Decimal::infinity(Positive).isNegative()); - EXPECT_TRUE(Decimal::infinity(Positive).isSpecial()); - EXPECT_FALSE(Decimal::infinity(Positive).isZero()); - - EXPECT_FALSE(Decimal::infinity(Negative).isFinite()); - EXPECT_TRUE(Decimal::infinity(Negative).isInfinity()); - EXPECT_FALSE(Decimal::infinity(Negative).isNaN()); - EXPECT_FALSE(Decimal::infinity(Negative).isPositive()); - EXPECT_TRUE(Decimal::infinity(Negative).isNegative()); - EXPECT_TRUE(Decimal::infinity(Negative).isSpecial()); - EXPECT_FALSE(Decimal::infinity(Negative).isZero()); - - EXPECT_FALSE(Decimal::nan().isFinite()); - EXPECT_FALSE(Decimal::nan().isInfinity()); - EXPECT_TRUE(Decimal::nan().isNaN()); - EXPECT_TRUE(Decimal::nan().isSpecial()); - EXPECT_FALSE(Decimal::nan().isZero()); +TEST_F(DecimalTest, PredicatesSpecialValues) { + EXPECT_FALSE(Decimal::infinity(Positive).isFinite()); + EXPECT_TRUE(Decimal::infinity(Positive).isInfinity()); + EXPECT_FALSE(Decimal::infinity(Positive).isNaN()); + EXPECT_TRUE(Decimal::infinity(Positive).isPositive()); + EXPECT_FALSE(Decimal::infinity(Positive).isNegative()); + EXPECT_TRUE(Decimal::infinity(Positive).isSpecial()); + EXPECT_FALSE(Decimal::infinity(Positive).isZero()); + + EXPECT_FALSE(Decimal::infinity(Negative).isFinite()); + EXPECT_TRUE(Decimal::infinity(Negative).isInfinity()); + EXPECT_FALSE(Decimal::infinity(Negative).isNaN()); + EXPECT_FALSE(Decimal::infinity(Negative).isPositive()); + EXPECT_TRUE(Decimal::infinity(Negative).isNegative()); + EXPECT_TRUE(Decimal::infinity(Negative).isSpecial()); + EXPECT_FALSE(Decimal::infinity(Negative).isZero()); + + EXPECT_FALSE(Decimal::nan().isFinite()); + EXPECT_FALSE(Decimal::nan().isInfinity()); + EXPECT_TRUE(Decimal::nan().isNaN()); + EXPECT_TRUE(Decimal::nan().isSpecial()); + EXPECT_FALSE(Decimal::nan().isZero()); } // tests/fast/forms/number/number-stepup-stepdown-from-renderer -TEST_F(DecimalTest, RealWorldExampleNumberStepUpStepDownFromRenderer) -{ - EXPECT_DECIMAL_STREQ("10", stepDown("0", "100", "10", "19", 1)); - EXPECT_DECIMAL_STREQ("90", stepUp("0", "99", "10", "89", 1)); - EXPECT_DECIMAL_STREQ("1", stepUp("0", "1", "0.33333333333333333", "0", 3)); // step=1/3 - EXPECT_DECIMAL_STREQ("0.01", stepUp("0", "0.01", "0.0033333333333333333", "0", 3)); // step=1/300 - EXPECT_DECIMAL_STREQ("1", stepUp("0", "1", "0.003921568627450980", "0", 255)); // step=1/255 - EXPECT_DECIMAL_STREQ("1", stepUp("0", "1", "0.1", "0", 10)); +TEST_F(DecimalTest, RealWorldExampleNumberStepUpStepDownFromRenderer) { + EXPECT_DECIMAL_STREQ("10", stepDown("0", "100", "10", "19", 1)); + EXPECT_DECIMAL_STREQ("90", stepUp("0", "99", "10", "89", 1)); + EXPECT_DECIMAL_STREQ( + "1", stepUp("0", "1", "0.33333333333333333", "0", 3)); // step=1/3 + EXPECT_DECIMAL_STREQ("0.01", stepUp("0", "0.01", "0.0033333333333333333", "0", + 3)); // step=1/300 + EXPECT_DECIMAL_STREQ( + "1", stepUp("0", "1", "0.003921568627450980", "0", 255)); // step=1/255 + EXPECT_DECIMAL_STREQ("1", stepUp("0", "1", "0.1", "0", 10)); } -TEST_F(DecimalTest, RealWorldExampleNumberStepUpStepDownFromRendererRounding) -{ - EXPECT_DECIMAL_STREQ("5.015", stepUp("0", "100", "0.005", "5.005", 2)); - EXPECT_DECIMAL_STREQ("5.06", stepUp("0", "100", "0.005", "5.005", 11)); - EXPECT_DECIMAL_STREQ("5.065", stepUp("0", "100", "0.005", "5.005", 12)); +TEST_F(DecimalTest, RealWorldExampleNumberStepUpStepDownFromRendererRounding) { + EXPECT_DECIMAL_STREQ("5.015", stepUp("0", "100", "0.005", "5.005", 2)); + EXPECT_DECIMAL_STREQ("5.06", stepUp("0", "100", "0.005", "5.005", 11)); + EXPECT_DECIMAL_STREQ("5.065", stepUp("0", "100", "0.005", "5.005", 12)); - EXPECT_DECIMAL_STREQ("5.015", stepUp("4", "9", "0.005", "5.005", 2)); - EXPECT_DECIMAL_STREQ("5.06", stepUp("4", "9", "0.005", "5.005", 11)); - EXPECT_DECIMAL_STREQ("5.065", stepUp("4", "9", "0.005", "5.005", 12)); + EXPECT_DECIMAL_STREQ("5.015", stepUp("4", "9", "0.005", "5.005", 2)); + EXPECT_DECIMAL_STREQ("5.06", stepUp("4", "9", "0.005", "5.005", 11)); + EXPECT_DECIMAL_STREQ("5.065", stepUp("4", "9", "0.005", "5.005", 12)); } -TEST_F(DecimalTest, RealWorldExampleRangeStepUpStepDown) -{ - EXPECT_DECIMAL_STREQ("1e+38", stepUp("0", "1E38", "1", "1E38", 9)); - EXPECT_DECIMAL_STREQ("1e+38", stepDown("0", "1E38", "1", "1E38", 9)); +TEST_F(DecimalTest, RealWorldExampleRangeStepUpStepDown) { + EXPECT_DECIMAL_STREQ("1e+38", stepUp("0", "1E38", "1", "1E38", 9)); + EXPECT_DECIMAL_STREQ("1e+38", stepDown("0", "1E38", "1", "1E38", 9)); } -TEST_F(DecimalTest, Remainder) -{ - EXPECT_EQ(encode(21, -1, Positive), encode(21, -1, Positive).remainder(3)); - EXPECT_EQ(Decimal(1), Decimal(10).remainder(3)); - EXPECT_EQ(Decimal(1), Decimal(10).remainder(-3)); - EXPECT_EQ(encode(1, 0, Negative), Decimal(-10).remainder(3)); - EXPECT_EQ(Decimal(-1), Decimal(-10).remainder(-3)); - EXPECT_EQ(encode(2, -1, Positive), encode(102, -1, Positive).remainder(1)); - EXPECT_EQ(encode(1, -1, Positive), Decimal(10).remainder(encode(3, -1, Positive))); - EXPECT_EQ(Decimal(1), encode(36, -1, Positive).remainder(encode(13, -1, Positive))); - EXPECT_EQ(encode(1, 86, Positive), (encode(1234, 100, Positive).remainder(Decimal(3)))); - EXPECT_EQ(Decimal(500), (Decimal(500).remainder(1000))); - EXPECT_EQ(Decimal(-500), (Decimal(-500).remainder(1000))); +TEST_F(DecimalTest, Remainder) { + EXPECT_EQ(encode(21, -1, Positive), encode(21, -1, Positive).remainder(3)); + EXPECT_EQ(Decimal(1), Decimal(10).remainder(3)); + EXPECT_EQ(Decimal(1), Decimal(10).remainder(-3)); + EXPECT_EQ(encode(1, 0, Negative), Decimal(-10).remainder(3)); + EXPECT_EQ(Decimal(-1), Decimal(-10).remainder(-3)); + EXPECT_EQ(encode(2, -1, Positive), encode(102, -1, Positive).remainder(1)); + EXPECT_EQ(encode(1, -1, Positive), + Decimal(10).remainder(encode(3, -1, Positive))); + EXPECT_EQ(Decimal(1), + encode(36, -1, Positive).remainder(encode(13, -1, Positive))); + EXPECT_EQ(encode(1, 86, Positive), + (encode(1234, 100, Positive).remainder(Decimal(3)))); + EXPECT_EQ(Decimal(500), (Decimal(500).remainder(1000))); + EXPECT_EQ(Decimal(-500), (Decimal(-500).remainder(1000))); } -TEST_F(DecimalTest, RemainderBigExponent) -{ - EXPECT_EQ(encode(0, 1022, Positive), encode(1, 1022, Positive).remainder(encode(1, 0, Positive))); - EXPECT_EQ(encode(0, 1022, Positive), encode(1, 1022, Positive).remainder(encode(1, 1022, Positive))); - EXPECT_EQ(Decimal::infinity(Positive), encode(1, 1022, Positive).remainder(encode(1, -1000, Positive))); +TEST_F(DecimalTest, RemainderBigExponent) { + EXPECT_EQ(encode(0, 1022, Positive), + encode(1, 1022, Positive).remainder(encode(1, 0, Positive))); + EXPECT_EQ(encode(0, 1022, Positive), + encode(1, 1022, Positive).remainder(encode(1, 1022, Positive))); + EXPECT_EQ(Decimal::infinity(Positive), + encode(1, 1022, Positive).remainder(encode(1, -1000, Positive))); } -TEST_F(DecimalTest, RemainderSmallExponent) -{ - EXPECT_EQ(encode(1, -1022, Positive), encode(1, -1022, Positive).remainder(encode(1, 0, Positive))); - EXPECT_EQ(encode(0, -1022, Positive), encode(1, -1022, Positive).remainder(encode(1, -1022, Positive))); +TEST_F(DecimalTest, RemainderSmallExponent) { + EXPECT_EQ(encode(1, -1022, Positive), + encode(1, -1022, Positive).remainder(encode(1, 0, Positive))); + EXPECT_EQ(encode(0, -1022, Positive), + encode(1, -1022, Positive).remainder(encode(1, -1022, Positive))); } -TEST_F(DecimalTest, RemainderSpecialValues) -{ - EXPECT_EQ(Decimal::infinity(Positive), Decimal::infinity(Positive).remainder(1)); - EXPECT_EQ(Decimal::infinity(Negative), Decimal::infinity(Negative).remainder(1)); - EXPECT_EQ(Decimal::nan(), Decimal::nan().remainder(1)); - - EXPECT_EQ(Decimal::infinity(Negative), Decimal::infinity(Positive).remainder(-1)); - EXPECT_EQ(Decimal::infinity(Positive), Decimal::infinity(Negative).remainder(-1)); - EXPECT_EQ(Decimal::nan(), Decimal::nan().remainder(-1)); - - EXPECT_EQ(Decimal::infinity(Positive), Decimal::infinity(Positive).remainder(3)); - EXPECT_EQ(Decimal::infinity(Negative), Decimal::infinity(Negative).remainder(3)); - EXPECT_EQ(Decimal::nan(), Decimal::nan().remainder(3)); - - EXPECT_EQ(Decimal::infinity(Negative), Decimal::infinity(Positive).remainder(-1)); - EXPECT_EQ(Decimal::infinity(Positive), Decimal::infinity(Negative).remainder(-1)); - EXPECT_EQ(Decimal::nan(), Decimal::nan().remainder(-1)); - - EXPECT_EQ(Decimal::nan(), Decimal(1).remainder(Decimal::infinity(Positive))); - EXPECT_EQ(Decimal::nan(), Decimal(1).remainder(Decimal::infinity(Negative))); - EXPECT_EQ(Decimal::nan(), Decimal(1).remainder(Decimal::nan())); +TEST_F(DecimalTest, RemainderSpecialValues) { + EXPECT_EQ(Decimal::infinity(Positive), + Decimal::infinity(Positive).remainder(1)); + EXPECT_EQ(Decimal::infinity(Negative), + Decimal::infinity(Negative).remainder(1)); + EXPECT_EQ(Decimal::nan(), Decimal::nan().remainder(1)); + + EXPECT_EQ(Decimal::infinity(Negative), + Decimal::infinity(Positive).remainder(-1)); + EXPECT_EQ(Decimal::infinity(Positive), + Decimal::infinity(Negative).remainder(-1)); + EXPECT_EQ(Decimal::nan(), Decimal::nan().remainder(-1)); + + EXPECT_EQ(Decimal::infinity(Positive), + Decimal::infinity(Positive).remainder(3)); + EXPECT_EQ(Decimal::infinity(Negative), + Decimal::infinity(Negative).remainder(3)); + EXPECT_EQ(Decimal::nan(), Decimal::nan().remainder(3)); + + EXPECT_EQ(Decimal::infinity(Negative), + Decimal::infinity(Positive).remainder(-1)); + EXPECT_EQ(Decimal::infinity(Positive), + Decimal::infinity(Negative).remainder(-1)); + EXPECT_EQ(Decimal::nan(), Decimal::nan().remainder(-1)); + + EXPECT_EQ(Decimal::nan(), Decimal(1).remainder(Decimal::infinity(Positive))); + EXPECT_EQ(Decimal::nan(), Decimal(1).remainder(Decimal::infinity(Negative))); + EXPECT_EQ(Decimal::nan(), Decimal(1).remainder(Decimal::nan())); } -TEST_F(DecimalTest, Round) -{ - EXPECT_EQ(Decimal(1), (Decimal(9) / Decimal(10)).round()); - EXPECT_EQ(Decimal(25), (Decimal(5) / fromString("0.200")).round()); - EXPECT_EQ(Decimal(3), (Decimal(5) / Decimal(2)).round()); - EXPECT_EQ(Decimal(1), (Decimal(2) / Decimal(3)).round()); - EXPECT_EQ(Decimal(3), (Decimal(10) / Decimal(3)).round()); - EXPECT_EQ(Decimal(3), (Decimal(1) / fromString("0.3")).round()); - EXPECT_EQ(Decimal(10), (Decimal(1) / fromString("0.1")).round()); - EXPECT_EQ(Decimal(5), (Decimal(1) / fromString("0.2")).round()); - EXPECT_EQ(Decimal(10), (fromString("10.2") / 1).round()); - EXPECT_EQ(encode(1234, 100, Positive), encode(1234, 100, Positive).round()); - - EXPECT_EQ(Decimal(2), encode(190002, -5, Positive).round()); - EXPECT_EQ(Decimal(2), encode(150002, -5, Positive).round()); - EXPECT_EQ(Decimal(2), encode(150000, -5, Positive).round()); - EXPECT_EQ(Decimal(12), encode(12492, -3, Positive).round()); - EXPECT_EQ(Decimal(13), encode(12502, -3, Positive).round()); - - EXPECT_EQ(Decimal(-2), encode(190002, -5, Negative).round()); - EXPECT_EQ(Decimal(-2), encode(150002, -5, Negative).round()); - EXPECT_EQ(Decimal(-2), encode(150000, -5, Negative).round()); - EXPECT_EQ(Decimal(-12), encode(12492, -3, Negative).round()); - EXPECT_EQ(Decimal(-13), encode(12502, -3, Negative).round()); +TEST_F(DecimalTest, Round) { + EXPECT_EQ(Decimal(1), (Decimal(9) / Decimal(10)).round()); + EXPECT_EQ(Decimal(25), (Decimal(5) / fromString("0.200")).round()); + EXPECT_EQ(Decimal(3), (Decimal(5) / Decimal(2)).round()); + EXPECT_EQ(Decimal(1), (Decimal(2) / Decimal(3)).round()); + EXPECT_EQ(Decimal(3), (Decimal(10) / Decimal(3)).round()); + EXPECT_EQ(Decimal(3), (Decimal(1) / fromString("0.3")).round()); + EXPECT_EQ(Decimal(10), (Decimal(1) / fromString("0.1")).round()); + EXPECT_EQ(Decimal(5), (Decimal(1) / fromString("0.2")).round()); + EXPECT_EQ(Decimal(10), (fromString("10.2") / 1).round()); + EXPECT_EQ(encode(1234, 100, Positive), encode(1234, 100, Positive).round()); + + EXPECT_EQ(Decimal(2), encode(190002, -5, Positive).round()); + EXPECT_EQ(Decimal(2), encode(150002, -5, Positive).round()); + EXPECT_EQ(Decimal(2), encode(150000, -5, Positive).round()); + EXPECT_EQ(Decimal(12), encode(12492, -3, Positive).round()); + EXPECT_EQ(Decimal(13), encode(12502, -3, Positive).round()); + + EXPECT_EQ(Decimal(-2), encode(190002, -5, Negative).round()); + EXPECT_EQ(Decimal(-2), encode(150002, -5, Negative).round()); + EXPECT_EQ(Decimal(-2), encode(150000, -5, Negative).round()); + EXPECT_EQ(Decimal(-12), encode(12492, -3, Negative).round()); + EXPECT_EQ(Decimal(-13), encode(12502, -3, Negative).round()); } -TEST_F(DecimalTest, RoundSpecialValues) -{ - EXPECT_EQ(Decimal::infinity(Positive), Decimal::infinity(Positive).round()); - EXPECT_EQ(Decimal::infinity(Negative), Decimal::infinity(Negative).round()); - EXPECT_EQ(Decimal::nan(), Decimal::nan().round()); +TEST_F(DecimalTest, RoundSpecialValues) { + EXPECT_EQ(Decimal::infinity(Positive), Decimal::infinity(Positive).round()); + EXPECT_EQ(Decimal::infinity(Negative), Decimal::infinity(Negative).round()); + EXPECT_EQ(Decimal::nan(), Decimal::nan().round()); } -TEST_F(DecimalTest, Subtract) -{ - EXPECT_EQ(encode(0, 0, Positive), Decimal(0) - Decimal(0)); - EXPECT_EQ(encode(3, 0, Positive), Decimal(2) - Decimal(-1)); - EXPECT_EQ(encode(3, 0, Negative), Decimal(-1) - Decimal(2)); - EXPECT_EQ(encode(98, 0, Positive), Decimal(99) - Decimal(1)); - EXPECT_EQ(encode(0, 0, Positive), Decimal(-50) - Decimal(-50)); - EXPECT_EQ(encode(UINT64_C(1000000000000000), 35, Positive), encode(1, 50, Positive) - Decimal(1)); - EXPECT_EQ(encode(UINT64_C(1000000000000000), 35, Negative), Decimal(1) - encode(1, 50, Positive)); +TEST_F(DecimalTest, Subtract) { + EXPECT_EQ(encode(0, 0, Positive), Decimal(0) - Decimal(0)); + EXPECT_EQ(encode(3, 0, Positive), Decimal(2) - Decimal(-1)); + EXPECT_EQ(encode(3, 0, Negative), Decimal(-1) - Decimal(2)); + EXPECT_EQ(encode(98, 0, Positive), Decimal(99) - Decimal(1)); + EXPECT_EQ(encode(0, 0, Positive), Decimal(-50) - Decimal(-50)); + EXPECT_EQ(encode(UINT64_C(1000000000000000), 35, Positive), + encode(1, 50, Positive) - Decimal(1)); + EXPECT_EQ(encode(UINT64_C(1000000000000000), 35, Negative), + Decimal(1) - encode(1, 50, Positive)); } -TEST_F(DecimalTest, SubtractBigExponent) -{ - EXPECT_EQ(encode(1, 1022, Positive), encode(1, 1022, Positive) - encode(1, 0, Positive)); - EXPECT_EQ(encode(0, 0, Positive), encode(1, 1022, Positive) - encode(1, 1022, Positive)); - EXPECT_EQ(encode(1, 1022, Positive), encode(1, 1022, Positive) + encode(1, -1000, Positive)); +TEST_F(DecimalTest, SubtractBigExponent) { + EXPECT_EQ(encode(1, 1022, Positive), + encode(1, 1022, Positive) - encode(1, 0, Positive)); + EXPECT_EQ(encode(0, 0, Positive), + encode(1, 1022, Positive) - encode(1, 1022, Positive)); + EXPECT_EQ(encode(1, 1022, Positive), + encode(1, 1022, Positive) + encode(1, -1000, Positive)); } -TEST_F(DecimalTest, SubtractSmallExponent) -{ - EXPECT_EQ(encode(UINT64_C(10000000000000000), -16, Negative), encode(1, -1022, Positive) - encode(1, 0, Positive)); - EXPECT_EQ(encode(0, 0, Positive), encode(1, -1022, Positive) - encode(1, -1022, Positive)); +TEST_F(DecimalTest, SubtractSmallExponent) { + EXPECT_EQ(encode(UINT64_C(10000000000000000), -16, Negative), + encode(1, -1022, Positive) - encode(1, 0, Positive)); + EXPECT_EQ(encode(0, 0, Positive), + encode(1, -1022, Positive) - encode(1, -1022, Positive)); } -TEST_F(DecimalTest, SubtractSpecialValues) -{ - const Decimal Infinity(Decimal::infinity(Positive)); - const Decimal MinusInfinity(Decimal::infinity(Negative)); - const Decimal NaN(Decimal::nan()); - const Decimal Ten(10); - - EXPECT_EQ(NaN, Infinity - Infinity); - EXPECT_EQ(Infinity, Infinity - MinusInfinity); - EXPECT_EQ(MinusInfinity, MinusInfinity - Infinity); - EXPECT_EQ(NaN, MinusInfinity - MinusInfinity); - - EXPECT_EQ(Infinity, Infinity - Ten); - EXPECT_EQ(MinusInfinity, Ten - Infinity); - EXPECT_EQ(MinusInfinity, MinusInfinity - Ten); - EXPECT_EQ(Infinity, Ten - MinusInfinity); - - EXPECT_EQ(NaN, NaN - NaN); - EXPECT_EQ(NaN, NaN - Ten); - EXPECT_EQ(NaN, Ten - NaN); - - EXPECT_EQ(NaN, NaN - Infinity); - EXPECT_EQ(NaN, NaN - MinusInfinity); - EXPECT_EQ(NaN, Infinity - NaN); - EXPECT_EQ(NaN, MinusInfinity - NaN); +TEST_F(DecimalTest, SubtractSpecialValues) { + const Decimal Infinity(Decimal::infinity(Positive)); + const Decimal MinusInfinity(Decimal::infinity(Negative)); + const Decimal NaN(Decimal::nan()); + const Decimal Ten(10); + + EXPECT_EQ(NaN, Infinity - Infinity); + EXPECT_EQ(Infinity, Infinity - MinusInfinity); + EXPECT_EQ(MinusInfinity, MinusInfinity - Infinity); + EXPECT_EQ(NaN, MinusInfinity - MinusInfinity); + + EXPECT_EQ(Infinity, Infinity - Ten); + EXPECT_EQ(MinusInfinity, Ten - Infinity); + EXPECT_EQ(MinusInfinity, MinusInfinity - Ten); + EXPECT_EQ(Infinity, Ten - MinusInfinity); + + EXPECT_EQ(NaN, NaN - NaN); + EXPECT_EQ(NaN, NaN - Ten); + EXPECT_EQ(NaN, Ten - NaN); + + EXPECT_EQ(NaN, NaN - Infinity); + EXPECT_EQ(NaN, NaN - MinusInfinity); + EXPECT_EQ(NaN, Infinity - NaN); + EXPECT_EQ(NaN, MinusInfinity - NaN); } -TEST_F(DecimalTest, ToDouble) -{ - EXPECT_EQ(0.0, encode(0, 0, Positive).toDouble()); - EXPECT_EQ(-0.0, encode(0, 0, Negative).toDouble()); - - EXPECT_EQ(1.0, encode(1, 0, Positive).toDouble()); - EXPECT_EQ(-1.0, encode(1, 0, Negative).toDouble()); - - EXPECT_EQ(0.1, encode(1, -1, Positive).toDouble()); - EXPECT_EQ(-0.1, encode(1, -1, Negative).toDouble()); - EXPECT_EQ(0.3, encode(3, -1, Positive).toDouble()); - EXPECT_EQ(-0.3, encode(3, -1, Negative).toDouble()); - EXPECT_EQ(0.6, encode(6, -1, Positive).toDouble()); - EXPECT_EQ(-0.6, encode(6, -1, Negative).toDouble()); - EXPECT_EQ(0.7, encode(7, -1, Positive).toDouble()); - EXPECT_EQ(-0.7, encode(7, -1, Negative).toDouble()); - - EXPECT_EQ(0.01, encode(1, -2, Positive).toDouble()); - EXPECT_EQ(0.001, encode(1, -3, Positive).toDouble()); - EXPECT_EQ(0.0001, encode(1, -4, Positive).toDouble()); - EXPECT_EQ(0.00001, encode(1, -5, Positive).toDouble()); - - EXPECT_EQ(1e+308, encode(1, 308, Positive).toDouble()); - EXPECT_EQ(1e-307, encode(1, -307, Positive).toDouble()); - - EXPECT_TRUE(std::isinf(encode(1, 1000, Positive).toDouble())); - EXPECT_EQ(0.0, encode(1, -1000, Positive).toDouble()); +TEST_F(DecimalTest, ToDouble) { + EXPECT_EQ(0.0, encode(0, 0, Positive).toDouble()); + EXPECT_EQ(-0.0, encode(0, 0, Negative).toDouble()); + + EXPECT_EQ(1.0, encode(1, 0, Positive).toDouble()); + EXPECT_EQ(-1.0, encode(1, 0, Negative).toDouble()); + + EXPECT_EQ(0.1, encode(1, -1, Positive).toDouble()); + EXPECT_EQ(-0.1, encode(1, -1, Negative).toDouble()); + EXPECT_EQ(0.3, encode(3, -1, Positive).toDouble()); + EXPECT_EQ(-0.3, encode(3, -1, Negative).toDouble()); + EXPECT_EQ(0.6, encode(6, -1, Positive).toDouble()); + EXPECT_EQ(-0.6, encode(6, -1, Negative).toDouble()); + EXPECT_EQ(0.7, encode(7, -1, Positive).toDouble()); + EXPECT_EQ(-0.7, encode(7, -1, Negative).toDouble()); + + EXPECT_EQ(0.01, encode(1, -2, Positive).toDouble()); + EXPECT_EQ(0.001, encode(1, -3, Positive).toDouble()); + EXPECT_EQ(0.0001, encode(1, -4, Positive).toDouble()); + EXPECT_EQ(0.00001, encode(1, -5, Positive).toDouble()); + + EXPECT_EQ(1e+308, encode(1, 308, Positive).toDouble()); + EXPECT_EQ(1e-307, encode(1, -307, Positive).toDouble()); + + EXPECT_TRUE(std::isinf(encode(1, 1000, Positive).toDouble())); + EXPECT_EQ(0.0, encode(1, -1000, Positive).toDouble()); } -TEST_F(DecimalTest, ToDoubleSpecialValues) -{ - EXPECT_TRUE(std::isinf(Decimal::infinity(Decimal::Positive).toDouble())); - EXPECT_TRUE(std::isinf(Decimal::infinity(Decimal::Negative).toDouble())); - EXPECT_TRUE(std::isnan(Decimal::nan().toDouble())); +TEST_F(DecimalTest, ToDoubleSpecialValues) { + EXPECT_TRUE(std::isinf(Decimal::infinity(Decimal::Positive).toDouble())); + EXPECT_TRUE(std::isinf(Decimal::infinity(Decimal::Negative).toDouble())); + EXPECT_TRUE(std::isnan(Decimal::nan().toDouble())); } -TEST_F(DecimalTest, ToString) -{ - EXPECT_DECIMAL_STREQ("0", Decimal::zero(Positive)); - EXPECT_DECIMAL_STREQ("-0", Decimal::zero(Negative)); - EXPECT_DECIMAL_STREQ("1", Decimal(1)); - EXPECT_DECIMAL_STREQ("-1", Decimal(-1)); - EXPECT_DECIMAL_STREQ("1234567", Decimal(1234567)); - EXPECT_DECIMAL_STREQ("-1234567", Decimal(-1234567)); - EXPECT_DECIMAL_STREQ("0.5", encode(5, -1, Positive)); - EXPECT_DECIMAL_STREQ("-0.5", encode(5, -1, Negative)); - EXPECT_DECIMAL_STREQ("12.345", encode(12345, -3, Positive)); - EXPECT_DECIMAL_STREQ("-12.345", encode(12345, -3, Negative)); - EXPECT_DECIMAL_STREQ("0.12345", encode(12345, -5, Positive)); - EXPECT_DECIMAL_STREQ("-0.12345", encode(12345, -5, Negative)); - EXPECT_DECIMAL_STREQ("50", encode(50, 0, Positive)); - EXPECT_DECIMAL_STREQ("-50", encode(50, 0, Negative)); - EXPECT_DECIMAL_STREQ("5e+1", encode(5, 1, Positive)); - EXPECT_DECIMAL_STREQ("-5e+1", encode(5, 1, Negative)); - EXPECT_DECIMAL_STREQ("5.678e+103", encode(5678, 100, Positive)); - EXPECT_DECIMAL_STREQ("-5.678e+103", encode(5678, 100, Negative)); - EXPECT_DECIMAL_STREQ("5.678e-97", encode(5678, -100, Positive)); - EXPECT_DECIMAL_STREQ("-5.678e-97", encode(5678, -100, Negative)); - EXPECT_DECIMAL_STREQ("8639999913600001", encode(UINT64_C(8639999913600001), 0, Positive)); - EXPECT_DECIMAL_STREQ("9007199254740991", encode((static_cast(1) << DBL_MANT_DIG) - 1, 0, Positive)); - EXPECT_DECIMAL_STREQ("99999999999999999", encode(UINT64_C(99999999999999999), 0, Positive)); - EXPECT_DECIMAL_STREQ("9.9999999999999999e+17", encode(UINT64_C(99999999999999999), 1, Positive)); - EXPECT_DECIMAL_STREQ("9.9999999999999999e+18", encode(UINT64_C(99999999999999999), 2, Positive)); - EXPECT_DECIMAL_STREQ("1e+16", encode(UINT64_C(99999999999999999), -1, Positive)); - EXPECT_DECIMAL_STREQ("1000000000000000", encode(UINT64_C(99999999999999999), -2, Positive)); - EXPECT_DECIMAL_STREQ("1", encode(UINT64_C(99999999999999999), -17, Positive)); - EXPECT_DECIMAL_STREQ("0.001", encode(UINT64_C(99999999999999999), -20, Positive)); - EXPECT_DECIMAL_STREQ("1e-83", encode(UINT64_C(99999999999999999), -100, Positive)); +TEST_F(DecimalTest, ToString) { + EXPECT_DECIMAL_STREQ("0", Decimal::zero(Positive)); + EXPECT_DECIMAL_STREQ("-0", Decimal::zero(Negative)); + EXPECT_DECIMAL_STREQ("1", Decimal(1)); + EXPECT_DECIMAL_STREQ("-1", Decimal(-1)); + EXPECT_DECIMAL_STREQ("1234567", Decimal(1234567)); + EXPECT_DECIMAL_STREQ("-1234567", Decimal(-1234567)); + EXPECT_DECIMAL_STREQ("0.5", encode(5, -1, Positive)); + EXPECT_DECIMAL_STREQ("-0.5", encode(5, -1, Negative)); + EXPECT_DECIMAL_STREQ("12.345", encode(12345, -3, Positive)); + EXPECT_DECIMAL_STREQ("-12.345", encode(12345, -3, Negative)); + EXPECT_DECIMAL_STREQ("0.12345", encode(12345, -5, Positive)); + EXPECT_DECIMAL_STREQ("-0.12345", encode(12345, -5, Negative)); + EXPECT_DECIMAL_STREQ("50", encode(50, 0, Positive)); + EXPECT_DECIMAL_STREQ("-50", encode(50, 0, Negative)); + EXPECT_DECIMAL_STREQ("5e+1", encode(5, 1, Positive)); + EXPECT_DECIMAL_STREQ("-5e+1", encode(5, 1, Negative)); + EXPECT_DECIMAL_STREQ("5.678e+103", encode(5678, 100, Positive)); + EXPECT_DECIMAL_STREQ("-5.678e+103", encode(5678, 100, Negative)); + EXPECT_DECIMAL_STREQ("5.678e-97", encode(5678, -100, Positive)); + EXPECT_DECIMAL_STREQ("-5.678e-97", encode(5678, -100, Negative)); + EXPECT_DECIMAL_STREQ("8639999913600001", + encode(UINT64_C(8639999913600001), 0, Positive)); + EXPECT_DECIMAL_STREQ( + "9007199254740991", + encode((static_cast(1) << DBL_MANT_DIG) - 1, 0, Positive)); + EXPECT_DECIMAL_STREQ("99999999999999999", + encode(UINT64_C(99999999999999999), 0, Positive)); + EXPECT_DECIMAL_STREQ("9.9999999999999999e+17", + encode(UINT64_C(99999999999999999), 1, Positive)); + EXPECT_DECIMAL_STREQ("9.9999999999999999e+18", + encode(UINT64_C(99999999999999999), 2, Positive)); + EXPECT_DECIMAL_STREQ("1e+16", + encode(UINT64_C(99999999999999999), -1, Positive)); + EXPECT_DECIMAL_STREQ("1000000000000000", + encode(UINT64_C(99999999999999999), -2, Positive)); + EXPECT_DECIMAL_STREQ("1", encode(UINT64_C(99999999999999999), -17, Positive)); + EXPECT_DECIMAL_STREQ("0.001", + encode(UINT64_C(99999999999999999), -20, Positive)); + EXPECT_DECIMAL_STREQ("1e-83", + encode(UINT64_C(99999999999999999), -100, Positive)); } -TEST_F(DecimalTest, ToStringSpecialValues) -{ - EXPECT_DECIMAL_STREQ("Infinity", Decimal::infinity(Positive)); - EXPECT_DECIMAL_STREQ("-Infinity", Decimal::infinity(Negative)); - EXPECT_DECIMAL_STREQ("NaN", Decimal::nan()); +TEST_F(DecimalTest, ToStringSpecialValues) { + EXPECT_DECIMAL_STREQ("Infinity", Decimal::infinity(Positive)); + EXPECT_DECIMAL_STREQ("-Infinity", Decimal::infinity(Negative)); + EXPECT_DECIMAL_STREQ("NaN", Decimal::nan()); } diff --git a/sky/engine/platform/FloatConversion.h b/sky/engine/platform/FloatConversion.h index 1cc9b08f32415..04c6353e359d2 100644 --- a/sky/engine/platform/FloatConversion.h +++ b/sky/engine/platform/FloatConversion.h @@ -31,15 +31,14 @@ namespace blink { -template +template float narrowPrecisionToFloat(T); -template<> -inline float narrowPrecisionToFloat(double number) -{ - return static_cast(number); +template <> +inline float narrowPrecisionToFloat(double number) { + return static_cast(number); } -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_FLOATCONVERSION_H_ diff --git a/sky/engine/platform/Language.cpp b/sky/engine/platform/Language.cpp index cc891c768546e..be0c51fc3058b 100644 --- a/sky/engine/platform/Language.cpp +++ b/sky/engine/platform/Language.cpp @@ -30,102 +30,103 @@ namespace blink { -static const AtomicString& platformLanguage() -{ - DEFINE_STATIC_LOCAL(AtomicString, computedDefaultLanguage, ()); - if (computedDefaultLanguage.isEmpty()) { - std::string defaultLocale = blink::Platform::current()->defaultLocale(); - computedDefaultLanguage = AtomicString::fromUTF8(defaultLocale.data(), defaultLocale.size()); - ASSERT(!computedDefaultLanguage.isEmpty()); - } - return computedDefaultLanguage; +static const AtomicString& platformLanguage() { + DEFINE_STATIC_LOCAL(AtomicString, computedDefaultLanguage, ()); + if (computedDefaultLanguage.isEmpty()) { + std::string defaultLocale = blink::Platform::current()->defaultLocale(); + computedDefaultLanguage = + AtomicString::fromUTF8(defaultLocale.data(), defaultLocale.size()); + ASSERT(!computedDefaultLanguage.isEmpty()); + } + return computedDefaultLanguage; } -AtomicString defaultLanguage() -{ - Vector languages = userPreferredLanguages(); - if (!languages.isEmpty()) - return languages[0]; +AtomicString defaultLanguage() { + Vector languages = userPreferredLanguages(); + if (!languages.isEmpty()) + return languages[0]; - return emptyAtom; + return emptyAtom; } -static Vector& preferredLanguagesOverride() -{ - DEFINE_STATIC_LOCAL(Vector, override, ()); - return override; +static Vector& preferredLanguagesOverride() { + DEFINE_STATIC_LOCAL(Vector, override, ()); + return override; } -Vector userPreferredLanguagesOverride() -{ - return preferredLanguagesOverride(); +Vector userPreferredLanguagesOverride() { + return preferredLanguagesOverride(); } -void overrideUserPreferredLanguages(const Vector& override) -{ - preferredLanguagesOverride() = override; +void overrideUserPreferredLanguages(const Vector& override) { + preferredLanguagesOverride() = override; } -Vector userPreferredLanguages() -{ - Vector& override = preferredLanguagesOverride(); - if (!override.isEmpty()) - return override; +Vector userPreferredLanguages() { + Vector& override = preferredLanguagesOverride(); + if (!override.isEmpty()) + return override; - Vector languages; - languages.reserveInitialCapacity(1); - languages.append(platformLanguage()); - return languages; + Vector languages; + languages.reserveInitialCapacity(1); + languages.append(platformLanguage()); + return languages; } -static String canonicalLanguageIdentifier(const String& languageCode) -{ - String lowercaseLanguageCode = languageCode.lower(); +static String canonicalLanguageIdentifier(const String& languageCode) { + String lowercaseLanguageCode = languageCode.lower(); - if (lowercaseLanguageCode.length() >= 3 && lowercaseLanguageCode[2] == '_') - lowercaseLanguageCode.replace(2, 1, "-"); + if (lowercaseLanguageCode.length() >= 3 && lowercaseLanguageCode[2] == '_') + lowercaseLanguageCode.replace(2, 1, "-"); - return lowercaseLanguageCode; + return lowercaseLanguageCode; } -size_t indexOfBestMatchingLanguageInList(const AtomicString& language, const Vector& languageList) -{ - AtomicString languageWithoutLocaleMatch; - AtomicString languageMatchButNotLocale; - size_t languageWithoutLocaleMatchIndex = 0; - size_t languageMatchButNotLocaleMatchIndex = 0; - bool canMatchLanguageOnly = (language.length() == 2 || (language.length() >= 3 && language[2] == '-')); - - for (size_t i = 0; i < languageList.size(); ++i) { - String canonicalizedLanguageFromList = canonicalLanguageIdentifier(languageList[i]); - - if (language == canonicalizedLanguageFromList) - return i; - - if (canMatchLanguageOnly && canonicalizedLanguageFromList.length() >= 2) { - if (language[0] == canonicalizedLanguageFromList[0] && language[1] == canonicalizedLanguageFromList[1]) { - if (!languageWithoutLocaleMatch.length() && canonicalizedLanguageFromList.length() == 2) { - languageWithoutLocaleMatch = languageList[i]; - languageWithoutLocaleMatchIndex = i; - } - if (!languageMatchButNotLocale.length() && canonicalizedLanguageFromList.length() >= 3) { - languageMatchButNotLocale = languageList[i]; - languageMatchButNotLocaleMatchIndex = i; - } - } +size_t indexOfBestMatchingLanguageInList( + const AtomicString& language, + const Vector& languageList) { + AtomicString languageWithoutLocaleMatch; + AtomicString languageMatchButNotLocale; + size_t languageWithoutLocaleMatchIndex = 0; + size_t languageMatchButNotLocaleMatchIndex = 0; + bool canMatchLanguageOnly = (language.length() == 2 || + (language.length() >= 3 && language[2] == '-')); + + for (size_t i = 0; i < languageList.size(); ++i) { + String canonicalizedLanguageFromList = + canonicalLanguageIdentifier(languageList[i]); + + if (language == canonicalizedLanguageFromList) + return i; + + if (canMatchLanguageOnly && canonicalizedLanguageFromList.length() >= 2) { + if (language[0] == canonicalizedLanguageFromList[0] && + language[1] == canonicalizedLanguageFromList[1]) { + if (!languageWithoutLocaleMatch.length() && + canonicalizedLanguageFromList.length() == 2) { + languageWithoutLocaleMatch = languageList[i]; + languageWithoutLocaleMatchIndex = i; } + if (!languageMatchButNotLocale.length() && + canonicalizedLanguageFromList.length() >= 3) { + languageMatchButNotLocale = languageList[i]; + languageMatchButNotLocaleMatchIndex = i; + } + } } + } - // If we have both a language-only match and a languge-but-not-locale match, return the - // languge-only match as is considered a "better" match. For example, if the list - // provided has both "en-GB" and "en" and the user prefers "en-US" we will return "en". - if (languageWithoutLocaleMatch.length()) - return languageWithoutLocaleMatchIndex; + // If we have both a language-only match and a languge-but-not-locale match, + // return the languge-only match as is considered a "better" match. For + // example, if the list provided has both "en-GB" and "en" and the user + // prefers "en-US" we will return "en". + if (languageWithoutLocaleMatch.length()) + return languageWithoutLocaleMatchIndex; - if (languageMatchButNotLocale.length()) - return languageMatchButNotLocaleMatchIndex; + if (languageMatchButNotLocale.length()) + return languageMatchButNotLocaleMatchIndex; - return languageList.size(); + return languageList.size(); } -} +} // namespace blink diff --git a/sky/engine/platform/Language.h b/sky/engine/platform/Language.h index 19b2db6df00db..21928927272e1 100644 --- a/sky/engine/platform/Language.h +++ b/sky/engine/platform/Language.h @@ -35,9 +35,12 @@ namespace blink { PLATFORM_EXPORT AtomicString defaultLanguage(); PLATFORM_EXPORT Vector userPreferredLanguages(); PLATFORM_EXPORT Vector userPreferredLanguagesOverride(); -PLATFORM_EXPORT void overrideUserPreferredLanguages(const Vector&); -PLATFORM_EXPORT size_t indexOfBestMatchingLanguageInList(const AtomicString& language, const Vector& languageList); +PLATFORM_EXPORT void overrideUserPreferredLanguages( + const Vector&); +PLATFORM_EXPORT size_t +indexOfBestMatchingLanguageInList(const AtomicString& language, + const Vector& languageList); -} +} // namespace blink #endif // SKY_ENGINE_PLATFORM_LANGUAGE_H_ diff --git a/sky/engine/platform/LayoutUnit.h b/sky/engine/platform/LayoutUnit.h index 81c652f073a24..44a5208d50a15 100644 --- a/sky/engine/platform/LayoutUnit.h +++ b/sky/engine/platform/LayoutUnit.h @@ -46,11 +46,13 @@ namespace blink { #else -#define REPORT_OVERFLOW(doesOverflow) do \ - if (!(doesOverflow)) { \ - WTFReportError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, "!(%s)", #doesOverflow); \ - } \ -while (0) +#define REPORT_OVERFLOW(doesOverflow) \ + do \ + if (!(doesOverflow)) { \ + WTFReportError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, "!(%s)", \ + #doesOverflow); \ + } \ + while (0) #endif @@ -61,733 +63,645 @@ const int intMaxForLayoutUnit = INT_MAX / kFixedPointDenominator; const int intMinForLayoutUnit = INT_MIN / kFixedPointDenominator; class LayoutUnit { -public: - LayoutUnit() : m_value(0) { } - LayoutUnit(int value) { setValue(value); } - LayoutUnit(unsigned short value) { setValue(value); } - LayoutUnit(unsigned value) { setValue(value); } - LayoutUnit(unsigned long value) { m_value = clampTo(value * kFixedPointDenominator); } - LayoutUnit(unsigned long long value) { m_value = clampTo(value * kFixedPointDenominator); } - LayoutUnit(float value) { m_value = clampTo(value * kFixedPointDenominator); } - LayoutUnit(double value) { m_value = clampTo(value * kFixedPointDenominator); } - - static LayoutUnit fromFloatCeil(float value) - { - LayoutUnit v; - v.m_value = clampToInteger(ceilf(value * kFixedPointDenominator)); - return v; - } - - static LayoutUnit fromFloatFloor(float value) - { - LayoutUnit v; - v.m_value = clampToInteger(floorf(value * kFixedPointDenominator)); - return v; - } - - static LayoutUnit fromFloatRound(float value) - { - if (value >= 0) - return clamp(value + epsilon() / 2.0f); - return clamp(value - epsilon() / 2.0f); - } - - int toInt() const { return m_value / kFixedPointDenominator; } - float toFloat() const { return static_cast(m_value) / kFixedPointDenominator; } - double toDouble() const { return static_cast(m_value) / kFixedPointDenominator; } - float ceilToFloat() const - { - float floatValue = toFloat(); - if (static_cast(floatValue * kFixedPointDenominator) == m_value) - return floatValue; - if (floatValue > 0) - return nextafterf(floatValue, std::numeric_limits::max()); - return nextafterf(floatValue, std::numeric_limits::min()); - } - unsigned toUnsigned() const { REPORT_OVERFLOW(m_value >= 0); return toInt(); } - - operator int() const { return toInt(); } - operator unsigned() const { return toUnsigned(); } - operator double() const { return toDouble(); } - operator bool() const { return m_value; } - - LayoutUnit operator++(int) - { - m_value += kFixedPointDenominator; - return *this; - } - - inline int rawValue() const { return m_value; } - inline void setRawValue(int value) { m_value = value; } - void setRawValue(long long value) - { - REPORT_OVERFLOW(value > std::numeric_limits::min() && value < std::numeric_limits::max()); - m_value = static_cast(value); - } - - LayoutUnit abs() const - { - LayoutUnit returnValue; - returnValue.setRawValue(::abs(m_value)); - return returnValue; - } - int ceil() const - { - if (UNLIKELY(m_value >= INT_MAX - kFixedPointDenominator + 1)) - return intMaxForLayoutUnit; - - if (m_value >= 0) - return (m_value + kFixedPointDenominator - 1) / kFixedPointDenominator; - return toInt(); - } - ALWAYS_INLINE int round() const - { - return saturatedAddition(rawValue(), kFixedPointDenominator / 2) >> kLayoutUnitFractionalBits; - } - - int floor() const - { - if (UNLIKELY(m_value <= INT_MIN + kFixedPointDenominator - 1)) - return intMinForLayoutUnit; - - return m_value >> kLayoutUnitFractionalBits; - } - - LayoutUnit fraction() const - { - // Add the fraction to the size (as opposed to the full location) to avoid overflows. - // Compute fraction using the mod operator to preserve the sign of the value as it may affect rounding. - LayoutUnit fraction; - fraction.setRawValue(rawValue() % kFixedPointDenominator); - return fraction; - } - - bool mightBeSaturated() const - { - return rawValue() == std::numeric_limits::max() - || rawValue() == std::numeric_limits::min(); - } - - static float epsilon() { return 1.0f / kFixedPointDenominator; } - - static const LayoutUnit max() - { - LayoutUnit m; - m.m_value = std::numeric_limits::max(); - return m; - } - static const LayoutUnit min() - { - LayoutUnit m; - m.m_value = std::numeric_limits::min(); - return m; - } - - // Versions of max/min that are slightly smaller/larger than max/min() to allow for roinding without overflowing. - static const LayoutUnit nearlyMax() - { - LayoutUnit m; - m.m_value = std::numeric_limits::max() - kFixedPointDenominator / 2; - return m; - } - static const LayoutUnit nearlyMin() - { - LayoutUnit m; - m.m_value = std::numeric_limits::min() + kFixedPointDenominator / 2; - return m; - } - - static LayoutUnit clamp(double value) - { - return clampTo(value, LayoutUnit::min(), LayoutUnit::max()); - } - -private: - static bool isInBounds(int value) - { - return ::abs(value) <= std::numeric_limits::max() / kFixedPointDenominator; - } - static bool isInBounds(unsigned value) - { - return value <= static_cast(std::numeric_limits::max()) / kFixedPointDenominator; - } - static bool isInBounds(double value) - { - return ::fabs(value) <= std::numeric_limits::max() / kFixedPointDenominator; - } - - ALWAYS_INLINE void setValue(int value) - { - m_value = saturatedSet(value, kLayoutUnitFractionalBits); - } - - inline void setValue(unsigned value) - { - m_value = saturatedSet(value, kLayoutUnitFractionalBits); - } - - int m_value; + public: + LayoutUnit() : m_value(0) {} + LayoutUnit(int value) { setValue(value); } + LayoutUnit(unsigned short value) { setValue(value); } + LayoutUnit(unsigned value) { setValue(value); } + LayoutUnit(unsigned long value) { + m_value = clampTo(value * kFixedPointDenominator); + } + LayoutUnit(unsigned long long value) { + m_value = clampTo(value * kFixedPointDenominator); + } + LayoutUnit(float value) { + m_value = clampTo(value * kFixedPointDenominator); + } + LayoutUnit(double value) { + m_value = clampTo(value * kFixedPointDenominator); + } + + static LayoutUnit fromFloatCeil(float value) { + LayoutUnit v; + v.m_value = clampToInteger(ceilf(value * kFixedPointDenominator)); + return v; + } + + static LayoutUnit fromFloatFloor(float value) { + LayoutUnit v; + v.m_value = clampToInteger(floorf(value * kFixedPointDenominator)); + return v; + } + + static LayoutUnit fromFloatRound(float value) { + if (value >= 0) + return clamp(value + epsilon() / 2.0f); + return clamp(value - epsilon() / 2.0f); + } + + int toInt() const { return m_value / kFixedPointDenominator; } + float toFloat() const { + return static_cast(m_value) / kFixedPointDenominator; + } + double toDouble() const { + return static_cast(m_value) / kFixedPointDenominator; + } + float ceilToFloat() const { + float floatValue = toFloat(); + if (static_cast(floatValue * kFixedPointDenominator) == m_value) + return floatValue; + if (floatValue > 0) + return nextafterf(floatValue, std::numeric_limits::max()); + return nextafterf(floatValue, std::numeric_limits::min()); + } + unsigned toUnsigned() const { + REPORT_OVERFLOW(m_value >= 0); + return toInt(); + } + + operator int() const { return toInt(); } + operator unsigned() const { return toUnsigned(); } + operator double() const { return toDouble(); } + operator bool() const { return m_value; } + + LayoutUnit operator++(int) { + m_value += kFixedPointDenominator; + return *this; + } + + inline int rawValue() const { return m_value; } + inline void setRawValue(int value) { m_value = value; } + void setRawValue(long long value) { + REPORT_OVERFLOW(value > std::numeric_limits::min() && + value < std::numeric_limits::max()); + m_value = static_cast(value); + } + + LayoutUnit abs() const { + LayoutUnit returnValue; + returnValue.setRawValue(::abs(m_value)); + return returnValue; + } + int ceil() const { + if (UNLIKELY(m_value >= INT_MAX - kFixedPointDenominator + 1)) + return intMaxForLayoutUnit; + + if (m_value >= 0) + return (m_value + kFixedPointDenominator - 1) / kFixedPointDenominator; + return toInt(); + } + ALWAYS_INLINE int round() const { + return saturatedAddition(rawValue(), kFixedPointDenominator / 2) >> + kLayoutUnitFractionalBits; + } + + int floor() const { + if (UNLIKELY(m_value <= INT_MIN + kFixedPointDenominator - 1)) + return intMinForLayoutUnit; + + return m_value >> kLayoutUnitFractionalBits; + } + + LayoutUnit fraction() const { + // Add the fraction to the size (as opposed to the full location) to avoid + // overflows. Compute fraction using the mod operator to preserve the sign + // of the value as it may affect rounding. + LayoutUnit fraction; + fraction.setRawValue(rawValue() % kFixedPointDenominator); + return fraction; + } + + bool mightBeSaturated() const { + return rawValue() == std::numeric_limits::max() || + rawValue() == std::numeric_limits::min(); + } + + static float epsilon() { return 1.0f / kFixedPointDenominator; } + + static const LayoutUnit max() { + LayoutUnit m; + m.m_value = std::numeric_limits::max(); + return m; + } + static const LayoutUnit min() { + LayoutUnit m; + m.m_value = std::numeric_limits::min(); + return m; + } + + // Versions of max/min that are slightly smaller/larger than max/min() to + // allow for roinding without overflowing. + static const LayoutUnit nearlyMax() { + LayoutUnit m; + m.m_value = std::numeric_limits::max() - kFixedPointDenominator / 2; + return m; + } + static const LayoutUnit nearlyMin() { + LayoutUnit m; + m.m_value = std::numeric_limits::min() + kFixedPointDenominator / 2; + return m; + } + + static LayoutUnit clamp(double value) { + return clampTo(value, LayoutUnit::min(), LayoutUnit::max()); + } + + private: + static bool isInBounds(int value) { + return ::abs(value) <= + std::numeric_limits::max() / kFixedPointDenominator; + } + static bool isInBounds(unsigned value) { + return value <= static_cast(std::numeric_limits::max()) / + kFixedPointDenominator; + } + static bool isInBounds(double value) { + return ::fabs(value) <= + std::numeric_limits::max() / kFixedPointDenominator; + } + + ALWAYS_INLINE void setValue(int value) { + m_value = saturatedSet(value, kLayoutUnitFractionalBits); + } + + inline void setValue(unsigned value) { + m_value = saturatedSet(value, kLayoutUnitFractionalBits); + } + + int m_value; }; -inline bool operator<=(const LayoutUnit& a, const LayoutUnit& b) -{ - return a.rawValue() <= b.rawValue(); +inline bool operator<=(const LayoutUnit& a, const LayoutUnit& b) { + return a.rawValue() <= b.rawValue(); } -inline bool operator<=(const LayoutUnit& a, float b) -{ - return a.toFloat() <= b; +inline bool operator<=(const LayoutUnit& a, float b) { + return a.toFloat() <= b; } -inline bool operator<=(const LayoutUnit& a, int b) -{ - return a <= LayoutUnit(b); +inline bool operator<=(const LayoutUnit& a, int b) { + return a <= LayoutUnit(b); } -inline bool operator<=(const float a, const LayoutUnit& b) -{ - return a <= b.toFloat(); +inline bool operator<=(const float a, const LayoutUnit& b) { + return a <= b.toFloat(); } -inline bool operator<=(const int a, const LayoutUnit& b) -{ - return LayoutUnit(a) <= b; +inline bool operator<=(const int a, const LayoutUnit& b) { + return LayoutUnit(a) <= b; } -inline bool operator>=(const LayoutUnit& a, const LayoutUnit& b) -{ - return a.rawValue() >= b.rawValue(); +inline bool operator>=(const LayoutUnit& a, const LayoutUnit& b) { + return a.rawValue() >= b.rawValue(); } -inline bool operator>=(const LayoutUnit& a, int b) -{ - return a >= LayoutUnit(b); +inline bool operator>=(const LayoutUnit& a, int b) { + return a >= LayoutUnit(b); } -inline bool operator>=(const float a, const LayoutUnit& b) -{ - return a >= b.toFloat(); +inline bool operator>=(const float a, const LayoutUnit& b) { + return a >= b.toFloat(); } -inline bool operator>=(const LayoutUnit& a, float b) -{ - return a.toFloat() >= b; +inline bool operator>=(const LayoutUnit& a, float b) { + return a.toFloat() >= b; } -inline bool operator>=(const int a, const LayoutUnit& b) -{ - return LayoutUnit(a) >= b; +inline bool operator>=(const int a, const LayoutUnit& b) { + return LayoutUnit(a) >= b; } -inline bool operator<(const LayoutUnit& a, const LayoutUnit& b) -{ - return a.rawValue() < b.rawValue(); +inline bool operator<(const LayoutUnit& a, const LayoutUnit& b) { + return a.rawValue() < b.rawValue(); } -inline bool operator<(const LayoutUnit& a, int b) -{ - return a < LayoutUnit(b); +inline bool operator<(const LayoutUnit& a, int b) { + return a < LayoutUnit(b); } -inline bool operator<(const LayoutUnit& a, float b) -{ - return a.toFloat() < b; +inline bool operator<(const LayoutUnit& a, float b) { + return a.toFloat() < b; } -inline bool operator<(const LayoutUnit& a, double b) -{ - return a.toDouble() < b; +inline bool operator<(const LayoutUnit& a, double b) { + return a.toDouble() < b; } -inline bool operator<(const int a, const LayoutUnit& b) -{ - return LayoutUnit(a) < b; +inline bool operator<(const int a, const LayoutUnit& b) { + return LayoutUnit(a) < b; } -inline bool operator<(const float a, const LayoutUnit& b) -{ - return a < b.toFloat(); +inline bool operator<(const float a, const LayoutUnit& b) { + return a < b.toFloat(); } -inline bool operator>(const LayoutUnit& a, const LayoutUnit& b) -{ - return a.rawValue() > b.rawValue(); +inline bool operator>(const LayoutUnit& a, const LayoutUnit& b) { + return a.rawValue() > b.rawValue(); } -inline bool operator>(const LayoutUnit& a, double b) -{ - return a.toDouble() > b; +inline bool operator>(const LayoutUnit& a, double b) { + return a.toDouble() > b; } -inline bool operator>(const LayoutUnit& a, float b) -{ - return a.toFloat() > b; +inline bool operator>(const LayoutUnit& a, float b) { + return a.toFloat() > b; } -inline bool operator>(const LayoutUnit& a, int b) -{ - return a > LayoutUnit(b); +inline bool operator>(const LayoutUnit& a, int b) { + return a > LayoutUnit(b); } -inline bool operator>(const int a, const LayoutUnit& b) -{ - return LayoutUnit(a) > b; +inline bool operator>(const int a, const LayoutUnit& b) { + return LayoutUnit(a) > b; } -inline bool operator>(const float a, const LayoutUnit& b) -{ - return a > b.toFloat(); +inline bool operator>(const float a, const LayoutUnit& b) { + return a > b.toFloat(); } -inline bool operator>(const double a, const LayoutUnit& b) -{ - return a > b.toDouble(); +inline bool operator>(const double a, const LayoutUnit& b) { + return a > b.toDouble(); } -inline bool operator!=(const LayoutUnit& a, const LayoutUnit& b) -{ - return a.rawValue() != b.rawValue(); +inline bool operator!=(const LayoutUnit& a, const LayoutUnit& b) { + return a.rawValue() != b.rawValue(); } -inline bool operator!=(const LayoutUnit& a, float b) -{ - return a != LayoutUnit(b); +inline bool operator!=(const LayoutUnit& a, float b) { + return a != LayoutUnit(b); } -inline bool operator!=(const int a, const LayoutUnit& b) -{ - return LayoutUnit(a) != b; +inline bool operator!=(const int a, const LayoutUnit& b) { + return LayoutUnit(a) != b; } -inline bool operator!=(const LayoutUnit& a, int b) -{ - return a != LayoutUnit(b); +inline bool operator!=(const LayoutUnit& a, int b) { + return a != LayoutUnit(b); } -inline bool operator==(const LayoutUnit& a, const LayoutUnit& b) -{ - return a.rawValue() == b.rawValue(); +inline bool operator==(const LayoutUnit& a, const LayoutUnit& b) { + return a.rawValue() == b.rawValue(); } -inline bool operator==(const LayoutUnit& a, int b) -{ - return a == LayoutUnit(b); +inline bool operator==(const LayoutUnit& a, int b) { + return a == LayoutUnit(b); } -inline bool operator==(const int a, const LayoutUnit& b) -{ - return LayoutUnit(a) == b; +inline bool operator==(const int a, const LayoutUnit& b) { + return LayoutUnit(a) == b; } -inline bool operator==(const LayoutUnit& a, float b) -{ - return a.toFloat() == b; +inline bool operator==(const LayoutUnit& a, float b) { + return a.toFloat() == b; } -inline bool operator==(const float a, const LayoutUnit& b) -{ - return a == b.toFloat(); +inline bool operator==(const float a, const LayoutUnit& b) { + return a == b.toFloat(); } -// For multiplication that's prone to overflow, this bounds it to LayoutUnit::max() and ::min() -inline LayoutUnit boundedMultiply(const LayoutUnit& a, const LayoutUnit& b) -{ - int64_t result = static_cast(a.rawValue()) * static_cast(b.rawValue()) / kFixedPointDenominator; - int32_t high = static_cast(result >> 32); - int32_t low = static_cast(result); - uint32_t saturated = (static_cast(a.rawValue() ^ b.rawValue()) >> 31) + std::numeric_limits::max(); - // If the higher 32 bits does not match the lower 32 with sign extension the operation overflowed. - if (high != low >> 31) - result = saturated; +// For multiplication that's prone to overflow, this bounds it to +// LayoutUnit::max() and ::min() +inline LayoutUnit boundedMultiply(const LayoutUnit& a, const LayoutUnit& b) { + int64_t result = static_cast(a.rawValue()) * + static_cast(b.rawValue()) / kFixedPointDenominator; + int32_t high = static_cast(result >> 32); + int32_t low = static_cast(result); + uint32_t saturated = + (static_cast(a.rawValue() ^ b.rawValue()) >> 31) + + std::numeric_limits::max(); + // If the higher 32 bits does not match the lower 32 with sign extension the + // operation overflowed. + if (high != low >> 31) + result = saturated; - LayoutUnit returnVal; - returnVal.setRawValue(static_cast(result)); - return returnVal; + LayoutUnit returnVal; + returnVal.setRawValue(static_cast(result)); + return returnVal; } -inline LayoutUnit operator*(const LayoutUnit& a, const LayoutUnit& b) -{ - return boundedMultiply(a, b); +inline LayoutUnit operator*(const LayoutUnit& a, const LayoutUnit& b) { + return boundedMultiply(a, b); } -inline double operator*(const LayoutUnit& a, double b) -{ - return a.toDouble() * b; +inline double operator*(const LayoutUnit& a, double b) { + return a.toDouble() * b; } -inline float operator*(const LayoutUnit& a, float b) -{ - return a.toFloat() * b; +inline float operator*(const LayoutUnit& a, float b) { + return a.toFloat() * b; } -inline LayoutUnit operator*(const LayoutUnit& a, int b) -{ - return a * LayoutUnit(b); +inline LayoutUnit operator*(const LayoutUnit& a, int b) { + return a * LayoutUnit(b); } -inline LayoutUnit operator*(const LayoutUnit& a, unsigned short b) -{ - return a * LayoutUnit(b); +inline LayoutUnit operator*(const LayoutUnit& a, unsigned short b) { + return a * LayoutUnit(b); } -inline LayoutUnit operator*(const LayoutUnit& a, unsigned b) -{ - return a * LayoutUnit(b); +inline LayoutUnit operator*(const LayoutUnit& a, unsigned b) { + return a * LayoutUnit(b); } -inline LayoutUnit operator*(const LayoutUnit& a, unsigned long b) -{ - return a * LayoutUnit(b); +inline LayoutUnit operator*(const LayoutUnit& a, unsigned long b) { + return a * LayoutUnit(b); } -inline LayoutUnit operator*(const LayoutUnit& a, unsigned long long b) -{ - return a * LayoutUnit(b); +inline LayoutUnit operator*(const LayoutUnit& a, unsigned long long b) { + return a * LayoutUnit(b); } -inline LayoutUnit operator*(unsigned short a, const LayoutUnit& b) -{ - return LayoutUnit(a) * b; +inline LayoutUnit operator*(unsigned short a, const LayoutUnit& b) { + return LayoutUnit(a) * b; } -inline LayoutUnit operator*(unsigned a, const LayoutUnit& b) -{ - return LayoutUnit(a) * b; +inline LayoutUnit operator*(unsigned a, const LayoutUnit& b) { + return LayoutUnit(a) * b; } -inline LayoutUnit operator*(unsigned long a, const LayoutUnit& b) -{ - return LayoutUnit(a) * b; +inline LayoutUnit operator*(unsigned long a, const LayoutUnit& b) { + return LayoutUnit(a) * b; } -inline LayoutUnit operator*(unsigned long long a, const LayoutUnit& b) -{ - return LayoutUnit(a) * b; +inline LayoutUnit operator*(unsigned long long a, const LayoutUnit& b) { + return LayoutUnit(a) * b; } -inline LayoutUnit operator*(const int a, const LayoutUnit& b) -{ - return LayoutUnit(a) * b; +inline LayoutUnit operator*(const int a, const LayoutUnit& b) { + return LayoutUnit(a) * b; } -inline float operator*(const float a, const LayoutUnit& b) -{ - return a * b.toFloat(); +inline float operator*(const float a, const LayoutUnit& b) { + return a * b.toFloat(); } -inline double operator*(const double a, const LayoutUnit& b) -{ - return a * b.toDouble(); +inline double operator*(const double a, const LayoutUnit& b) { + return a * b.toDouble(); } -inline LayoutUnit operator/(const LayoutUnit& a, const LayoutUnit& b) -{ - LayoutUnit returnVal; - long long rawVal = static_cast(kFixedPointDenominator) * a.rawValue() / b.rawValue(); - returnVal.setRawValue(clampTo(rawVal)); - return returnVal; +inline LayoutUnit operator/(const LayoutUnit& a, const LayoutUnit& b) { + LayoutUnit returnVal; + long long rawVal = static_cast(kFixedPointDenominator) * + a.rawValue() / b.rawValue(); + returnVal.setRawValue(clampTo(rawVal)); + return returnVal; } -inline float operator/(const LayoutUnit& a, float b) -{ - return a.toFloat() / b; +inline float operator/(const LayoutUnit& a, float b) { + return a.toFloat() / b; } -inline double operator/(const LayoutUnit& a, double b) -{ - return a.toDouble() / b; +inline double operator/(const LayoutUnit& a, double b) { + return a.toDouble() / b; } -inline LayoutUnit operator/(const LayoutUnit& a, int b) -{ - return a / LayoutUnit(b); +inline LayoutUnit operator/(const LayoutUnit& a, int b) { + return a / LayoutUnit(b); } -inline LayoutUnit operator/(const LayoutUnit& a, unsigned short b) -{ - return a / LayoutUnit(b); +inline LayoutUnit operator/(const LayoutUnit& a, unsigned short b) { + return a / LayoutUnit(b); } -inline LayoutUnit operator/(const LayoutUnit& a, unsigned b) -{ - return a / LayoutUnit(b); +inline LayoutUnit operator/(const LayoutUnit& a, unsigned b) { + return a / LayoutUnit(b); } -inline LayoutUnit operator/(const LayoutUnit& a, unsigned long b) -{ - return a / LayoutUnit(b); +inline LayoutUnit operator/(const LayoutUnit& a, unsigned long b) { + return a / LayoutUnit(b); } -inline LayoutUnit operator/(const LayoutUnit& a, unsigned long long b) -{ - return a / LayoutUnit(b); +inline LayoutUnit operator/(const LayoutUnit& a, unsigned long long b) { + return a / LayoutUnit(b); } -inline float operator/(const float a, const LayoutUnit& b) -{ - return a / b.toFloat(); +inline float operator/(const float a, const LayoutUnit& b) { + return a / b.toFloat(); } -inline double operator/(const double a, const LayoutUnit& b) -{ - return a / b.toDouble(); +inline double operator/(const double a, const LayoutUnit& b) { + return a / b.toDouble(); } -inline LayoutUnit operator/(const int a, const LayoutUnit& b) -{ - return LayoutUnit(a) / b; +inline LayoutUnit operator/(const int a, const LayoutUnit& b) { + return LayoutUnit(a) / b; } -inline LayoutUnit operator/(unsigned short a, const LayoutUnit& b) -{ - return LayoutUnit(a) / b; +inline LayoutUnit operator/(unsigned short a, const LayoutUnit& b) { + return LayoutUnit(a) / b; } -inline LayoutUnit operator/(unsigned a, const LayoutUnit& b) -{ - return LayoutUnit(a) / b; +inline LayoutUnit operator/(unsigned a, const LayoutUnit& b) { + return LayoutUnit(a) / b; } -inline LayoutUnit operator/(unsigned long a, const LayoutUnit& b) -{ - return LayoutUnit(a) / b; +inline LayoutUnit operator/(unsigned long a, const LayoutUnit& b) { + return LayoutUnit(a) / b; } -inline LayoutUnit operator/(unsigned long long a, const LayoutUnit& b) -{ - return LayoutUnit(a) / b; +inline LayoutUnit operator/(unsigned long long a, const LayoutUnit& b) { + return LayoutUnit(a) / b; } -ALWAYS_INLINE LayoutUnit operator+(const LayoutUnit& a, const LayoutUnit& b) -{ - LayoutUnit returnVal; - returnVal.setRawValue(saturatedAddition(a.rawValue(), b.rawValue())); - return returnVal; +ALWAYS_INLINE LayoutUnit operator+(const LayoutUnit& a, const LayoutUnit& b) { + LayoutUnit returnVal; + returnVal.setRawValue(saturatedAddition(a.rawValue(), b.rawValue())); + return returnVal; } -inline LayoutUnit operator+(const LayoutUnit& a, int b) -{ - return a + LayoutUnit(b); +inline LayoutUnit operator+(const LayoutUnit& a, int b) { + return a + LayoutUnit(b); } -inline float operator+(const LayoutUnit& a, float b) -{ - return a.toFloat() + b; +inline float operator+(const LayoutUnit& a, float b) { + return a.toFloat() + b; } -inline double operator+(const LayoutUnit& a, double b) -{ - return a.toDouble() + b; +inline double operator+(const LayoutUnit& a, double b) { + return a.toDouble() + b; } -inline LayoutUnit operator+(const int a, const LayoutUnit& b) -{ - return LayoutUnit(a) + b; +inline LayoutUnit operator+(const int a, const LayoutUnit& b) { + return LayoutUnit(a) + b; } -inline float operator+(const float a, const LayoutUnit& b) -{ - return a + b.toFloat(); +inline float operator+(const float a, const LayoutUnit& b) { + return a + b.toFloat(); } -inline double operator+(const double a, const LayoutUnit& b) -{ - return a + b.toDouble(); +inline double operator+(const double a, const LayoutUnit& b) { + return a + b.toDouble(); } -ALWAYS_INLINE LayoutUnit operator-(const LayoutUnit& a, const LayoutUnit& b) -{ - LayoutUnit returnVal; - returnVal.setRawValue(saturatedSubtraction(a.rawValue(), b.rawValue())); - return returnVal; +ALWAYS_INLINE LayoutUnit operator-(const LayoutUnit& a, const LayoutUnit& b) { + LayoutUnit returnVal; + returnVal.setRawValue(saturatedSubtraction(a.rawValue(), b.rawValue())); + return returnVal; } -inline LayoutUnit operator-(const LayoutUnit& a, int b) -{ - return a - LayoutUnit(b); +inline LayoutUnit operator-(const LayoutUnit& a, int b) { + return a - LayoutUnit(b); } -inline LayoutUnit operator-(const LayoutUnit& a, unsigned b) -{ - return a - LayoutUnit(b); +inline LayoutUnit operator-(const LayoutUnit& a, unsigned b) { + return a - LayoutUnit(b); } -inline float operator-(const LayoutUnit& a, float b) -{ - return a.toFloat() - b; +inline float operator-(const LayoutUnit& a, float b) { + return a.toFloat() - b; } -inline LayoutUnit operator-(const int a, const LayoutUnit& b) -{ - return LayoutUnit(a) - b; +inline LayoutUnit operator-(const int a, const LayoutUnit& b) { + return LayoutUnit(a) - b; } -inline float operator-(const float a, const LayoutUnit& b) -{ - return a - b.toFloat(); +inline float operator-(const float a, const LayoutUnit& b) { + return a - b.toFloat(); } -inline LayoutUnit operator-(const LayoutUnit& a) -{ - LayoutUnit returnVal; - returnVal.setRawValue(-a.rawValue()); - return returnVal; +inline LayoutUnit operator-(const LayoutUnit& a) { + LayoutUnit returnVal; + returnVal.setRawValue(-a.rawValue()); + return returnVal; } // For returning the remainder after a division with integer results. -inline LayoutUnit intMod(const LayoutUnit& a, const LayoutUnit& b) -{ - // This calculates the modulo so that: a = static_cast(a / b) * b + intMod(a, b). - LayoutUnit returnVal; - returnVal.setRawValue(a.rawValue() % b.rawValue()); - return returnVal; +inline LayoutUnit intMod(const LayoutUnit& a, const LayoutUnit& b) { + // This calculates the modulo so that: a = static_cast(a / b) * b + + // intMod(a, b). + LayoutUnit returnVal; + returnVal.setRawValue(a.rawValue() % b.rawValue()); + return returnVal; } -inline LayoutUnit operator%(const LayoutUnit& a, const LayoutUnit& b) -{ - // This calculates the modulo so that: a = (a / b) * b + a % b. - LayoutUnit returnVal; - long long rawVal = (static_cast(kFixedPointDenominator) * a.rawValue()) % b.rawValue(); - returnVal.setRawValue(rawVal / kFixedPointDenominator); - return returnVal; +inline LayoutUnit operator%(const LayoutUnit& a, const LayoutUnit& b) { + // This calculates the modulo so that: a = (a / b) * b + a % b. + LayoutUnit returnVal; + long long rawVal = + (static_cast(kFixedPointDenominator) * a.rawValue()) % + b.rawValue(); + returnVal.setRawValue(rawVal / kFixedPointDenominator); + return returnVal; } -inline LayoutUnit operator%(const LayoutUnit& a, int b) -{ - return a % LayoutUnit(b); +inline LayoutUnit operator%(const LayoutUnit& a, int b) { + return a % LayoutUnit(b); } -inline LayoutUnit operator%(int a, const LayoutUnit& b) -{ - return LayoutUnit(a) % b; +inline LayoutUnit operator%(int a, const LayoutUnit& b) { + return LayoutUnit(a) % b; } -inline LayoutUnit& operator+=(LayoutUnit& a, const LayoutUnit& b) -{ - a.setRawValue(saturatedAddition(a.rawValue(), b.rawValue())); - return a; +inline LayoutUnit& operator+=(LayoutUnit& a, const LayoutUnit& b) { + a.setRawValue(saturatedAddition(a.rawValue(), b.rawValue())); + return a; } -inline LayoutUnit& operator+=(LayoutUnit& a, int b) -{ - a = a + b; - return a; +inline LayoutUnit& operator+=(LayoutUnit& a, int b) { + a = a + b; + return a; } -inline LayoutUnit& operator+=(LayoutUnit& a, float b) -{ - a = a + b; - return a; +inline LayoutUnit& operator+=(LayoutUnit& a, float b) { + a = a + b; + return a; } -inline float& operator+=(float& a, const LayoutUnit& b) -{ - a = a + b; - return a; +inline float& operator+=(float& a, const LayoutUnit& b) { + a = a + b; + return a; } -inline LayoutUnit& operator-=(LayoutUnit& a, int b) -{ - a = a - b; - return a; +inline LayoutUnit& operator-=(LayoutUnit& a, int b) { + a = a - b; + return a; } -inline LayoutUnit& operator-=(LayoutUnit& a, const LayoutUnit& b) -{ - a.setRawValue(saturatedSubtraction(a.rawValue(), b.rawValue())); - return a; +inline LayoutUnit& operator-=(LayoutUnit& a, const LayoutUnit& b) { + a.setRawValue(saturatedSubtraction(a.rawValue(), b.rawValue())); + return a; } -inline LayoutUnit& operator-=(LayoutUnit& a, float b) -{ - a = a - b; - return a; +inline LayoutUnit& operator-=(LayoutUnit& a, float b) { + a = a - b; + return a; } -inline float& operator-=(float& a, const LayoutUnit& b) -{ - a = a - b; - return a; +inline float& operator-=(float& a, const LayoutUnit& b) { + a = a - b; + return a; } -inline LayoutUnit& operator*=(LayoutUnit& a, const LayoutUnit& b) -{ - a = a * b; - return a; +inline LayoutUnit& operator*=(LayoutUnit& a, const LayoutUnit& b) { + a = a * b; + return a; } -// operator*=(LayoutUnit& a, int b) is supported by the operator above plus LayoutUnit(int). +// operator*=(LayoutUnit& a, int b) is supported by the operator above plus +// LayoutUnit(int). -inline LayoutUnit& operator*=(LayoutUnit& a, float b) -{ - a = a * b; - return a; +inline LayoutUnit& operator*=(LayoutUnit& a, float b) { + a = a * b; + return a; } -inline float& operator*=(float& a, const LayoutUnit& b) -{ - a = a * b; - return a; +inline float& operator*=(float& a, const LayoutUnit& b) { + a = a * b; + return a; } -inline LayoutUnit& operator/=(LayoutUnit& a, const LayoutUnit& b) -{ - a = a / b; - return a; +inline LayoutUnit& operator/=(LayoutUnit& a, const LayoutUnit& b) { + a = a / b; + return a; } -// operator/=(LayoutUnit& a, int b) is supported by the operator above plus LayoutUnit(int). +// operator/=(LayoutUnit& a, int b) is supported by the operator above plus +// LayoutUnit(int). -inline LayoutUnit& operator/=(LayoutUnit& a, float b) -{ - a = a / b; - return a; +inline LayoutUnit& operator/=(LayoutUnit& a, float b) { + a = a / b; + return a; } -inline float& operator/=(float& a, const LayoutUnit& b) -{ - a = a / b; - return a; +inline float& operator/=(float& a, const LayoutUnit& b) { + a = a / b; + return a; } -inline int snapSizeToPixel(LayoutUnit size, LayoutUnit location) -{ - LayoutUnit fraction = location.fraction(); - return (fraction + size).round() - fraction.round(); +inline int snapSizeToPixel(LayoutUnit size, LayoutUnit location) { + LayoutUnit fraction = location.fraction(); + return (fraction + size).round() - fraction.round(); } -inline int roundToInt(LayoutUnit value) -{ - return value.round(); +inline int roundToInt(LayoutUnit value) { + return value.round(); } -inline int floorToInt(LayoutUnit value) -{ - return value.floor(); +inline int floorToInt(LayoutUnit value) { + return value.floor(); } -inline LayoutUnit absoluteValue(const LayoutUnit& value) -{ - return value.abs(); +inline LayoutUnit absoluteValue(const LayoutUnit& value) { + return value.abs(); } -inline LayoutUnit layoutMod(const LayoutUnit& numerator, const LayoutUnit& denominator) -{ - return numerator % denominator; +inline LayoutUnit layoutMod(const LayoutUnit& numerator, + const LayoutUnit& denominator) { + return numerator % denominator; } -inline bool isIntegerValue(const LayoutUnit value) -{ - return value.toInt() == value; +inline bool isIntegerValue(const LayoutUnit value) { + return value.toInt() == value; } -inline LayoutUnit clampToLayoutUnit(LayoutUnit value, LayoutUnit min, LayoutUnit max) -{ - if (value >= max) - return max; - if (value <= min) - return min; - return value; +inline LayoutUnit clampToLayoutUnit(LayoutUnit value, + LayoutUnit min, + LayoutUnit max) { + if (value >= max) + return max; + if (value <= min) + return min; + return value; } -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_LAYOUTUNIT_H_ diff --git a/sky/engine/platform/LayoutUnitTest.cpp b/sky/engine/platform/LayoutUnitTest.cpp index f4c0d7d23ac5b..7a3d0d16edc51 100644 --- a/sky/engine/platform/LayoutUnitTest.cpp +++ b/sky/engine/platform/LayoutUnitTest.cpp @@ -37,225 +37,230 @@ using namespace blink; namespace { -TEST(WebCoreLayoutUnit, LayoutUnitInt) -{ - ASSERT_EQ(LayoutUnit(INT_MIN).toInt(), intMinForLayoutUnit); - ASSERT_EQ(LayoutUnit(INT_MIN / 2).toInt(), intMinForLayoutUnit); - ASSERT_EQ(LayoutUnit(intMinForLayoutUnit - 1).toInt(), intMinForLayoutUnit); - ASSERT_EQ(LayoutUnit(intMinForLayoutUnit).toInt(), intMinForLayoutUnit); - ASSERT_EQ(LayoutUnit(intMinForLayoutUnit + 1).toInt(), intMinForLayoutUnit + 1); - ASSERT_EQ(LayoutUnit(intMinForLayoutUnit / 2).toInt(), intMinForLayoutUnit / 2); - ASSERT_EQ(LayoutUnit(-10000).toInt(), -10000); - ASSERT_EQ(LayoutUnit(-1000).toInt(), -1000); - ASSERT_EQ(LayoutUnit(-100).toInt(), -100); - ASSERT_EQ(LayoutUnit(-10).toInt(), -10); - ASSERT_EQ(LayoutUnit(-1).toInt(), -1); - ASSERT_EQ(LayoutUnit(0).toInt(), 0); - ASSERT_EQ(LayoutUnit(1).toInt(), 1); - ASSERT_EQ(LayoutUnit(100).toInt(), 100); - ASSERT_EQ(LayoutUnit(1000).toInt(), 1000); - ASSERT_EQ(LayoutUnit(10000).toInt(), 10000); - ASSERT_EQ(LayoutUnit(intMaxForLayoutUnit / 2).toInt(), intMaxForLayoutUnit / 2); - ASSERT_EQ(LayoutUnit(intMaxForLayoutUnit - 1).toInt(), intMaxForLayoutUnit - 1); - ASSERT_EQ(LayoutUnit(intMaxForLayoutUnit).toInt(), intMaxForLayoutUnit); - ASSERT_EQ(LayoutUnit(intMaxForLayoutUnit + 1).toInt(), intMaxForLayoutUnit); - ASSERT_EQ(LayoutUnit(INT_MAX / 2).toInt(), intMaxForLayoutUnit); - ASSERT_EQ(LayoutUnit(INT_MAX).toInt(), intMaxForLayoutUnit); +TEST(WebCoreLayoutUnit, LayoutUnitInt) { + ASSERT_EQ(LayoutUnit(INT_MIN).toInt(), intMinForLayoutUnit); + ASSERT_EQ(LayoutUnit(INT_MIN / 2).toInt(), intMinForLayoutUnit); + ASSERT_EQ(LayoutUnit(intMinForLayoutUnit - 1).toInt(), intMinForLayoutUnit); + ASSERT_EQ(LayoutUnit(intMinForLayoutUnit).toInt(), intMinForLayoutUnit); + ASSERT_EQ(LayoutUnit(intMinForLayoutUnit + 1).toInt(), + intMinForLayoutUnit + 1); + ASSERT_EQ(LayoutUnit(intMinForLayoutUnit / 2).toInt(), + intMinForLayoutUnit / 2); + ASSERT_EQ(LayoutUnit(-10000).toInt(), -10000); + ASSERT_EQ(LayoutUnit(-1000).toInt(), -1000); + ASSERT_EQ(LayoutUnit(-100).toInt(), -100); + ASSERT_EQ(LayoutUnit(-10).toInt(), -10); + ASSERT_EQ(LayoutUnit(-1).toInt(), -1); + ASSERT_EQ(LayoutUnit(0).toInt(), 0); + ASSERT_EQ(LayoutUnit(1).toInt(), 1); + ASSERT_EQ(LayoutUnit(100).toInt(), 100); + ASSERT_EQ(LayoutUnit(1000).toInt(), 1000); + ASSERT_EQ(LayoutUnit(10000).toInt(), 10000); + ASSERT_EQ(LayoutUnit(intMaxForLayoutUnit / 2).toInt(), + intMaxForLayoutUnit / 2); + ASSERT_EQ(LayoutUnit(intMaxForLayoutUnit - 1).toInt(), + intMaxForLayoutUnit - 1); + ASSERT_EQ(LayoutUnit(intMaxForLayoutUnit).toInt(), intMaxForLayoutUnit); + ASSERT_EQ(LayoutUnit(intMaxForLayoutUnit + 1).toInt(), intMaxForLayoutUnit); + ASSERT_EQ(LayoutUnit(INT_MAX / 2).toInt(), intMaxForLayoutUnit); + ASSERT_EQ(LayoutUnit(INT_MAX).toInt(), intMaxForLayoutUnit); } -TEST(WebCoreLayoutUnit, LayoutUnitFloat) -{ - const float tolerance = 1.0f / kFixedPointDenominator; - ASSERT_FLOAT_EQ(LayoutUnit(1.0f).toFloat(), 1.0f); - ASSERT_FLOAT_EQ(LayoutUnit(1.25f).toFloat(), 1.25f); - ASSERT_NEAR(LayoutUnit(1.1f).toFloat(), 1.1f, tolerance); - ASSERT_NEAR(LayoutUnit(1.33f).toFloat(), 1.33f, tolerance); - ASSERT_NEAR(LayoutUnit(1.3333f).toFloat(), 1.3333f, tolerance); - ASSERT_NEAR(LayoutUnit(1.53434f).toFloat(), 1.53434f, tolerance); - ASSERT_NEAR(LayoutUnit(345634).toFloat(), 345634.0f, tolerance); - ASSERT_NEAR(LayoutUnit(345634.12335f).toFloat(), 345634.12335f, tolerance); - ASSERT_NEAR(LayoutUnit(-345634.12335f).toFloat(), -345634.12335f, tolerance); - ASSERT_NEAR(LayoutUnit(-345634).toFloat(), -345634.0f, tolerance); +TEST(WebCoreLayoutUnit, LayoutUnitFloat) { + const float tolerance = 1.0f / kFixedPointDenominator; + ASSERT_FLOAT_EQ(LayoutUnit(1.0f).toFloat(), 1.0f); + ASSERT_FLOAT_EQ(LayoutUnit(1.25f).toFloat(), 1.25f); + ASSERT_NEAR(LayoutUnit(1.1f).toFloat(), 1.1f, tolerance); + ASSERT_NEAR(LayoutUnit(1.33f).toFloat(), 1.33f, tolerance); + ASSERT_NEAR(LayoutUnit(1.3333f).toFloat(), 1.3333f, tolerance); + ASSERT_NEAR(LayoutUnit(1.53434f).toFloat(), 1.53434f, tolerance); + ASSERT_NEAR(LayoutUnit(345634).toFloat(), 345634.0f, tolerance); + ASSERT_NEAR(LayoutUnit(345634.12335f).toFloat(), 345634.12335f, tolerance); + ASSERT_NEAR(LayoutUnit(-345634.12335f).toFloat(), -345634.12335f, tolerance); + ASSERT_NEAR(LayoutUnit(-345634).toFloat(), -345634.0f, tolerance); } -TEST(WebCoreLayoutUnit, LayoutUnitRounding) -{ - ASSERT_EQ(LayoutUnit(-1.9f).round(), -2); - ASSERT_EQ(LayoutUnit(-1.6f).round(), -2); - ASSERT_EQ(LayoutUnit::fromFloatRound(-1.51f).round(), -2); - ASSERT_EQ(LayoutUnit::fromFloatRound(-1.5f).round(), -1); - ASSERT_EQ(LayoutUnit::fromFloatRound(-1.49f).round(), -1); - ASSERT_EQ(LayoutUnit(-1.0f).round(), -1); - ASSERT_EQ(LayoutUnit::fromFloatRound(-0.99f).round(), -1); - ASSERT_EQ(LayoutUnit::fromFloatRound(-0.51f).round(), -1); - ASSERT_EQ(LayoutUnit::fromFloatRound(-0.50f).round(), 0); - ASSERT_EQ(LayoutUnit::fromFloatRound(-0.49f).round(), 0); - ASSERT_EQ(LayoutUnit(-0.1f).round(), 0); - ASSERT_EQ(LayoutUnit(0.0f).round(), 0); - ASSERT_EQ(LayoutUnit(0.1f).round(), 0); - ASSERT_EQ(LayoutUnit::fromFloatRound(0.49f).round(), 0); - ASSERT_EQ(LayoutUnit::fromFloatRound(0.50f).round(), 1); - ASSERT_EQ(LayoutUnit::fromFloatRound(0.51f).round(), 1); - ASSERT_EQ(LayoutUnit(0.99f).round(), 1); - ASSERT_EQ(LayoutUnit(1.0f).round(), 1); - ASSERT_EQ(LayoutUnit::fromFloatRound(1.49f).round(), 1); - ASSERT_EQ(LayoutUnit::fromFloatRound(1.5f).round(), 2); - ASSERT_EQ(LayoutUnit::fromFloatRound(1.51f).round(), 2); +TEST(WebCoreLayoutUnit, LayoutUnitRounding) { + ASSERT_EQ(LayoutUnit(-1.9f).round(), -2); + ASSERT_EQ(LayoutUnit(-1.6f).round(), -2); + ASSERT_EQ(LayoutUnit::fromFloatRound(-1.51f).round(), -2); + ASSERT_EQ(LayoutUnit::fromFloatRound(-1.5f).round(), -1); + ASSERT_EQ(LayoutUnit::fromFloatRound(-1.49f).round(), -1); + ASSERT_EQ(LayoutUnit(-1.0f).round(), -1); + ASSERT_EQ(LayoutUnit::fromFloatRound(-0.99f).round(), -1); + ASSERT_EQ(LayoutUnit::fromFloatRound(-0.51f).round(), -1); + ASSERT_EQ(LayoutUnit::fromFloatRound(-0.50f).round(), 0); + ASSERT_EQ(LayoutUnit::fromFloatRound(-0.49f).round(), 0); + ASSERT_EQ(LayoutUnit(-0.1f).round(), 0); + ASSERT_EQ(LayoutUnit(0.0f).round(), 0); + ASSERT_EQ(LayoutUnit(0.1f).round(), 0); + ASSERT_EQ(LayoutUnit::fromFloatRound(0.49f).round(), 0); + ASSERT_EQ(LayoutUnit::fromFloatRound(0.50f).round(), 1); + ASSERT_EQ(LayoutUnit::fromFloatRound(0.51f).round(), 1); + ASSERT_EQ(LayoutUnit(0.99f).round(), 1); + ASSERT_EQ(LayoutUnit(1.0f).round(), 1); + ASSERT_EQ(LayoutUnit::fromFloatRound(1.49f).round(), 1); + ASSERT_EQ(LayoutUnit::fromFloatRound(1.5f).round(), 2); + ASSERT_EQ(LayoutUnit::fromFloatRound(1.51f).round(), 2); } -TEST(WebCoreLayoutUnit, LayoutUnitSnapSizeToPixel) -{ - ASSERT_EQ(snapSizeToPixel(LayoutUnit(1), LayoutUnit(0)), 1); - ASSERT_EQ(snapSizeToPixel(LayoutUnit(1), LayoutUnit(0.5)), 1); - ASSERT_EQ(snapSizeToPixel(LayoutUnit(1.5), LayoutUnit(0)), 2); - ASSERT_EQ(snapSizeToPixel(LayoutUnit(1.5), LayoutUnit(0.49)), 2); - ASSERT_EQ(snapSizeToPixel(LayoutUnit(1.5), LayoutUnit(0.5)), 1); - ASSERT_EQ(snapSizeToPixel(LayoutUnit(1.5), LayoutUnit(0.75)), 1); - ASSERT_EQ(snapSizeToPixel(LayoutUnit(1.5), LayoutUnit(0.99)), 1); - ASSERT_EQ(snapSizeToPixel(LayoutUnit(1.5), LayoutUnit(1)), 2); - - ASSERT_EQ(snapSizeToPixel(LayoutUnit(0.5), LayoutUnit(1.5)), 0); - ASSERT_EQ(snapSizeToPixel(LayoutUnit(0.99), LayoutUnit(1.5)), 0); - ASSERT_EQ(snapSizeToPixel(LayoutUnit(1.0), LayoutUnit(1.5)), 1); - ASSERT_EQ(snapSizeToPixel(LayoutUnit(1.49), LayoutUnit(1.5)), 1); - ASSERT_EQ(snapSizeToPixel(LayoutUnit(1.5), LayoutUnit(1.5)), 1); - - ASSERT_EQ(snapSizeToPixel(LayoutUnit(100.5), LayoutUnit(100)), 101); - ASSERT_EQ(snapSizeToPixel(LayoutUnit(intMaxForLayoutUnit), LayoutUnit(0.3)), intMaxForLayoutUnit); - ASSERT_EQ(snapSizeToPixel(LayoutUnit(intMinForLayoutUnit), LayoutUnit(-0.3)), intMinForLayoutUnit); +TEST(WebCoreLayoutUnit, LayoutUnitSnapSizeToPixel) { + ASSERT_EQ(snapSizeToPixel(LayoutUnit(1), LayoutUnit(0)), 1); + ASSERT_EQ(snapSizeToPixel(LayoutUnit(1), LayoutUnit(0.5)), 1); + ASSERT_EQ(snapSizeToPixel(LayoutUnit(1.5), LayoutUnit(0)), 2); + ASSERT_EQ(snapSizeToPixel(LayoutUnit(1.5), LayoutUnit(0.49)), 2); + ASSERT_EQ(snapSizeToPixel(LayoutUnit(1.5), LayoutUnit(0.5)), 1); + ASSERT_EQ(snapSizeToPixel(LayoutUnit(1.5), LayoutUnit(0.75)), 1); + ASSERT_EQ(snapSizeToPixel(LayoutUnit(1.5), LayoutUnit(0.99)), 1); + ASSERT_EQ(snapSizeToPixel(LayoutUnit(1.5), LayoutUnit(1)), 2); + + ASSERT_EQ(snapSizeToPixel(LayoutUnit(0.5), LayoutUnit(1.5)), 0); + ASSERT_EQ(snapSizeToPixel(LayoutUnit(0.99), LayoutUnit(1.5)), 0); + ASSERT_EQ(snapSizeToPixel(LayoutUnit(1.0), LayoutUnit(1.5)), 1); + ASSERT_EQ(snapSizeToPixel(LayoutUnit(1.49), LayoutUnit(1.5)), 1); + ASSERT_EQ(snapSizeToPixel(LayoutUnit(1.5), LayoutUnit(1.5)), 1); + + ASSERT_EQ(snapSizeToPixel(LayoutUnit(100.5), LayoutUnit(100)), 101); + ASSERT_EQ(snapSizeToPixel(LayoutUnit(intMaxForLayoutUnit), LayoutUnit(0.3)), + intMaxForLayoutUnit); + ASSERT_EQ(snapSizeToPixel(LayoutUnit(intMinForLayoutUnit), LayoutUnit(-0.3)), + intMinForLayoutUnit); } -TEST(WebCoreLayoutUnit, LayoutUnitMultiplication) -{ - ASSERT_EQ((LayoutUnit(1) * LayoutUnit(1)).toInt(), 1); - ASSERT_EQ((LayoutUnit(1) * LayoutUnit(2)).toInt(), 2); - ASSERT_EQ((LayoutUnit(2) * LayoutUnit(1)).toInt(), 2); - ASSERT_EQ((LayoutUnit(2) * LayoutUnit(0.5)).toInt(), 1); - ASSERT_EQ((LayoutUnit(0.5) * LayoutUnit(2)).toInt(), 1); - ASSERT_EQ((LayoutUnit(100) * LayoutUnit(1)).toInt(), 100); - - ASSERT_EQ((LayoutUnit(-1) * LayoutUnit(1)).toInt(), -1); - ASSERT_EQ((LayoutUnit(-1) * LayoutUnit(2)).toInt(), -2); - ASSERT_EQ((LayoutUnit(-2) * LayoutUnit(1)).toInt(), -2); - ASSERT_EQ((LayoutUnit(-2) * LayoutUnit(0.5)).toInt(), -1); - ASSERT_EQ((LayoutUnit(-0.5) * LayoutUnit(2)).toInt(), -1); - ASSERT_EQ((LayoutUnit(-100) * LayoutUnit(1)).toInt(), -100); - - ASSERT_EQ((LayoutUnit(-1) * LayoutUnit(-1)).toInt(), 1); - ASSERT_EQ((LayoutUnit(-1) * LayoutUnit(-2)).toInt(), 2); - ASSERT_EQ((LayoutUnit(-2) * LayoutUnit(-1)).toInt(), 2); - ASSERT_EQ((LayoutUnit(-2) * LayoutUnit(-0.5)).toInt(), 1); - ASSERT_EQ((LayoutUnit(-0.5) * LayoutUnit(-2)).toInt(), 1); - ASSERT_EQ((LayoutUnit(-100) * LayoutUnit(-1)).toInt(), 100); - - ASSERT_EQ((LayoutUnit(100) * LayoutUnit(3.33)).round(), 333); - ASSERT_EQ((LayoutUnit(-100) * LayoutUnit(3.33)).round(), -333); - ASSERT_EQ((LayoutUnit(-100) * LayoutUnit(-3.33)).round(), 333); - - size_t aHundredSizeT = 100; - ASSERT_EQ((LayoutUnit(aHundredSizeT) * LayoutUnit(1)).toInt(), 100); - ASSERT_EQ((aHundredSizeT * LayoutUnit(4)).toInt(), 400); - ASSERT_EQ((LayoutUnit(4) * aHundredSizeT).toInt(), 400); - - int quarterMax = intMaxForLayoutUnit / 4; - ASSERT_EQ((LayoutUnit(quarterMax) * LayoutUnit(2)).toInt(), quarterMax * 2); - ASSERT_EQ((LayoutUnit(quarterMax) * LayoutUnit(3)).toInt(), quarterMax * 3); - ASSERT_EQ((LayoutUnit(quarterMax) * LayoutUnit(4)).toInt(), quarterMax * 4); - ASSERT_EQ((LayoutUnit(quarterMax) * LayoutUnit(5)).toInt(), intMaxForLayoutUnit); - - size_t overflowIntSizeT = intMaxForLayoutUnit * 4; - ASSERT_EQ((LayoutUnit(overflowIntSizeT) * LayoutUnit(2)).toInt(), intMaxForLayoutUnit); - ASSERT_EQ((overflowIntSizeT * LayoutUnit(4)).toInt(), intMaxForLayoutUnit); - ASSERT_EQ((LayoutUnit(4) * overflowIntSizeT).toInt(), intMaxForLayoutUnit); +TEST(WebCoreLayoutUnit, LayoutUnitMultiplication) { + ASSERT_EQ((LayoutUnit(1) * LayoutUnit(1)).toInt(), 1); + ASSERT_EQ((LayoutUnit(1) * LayoutUnit(2)).toInt(), 2); + ASSERT_EQ((LayoutUnit(2) * LayoutUnit(1)).toInt(), 2); + ASSERT_EQ((LayoutUnit(2) * LayoutUnit(0.5)).toInt(), 1); + ASSERT_EQ((LayoutUnit(0.5) * LayoutUnit(2)).toInt(), 1); + ASSERT_EQ((LayoutUnit(100) * LayoutUnit(1)).toInt(), 100); + + ASSERT_EQ((LayoutUnit(-1) * LayoutUnit(1)).toInt(), -1); + ASSERT_EQ((LayoutUnit(-1) * LayoutUnit(2)).toInt(), -2); + ASSERT_EQ((LayoutUnit(-2) * LayoutUnit(1)).toInt(), -2); + ASSERT_EQ((LayoutUnit(-2) * LayoutUnit(0.5)).toInt(), -1); + ASSERT_EQ((LayoutUnit(-0.5) * LayoutUnit(2)).toInt(), -1); + ASSERT_EQ((LayoutUnit(-100) * LayoutUnit(1)).toInt(), -100); + + ASSERT_EQ((LayoutUnit(-1) * LayoutUnit(-1)).toInt(), 1); + ASSERT_EQ((LayoutUnit(-1) * LayoutUnit(-2)).toInt(), 2); + ASSERT_EQ((LayoutUnit(-2) * LayoutUnit(-1)).toInt(), 2); + ASSERT_EQ((LayoutUnit(-2) * LayoutUnit(-0.5)).toInt(), 1); + ASSERT_EQ((LayoutUnit(-0.5) * LayoutUnit(-2)).toInt(), 1); + ASSERT_EQ((LayoutUnit(-100) * LayoutUnit(-1)).toInt(), 100); + + ASSERT_EQ((LayoutUnit(100) * LayoutUnit(3.33)).round(), 333); + ASSERT_EQ((LayoutUnit(-100) * LayoutUnit(3.33)).round(), -333); + ASSERT_EQ((LayoutUnit(-100) * LayoutUnit(-3.33)).round(), 333); + + size_t aHundredSizeT = 100; + ASSERT_EQ((LayoutUnit(aHundredSizeT) * LayoutUnit(1)).toInt(), 100); + ASSERT_EQ((aHundredSizeT * LayoutUnit(4)).toInt(), 400); + ASSERT_EQ((LayoutUnit(4) * aHundredSizeT).toInt(), 400); + + int quarterMax = intMaxForLayoutUnit / 4; + ASSERT_EQ((LayoutUnit(quarterMax) * LayoutUnit(2)).toInt(), quarterMax * 2); + ASSERT_EQ((LayoutUnit(quarterMax) * LayoutUnit(3)).toInt(), quarterMax * 3); + ASSERT_EQ((LayoutUnit(quarterMax) * LayoutUnit(4)).toInt(), quarterMax * 4); + ASSERT_EQ((LayoutUnit(quarterMax) * LayoutUnit(5)).toInt(), + intMaxForLayoutUnit); + + size_t overflowIntSizeT = intMaxForLayoutUnit * 4; + ASSERT_EQ((LayoutUnit(overflowIntSizeT) * LayoutUnit(2)).toInt(), + intMaxForLayoutUnit); + ASSERT_EQ((overflowIntSizeT * LayoutUnit(4)).toInt(), intMaxForLayoutUnit); + ASSERT_EQ((LayoutUnit(4) * overflowIntSizeT).toInt(), intMaxForLayoutUnit); } -TEST(WebCoreLayoutUnit, LayoutUnitDivision) -{ - ASSERT_EQ((LayoutUnit(1) / LayoutUnit(1)).toInt(), 1); - ASSERT_EQ((LayoutUnit(1) / LayoutUnit(2)).toInt(), 0); - ASSERT_EQ((LayoutUnit(2) / LayoutUnit(1)).toInt(), 2); - ASSERT_EQ((LayoutUnit(2) / LayoutUnit(0.5)).toInt(), 4); - ASSERT_EQ((LayoutUnit(0.5) / LayoutUnit(2)).toInt(), 0); - ASSERT_EQ((LayoutUnit(100) / LayoutUnit(10)).toInt(), 10); - ASSERT_FLOAT_EQ((LayoutUnit(1) / LayoutUnit(2)).toFloat(), 0.5f); - ASSERT_FLOAT_EQ((LayoutUnit(0.5) / LayoutUnit(2)).toFloat(), 0.25f); - - ASSERT_EQ((LayoutUnit(-1) / LayoutUnit(1)).toInt(), -1); - ASSERT_EQ((LayoutUnit(-1) / LayoutUnit(2)).toInt(), 0); - ASSERT_EQ((LayoutUnit(-2) / LayoutUnit(1)).toInt(), -2); - ASSERT_EQ((LayoutUnit(-2) / LayoutUnit(0.5)).toInt(), -4); - ASSERT_EQ((LayoutUnit(-0.5) / LayoutUnit(2)).toInt(), 0); - ASSERT_EQ((LayoutUnit(-100) / LayoutUnit(10)).toInt(), -10); - ASSERT_FLOAT_EQ((LayoutUnit(-1) / LayoutUnit(2)).toFloat(), -0.5f); - ASSERT_FLOAT_EQ((LayoutUnit(-0.5) / LayoutUnit(2)).toFloat(), -0.25f); - - ASSERT_EQ((LayoutUnit(-1) / LayoutUnit(-1)).toInt(), 1); - ASSERT_EQ((LayoutUnit(-1) / LayoutUnit(-2)).toInt(), 0); - ASSERT_EQ((LayoutUnit(-2) / LayoutUnit(-1)).toInt(), 2); - ASSERT_EQ((LayoutUnit(-2) / LayoutUnit(-0.5)).toInt(), 4); - ASSERT_EQ((LayoutUnit(-0.5) / LayoutUnit(-2)).toInt(), 0); - ASSERT_EQ((LayoutUnit(-100) / LayoutUnit(-10)).toInt(), 10); - ASSERT_FLOAT_EQ((LayoutUnit(-1) / LayoutUnit(-2)).toFloat(), 0.5f); - ASSERT_FLOAT_EQ((LayoutUnit(-0.5) / LayoutUnit(-2)).toFloat(), 0.25f); - - size_t aHundredSizeT = 100; - ASSERT_EQ((LayoutUnit(aHundredSizeT) / LayoutUnit(2)).toInt(), 50); - ASSERT_EQ((aHundredSizeT / LayoutUnit(4)).toInt(), 25); - ASSERT_EQ((LayoutUnit(400) / aHundredSizeT).toInt(), 4); - - ASSERT_EQ((LayoutUnit(intMaxForLayoutUnit) / LayoutUnit(2)).toInt(), intMaxForLayoutUnit / 2); - ASSERT_EQ((LayoutUnit(intMaxForLayoutUnit) / LayoutUnit(0.5)).toInt(), intMaxForLayoutUnit); +TEST(WebCoreLayoutUnit, LayoutUnitDivision) { + ASSERT_EQ((LayoutUnit(1) / LayoutUnit(1)).toInt(), 1); + ASSERT_EQ((LayoutUnit(1) / LayoutUnit(2)).toInt(), 0); + ASSERT_EQ((LayoutUnit(2) / LayoutUnit(1)).toInt(), 2); + ASSERT_EQ((LayoutUnit(2) / LayoutUnit(0.5)).toInt(), 4); + ASSERT_EQ((LayoutUnit(0.5) / LayoutUnit(2)).toInt(), 0); + ASSERT_EQ((LayoutUnit(100) / LayoutUnit(10)).toInt(), 10); + ASSERT_FLOAT_EQ((LayoutUnit(1) / LayoutUnit(2)).toFloat(), 0.5f); + ASSERT_FLOAT_EQ((LayoutUnit(0.5) / LayoutUnit(2)).toFloat(), 0.25f); + + ASSERT_EQ((LayoutUnit(-1) / LayoutUnit(1)).toInt(), -1); + ASSERT_EQ((LayoutUnit(-1) / LayoutUnit(2)).toInt(), 0); + ASSERT_EQ((LayoutUnit(-2) / LayoutUnit(1)).toInt(), -2); + ASSERT_EQ((LayoutUnit(-2) / LayoutUnit(0.5)).toInt(), -4); + ASSERT_EQ((LayoutUnit(-0.5) / LayoutUnit(2)).toInt(), 0); + ASSERT_EQ((LayoutUnit(-100) / LayoutUnit(10)).toInt(), -10); + ASSERT_FLOAT_EQ((LayoutUnit(-1) / LayoutUnit(2)).toFloat(), -0.5f); + ASSERT_FLOAT_EQ((LayoutUnit(-0.5) / LayoutUnit(2)).toFloat(), -0.25f); + + ASSERT_EQ((LayoutUnit(-1) / LayoutUnit(-1)).toInt(), 1); + ASSERT_EQ((LayoutUnit(-1) / LayoutUnit(-2)).toInt(), 0); + ASSERT_EQ((LayoutUnit(-2) / LayoutUnit(-1)).toInt(), 2); + ASSERT_EQ((LayoutUnit(-2) / LayoutUnit(-0.5)).toInt(), 4); + ASSERT_EQ((LayoutUnit(-0.5) / LayoutUnit(-2)).toInt(), 0); + ASSERT_EQ((LayoutUnit(-100) / LayoutUnit(-10)).toInt(), 10); + ASSERT_FLOAT_EQ((LayoutUnit(-1) / LayoutUnit(-2)).toFloat(), 0.5f); + ASSERT_FLOAT_EQ((LayoutUnit(-0.5) / LayoutUnit(-2)).toFloat(), 0.25f); + + size_t aHundredSizeT = 100; + ASSERT_EQ((LayoutUnit(aHundredSizeT) / LayoutUnit(2)).toInt(), 50); + ASSERT_EQ((aHundredSizeT / LayoutUnit(4)).toInt(), 25); + ASSERT_EQ((LayoutUnit(400) / aHundredSizeT).toInt(), 4); + + ASSERT_EQ((LayoutUnit(intMaxForLayoutUnit) / LayoutUnit(2)).toInt(), + intMaxForLayoutUnit / 2); + ASSERT_EQ((LayoutUnit(intMaxForLayoutUnit) / LayoutUnit(0.5)).toInt(), + intMaxForLayoutUnit); } -TEST(WebCoreLayoutUnit, LayoutUnitCeil) -{ - ASSERT_EQ(LayoutUnit(0).ceil(), 0); - ASSERT_EQ(LayoutUnit(0.1).ceil(), 1); - ASSERT_EQ(LayoutUnit(0.5).ceil(), 1); - ASSERT_EQ(LayoutUnit(0.9).ceil(), 1); - ASSERT_EQ(LayoutUnit(1.0).ceil(), 1); - ASSERT_EQ(LayoutUnit(1.1).ceil(), 2); - - ASSERT_EQ(LayoutUnit(-0.1).ceil(), 0); - ASSERT_EQ(LayoutUnit(-0.5).ceil(), 0); - ASSERT_EQ(LayoutUnit(-0.9).ceil(), 0); - ASSERT_EQ(LayoutUnit(-1.0).ceil(), -1); - - ASSERT_EQ(LayoutUnit(intMaxForLayoutUnit).ceil(), intMaxForLayoutUnit); - ASSERT_EQ((LayoutUnit(intMaxForLayoutUnit) - LayoutUnit(0.5)).ceil(), intMaxForLayoutUnit); - ASSERT_EQ((LayoutUnit(intMaxForLayoutUnit) - LayoutUnit(1)).ceil(), intMaxForLayoutUnit - 1); - - ASSERT_EQ(LayoutUnit(intMinForLayoutUnit).ceil(), intMinForLayoutUnit); +TEST(WebCoreLayoutUnit, LayoutUnitCeil) { + ASSERT_EQ(LayoutUnit(0).ceil(), 0); + ASSERT_EQ(LayoutUnit(0.1).ceil(), 1); + ASSERT_EQ(LayoutUnit(0.5).ceil(), 1); + ASSERT_EQ(LayoutUnit(0.9).ceil(), 1); + ASSERT_EQ(LayoutUnit(1.0).ceil(), 1); + ASSERT_EQ(LayoutUnit(1.1).ceil(), 2); + + ASSERT_EQ(LayoutUnit(-0.1).ceil(), 0); + ASSERT_EQ(LayoutUnit(-0.5).ceil(), 0); + ASSERT_EQ(LayoutUnit(-0.9).ceil(), 0); + ASSERT_EQ(LayoutUnit(-1.0).ceil(), -1); + + ASSERT_EQ(LayoutUnit(intMaxForLayoutUnit).ceil(), intMaxForLayoutUnit); + ASSERT_EQ((LayoutUnit(intMaxForLayoutUnit) - LayoutUnit(0.5)).ceil(), + intMaxForLayoutUnit); + ASSERT_EQ((LayoutUnit(intMaxForLayoutUnit) - LayoutUnit(1)).ceil(), + intMaxForLayoutUnit - 1); + + ASSERT_EQ(LayoutUnit(intMinForLayoutUnit).ceil(), intMinForLayoutUnit); } -TEST(WebCoreLayoutUnit, LayoutUnitFloor) -{ - ASSERT_EQ(LayoutUnit(0).floor(), 0); - ASSERT_EQ(LayoutUnit(0.1).floor(), 0); - ASSERT_EQ(LayoutUnit(0.5).floor(), 0); - ASSERT_EQ(LayoutUnit(0.9).floor(), 0); - ASSERT_EQ(LayoutUnit(1.0).floor(), 1); - ASSERT_EQ(LayoutUnit(1.1).floor(), 1); - - ASSERT_EQ(LayoutUnit(-0.1).floor(), -1); - ASSERT_EQ(LayoutUnit(-0.5).floor(), -1); - ASSERT_EQ(LayoutUnit(-0.9).floor(), -1); - ASSERT_EQ(LayoutUnit(-1.0).floor(), -1); - - ASSERT_EQ(LayoutUnit(intMaxForLayoutUnit).floor(), intMaxForLayoutUnit); - - ASSERT_EQ(LayoutUnit(intMinForLayoutUnit).floor(), intMinForLayoutUnit); - ASSERT_EQ((LayoutUnit(intMinForLayoutUnit) + LayoutUnit(0.5)).floor(), intMinForLayoutUnit); - ASSERT_EQ((LayoutUnit(intMinForLayoutUnit) + LayoutUnit(1)).floor(), intMinForLayoutUnit + 1); +TEST(WebCoreLayoutUnit, LayoutUnitFloor) { + ASSERT_EQ(LayoutUnit(0).floor(), 0); + ASSERT_EQ(LayoutUnit(0.1).floor(), 0); + ASSERT_EQ(LayoutUnit(0.5).floor(), 0); + ASSERT_EQ(LayoutUnit(0.9).floor(), 0); + ASSERT_EQ(LayoutUnit(1.0).floor(), 1); + ASSERT_EQ(LayoutUnit(1.1).floor(), 1); + + ASSERT_EQ(LayoutUnit(-0.1).floor(), -1); + ASSERT_EQ(LayoutUnit(-0.5).floor(), -1); + ASSERT_EQ(LayoutUnit(-0.9).floor(), -1); + ASSERT_EQ(LayoutUnit(-1.0).floor(), -1); + + ASSERT_EQ(LayoutUnit(intMaxForLayoutUnit).floor(), intMaxForLayoutUnit); + + ASSERT_EQ(LayoutUnit(intMinForLayoutUnit).floor(), intMinForLayoutUnit); + ASSERT_EQ((LayoutUnit(intMinForLayoutUnit) + LayoutUnit(0.5)).floor(), + intMinForLayoutUnit); + ASSERT_EQ((LayoutUnit(intMinForLayoutUnit) + LayoutUnit(1)).floor(), + intMinForLayoutUnit + 1); } -TEST(WebCoreLayoutUnit, LayoutUnitFloatOverflow) -{ - // These should overflow to the max/min according to their sign. - ASSERT_EQ(intMaxForLayoutUnit, LayoutUnit(176972000.0f).toInt()); - ASSERT_EQ(intMinForLayoutUnit, LayoutUnit(-176972000.0f).toInt()); - ASSERT_EQ(intMaxForLayoutUnit, LayoutUnit(176972000.0).toInt()); - ASSERT_EQ(intMinForLayoutUnit, LayoutUnit(-176972000.0).toInt()); +TEST(WebCoreLayoutUnit, LayoutUnitFloatOverflow) { + // These should overflow to the max/min according to their sign. + ASSERT_EQ(intMaxForLayoutUnit, LayoutUnit(176972000.0f).toInt()); + ASSERT_EQ(intMinForLayoutUnit, LayoutUnit(-176972000.0f).toInt()); + ASSERT_EQ(intMaxForLayoutUnit, LayoutUnit(176972000.0).toInt()); + ASSERT_EQ(intMinForLayoutUnit, LayoutUnit(-176972000.0).toInt()); } -} // namespace +} // namespace diff --git a/sky/engine/platform/Length.cpp b/sky/engine/platform/Length.cpp index 9a3a9cecd3f0a..b0f4b02d7464e 100644 --- a/sky/engine/platform/Length.cpp +++ b/sky/engine/platform/Length.cpp @@ -2,7 +2,8 @@ * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2001 Dirk Mueller ( mueller@kde.org ) - * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. + * All rights reserved. * Copyright (C) 2006 Andrew Wellington (proton@wiretapped.net) * * This library is free software; you can redistribute it and/or @@ -34,184 +35,176 @@ using namespace WTF; namespace blink { -template -static unsigned splitLength(const CharType* data, unsigned length, unsigned& intLength, unsigned& doubleLength) -{ - ASSERT(length); - - unsigned i = 0; - while (i < length && isSpaceOrNewline(data[i])) - ++i; - if (i < length && (data[i] == '+' || data[i] == '-')) - ++i; - while (i < length && isASCIIDigit(data[i])) - ++i; - intLength = i; - while (i < length && (isASCIIDigit(data[i]) || data[i] == '.')) - ++i; - doubleLength = i; - - // IE quirk: Skip whitespace between the number and the % character (20 % => 20%). - while (i < length && isSpaceOrNewline(data[i])) - ++i; - - return i; +template +static unsigned splitLength(const CharType* data, + unsigned length, + unsigned& intLength, + unsigned& doubleLength) { + ASSERT(length); + + unsigned i = 0; + while (i < length && isSpaceOrNewline(data[i])) + ++i; + if (i < length && (data[i] == '+' || data[i] == '-')) + ++i; + while (i < length && isASCIIDigit(data[i])) + ++i; + intLength = i; + while (i < length && (isASCIIDigit(data[i]) || data[i] == '.')) + ++i; + doubleLength = i; + + // IE quirk: Skip whitespace between the number and the % character (20 % => + // 20%). + while (i < length && isSpaceOrNewline(data[i])) + ++i; + + return i; } -template -static Length parseHTMLAreaCoordinate(const CharType* data, unsigned length) -{ - unsigned intLength; - unsigned doubleLength; - splitLength(data, length, intLength, doubleLength); - - bool ok; - int r = charactersToIntStrict(data, intLength, &ok); - if (ok) - return Length(r, Fixed); - return Length(0, Fixed); +template +static Length parseHTMLAreaCoordinate(const CharType* data, unsigned length) { + unsigned intLength; + unsigned doubleLength; + splitLength(data, length, intLength, doubleLength); + + bool ok; + int r = charactersToIntStrict(data, intLength, &ok); + if (ok) + return Length(r, Fixed); + return Length(0, Fixed); } class CalculationValueHandleMap { - WTF_MAKE_FAST_ALLOCATED; -public: - CalculationValueHandleMap() - : m_index(1) - { + WTF_MAKE_FAST_ALLOCATED; + + public: + CalculationValueHandleMap() : m_index(1) {} + + int insert(PassRefPtr calcValue) { + ASSERT(m_index); + // FIXME calc(): https://bugs.webkit.org/show_bug.cgi?id=80489 + // This monotonically increasing handle generation scheme is potentially + // wasteful of the handle space. Consider reusing empty handles. + while (m_map.contains(m_index)) + m_index++; + + m_map.set(m_index, calcValue); + + return m_index; + } + + void remove(int index) { + ASSERT(m_map.contains(index)); + m_map.remove(index); + } + + CalculationValue& get(int index) { + ASSERT(m_map.contains(index)); + return *m_map.get(index); + } + + void decrementRef(int index) { + ASSERT(m_map.contains(index)); + CalculationValue* value = m_map.get(index); + if (value->hasOneRef()) { + // Force the CalculationValue destructor early to avoid a potential + // recursive call inside HashMap remove(). + m_map.set(index, nullptr); + m_map.remove(index); + } else { + value->deref(); } + } - int insert(PassRefPtr calcValue) - { - ASSERT(m_index); - // FIXME calc(): https://bugs.webkit.org/show_bug.cgi?id=80489 - // This monotonically increasing handle generation scheme is potentially wasteful - // of the handle space. Consider reusing empty handles. - while (m_map.contains(m_index)) - m_index++; - - m_map.set(m_index, calcValue); - - return m_index; - } - - void remove(int index) - { - ASSERT(m_map.contains(index)); - m_map.remove(index); - } - - CalculationValue& get(int index) - { - ASSERT(m_map.contains(index)); - return *m_map.get(index); - } - - void decrementRef(int index) - { - ASSERT(m_map.contains(index)); - CalculationValue* value = m_map.get(index); - if (value->hasOneRef()) { - // Force the CalculationValue destructor early to avoid a potential recursive call inside HashMap remove(). - m_map.set(index, nullptr); - m_map.remove(index); - } else { - value->deref(); - } - } - -private: - int m_index; - HashMap > m_map; + private: + int m_index; + HashMap> m_map; }; -static CalculationValueHandleMap& calcHandles() -{ - DEFINE_STATIC_LOCAL(CalculationValueHandleMap, handleMap, ()); - return handleMap; +static CalculationValueHandleMap& calcHandles() { + DEFINE_STATIC_LOCAL(CalculationValueHandleMap, handleMap, ()); + return handleMap; } Length::Length(PassRefPtr calc) - : m_quirk(false) - , m_type(Calculated) - , m_isFloat(false) -{ - m_intValue = calcHandles().insert(calc); + : m_quirk(false), m_type(Calculated), m_isFloat(false) { + m_intValue = calcHandles().insert(calc); } -Length Length::blendMixedTypes(const Length& from, double progress, ValueRange range) const -{ - ASSERT(from.isSpecified()); - ASSERT(isSpecified()); - PixelsAndPercent fromPixelsAndPercent = from.pixelsAndPercent(); - PixelsAndPercent toPixelsAndPercent = pixelsAndPercent(); - const float pixels = blink::blend(fromPixelsAndPercent.pixels, toPixelsAndPercent.pixels, progress); - const float percent = blink::blend(fromPixelsAndPercent.percent, toPixelsAndPercent.percent, progress); - return Length(CalculationValue::create(PixelsAndPercent(pixels, percent), range)); +Length Length::blendMixedTypes(const Length& from, + double progress, + ValueRange range) const { + ASSERT(from.isSpecified()); + ASSERT(isSpecified()); + PixelsAndPercent fromPixelsAndPercent = from.pixelsAndPercent(); + PixelsAndPercent toPixelsAndPercent = pixelsAndPercent(); + const float pixels = blink::blend(fromPixelsAndPercent.pixels, + toPixelsAndPercent.pixels, progress); + const float percent = blink::blend(fromPixelsAndPercent.percent, + toPixelsAndPercent.percent, progress); + return Length( + CalculationValue::create(PixelsAndPercent(pixels, percent), range)); } -PixelsAndPercent Length::pixelsAndPercent() const -{ - switch (type()) { +PixelsAndPercent Length::pixelsAndPercent() const { + switch (type()) { case Fixed: - return PixelsAndPercent(value(), 0); + return PixelsAndPercent(value(), 0); case Percent: - return PixelsAndPercent(0, value()); + return PixelsAndPercent(0, value()); case Calculated: - return calculationValue().pixelsAndPercent(); + return calculationValue().pixelsAndPercent(); default: - ASSERT_NOT_REACHED(); - return PixelsAndPercent(0, 0); - } + ASSERT_NOT_REACHED(); + return PixelsAndPercent(0, 0); + } } -Length Length::subtractFromOneHundredPercent() const -{ - PixelsAndPercent result = pixelsAndPercent(); - result.pixels = -result.pixels; - result.percent = 100 - result.percent; - if (result.pixels && result.percent) - return Length(CalculationValue::create(result, ValueRangeAll)); - if (result.percent) - return Length(result.percent, Percent); - return Length(result.pixels, Fixed); +Length Length::subtractFromOneHundredPercent() const { + PixelsAndPercent result = pixelsAndPercent(); + result.pixels = -result.pixels; + result.percent = 100 - result.percent; + if (result.pixels && result.percent) + return Length(CalculationValue::create(result, ValueRangeAll)); + if (result.percent) + return Length(result.percent, Percent); + return Length(result.pixels, Fixed); } -CalculationValue& Length::calculationValue() const -{ - ASSERT(isCalculated()); - return calcHandles().get(calculationHandle()); +CalculationValue& Length::calculationValue() const { + ASSERT(isCalculated()); + return calcHandles().get(calculationHandle()); } -void Length::incrementCalculatedRef() const -{ - ASSERT(isCalculated()); - calculationValue().ref(); +void Length::incrementCalculatedRef() const { + ASSERT(isCalculated()); + calculationValue().ref(); } -void Length::decrementCalculatedRef() const -{ - ASSERT(isCalculated()); - calcHandles().decrementRef(calculationHandle()); +void Length::decrementCalculatedRef() const { + ASSERT(isCalculated()); + calcHandles().decrementRef(calculationHandle()); } -float Length::nonNanCalculatedValue(int maxValue) const -{ - ASSERT(isCalculated()); - float result = calculationValue().evaluate(maxValue); - if (std::isnan(result)) - return 0; - return result; +float Length::nonNanCalculatedValue(int maxValue) const { + ASSERT(isCalculated()); + float result = calculationValue().evaluate(maxValue); + if (std::isnan(result)) + return 0; + return result; } -bool Length::isCalculatedEqual(const Length& o) const -{ - return isCalculated() && (&calculationValue() == &o.calculationValue() || calculationValue() == o.calculationValue()); +bool Length::isCalculatedEqual(const Length& o) const { + return isCalculated() && (&calculationValue() == &o.calculationValue() || + calculationValue() == o.calculationValue()); } struct SameSizeAsLength { - int32_t value; - int32_t metaData; + int32_t value; + int32_t metaData; }; -COMPILE_ASSERT(sizeof(Length) == sizeof(SameSizeAsLength), length_should_stay_small); +COMPILE_ASSERT(sizeof(Length) == sizeof(SameSizeAsLength), + length_should_stay_small); -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/Length.h b/sky/engine/platform/Length.h index 9cd4fc674a7fa..f5f27d75472e9 100644 --- a/sky/engine/platform/Length.h +++ b/sky/engine/platform/Length.h @@ -38,316 +38,295 @@ namespace blink { // FIXME: This enum makes it hard to tell in general what values may be // appropriate for any given Length. enum LengthType { - Auto, Percent, Fixed, - Intrinsic, MinIntrinsic, - MinContent, MaxContent, FillAvailable, FitContent, - Calculated, - DeviceWidth, DeviceHeight, - MaxSizeNone + Auto, + Percent, + Fixed, + Intrinsic, + MinIntrinsic, + MinContent, + MaxContent, + FillAvailable, + FitContent, + Calculated, + DeviceWidth, + DeviceHeight, + MaxSizeNone }; -enum ValueRange { - ValueRangeAll, - ValueRangeNonNegative -}; +enum ValueRange { ValueRangeAll, ValueRangeNonNegative }; struct PixelsAndPercent { - PixelsAndPercent(float pixels, float percent) - : pixels(pixels) - , percent(percent) - { - } - float pixels; - float percent; + PixelsAndPercent(float pixels, float percent) + : pixels(pixels), percent(percent) {} + float pixels; + float percent; }; class CalculationValue; class PLATFORM_EXPORT Length { - WTF_MAKE_FAST_ALLOCATED; -public: - Length() - : m_intValue(0), m_quirk(false), m_type(Auto), m_isFloat(false) - { - } - - Length(LengthType t) - : m_intValue(0), m_quirk(false), m_type(t), m_isFloat(false) - { - ASSERT(t != Calculated); - } - - Length(int v, LengthType t, bool q = false) - : m_intValue(v), m_quirk(q), m_type(t), m_isFloat(false) - { - ASSERT(t != Calculated); - } - - Length(LayoutUnit v, LengthType t, bool q = false) - : m_floatValue(v.toFloat()), m_quirk(q), m_type(t), m_isFloat(true) - { - ASSERT(t != Calculated); - } - - Length(float v, LengthType t, bool q = false) - : m_floatValue(v), m_quirk(q), m_type(t), m_isFloat(true) - { - ASSERT(t != Calculated); - } - - Length(double v, LengthType t, bool q = false) - : m_quirk(q), m_type(t), m_isFloat(true) - { - m_floatValue = static_cast(v); - } - - explicit Length(PassRefPtr); - - Length(const Length& length) - { - memcpy(this, &length, sizeof(Length)); - if (isCalculated()) - incrementCalculatedRef(); - } - - Length& operator=(const Length& length) - { - if (length.isCalculated()) - length.incrementCalculatedRef(); - if (isCalculated()) - decrementCalculatedRef(); - memcpy(this, &length, sizeof(Length)); - return *this; - } - - Length(Length&& length) - { - memcpy(this, &length, sizeof(Length)); - - // Reset |length|'s type to Auto to make sure its destructor - // won't call decrementCalculatedRef() as we don't call - // incrementCalculatedRef() here. - length.m_type = Auto; - } - - Length& operator=(Length&& length) - { - if (this == &length) - return *this; - - if (isCalculated()) - decrementCalculatedRef(); - - memcpy(this, &length, sizeof(Length)); - - // Reset |length|'s type to Auto to make sure its destructor - // won't call decrementCalculatedRef() as we don't call - // incrementCalculatedRef() here. - length.m_type = Auto; - - return *this; - } - - ~Length() - { - if (isCalculated()) - decrementCalculatedRef(); - } - - bool operator==(const Length& o) const { return (m_type == o.m_type) && (m_quirk == o.m_quirk) && (isMaxSizeNone() || (getFloatValue() == o.getFloatValue()) || isCalculatedEqual(o)); } - bool operator!=(const Length& o) const { return !(*this == o); } - - const Length& operator*=(float v) - { - if (isCalculated()) { - ASSERT_NOT_REACHED(); - return *this; - } - - if (m_isFloat) - m_floatValue = static_cast(m_floatValue * v); - else - m_intValue = static_cast(m_intValue * v); - - return *this; + WTF_MAKE_FAST_ALLOCATED; + + public: + Length() : m_intValue(0), m_quirk(false), m_type(Auto), m_isFloat(false) {} + + Length(LengthType t) + : m_intValue(0), m_quirk(false), m_type(t), m_isFloat(false) { + ASSERT(t != Calculated); + } + + Length(int v, LengthType t, bool q = false) + : m_intValue(v), m_quirk(q), m_type(t), m_isFloat(false) { + ASSERT(t != Calculated); + } + + Length(LayoutUnit v, LengthType t, bool q = false) + : m_floatValue(v.toFloat()), m_quirk(q), m_type(t), m_isFloat(true) { + ASSERT(t != Calculated); + } + + Length(float v, LengthType t, bool q = false) + : m_floatValue(v), m_quirk(q), m_type(t), m_isFloat(true) { + ASSERT(t != Calculated); + } + + Length(double v, LengthType t, bool q = false) + : m_quirk(q), m_type(t), m_isFloat(true) { + m_floatValue = static_cast(v); + } + + explicit Length(PassRefPtr); + + Length(const Length& length) { + memcpy(this, &length, sizeof(Length)); + if (isCalculated()) + incrementCalculatedRef(); + } + + Length& operator=(const Length& length) { + if (length.isCalculated()) + length.incrementCalculatedRef(); + if (isCalculated()) + decrementCalculatedRef(); + memcpy(this, &length, sizeof(Length)); + return *this; + } + + Length(Length&& length) { + memcpy(this, &length, sizeof(Length)); + + // Reset |length|'s type to Auto to make sure its destructor + // won't call decrementCalculatedRef() as we don't call + // incrementCalculatedRef() here. + length.m_type = Auto; + } + + Length& operator=(Length&& length) { + if (this == &length) + return *this; + + if (isCalculated()) + decrementCalculatedRef(); + + memcpy(this, &length, sizeof(Length)); + + // Reset |length|'s type to Auto to make sure its destructor + // won't call decrementCalculatedRef() as we don't call + // incrementCalculatedRef() here. + length.m_type = Auto; + + return *this; + } + + ~Length() { + if (isCalculated()) + decrementCalculatedRef(); + } + + bool operator==(const Length& o) const { + return (m_type == o.m_type) && (m_quirk == o.m_quirk) && + (isMaxSizeNone() || (getFloatValue() == o.getFloatValue()) || + isCalculatedEqual(o)); + } + bool operator!=(const Length& o) const { return !(*this == o); } + + const Length& operator*=(float v) { + if (isCalculated()) { + ASSERT_NOT_REACHED(); + return *this; } - inline float value() const - { - return getFloatValue(); - } - - int intValue() const - { - if (isCalculated()) { - ASSERT_NOT_REACHED(); - return 0; - } - return getIntValue(); - } + if (m_isFloat) + m_floatValue = static_cast(m_floatValue * v); + else + m_intValue = static_cast(m_intValue * v); - float percent() const - { - ASSERT(type() == Percent); - return getFloatValue(); - } - PixelsAndPercent pixelsAndPercent() const; + return *this; + } - CalculationValue& calculationValue() const; + inline float value() const { return getFloatValue(); } - LengthType type() const { return static_cast(m_type); } - bool quirk() const { return m_quirk; } - - void setQuirk(bool quirk) - { - m_quirk = quirk; + int intValue() const { + if (isCalculated()) { + ASSERT_NOT_REACHED(); + return 0; } + return getIntValue(); + } - void setValue(LengthType t, int value) - { - m_type = t; - m_intValue = value; - m_isFloat = false; - } + float percent() const { + ASSERT(type() == Percent); + return getFloatValue(); + } + PixelsAndPercent pixelsAndPercent() const; - void setValue(int value) - { - if (isCalculated()) { - ASSERT_NOT_REACHED(); - return; - } - setValue(Fixed, value); - } + CalculationValue& calculationValue() const; - void setValue(LengthType t, float value) - { - m_type = t; - m_floatValue = value; - m_isFloat = true; - } + LengthType type() const { return static_cast(m_type); } + bool quirk() const { return m_quirk; } - void setValue(LengthType t, LayoutUnit value) - { - m_type = t; - m_floatValue = value.toFloat(); - m_isFloat = true; - } - - void setValue(float value) - { - *this = Length(value, Fixed); - } - - bool isMaxSizeNone() const { return type() == MaxSizeNone; } - - // FIXME calc: https://bugs.webkit.org/show_bug.cgi?id=80357. A calculated Length - // always contains a percentage, and without a maxValue passed to these functions - // it's impossible to determine the sign or zero-ness. We assume all calc values - // are positive and non-zero for now. - bool isZero() const - { - ASSERT(!isMaxSizeNone()); - if (isCalculated()) - return false; - - return m_isFloat ? !m_floatValue : !m_intValue; - } - bool isPositive() const - { - if (isMaxSizeNone()) - return false; - if (isCalculated()) - return true; - - return getFloatValue() > 0; - } - bool isNegative() const - { - if (isMaxSizeNone() || isCalculated()) - return false; - - return getFloatValue() < 0; - } - - bool isAuto() const { return type() == Auto; } - bool isPercent() const { return type() == Percent || type() == Calculated; } - bool isFixed() const { return type() == Fixed; } - bool isIntrinsicOrAuto() const { return type() == Auto || isLegacyIntrinsic() || isIntrinsic(); } - bool isLegacyIntrinsic() const { return type() == Intrinsic || type() == MinIntrinsic; } - bool isIntrinsic() const { return type() == MinContent || type() == MaxContent || type() == FillAvailable || type() == FitContent; } - bool isSpecified() const { return type() == Fixed || type() == Percent || type() == Calculated; } - bool isSpecifiedOrIntrinsic() const { return isSpecified() || isIntrinsic(); } - bool isCalculated() const { return type() == Calculated; } - bool isCalculatedEqual(const Length&) const; - bool isMinContent() const { return type() == MinContent; } - bool isMaxContent() const { return type() == MaxContent; } - bool isFillAvailable() const { return type() == FillAvailable; } - bool isFitContent() const { return type() == FitContent; } - - Length blend(const Length& from, double progress, ValueRange range) const - { - ASSERT(isSpecified() && from.isSpecified()); - - if (progress == 0.0) - return from; - - if (progress == 1.0) - return *this; - - if (from.type() == Calculated || type() == Calculated) - return blendMixedTypes(from, progress, range); - - if (!from.isZero() && !isZero() && from.type() != type()) - return blendMixedTypes(from, progress, range); - - if (from.isZero() && isZero()) - return *this; - - LengthType resultType = type(); - if (isZero()) - resultType = from.type(); - - float blendedValue = blink::blend(from.value(), value(), progress); - if (range == ValueRangeNonNegative) - blendedValue = clampTo(blendedValue, 0); - return Length(blendedValue, resultType); - } - - float getFloatValue() const - { - ASSERT(!isMaxSizeNone()); - return m_isFloat ? m_floatValue : m_intValue; - } - float nonNanCalculatedValue(int maxValue) const; - - Length subtractFromOneHundredPercent() const; - -private: - int getIntValue() const - { - ASSERT(!isMaxSizeNone()); - return m_isFloat ? static_cast(m_floatValue) : m_intValue; - } + void setQuirk(bool quirk) { m_quirk = quirk; } - Length blendMixedTypes(const Length& from, double progress, ValueRange) const; + void setValue(LengthType t, int value) { + m_type = t; + m_intValue = value; + m_isFloat = false; + } - int calculationHandle() const - { - ASSERT(isCalculated()); - return getIntValue(); + void setValue(int value) { + if (isCalculated()) { + ASSERT_NOT_REACHED(); + return; } - void incrementCalculatedRef() const; - void decrementCalculatedRef() const; - - union { - int m_intValue; - float m_floatValue; - }; - bool m_quirk; - unsigned char m_type; - bool m_isFloat; + setValue(Fixed, value); + } + + void setValue(LengthType t, float value) { + m_type = t; + m_floatValue = value; + m_isFloat = true; + } + + void setValue(LengthType t, LayoutUnit value) { + m_type = t; + m_floatValue = value.toFloat(); + m_isFloat = true; + } + + void setValue(float value) { *this = Length(value, Fixed); } + + bool isMaxSizeNone() const { return type() == MaxSizeNone; } + + // FIXME calc: https://bugs.webkit.org/show_bug.cgi?id=80357. A calculated + // Length always contains a percentage, and without a maxValue passed to these + // functions it's impossible to determine the sign or zero-ness. We assume all + // calc values are positive and non-zero for now. + bool isZero() const { + ASSERT(!isMaxSizeNone()); + if (isCalculated()) + return false; + + return m_isFloat ? !m_floatValue : !m_intValue; + } + bool isPositive() const { + if (isMaxSizeNone()) + return false; + if (isCalculated()) + return true; + + return getFloatValue() > 0; + } + bool isNegative() const { + if (isMaxSizeNone() || isCalculated()) + return false; + + return getFloatValue() < 0; + } + + bool isAuto() const { return type() == Auto; } + bool isPercent() const { return type() == Percent || type() == Calculated; } + bool isFixed() const { return type() == Fixed; } + bool isIntrinsicOrAuto() const { + return type() == Auto || isLegacyIntrinsic() || isIntrinsic(); + } + bool isLegacyIntrinsic() const { + return type() == Intrinsic || type() == MinIntrinsic; + } + bool isIntrinsic() const { + return type() == MinContent || type() == MaxContent || + type() == FillAvailable || type() == FitContent; + } + bool isSpecified() const { + return type() == Fixed || type() == Percent || type() == Calculated; + } + bool isSpecifiedOrIntrinsic() const { return isSpecified() || isIntrinsic(); } + bool isCalculated() const { return type() == Calculated; } + bool isCalculatedEqual(const Length&) const; + bool isMinContent() const { return type() == MinContent; } + bool isMaxContent() const { return type() == MaxContent; } + bool isFillAvailable() const { return type() == FillAvailable; } + bool isFitContent() const { return type() == FitContent; } + + Length blend(const Length& from, double progress, ValueRange range) const { + ASSERT(isSpecified() && from.isSpecified()); + + if (progress == 0.0) + return from; + + if (progress == 1.0) + return *this; + + if (from.type() == Calculated || type() == Calculated) + return blendMixedTypes(from, progress, range); + + if (!from.isZero() && !isZero() && from.type() != type()) + return blendMixedTypes(from, progress, range); + + if (from.isZero() && isZero()) + return *this; + + LengthType resultType = type(); + if (isZero()) + resultType = from.type(); + + float blendedValue = blink::blend(from.value(), value(), progress); + if (range == ValueRangeNonNegative) + blendedValue = clampTo(blendedValue, 0); + return Length(blendedValue, resultType); + } + + float getFloatValue() const { + ASSERT(!isMaxSizeNone()); + return m_isFloat ? m_floatValue : m_intValue; + } + float nonNanCalculatedValue(int maxValue) const; + + Length subtractFromOneHundredPercent() const; + + private: + int getIntValue() const { + ASSERT(!isMaxSizeNone()); + return m_isFloat ? static_cast(m_floatValue) : m_intValue; + } + + Length blendMixedTypes(const Length& from, double progress, ValueRange) const; + + int calculationHandle() const { + ASSERT(isCalculated()); + return getIntValue(); + } + void incrementCalculatedRef() const; + void decrementCalculatedRef() const; + + union { + int m_intValue; + float m_floatValue; + }; + bool m_quirk; + unsigned char m_type; + bool m_isFloat; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_LENGTH_H_ diff --git a/sky/engine/platform/LengthBox.cpp b/sky/engine/platform/LengthBox.cpp index e3362b3cb5df9..7d34d952b9570 100644 --- a/sky/engine/platform/LengthBox.cpp +++ b/sky/engine/platform/LengthBox.cpp @@ -32,36 +32,30 @@ namespace blink { -const Length& LengthBox::logicalLeft() const -{ - return m_left; +const Length& LengthBox::logicalLeft() const { + return m_left; } -const Length& LengthBox::logicalRight() const -{ - return m_right; +const Length& LengthBox::logicalRight() const { + return m_right; } -const Length& LengthBox::before() const -{ - // FIXME(sky): Remove - return m_top; +const Length& LengthBox::before() const { + // FIXME(sky): Remove + return m_top; } -const Length& LengthBox::after() const -{ - // FIXME(sky): Remove - return m_bottom; +const Length& LengthBox::after() const { + // FIXME(sky): Remove + return m_bottom; } -const Length& LengthBox::start(TextDirection direction) const -{ - return isLeftToRightDirection(direction) ? m_left : m_right; +const Length& LengthBox::start(TextDirection direction) const { + return isLeftToRightDirection(direction) ? m_left : m_right; } -const Length& LengthBox::end(TextDirection direction) const -{ - return isLeftToRightDirection(direction) ? m_right : m_left; +const Length& LengthBox::end(TextDirection direction) const { + return isLeftToRightDirection(direction) ? m_right : m_left; } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/LengthBox.h b/sky/engine/platform/LengthBox.h index 2296e5613f916..148b1bd19b3be 100644 --- a/sky/engine/platform/LengthBox.h +++ b/sky/engine/platform/LengthBox.h @@ -29,78 +29,58 @@ namespace blink { class PLATFORM_EXPORT LengthBox { -public: - LengthBox() - { - } - - LengthBox(LengthType t) - : m_left(t) - , m_right(t) - , m_top(t) - , m_bottom(t) - { - } - - LengthBox(int v) - : m_left(Length(v, Fixed)) - , m_right(Length(v, Fixed)) - , m_top(Length(v, Fixed)) - , m_bottom(Length(v, Fixed)) - { - } - - LengthBox(const Length& t, const Length& r, const Length& b, const Length& l) - : m_left(l) - , m_right(r) - , m_top(t) - , m_bottom(b) - { - } - - LengthBox(int t, int r, int b, int l) - : m_left(Length(l, Fixed)) - , m_right(Length(r, Fixed)) - , m_top(Length(t, Fixed)) - , m_bottom(Length(b, Fixed)) - { - } - - const Length& left() const { return m_left; } - const Length& right() const { return m_right; } - const Length& top() const { return m_top; } - const Length& bottom() const { return m_bottom; } - - const Length& logicalLeft() const; - const Length& logicalRight() const; - - const Length& before() const; - const Length& after() const; - const Length& start(TextDirection) const; - const Length& end(TextDirection) const; - - bool operator==(const LengthBox& o) const - { - return m_left == o.m_left && m_right == o.m_right && m_top == o.m_top && m_bottom == o.m_bottom; - } - - bool operator!=(const LengthBox& o) const - { - return !(*this == o); - } - - bool nonZero() const - { - return !(m_left.isZero() && m_right.isZero() && m_top.isZero() && m_bottom.isZero()); - } - - // Must be public for SET_VAR in RenderStyle.h - Length m_left; - Length m_right; - Length m_top; - Length m_bottom; + public: + LengthBox() {} + + LengthBox(LengthType t) : m_left(t), m_right(t), m_top(t), m_bottom(t) {} + + LengthBox(int v) + : m_left(Length(v, Fixed)), + m_right(Length(v, Fixed)), + m_top(Length(v, Fixed)), + m_bottom(Length(v, Fixed)) {} + + LengthBox(const Length& t, const Length& r, const Length& b, const Length& l) + : m_left(l), m_right(r), m_top(t), m_bottom(b) {} + + LengthBox(int t, int r, int b, int l) + : m_left(Length(l, Fixed)), + m_right(Length(r, Fixed)), + m_top(Length(t, Fixed)), + m_bottom(Length(b, Fixed)) {} + + const Length& left() const { return m_left; } + const Length& right() const { return m_right; } + const Length& top() const { return m_top; } + const Length& bottom() const { return m_bottom; } + + const Length& logicalLeft() const; + const Length& logicalRight() const; + + const Length& before() const; + const Length& after() const; + const Length& start(TextDirection) const; + const Length& end(TextDirection) const; + + bool operator==(const LengthBox& o) const { + return m_left == o.m_left && m_right == o.m_right && m_top == o.m_top && + m_bottom == o.m_bottom; + } + + bool operator!=(const LengthBox& o) const { return !(*this == o); } + + bool nonZero() const { + return !(m_left.isZero() && m_right.isZero() && m_top.isZero() && + m_bottom.isZero()); + } + + // Must be public for SET_VAR in RenderStyle.h + Length m_left; + Length m_right; + Length m_top; + Length m_bottom; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_LENGTHBOX_H_ diff --git a/sky/engine/platform/LengthFunctions.cpp b/sky/engine/platform/LengthFunctions.cpp index 91cb98e69e064..bd294cfe8981d 100644 --- a/sky/engine/platform/LengthFunctions.cpp +++ b/sky/engine/platform/LengthFunctions.cpp @@ -28,23 +28,21 @@ namespace blink { -int intValueForLength(const Length& length, LayoutUnit maximumValue) -{ - return static_cast(valueForLength(length, maximumValue)); +int intValueForLength(const Length& length, LayoutUnit maximumValue) { + return static_cast(valueForLength(length, maximumValue)); } -float floatValueForLength(const Length& length, float maximumValue) -{ - switch (length.type()) { +float floatValueForLength(const Length& length, float maximumValue) { + switch (length.type()) { case Fixed: - return length.getFloatValue(); + return length.getFloatValue(); case Percent: - return static_cast(maximumValue * length.percent() / 100.0f); + return static_cast(maximumValue * length.percent() / 100.0f); case FillAvailable: case Auto: - return static_cast(maximumValue); + return static_cast(maximumValue); case Calculated: - return length.nonNanCalculatedValue(maximumValue); + return length.nonNanCalculatedValue(maximumValue); case Intrinsic: case MinIntrinsic: case MinContent: @@ -53,26 +51,27 @@ float floatValueForLength(const Length& length, float maximumValue) case DeviceWidth: case DeviceHeight: case MaxSizeNone: - ASSERT_NOT_REACHED(); - return 0; - } - ASSERT_NOT_REACHED(); - return 0; + ASSERT_NOT_REACHED(); + return 0; + } + ASSERT_NOT_REACHED(); + return 0; } -LayoutUnit minimumValueForLength(const Length& length, LayoutUnit maximumValue) -{ - switch (length.type()) { +LayoutUnit minimumValueForLength(const Length& length, + LayoutUnit maximumValue) { + switch (length.type()) { case Fixed: - return length.value(); + return length.value(); case Percent: - // Don't remove the extra cast to float. It is needed for rounding on 32-bit Intel machines that use the FPU stack. - return static_cast(maximumValue * length.percent() / 100.0f); + // Don't remove the extra cast to float. It is needed for rounding on + // 32-bit Intel machines that use the FPU stack. + return static_cast(maximumValue * length.percent() / 100.0f); case Calculated: - return length.nonNanCalculatedValue(maximumValue); + return length.nonNanCalculatedValue(maximumValue); case FillAvailable: case Auto: - return 0; + return 0; case Intrinsic: case MinIntrinsic: case MinContent: @@ -81,30 +80,30 @@ LayoutUnit minimumValueForLength(const Length& length, LayoutUnit maximumValue) case DeviceWidth: case DeviceHeight: case MaxSizeNone: - ASSERT_NOT_REACHED(); - return 0; - } - ASSERT_NOT_REACHED(); - return 0; + ASSERT_NOT_REACHED(); + return 0; + } + ASSERT_NOT_REACHED(); + return 0; } -LayoutUnit roundedMinimumValueForLength(const Length& length, LayoutUnit maximumValue) -{ - if (length.type() == Percent) - return static_cast(round(maximumValue * length.percent() / 100.0f)); - return minimumValueForLength(length, maximumValue); +LayoutUnit roundedMinimumValueForLength(const Length& length, + LayoutUnit maximumValue) { + if (length.type() == Percent) + return static_cast( + round(maximumValue * length.percent() / 100.0f)); + return minimumValueForLength(length, maximumValue); } -LayoutUnit valueForLength(const Length& length, LayoutUnit maximumValue) -{ - switch (length.type()) { +LayoutUnit valueForLength(const Length& length, LayoutUnit maximumValue) { + switch (length.type()) { case Fixed: case Percent: case Calculated: - return minimumValueForLength(length, maximumValue); + return minimumValueForLength(length, maximumValue); case FillAvailable: case Auto: - return maximumValue; + return maximumValue; case Intrinsic: case MinIntrinsic: case MinContent: @@ -113,16 +112,17 @@ LayoutUnit valueForLength(const Length& length, LayoutUnit maximumValue) case DeviceWidth: case DeviceHeight: case MaxSizeNone: - ASSERT_NOT_REACHED(); - return 0; - } - ASSERT_NOT_REACHED(); - return 0; + ASSERT_NOT_REACHED(); + return 0; + } + ASSERT_NOT_REACHED(); + return 0; } -FloatSize floatSizeForLengthSize(const LengthSize& lengthSize, const FloatSize& boxSize) -{ - return FloatSize(floatValueForLength(lengthSize.width(), boxSize.width()), floatValueForLength(lengthSize.height(), boxSize.height())); +FloatSize floatSizeForLengthSize(const LengthSize& lengthSize, + const FloatSize& boxSize) { + return FloatSize(floatValueForLength(lengthSize.width(), boxSize.width()), + floatValueForLength(lengthSize.height(), boxSize.height())); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/LengthFunctions.h b/sky/engine/platform/LengthFunctions.h index 4d2789456e5e9..1102447cd10cf 100644 --- a/sky/engine/platform/LengthFunctions.h +++ b/sky/engine/platform/LengthFunctions.h @@ -35,11 +35,15 @@ class LengthSize; PLATFORM_EXPORT int intValueForLength(const Length&, LayoutUnit maximumValue); PLATFORM_EXPORT float floatValueForLength(const Length&, float maximumValue); -PLATFORM_EXPORT LayoutUnit minimumValueForLength(const Length&, LayoutUnit maximumValue); -PLATFORM_EXPORT LayoutUnit roundedMinimumValueForLength(const Length&, LayoutUnit maximumValue); -PLATFORM_EXPORT LayoutUnit valueForLength(const Length&, LayoutUnit maximumValue); -PLATFORM_EXPORT FloatSize floatSizeForLengthSize(const LengthSize&, const FloatSize& boxSize); - -} // namespace blink +PLATFORM_EXPORT LayoutUnit minimumValueForLength(const Length&, + LayoutUnit maximumValue); +PLATFORM_EXPORT LayoutUnit +roundedMinimumValueForLength(const Length&, LayoutUnit maximumValue); +PLATFORM_EXPORT LayoutUnit valueForLength(const Length&, + LayoutUnit maximumValue); +PLATFORM_EXPORT FloatSize floatSizeForLengthSize(const LengthSize&, + const FloatSize& boxSize); + +} // namespace blink #endif // SKY_ENGINE_PLATFORM_LENGTHFUNCTIONS_H_ diff --git a/sky/engine/platform/LengthPoint.h b/sky/engine/platform/LengthPoint.h index 94680a6efec0b..aa1b80c5cd3f7 100644 --- a/sky/engine/platform/LengthPoint.h +++ b/sky/engine/platform/LengthPoint.h @@ -35,31 +35,29 @@ namespace blink { struct LengthPoint { -public: - LengthPoint() - { - } + public: + LengthPoint() {} - LengthPoint(const Length& x, const Length& y) - : m_x(x) - , m_y(y) - { - } + LengthPoint(const Length& x, const Length& y) : m_x(x), m_y(y) {} - bool operator==(const LengthPoint& o) const { return m_x == o.m_x && m_y == o.m_y; } - bool operator!=(const LengthPoint& o) const { return m_x != o.m_x || m_y != o.m_y; } + bool operator==(const LengthPoint& o) const { + return m_x == o.m_x && m_y == o.m_y; + } + bool operator!=(const LengthPoint& o) const { + return m_x != o.m_x || m_y != o.m_y; + } - void setX(const Length& x) { m_x = x; } - const Length& x() const { return m_x; } + void setX(const Length& x) { m_x = x; } + const Length& x() const { return m_x; } - void setY(const Length& y) { m_y = y; } - const Length& y() const { return m_y; } + void setY(const Length& y) { m_y = y; } + const Length& y() const { return m_y; } -private: - Length m_x; - Length m_y; + private: + Length m_x; + Length m_y; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_LENGTHPOINT_H_ diff --git a/sky/engine/platform/LengthSize.h b/sky/engine/platform/LengthSize.h index c81e32a41c724..036a2b5189374 100644 --- a/sky/engine/platform/LengthSize.h +++ b/sky/engine/platform/LengthSize.h @@ -26,33 +26,27 @@ namespace blink { class LengthSize { -public: - LengthSize() - { - } - - LengthSize(const Length& width, const Length& height) - : m_width(width) - , m_height(height) - { - } - - bool operator==(const LengthSize& o) const - { - return m_width == o.m_width && m_height == o.m_height; - } - - void setWidth(const Length& width) { m_width = width; } - const Length& width() const { return m_width; } - - void setHeight(const Length& height) { m_height = height; } - const Length& height() const { return m_height; } - -private: - Length m_width; - Length m_height; + public: + LengthSize() {} + + LengthSize(const Length& width, const Length& height) + : m_width(width), m_height(height) {} + + bool operator==(const LengthSize& o) const { + return m_width == o.m_width && m_height == o.m_height; + } + + void setWidth(const Length& width) { m_width = width; } + const Length& width() const { return m_width; } + + void setHeight(const Length& height) { m_height = height; } + const Length& height() const { return m_height; } + + private: + Length m_width; + Length m_height; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_LENGTHSIZE_H_ diff --git a/sky/engine/platform/Partitions.cpp b/sky/engine/platform/Partitions.cpp index 551b057ba5aa4..0fff7345b6b1c 100644 --- a/sky/engine/platform/Partitions.cpp +++ b/sky/engine/platform/Partitions.cpp @@ -35,19 +35,17 @@ namespace blink { SizeSpecificPartitionAllocator<3072> Partitions::m_objectModelAllocator; SizeSpecificPartitionAllocator<1024> Partitions::m_renderingAllocator; -void Partitions::init() -{ - m_objectModelAllocator.init(); - m_renderingAllocator.init(); +void Partitions::init() { + m_objectModelAllocator.init(); + m_renderingAllocator.init(); } -void Partitions::shutdown() -{ - // We could ASSERT here for a memory leak within the partition, but it leads - // to very hard to diagnose ASSERTs, so it's best to leave leak checking for - // the valgrind and heapcheck bots, which run without partitions. - (void) m_renderingAllocator.shutdown(); - (void) m_objectModelAllocator.shutdown(); +void Partitions::shutdown() { + // We could ASSERT here for a memory leak within the partition, but it leads + // to very hard to diagnose ASSERTs, so it's best to leave leak checking for + // the valgrind and heapcheck bots, which run without partitions. + (void)m_renderingAllocator.shutdown(); + (void)m_objectModelAllocator.shutdown(); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/Partitions.h b/sky/engine/platform/Partitions.h index db83050865706..0b8955870a88b 100644 --- a/sky/engine/platform/Partitions.h +++ b/sky/engine/platform/Partitions.h @@ -37,23 +37,26 @@ namespace blink { class PLATFORM_EXPORT Partitions { -public: - static void init(); - static void shutdown(); - - ALWAYS_INLINE static PartitionRoot* getObjectModelPartition() { return m_objectModelAllocator.root(); } - ALWAYS_INLINE static PartitionRoot* getRenderingPartition() { return m_renderingAllocator.root(); } - - static size_t currentDOMMemoryUsage() - { - return m_objectModelAllocator.root()->totalSizeOfCommittedPages; - } - -private: - static SizeSpecificPartitionAllocator<3072> m_objectModelAllocator; - static SizeSpecificPartitionAllocator<1024> m_renderingAllocator; + public: + static void init(); + static void shutdown(); + + ALWAYS_INLINE static PartitionRoot* getObjectModelPartition() { + return m_objectModelAllocator.root(); + } + ALWAYS_INLINE static PartitionRoot* getRenderingPartition() { + return m_renderingAllocator.root(); + } + + static size_t currentDOMMemoryUsage() { + return m_objectModelAllocator.root()->totalSizeOfCommittedPages; + } + + private: + static SizeSpecificPartitionAllocator<3072> m_objectModelAllocator; + static SizeSpecificPartitionAllocator<1024> m_renderingAllocator; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_PARTITIONS_H_ diff --git a/sky/engine/platform/PlatformExport.h b/sky/engine/platform/PlatformExport.h index 25f2acef03071..07831305f57fa 100644 --- a/sky/engine/platform/PlatformExport.h +++ b/sky/engine/platform/PlatformExport.h @@ -28,7 +28,6 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #ifndef SKY_ENGINE_PLATFORM_PLATFORMEXPORT_H_ #define SKY_ENGINE_PLATFORM_PLATFORMEXPORT_H_ @@ -38,7 +37,7 @@ #if defined(COMPONENT_BUILD) #define PLATFORM_EXPORT __attribute__((visibility("default"))) -#else // defined(COMPONENT_BUILD) +#else // defined(COMPONENT_BUILD) #define PLATFORM_EXPORT #endif diff --git a/sky/engine/platform/PurgeableVector.cpp b/sky/engine/platform/PurgeableVector.cpp index 53c1d5f6edf72..b0b5b059eb089 100644 --- a/sky/engine/platform/PurgeableVector.cpp +++ b/sky/engine/platform/PurgeableVector.cpp @@ -46,216 +46,208 @@ namespace blink { static const size_t minimumDiscardableAllocationSize = 4 * 4096; PurgeableVector::PurgeableVector(PurgeableOption purgeable) - : m_discardableCapacity(0) - , m_discardableSize(0) - , m_isPurgeable(purgeable == Purgeable) - , m_locksCount(1) // The buffer is locked at creation. -{ + : m_discardableCapacity(0), + m_discardableSize(0), + m_isPurgeable(purgeable == Purgeable), + m_locksCount(1) // The buffer is locked at creation. +{} + +PurgeableVector::~PurgeableVector() {} + +void PurgeableVector::reserveCapacity(size_t capacity) { + ASSERT(isLocked()); + + if (m_isPurgeable) { + if (reservePurgeableCapacity(capacity, UseExactCapacity)) + return; + // Fallback to non-purgeable buffer allocation in case discardable memory + // allocation failed. + } + + if (!m_vector.capacity()) { + // Using reserveInitialCapacity() on the underlying vector ensures that the + // vector uses the exact specified capacity to avoid consuming too much + // memory for small resources. + m_vector.reserveInitialCapacity(capacity); + } else { + m_vector.reserveCapacity(capacity); + } + + moveDataFromDiscardableToVector(); +} + +void PurgeableVector::moveDataFromDiscardableToVector() { + if (m_discardable) { + m_vector.append(static_cast(m_discardable->data()), + m_discardableSize); + clearDiscardable(); + } } -PurgeableVector::~PurgeableVector() -{ +void PurgeableVector::clearDiscardable() { + m_discardable.clear(); + m_discardableCapacity = 0; + m_discardableSize = 0; } -void PurgeableVector::reserveCapacity(size_t capacity) -{ - ASSERT(isLocked()); +void PurgeableVector::append(const char* data, size_t length) { + ASSERT(isLocked()); - if (m_isPurgeable) { - if (reservePurgeableCapacity(capacity, UseExactCapacity)) - return; - // Fallback to non-purgeable buffer allocation in case discardable memory allocation failed. - } + if (!m_isPurgeable) { + m_vector.append(data, length); + return; + } - if (!m_vector.capacity()) { - // Using reserveInitialCapacity() on the underlying vector ensures that the vector uses the - // exact specified capacity to avoid consuming too much memory for small resources. - m_vector.reserveInitialCapacity(capacity); - } else { - m_vector.reserveCapacity(capacity); - } + const size_t currentSize = + m_discardable ? m_discardableSize : m_vector.size(); + const size_t newBufferSize = currentSize + length; + if (!reservePurgeableCapacity(newBufferSize, UseExponentialGrowth)) { moveDataFromDiscardableToVector(); -} + m_vector.append(data, length); + return; + } -void PurgeableVector::moveDataFromDiscardableToVector() -{ - if (m_discardable) { - m_vector.append(static_cast(m_discardable->data()), m_discardableSize); - clearDiscardable(); - } + ASSERT(m_discardableSize + length <= m_discardableCapacity); + memcpy(static_cast(m_discardable->data()) + m_discardableSize, data, + length); + m_discardableSize += length; } -void PurgeableVector::clearDiscardable() -{ - m_discardable.clear(); - m_discardableCapacity = 0; - m_discardableSize = 0; -} - -void PurgeableVector::append(const char* data, size_t length) -{ - ASSERT(isLocked()); +void PurgeableVector::grow(size_t newSize) { + ASSERT(newSize >= size()); - if (!m_isPurgeable) { - m_vector.append(data, length); - return; - } - - const size_t currentSize = m_discardable ? m_discardableSize : m_vector.size(); - const size_t newBufferSize = currentSize + length; - - if (!reservePurgeableCapacity(newBufferSize, UseExponentialGrowth)) { - moveDataFromDiscardableToVector(); - m_vector.append(data, length); - return; + if (m_isPurgeable) { + if (reservePurgeableCapacity(newSize, UseExponentialGrowth)) { + m_discardableSize = newSize; + return; } + moveDataFromDiscardableToVector(); + } - ASSERT(m_discardableSize + length <= m_discardableCapacity); - memcpy(static_cast(m_discardable->data()) + m_discardableSize, data, length); - m_discardableSize += length; + m_vector.resize(newSize); } -void PurgeableVector::grow(size_t newSize) -{ - ASSERT(newSize >= size()); - - if (m_isPurgeable) { - if (reservePurgeableCapacity(newSize, UseExponentialGrowth)) { - m_discardableSize = newSize; - return; - } - moveDataFromDiscardableToVector(); - } - - m_vector.resize(newSize); +void PurgeableVector::clear() { + clearDiscardable(); + m_vector.clear(); } -void PurgeableVector::clear() -{ - clearDiscardable(); - m_vector.clear(); +char* PurgeableVector::data() { + ASSERT(isLocked()); + return m_discardable ? static_cast(m_discardable->data()) + : m_vector.data(); } -char* PurgeableVector::data() -{ - ASSERT(isLocked()); - return m_discardable ? static_cast(m_discardable->data()) : m_vector.data(); +size_t PurgeableVector::size() const { + return m_discardable ? m_discardableSize : m_vector.size(); } -size_t PurgeableVector::size() const -{ - return m_discardable ? m_discardableSize : m_vector.size(); -} +void PurgeableVector::adopt(Vector& other) { + if (size() > 0) + clear(); -void PurgeableVector::adopt(Vector& other) -{ - if (size() > 0) - clear(); + if (!m_isPurgeable) { + m_vector.swap(other); + return; + } - if (!m_isPurgeable) { - m_vector.swap(other); - return; - } - - if (other.isEmpty()) - return; + if (other.isEmpty()) + return; - append(other.data(), other.size()); - other.clear(); + append(other.data(), other.size()); + other.clear(); } -bool PurgeableVector::lock() -{ - ++m_locksCount; - if (m_locksCount > 1) - return true; +bool PurgeableVector::lock() { + ++m_locksCount; + if (m_locksCount > 1) + return true; - ASSERT(m_locksCount == 1); - if (!m_discardable) - return true; + ASSERT(m_locksCount == 1); + if (!m_discardable) + return true; - return m_discardable->lock(); + return m_discardable->lock(); } -void PurgeableVector::unlock() -{ - ASSERT(isLocked()); - --m_locksCount; - if (m_locksCount > 0) - return; - - if (!m_vector.isEmpty()) { - ASSERT(!m_discardable); - m_isPurgeable = true; - if (!reservePurgeableCapacity(m_vector.size(), UseExactCapacity)) - return; - } +void PurgeableVector::unlock() { + ASSERT(isLocked()); + --m_locksCount; + if (m_locksCount > 0) + return; - if (m_discardable) - m_discardable->unlock(); -} + if (!m_vector.isEmpty()) { + ASSERT(!m_discardable); + m_isPurgeable = true; + if (!reservePurgeableCapacity(m_vector.size(), UseExactCapacity)) + return; + } -bool PurgeableVector::isLocked() const -{ - ASSERT(m_locksCount >= 0); - return m_locksCount > 0; + if (m_discardable) + m_discardable->unlock(); } -bool PurgeableVector::reservePurgeableCapacity(size_t capacity, PurgeableAllocationStrategy allocationStrategy) -{ - ASSERT(m_isPurgeable); - - if (m_discardable && m_discardableCapacity >= capacity) { - ASSERT(!m_vector.capacity()); - return true; - } - - if (capacity < minimumDiscardableAllocationSize) - return false; - - if (allocationStrategy == UseExponentialGrowth) - capacity = adjustPurgeableCapacity(capacity); - - OwnPtr discardable = adoptPtr( - blink::Platform::current()->allocateAndLockDiscardableMemory(capacity)); - if (!discardable) { - // Discardable memory is not supported. - m_isPurgeable = false; - return false; - } +bool PurgeableVector::isLocked() const { + ASSERT(m_locksCount >= 0); + return m_locksCount > 0; +} - m_discardableCapacity = capacity; - // Copy the data that was either in the previous purgeable buffer or in the vector to the new - // purgeable buffer. - if (m_discardable) { - memcpy(discardable->data(), m_discardable->data(), m_discardableSize); - } else { - memcpy(discardable->data(), m_vector.data(), m_vector.size()); - m_discardableSize = m_vector.size(); - m_vector.clear(); - } +bool PurgeableVector::reservePurgeableCapacity( + size_t capacity, + PurgeableAllocationStrategy allocationStrategy) { + ASSERT(m_isPurgeable); - m_discardable.swap(discardable); + if (m_discardable && m_discardableCapacity >= capacity) { ASSERT(!m_vector.capacity()); return true; + } + + if (capacity < minimumDiscardableAllocationSize) + return false; + + if (allocationStrategy == UseExponentialGrowth) + capacity = adjustPurgeableCapacity(capacity); + + OwnPtr discardable = adoptPtr( + blink::Platform::current()->allocateAndLockDiscardableMemory(capacity)); + if (!discardable) { + // Discardable memory is not supported. + m_isPurgeable = false; + return false; + } + + m_discardableCapacity = capacity; + // Copy the data that was either in the previous purgeable buffer or in the + // vector to the new purgeable buffer. + if (m_discardable) { + memcpy(discardable->data(), m_discardable->data(), m_discardableSize); + } else { + memcpy(discardable->data(), m_vector.data(), m_vector.size()); + m_discardableSize = m_vector.size(); + m_vector.clear(); + } + + m_discardable.swap(discardable); + ASSERT(!m_vector.capacity()); + return true; } -size_t PurgeableVector::adjustPurgeableCapacity(size_t capacity) const -{ - ASSERT(capacity >= minimumDiscardableAllocationSize); +size_t PurgeableVector::adjustPurgeableCapacity(size_t capacity) const { + ASSERT(capacity >= minimumDiscardableAllocationSize); - const float growthFactor = 1.5; - size_t newCapacity = std::max(capacity, static_cast(m_discardableCapacity * growthFactor)); + const float growthFactor = 1.5; + size_t newCapacity = std::max( + capacity, static_cast(m_discardableCapacity * growthFactor)); - // Discardable memory has page-granularity so align to the next page here to minimize - // fragmentation. - // Since the page size is only used below to minimize fragmentation it's still safe to use it - // even if it gets out of sync (e.g. due to the use of huge pages). - const size_t kPageSize = 4096; - newCapacity = (newCapacity + kPageSize - 1) & ~(kPageSize - 1); + // Discardable memory has page-granularity so align to the next page here to + // minimize fragmentation. Since the page size is only used below to minimize + // fragmentation it's still safe to use it even if it gets out of sync (e.g. + // due to the use of huge pages). + const size_t kPageSize = 4096; + newCapacity = (newCapacity + kPageSize - 1) & ~(kPageSize - 1); - return std::max(capacity, newCapacity); // Overflow check. + return std::max(capacity, newCapacity); // Overflow check. } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/PurgeableVector.h b/sky/engine/platform/PurgeableVector.h index da88d125ee926..427fe0e9ade66 100644 --- a/sky/engine/platform/PurgeableVector.h +++ b/sky/engine/platform/PurgeableVector.h @@ -45,79 +45,80 @@ class WebDiscardableMemory; // means that N calls to lock() must be followed by N+1 calls to unlock() to // actually make the vector purgeable. class PLATFORM_EXPORT PurgeableVector { - WTF_MAKE_NONCOPYABLE(PurgeableVector); -public: - enum PurgeableOption { - NotPurgeable, - Purgeable, - }; + WTF_MAKE_NONCOPYABLE(PurgeableVector); - // Clients who know in advance that they will call unlock() should construct - // the instance with the Purgeable option so that the instance uses - // discardable memory from the start and unlock() doesn't cause a memcpy(). - PurgeableVector(PurgeableOption = Purgeable); + public: + enum PurgeableOption { + NotPurgeable, + Purgeable, + }; - ~PurgeableVector(); + // Clients who know in advance that they will call unlock() should construct + // the instance with the Purgeable option so that the instance uses + // discardable memory from the start and unlock() doesn't cause a memcpy(). + PurgeableVector(PurgeableOption = Purgeable); - // WARNING: This causes a memcpy() if the instance was constructed with the - // Purgeable hint or had its internal vector moved to discardable memory - // after a call to unlock(). - void adopt(Vector& other); + ~PurgeableVector(); - void append(const char* data, size_t length); + // WARNING: This causes a memcpy() if the instance was constructed with the + // Purgeable hint or had its internal vector moved to discardable memory + // after a call to unlock(). + void adopt(Vector& other); - void grow(size_t); + void append(const char* data, size_t length); - void clear(); + void grow(size_t); - // The instance must be locked before calling this. - char* data(); + void clear(); - size_t size() const; + // The instance must be locked before calling this. + char* data(); - // Returns whether the memory is still resident. - bool lock(); + size_t size() const; - // WARNING: Calling unlock() on an instance that wasn't created with the - // Purgeable option does an extra memcpy(). - void unlock(); + // Returns whether the memory is still resident. + bool lock(); - bool isLocked() const; + // WARNING: Calling unlock() on an instance that wasn't created with the + // Purgeable option does an extra memcpy(). + void unlock(); - // Note that this method should be used carefully since it may not use - // exponential growth internally. This means that repeated/invalid uses of - // it can result in O(N^2) append(). If you don't exactly know what you are - // doing then you should probably not call this method. - void reserveCapacity(size_t capacity); + bool isLocked() const; -private: - enum PurgeableAllocationStrategy { - UseExactCapacity, - UseExponentialGrowth, - }; + // Note that this method should be used carefully since it may not use + // exponential growth internally. This means that repeated/invalid uses of + // it can result in O(N^2) append(). If you don't exactly know what you are + // doing then you should probably not call this method. + void reserveCapacity(size_t capacity); - // Copies data from the discardable buffer to the vector and clears the - // discardable buffer. - void moveDataFromDiscardableToVector(); + private: + enum PurgeableAllocationStrategy { + UseExactCapacity, + UseExponentialGrowth, + }; - void clearDiscardable(); + // Copies data from the discardable buffer to the vector and clears the + // discardable buffer. + void moveDataFromDiscardableToVector(); - bool reservePurgeableCapacity(size_t capacity, PurgeableAllocationStrategy); + void clearDiscardable(); - size_t adjustPurgeableCapacity(size_t capacity) const; + bool reservePurgeableCapacity(size_t capacity, PurgeableAllocationStrategy); - // Vector used when the instance is constructed without the purgeability - // hint or when discardable memory allocation fails. - // Note that there can't be data both in |m_vector| and - // |m_discardable|, i.e. only one of them is used at a given time. - Vector m_vector; - OwnPtr m_discardable; - size_t m_discardableCapacity; - size_t m_discardableSize; - bool m_isPurgeable; - int m_locksCount; + size_t adjustPurgeableCapacity(size_t capacity) const; + + // Vector used when the instance is constructed without the purgeability + // hint or when discardable memory allocation fails. + // Note that there can't be data both in |m_vector| and + // |m_discardable|, i.e. only one of them is used at a given time. + Vector m_vector; + OwnPtr m_discardable; + size_t m_discardableCapacity; + size_t m_discardableSize; + bool m_isPurgeable; + int m_locksCount; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_PURGEABLEVECTOR_H_ diff --git a/sky/engine/platform/PurgeableVectorTest.cpp b/sky/engine/platform/PurgeableVectorTest.cpp index 42a6f205a2704..0d56db123dbe6 100644 --- a/sky/engine/platform/PurgeableVectorTest.cpp +++ b/sky/engine/platform/PurgeableVectorTest.cpp @@ -46,291 +46,300 @@ namespace { const size_t kTestSize = 32 * 1024; enum DiscardableMemorySupport { - DontSupportDiscardableMemory, - SupportDiscardableMemory, + DontSupportDiscardableMemory, + SupportDiscardableMemory, }; -class PurgeableVectorTestWithPlatformSupport : public testing::TestWithParam { -public: - PurgeableVectorTestWithPlatformSupport() : m_testingPlatformSupport(makeTestingPlatformSupportConfig()) { } - -protected: - bool isDiscardableMemorySupported() const { return GetParam() == SupportDiscardableMemory; } - - TestingPlatformSupport::Config makeTestingPlatformSupportConfig() const - { - TestingPlatformSupport::Config config; - config.hasDiscardableMemorySupport = isDiscardableMemorySupported(); - return config; - } - - PurgeableVector::PurgeableOption makePurgeableOption() const - { - return isDiscardableMemorySupported() ? PurgeableVector::Purgeable : PurgeableVector::NotPurgeable; - } - -private: - TestingPlatformSupport m_testingPlatformSupport; +class PurgeableVectorTestWithPlatformSupport + : public testing::TestWithParam { + public: + PurgeableVectorTestWithPlatformSupport() + : m_testingPlatformSupport(makeTestingPlatformSupportConfig()) {} + + protected: + bool isDiscardableMemorySupported() const { + return GetParam() == SupportDiscardableMemory; + } + + TestingPlatformSupport::Config makeTestingPlatformSupportConfig() const { + TestingPlatformSupport::Config config; + config.hasDiscardableMemorySupport = isDiscardableMemorySupported(); + return config; + } + + PurgeableVector::PurgeableOption makePurgeableOption() const { + return isDiscardableMemorySupported() ? PurgeableVector::Purgeable + : PurgeableVector::NotPurgeable; + } + + private: + TestingPlatformSupport m_testingPlatformSupport; }; -TEST_P(PurgeableVectorTestWithPlatformSupport, grow) -{ - PurgeableVector purgeableVector(makePurgeableOption()); - purgeableVector.grow(kTestSize); - ASSERT_EQ(kTestSize, purgeableVector.size()); - // Make sure the underlying buffer was actually (re)allocated. - memset(purgeableVector.data(), 0, purgeableVector.size()); +TEST_P(PurgeableVectorTestWithPlatformSupport, grow) { + PurgeableVector purgeableVector(makePurgeableOption()); + purgeableVector.grow(kTestSize); + ASSERT_EQ(kTestSize, purgeableVector.size()); + // Make sure the underlying buffer was actually (re)allocated. + memset(purgeableVector.data(), 0, purgeableVector.size()); } -TEST_P(PurgeableVectorTestWithPlatformSupport, clear) -{ - Vector testData(kTestSize); - std::generate(testData.begin(), testData.end(), &std::rand); +TEST_P(PurgeableVectorTestWithPlatformSupport, clear) { + Vector testData(kTestSize); + std::generate(testData.begin(), testData.end(), &std::rand); - PurgeableVector purgeableVector(makePurgeableOption()); - purgeableVector.append(testData.data(), testData.size()); - EXPECT_EQ(testData.size(), purgeableVector.size()); + PurgeableVector purgeableVector(makePurgeableOption()); + purgeableVector.append(testData.data(), testData.size()); + EXPECT_EQ(testData.size(), purgeableVector.size()); - purgeableVector.clear(); - EXPECT_EQ(0U, purgeableVector.size()); - EXPECT_EQ(0, purgeableVector.data()); + purgeableVector.clear(); + EXPECT_EQ(0U, purgeableVector.size()); + EXPECT_EQ(0, purgeableVector.data()); } -TEST_P(PurgeableVectorTestWithPlatformSupport, clearDoesNotResetLockCounter) -{ - PurgeableVector purgeableVector(makePurgeableOption()); - purgeableVector.clear(); - EXPECT_TRUE(purgeableVector.isLocked()); - purgeableVector.unlock(); - EXPECT_FALSE(purgeableVector.isLocked()); +TEST_P(PurgeableVectorTestWithPlatformSupport, clearDoesNotResetLockCounter) { + PurgeableVector purgeableVector(makePurgeableOption()); + purgeableVector.clear(); + EXPECT_TRUE(purgeableVector.isLocked()); + purgeableVector.unlock(); + EXPECT_FALSE(purgeableVector.isLocked()); } -TEST_P(PurgeableVectorTestWithPlatformSupport, reserveCapacityDoesNotChangeSize) -{ - PurgeableVector purgeableVector(makePurgeableOption()); - EXPECT_EQ(0U, purgeableVector.size()); - purgeableVector.reserveCapacity(kTestSize); - EXPECT_EQ(0U, purgeableVector.size()); +TEST_P(PurgeableVectorTestWithPlatformSupport, + reserveCapacityDoesNotChangeSize) { + PurgeableVector purgeableVector(makePurgeableOption()); + EXPECT_EQ(0U, purgeableVector.size()); + purgeableVector.reserveCapacity(kTestSize); + EXPECT_EQ(0U, purgeableVector.size()); } -TEST_P(PurgeableVectorTestWithPlatformSupport, multipleAppends) -{ - Vector testData(kTestSize); - std::generate(testData.begin(), testData.end(), &std::rand); - - PurgeableVector purgeableVector(makePurgeableOption()); - // Force an allocation. - const char kSmallString[] = "hello"; - purgeableVector.append(kSmallString, sizeof(kSmallString)); - const char* const data = purgeableVector.data(); - - // Append all the testing data in 4 iterations. The |data| pointer should - // have been changed at the end of the unit test due to reallocations. - const size_t kIterationCount = 4; - ASSERT_EQ(0U, testData.size() % kIterationCount); - for (size_t i = 0; i < kIterationCount; ++i) { - const char* const testDataStart = testData.data() + i * (testData.size() / kIterationCount); - purgeableVector.append(testDataStart, testData.size() / kIterationCount); - ASSERT_EQ((i + 1) * testData.size() / kIterationCount, purgeableVector.size() - sizeof(kSmallString)); - } - - ASSERT_EQ(sizeof(kSmallString) + testData.size(), purgeableVector.size()); - EXPECT_NE(data, purgeableVector.data()); - EXPECT_EQ(0, memcmp(purgeableVector.data() + sizeof(kSmallString), testData.data(), testData.size())); +TEST_P(PurgeableVectorTestWithPlatformSupport, multipleAppends) { + Vector testData(kTestSize); + std::generate(testData.begin(), testData.end(), &std::rand); + + PurgeableVector purgeableVector(makePurgeableOption()); + // Force an allocation. + const char kSmallString[] = "hello"; + purgeableVector.append(kSmallString, sizeof(kSmallString)); + const char* const data = purgeableVector.data(); + + // Append all the testing data in 4 iterations. The |data| pointer should + // have been changed at the end of the unit test due to reallocations. + const size_t kIterationCount = 4; + ASSERT_EQ(0U, testData.size() % kIterationCount); + for (size_t i = 0; i < kIterationCount; ++i) { + const char* const testDataStart = + testData.data() + i * (testData.size() / kIterationCount); + purgeableVector.append(testDataStart, testData.size() / kIterationCount); + ASSERT_EQ((i + 1) * testData.size() / kIterationCount, + purgeableVector.size() - sizeof(kSmallString)); + } + + ASSERT_EQ(sizeof(kSmallString) + testData.size(), purgeableVector.size()); + EXPECT_NE(data, purgeableVector.data()); + EXPECT_EQ(0, memcmp(purgeableVector.data() + sizeof(kSmallString), + testData.data(), testData.size())); } -TEST_P(PurgeableVectorTestWithPlatformSupport, multipleAppendsAfterReserveCapacity) -{ - Vector testData(kTestSize); - std::generate(testData.begin(), testData.end(), &std::rand); - - PurgeableVector purgeableVector(makePurgeableOption()); - purgeableVector.reserveCapacity(testData.size()); - const char* const data = purgeableVector.data(); - - // The |data| pointer should be unchanged at the end of the unit test - // meaning that there should not have been any reallocation. - const size_t kIterationCount = 4; - ASSERT_EQ(0U, testData.size() % kIterationCount); - for (size_t i = 0; i < kIterationCount; ++i) { - const char* const testDataStart = testData.data() + i * (testData.size() / kIterationCount); - purgeableVector.append(testDataStart, testData.size() / kIterationCount); - ASSERT_EQ((i + 1) * testData.size() / kIterationCount, purgeableVector.size()); - } - - ASSERT_EQ(testData.size(), purgeableVector.size()); - EXPECT_EQ(data, purgeableVector.data()); - EXPECT_EQ(0, memcmp(purgeableVector.data(), testData.data(), testData.size())); +TEST_P(PurgeableVectorTestWithPlatformSupport, + multipleAppendsAfterReserveCapacity) { + Vector testData(kTestSize); + std::generate(testData.begin(), testData.end(), &std::rand); + + PurgeableVector purgeableVector(makePurgeableOption()); + purgeableVector.reserveCapacity(testData.size()); + const char* const data = purgeableVector.data(); + + // The |data| pointer should be unchanged at the end of the unit test + // meaning that there should not have been any reallocation. + const size_t kIterationCount = 4; + ASSERT_EQ(0U, testData.size() % kIterationCount); + for (size_t i = 0; i < kIterationCount; ++i) { + const char* const testDataStart = + testData.data() + i * (testData.size() / kIterationCount); + purgeableVector.append(testDataStart, testData.size() / kIterationCount); + ASSERT_EQ((i + 1) * testData.size() / kIterationCount, + purgeableVector.size()); + } + + ASSERT_EQ(testData.size(), purgeableVector.size()); + EXPECT_EQ(data, purgeableVector.data()); + EXPECT_EQ(0, + memcmp(purgeableVector.data(), testData.data(), testData.size())); } -TEST_P(PurgeableVectorTestWithPlatformSupport, reserveCapacityUsesExactCapacityWhenVectorIsEmpty) -{ - Vector testData(kTestSize); - std::generate(testData.begin(), testData.end(), &std::rand); - - PurgeableVector purgeableVector(makePurgeableOption()); - purgeableVector.reserveCapacity(kTestSize); - const char* const data = purgeableVector.data(); - - purgeableVector.append(testData.data(), testData.size()); - EXPECT_EQ(data, purgeableVector.data()); - EXPECT_EQ(0, memcmp(purgeableVector.data(), testData.data(), testData.size())); - - // This test is not reliable if the PurgeableVector uses a plain WTF::Vector - // for storage, as it does if discardable memory is not supported; the vectors - // capacity will always be expanded to fill the PartitionAlloc bucket. - if (isDiscardableMemorySupported()) { - // Appending one extra byte should cause a reallocation since the first - // allocation happened while the purgeable vector was empty. This behavior - // helps us guarantee that there is no memory waste on very small vectors - // (which SharedBuffer requires). - purgeableVector.append(testData.data(), 1); - EXPECT_NE(data, purgeableVector.data()); - } +TEST_P(PurgeableVectorTestWithPlatformSupport, + reserveCapacityUsesExactCapacityWhenVectorIsEmpty) { + Vector testData(kTestSize); + std::generate(testData.begin(), testData.end(), &std::rand); + + PurgeableVector purgeableVector(makePurgeableOption()); + purgeableVector.reserveCapacity(kTestSize); + const char* const data = purgeableVector.data(); + + purgeableVector.append(testData.data(), testData.size()); + EXPECT_EQ(data, purgeableVector.data()); + EXPECT_EQ(0, + memcmp(purgeableVector.data(), testData.data(), testData.size())); + + // This test is not reliable if the PurgeableVector uses a plain WTF::Vector + // for storage, as it does if discardable memory is not supported; the vectors + // capacity will always be expanded to fill the PartitionAlloc bucket. + if (isDiscardableMemorySupported()) { + // Appending one extra byte should cause a reallocation since the first + // allocation happened while the purgeable vector was empty. This behavior + // helps us guarantee that there is no memory waste on very small vectors + // (which SharedBuffer requires). + purgeableVector.append(testData.data(), 1); + EXPECT_NE(data, purgeableVector.data()); + } } -TEST_P(PurgeableVectorTestWithPlatformSupport, appendReservesCapacityIfNeeded) -{ - Vector testData(kTestSize); - std::generate(testData.begin(), testData.end(), &std::rand); +TEST_P(PurgeableVectorTestWithPlatformSupport, appendReservesCapacityIfNeeded) { + Vector testData(kTestSize); + std::generate(testData.begin(), testData.end(), &std::rand); - PurgeableVector purgeableVector(makePurgeableOption()); - // No reserveCapacity(). - ASSERT_FALSE(purgeableVector.data()); + PurgeableVector purgeableVector(makePurgeableOption()); + // No reserveCapacity(). + ASSERT_FALSE(purgeableVector.data()); - purgeableVector.append(testData.data(), testData.size()); - ASSERT_EQ(testData.size(), purgeableVector.size()); - ASSERT_EQ(0, memcmp(purgeableVector.data(), testData.data(), testData.size())); + purgeableVector.append(testData.data(), testData.size()); + ASSERT_EQ(testData.size(), purgeableVector.size()); + ASSERT_EQ(0, + memcmp(purgeableVector.data(), testData.data(), testData.size())); } -TEST_P(PurgeableVectorTestWithPlatformSupport, adopt) -{ - Vector testData(kTestSize); - std::generate(testData.begin(), testData.end(), &std::rand); - const Vector testDataCopy(testData); - const char* const testDataPtr = testData.data(); - - PurgeableVector purgeableVector(makePurgeableOption()); - purgeableVector.adopt(testData); - EXPECT_TRUE(testData.isEmpty()); - EXPECT_EQ(kTestSize, purgeableVector.size()); - ASSERT_EQ(0, memcmp(purgeableVector.data(), testDataCopy.data(), testDataCopy.size())); - - if (isDiscardableMemorySupported()) { - // An extra discardable memory allocation + memcpy() should have happened. - EXPECT_NE(testDataPtr, purgeableVector.data()); - } else { - // Vector::swap() should have been used. - EXPECT_EQ(testDataPtr, purgeableVector.data()); - } +TEST_P(PurgeableVectorTestWithPlatformSupport, adopt) { + Vector testData(kTestSize); + std::generate(testData.begin(), testData.end(), &std::rand); + const Vector testDataCopy(testData); + const char* const testDataPtr = testData.data(); + + PurgeableVector purgeableVector(makePurgeableOption()); + purgeableVector.adopt(testData); + EXPECT_TRUE(testData.isEmpty()); + EXPECT_EQ(kTestSize, purgeableVector.size()); + ASSERT_EQ(0, memcmp(purgeableVector.data(), testDataCopy.data(), + testDataCopy.size())); + + if (isDiscardableMemorySupported()) { + // An extra discardable memory allocation + memcpy() should have happened. + EXPECT_NE(testDataPtr, purgeableVector.data()); + } else { + // Vector::swap() should have been used. + EXPECT_EQ(testDataPtr, purgeableVector.data()); + } } -TEST_P(PurgeableVectorTestWithPlatformSupport, adoptEmptyVector) -{ - Vector testData; - PurgeableVector purgeableVector(makePurgeableOption()); - purgeableVector.adopt(testData); +TEST_P(PurgeableVectorTestWithPlatformSupport, adoptEmptyVector) { + Vector testData; + PurgeableVector purgeableVector(makePurgeableOption()); + purgeableVector.adopt(testData); } -TEST(PurgeableVectorTestWithPlatformSupport, adoptDiscardsPreviousData) -{ - Vector testData; - std::generate(testData.begin(), testData.end(), &std::rand); +TEST(PurgeableVectorTestWithPlatformSupport, adoptDiscardsPreviousData) { + Vector testData; + std::generate(testData.begin(), testData.end(), &std::rand); - PurgeableVector purgeableVector(PurgeableVector::NotPurgeable); - static const char smallString[] = "hello"; - purgeableVector.append(smallString, sizeof(smallString)); - ASSERT_EQ(0, memcmp(purgeableVector.data(), smallString, sizeof(smallString))); + PurgeableVector purgeableVector(PurgeableVector::NotPurgeable); + static const char smallString[] = "hello"; + purgeableVector.append(smallString, sizeof(smallString)); + ASSERT_EQ(0, + memcmp(purgeableVector.data(), smallString, sizeof(smallString))); - purgeableVector.adopt(testData); - EXPECT_EQ(testData.size(), purgeableVector.size()); - ASSERT_EQ(0, memcmp(purgeableVector.data(), testData.data(), testData.size())); + purgeableVector.adopt(testData); + EXPECT_EQ(testData.size(), purgeableVector.size()); + ASSERT_EQ(0, + memcmp(purgeableVector.data(), testData.data(), testData.size())); } -TEST_P(PurgeableVectorTestWithPlatformSupport, unlockWithoutHintAtConstruction) -{ - Vector testData(30000); - std::generate(testData.begin(), testData.end(), &std::rand); - - unsigned length = testData.size(); - PurgeableVector purgeableVector(PurgeableVector::NotPurgeable); - purgeableVector.append(testData.data(), length); - ASSERT_EQ(length, purgeableVector.size()); - const char* data = purgeableVector.data(); - - purgeableVector.unlock(); - - // Note that the purgeable vector must be locked before calling data(). - const bool wasPurged = !purgeableVector.lock(); - if (isDiscardableMemorySupported()) { - // The implementation of purgeable memory used for testing always purges data upon unlock(). - EXPECT_TRUE(wasPurged); - } - - if (isDiscardableMemorySupported()) { - // The data should have been moved from the heap-allocated vector to a purgeable buffer. - ASSERT_NE(data, purgeableVector.data()); - } else { - ASSERT_EQ(data, purgeableVector.data()); - } - - if (!wasPurged) - ASSERT_EQ(0, memcmp(purgeableVector.data(), testData.data(), length)); +TEST_P(PurgeableVectorTestWithPlatformSupport, + unlockWithoutHintAtConstruction) { + Vector testData(30000); + std::generate(testData.begin(), testData.end(), &std::rand); + + unsigned length = testData.size(); + PurgeableVector purgeableVector(PurgeableVector::NotPurgeable); + purgeableVector.append(testData.data(), length); + ASSERT_EQ(length, purgeableVector.size()); + const char* data = purgeableVector.data(); + + purgeableVector.unlock(); + + // Note that the purgeable vector must be locked before calling data(). + const bool wasPurged = !purgeableVector.lock(); + if (isDiscardableMemorySupported()) { + // The implementation of purgeable memory used for testing always purges + // data upon unlock(). + EXPECT_TRUE(wasPurged); + } + + if (isDiscardableMemorySupported()) { + // The data should have been moved from the heap-allocated vector to a + // purgeable buffer. + ASSERT_NE(data, purgeableVector.data()); + } else { + ASSERT_EQ(data, purgeableVector.data()); + } + + if (!wasPurged) + ASSERT_EQ(0, memcmp(purgeableVector.data(), testData.data(), length)); } -TEST(PurgeableVectorTest, unlockOnEmptyPurgeableVector) -{ - PurgeableVector purgeableVector; - ASSERT_EQ(0U, purgeableVector.size()); - purgeableVector.unlock(); - ASSERT_FALSE(purgeableVector.isLocked()); +TEST(PurgeableVectorTest, unlockOnEmptyPurgeableVector) { + PurgeableVector purgeableVector; + ASSERT_EQ(0U, purgeableVector.size()); + purgeableVector.unlock(); + ASSERT_FALSE(purgeableVector.isLocked()); } -TEST_P(PurgeableVectorTestWithPlatformSupport, unlockOnPurgeableVectorWithPurgeableHint) -{ - Vector testData(kTestSize); - std::generate(testData.begin(), testData.end(), &std::rand); - - PurgeableVector purgeableVector; - purgeableVector.append(testData.data(), kTestSize); - const char* const data = purgeableVector.data(); - - // unlock() should happen in place, i.e. without causing any reallocation. - // Note that the instance must be locked when data() is called. - purgeableVector.unlock(); - EXPECT_FALSE(purgeableVector.isLocked()); - purgeableVector.lock(); - EXPECT_TRUE(purgeableVector.isLocked()); - EXPECT_EQ(data, purgeableVector.data()); +TEST_P(PurgeableVectorTestWithPlatformSupport, + unlockOnPurgeableVectorWithPurgeableHint) { + Vector testData(kTestSize); + std::generate(testData.begin(), testData.end(), &std::rand); + + PurgeableVector purgeableVector; + purgeableVector.append(testData.data(), kTestSize); + const char* const data = purgeableVector.data(); + + // unlock() should happen in place, i.e. without causing any reallocation. + // Note that the instance must be locked when data() is called. + purgeableVector.unlock(); + EXPECT_FALSE(purgeableVector.isLocked()); + purgeableVector.lock(); + EXPECT_TRUE(purgeableVector.isLocked()); + EXPECT_EQ(data, purgeableVector.data()); } -TEST_P(PurgeableVectorTestWithPlatformSupport, lockingUsesACounter) -{ - Vector testData(kTestSize); - std::generate(testData.begin(), testData.end(), &std::rand); +TEST_P(PurgeableVectorTestWithPlatformSupport, lockingUsesACounter) { + Vector testData(kTestSize); + std::generate(testData.begin(), testData.end(), &std::rand); - PurgeableVector purgeableVector(PurgeableVector::NotPurgeable); - purgeableVector.append(testData.data(), testData.size()); - ASSERT_EQ(testData.size(), purgeableVector.size()); + PurgeableVector purgeableVector(PurgeableVector::NotPurgeable); + purgeableVector.append(testData.data(), testData.size()); + ASSERT_EQ(testData.size(), purgeableVector.size()); - ASSERT_TRUE(purgeableVector.isLocked()); // SharedBuffer is locked at creation. - ASSERT_TRUE(purgeableVector.lock()); // Add an extra lock. - ASSERT_TRUE(purgeableVector.isLocked()); + ASSERT_TRUE( + purgeableVector.isLocked()); // SharedBuffer is locked at creation. + ASSERT_TRUE(purgeableVector.lock()); // Add an extra lock. + ASSERT_TRUE(purgeableVector.isLocked()); - purgeableVector.unlock(); - ASSERT_TRUE(purgeableVector.isLocked()); + purgeableVector.unlock(); + ASSERT_TRUE(purgeableVector.isLocked()); - purgeableVector.unlock(); - ASSERT_FALSE(purgeableVector.isLocked()); + purgeableVector.unlock(); + ASSERT_FALSE(purgeableVector.isLocked()); - if (purgeableVector.lock()) - ASSERT_EQ(0, memcmp(purgeableVector.data(), testData.data(), testData.size())); + if (purgeableVector.lock()) + ASSERT_EQ(0, + memcmp(purgeableVector.data(), testData.data(), testData.size())); } -// Instantiates all the unit tests using the SharedBufferTestWithPlatformSupport fixture both with -// and without discardable memory support. -INSTANTIATE_TEST_CASE_P(testsWithPlatformSetUp, PurgeableVectorTestWithPlatformSupport, - ::testing::Values(DontSupportDiscardableMemory, SupportDiscardableMemory)); - -} // namespace +// Instantiates all the unit tests using the SharedBufferTestWithPlatformSupport +// fixture both with and without discardable memory support. +INSTANTIATE_TEST_CASE_P(testsWithPlatformSetUp, + PurgeableVectorTestWithPlatformSupport, + ::testing::Values(DontSupportDiscardableMemory, + SupportDiscardableMemory)); +} // namespace diff --git a/sky/engine/platform/SharedBuffer.cpp b/sky/engine/platform/SharedBuffer.cpp index dd74fc7403fbc..1f741a4465aed 100644 --- a/sky/engine/platform/SharedBuffer.cpp +++ b/sky/engine/platform/SharedBuffer.cpp @@ -28,8 +28,8 @@ #include "flutter/common/threads.h" #include "flutter/sky/engine/public/platform/Platform.h" -#include "flutter/sky/engine/wtf/unicode/Unicode.h" #include "flutter/sky/engine/wtf/unicode/UTF8.h" +#include "flutter/sky/engine/wtf/unicode/Unicode.h" #undef SHARED_BUFFER_STATS diff --git a/sky/engine/platform/SharedBuffer.h b/sky/engine/platform/SharedBuffer.h index 52e46a0064af7..ab3a19af4bc0c 100644 --- a/sky/engine/platform/SharedBuffer.h +++ b/sky/engine/platform/SharedBuffer.h @@ -38,78 +38,88 @@ namespace blink { class PLATFORM_EXPORT SharedBuffer : public RefCounted { -public: - static PassRefPtr create() { return adoptRef(new SharedBuffer); } - static PassRefPtr create(size_t size) { return adoptRef(new SharedBuffer(size)); } - static PassRefPtr create(const char* c, int i) { return adoptRef(new SharedBuffer(c, i)); } - static PassRefPtr create(const unsigned char* c, int i) { return adoptRef(new SharedBuffer(c, i)); } - - static PassRefPtr createPurgeable(const char* c, int i) { return adoptRef(new SharedBuffer(c, i, PurgeableVector::Purgeable)); } - - static PassRefPtr adoptVector(Vector&); - - ~SharedBuffer(); - - // Calling this function will force internal segmented buffers to be merged - // into a flat buffer. Use getSomeData() whenever possible for better - // performance. - const char* data() const; - - unsigned size() const; - - bool isEmpty() const { return !size(); } - - void append(PassRefPtr); - void append(const char*, unsigned); - void append(const Vector&); - - void clear(); - - PassRefPtr copy() const; - - // Return the number of consecutive bytes after "position". "data" - // points to the first byte. - // Return 0 when no more data left. - // When extracting all data with getSomeData(), the caller should - // repeat calling it until it returns 0. - // Usage: - // const char* segment; - // unsigned pos = 0; - // while (unsigned length = sharedBuffer->getSomeData(segment, pos)) { - // // Use the data. for example: decoder->decode(segment, length); - // pos += length; - // } - unsigned getSomeData(const char*& data, unsigned position = 0) const; - - // Creates an SkData and copies this SharedBuffer's contents to that - // SkData without merging segmented buffers into a flat buffer. - sk_sp getAsSkData() const; - - // See PurgeableVector::lock(). - bool lock(); - - // WARNING: Calling unlock() on a SharedBuffer that wasn't created with the - // purgeability option does an extra memcpy(). Please use - // SharedBuffer::createPurgeable() if you intend to call unlock(). - void unlock(); - - bool isLocked() const; - -private: - SharedBuffer(); - explicit SharedBuffer(size_t); - SharedBuffer(const char*, int); - SharedBuffer(const char*, int, PurgeableVector::PurgeableOption); - SharedBuffer(const unsigned char*, int); - - // See SharedBuffer::data(). - void mergeSegmentsIntoBuffer() const; - - unsigned m_size; - mutable PurgeableVector m_buffer; - mutable Vector m_segments; + public: + static PassRefPtr create() { + return adoptRef(new SharedBuffer); + } + static PassRefPtr create(size_t size) { + return adoptRef(new SharedBuffer(size)); + } + static PassRefPtr create(const char* c, int i) { + return adoptRef(new SharedBuffer(c, i)); + } + static PassRefPtr create(const unsigned char* c, int i) { + return adoptRef(new SharedBuffer(c, i)); + } + + static PassRefPtr createPurgeable(const char* c, int i) { + return adoptRef(new SharedBuffer(c, i, PurgeableVector::Purgeable)); + } + + static PassRefPtr adoptVector(Vector&); + + ~SharedBuffer(); + + // Calling this function will force internal segmented buffers to be merged + // into a flat buffer. Use getSomeData() whenever possible for better + // performance. + const char* data() const; + + unsigned size() const; + + bool isEmpty() const { return !size(); } + + void append(PassRefPtr); + void append(const char*, unsigned); + void append(const Vector&); + + void clear(); + + PassRefPtr copy() const; + + // Return the number of consecutive bytes after "position". "data" + // points to the first byte. + // Return 0 when no more data left. + // When extracting all data with getSomeData(), the caller should + // repeat calling it until it returns 0. + // Usage: + // const char* segment; + // unsigned pos = 0; + // while (unsigned length = sharedBuffer->getSomeData(segment, pos)) { + // // Use the data. for example: decoder->decode(segment, length); + // pos += length; + // } + unsigned getSomeData(const char*& data, unsigned position = 0) const; + + // Creates an SkData and copies this SharedBuffer's contents to that + // SkData without merging segmented buffers into a flat buffer. + sk_sp getAsSkData() const; + + // See PurgeableVector::lock(). + bool lock(); + + // WARNING: Calling unlock() on a SharedBuffer that wasn't created with the + // purgeability option does an extra memcpy(). Please use + // SharedBuffer::createPurgeable() if you intend to call unlock(). + void unlock(); + + bool isLocked() const; + + private: + SharedBuffer(); + explicit SharedBuffer(size_t); + SharedBuffer(const char*, int); + SharedBuffer(const char*, int, PurgeableVector::PurgeableOption); + SharedBuffer(const unsigned char*, int); + + // See SharedBuffer::data(). + void mergeSegmentsIntoBuffer() const; + + unsigned m_size; + mutable PurgeableVector m_buffer; + mutable Vector m_segments; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_SHAREDBUFFER_H_ diff --git a/sky/engine/platform/TestingPlatformSupport.cpp b/sky/engine/platform/TestingPlatformSupport.cpp index 056e1c62a5c37..930d51ffaa466 100644 --- a/sky/engine/platform/TestingPlatformSupport.cpp +++ b/sky/engine/platform/TestingPlatformSupport.cpp @@ -28,55 +28,48 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #include "flutter/sky/engine/platform/TestingPlatformSupport.h" namespace blink { -TestingDiscardableMemory::TestingDiscardableMemory(size_t size) : m_data(size), m_isLocked(true) -{ -} +TestingDiscardableMemory::TestingDiscardableMemory(size_t size) + : m_data(size), m_isLocked(true) {} -TestingDiscardableMemory::~TestingDiscardableMemory() -{ -} +TestingDiscardableMemory::~TestingDiscardableMemory() {} -bool TestingDiscardableMemory::lock() -{ - ASSERT(!m_isLocked); - m_isLocked = true; - return false; +bool TestingDiscardableMemory::lock() { + ASSERT(!m_isLocked); + m_isLocked = true; + return false; } -void* TestingDiscardableMemory::data() -{ - ASSERT(m_isLocked); - return m_data.data(); +void* TestingDiscardableMemory::data() { + ASSERT(m_isLocked); + return m_data.data(); } -void TestingDiscardableMemory::unlock() -{ - ASSERT(m_isLocked); - m_isLocked = false; - // Force eviction to catch clients not correctly checking the return value of lock(). - memset(m_data.data(), 0, m_data.size()); +void TestingDiscardableMemory::unlock() { + ASSERT(m_isLocked); + m_isLocked = false; + // Force eviction to catch clients not correctly checking the return value of + // lock(). + memset(m_data.data(), 0, m_data.size()); } TestingPlatformSupport::TestingPlatformSupport(const Config& config) - : m_config(config) - , m_oldPlatform(blink::Platform::current()) -{ - blink::Platform::initialize(this); + : m_config(config), m_oldPlatform(blink::Platform::current()) { + blink::Platform::initialize(this); } -TestingPlatformSupport::~TestingPlatformSupport() -{ - blink::Platform::initialize(m_oldPlatform); +TestingPlatformSupport::~TestingPlatformSupport() { + blink::Platform::initialize(m_oldPlatform); } -blink::WebDiscardableMemory* TestingPlatformSupport::allocateAndLockDiscardableMemory(size_t bytes) -{ - return !m_config.hasDiscardableMemorySupport ? 0 : new TestingDiscardableMemory(bytes); +blink::WebDiscardableMemory* +TestingPlatformSupport::allocateAndLockDiscardableMemory(size_t bytes) { + return !m_config.hasDiscardableMemorySupport + ? 0 + : new TestingDiscardableMemory(bytes); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/TestingPlatformSupport.h b/sky/engine/platform/TestingPlatformSupport.h index e9183caee8c21..2c1fe36fec022 100644 --- a/sky/engine/platform/TestingPlatformSupport.h +++ b/sky/engine/platform/TestingPlatformSupport.h @@ -38,40 +38,41 @@ namespace blink { class TestingDiscardableMemory : public WebDiscardableMemory { -public: - explicit TestingDiscardableMemory(size_t); - virtual ~TestingDiscardableMemory(); + public: + explicit TestingDiscardableMemory(size_t); + virtual ~TestingDiscardableMemory(); - // WebDiscardableMemory: - virtual bool lock() override; - virtual void* data() override; - virtual void unlock() override; + // WebDiscardableMemory: + virtual bool lock() override; + virtual void* data() override; + virtual void unlock() override; -private: - Vector m_data; - bool m_isLocked; + private: + Vector m_data; + bool m_isLocked; }; class TestingPlatformSupport : public Platform { -public: - struct Config { - Config() : hasDiscardableMemorySupport(false) { } + public: + struct Config { + Config() : hasDiscardableMemorySupport(false) {} - bool hasDiscardableMemorySupport; - }; + bool hasDiscardableMemorySupport; + }; - explicit TestingPlatformSupport(const Config&); + explicit TestingPlatformSupport(const Config&); - virtual ~TestingPlatformSupport(); + virtual ~TestingPlatformSupport(); - // Platform: - virtual WebDiscardableMemory* allocateAndLockDiscardableMemory(size_t bytes) override; + // Platform: + virtual WebDiscardableMemory* allocateAndLockDiscardableMemory( + size_t bytes) override; -private: - const Config m_config; - Platform* const m_oldPlatform; + private: + const Config m_config; + Platform* const m_oldPlatform; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_TESTINGPLATFORMSUPPORT_H_ diff --git a/sky/engine/platform/animation/AnimationUtilities.h b/sky/engine/platform/animation/AnimationUtilities.h index 4d236e838c69d..31f30aa8750c9 100644 --- a/sky/engine/platform/animation/AnimationUtilities.h +++ b/sky/engine/platform/animation/AnimationUtilities.h @@ -35,50 +35,50 @@ namespace blink { -inline int blend(int from, int to, double progress) -{ - return lround(from + (to - from) * progress); +inline int blend(int from, int to, double progress) { + return lround(from + (to - from) * progress); } // For unsigned types. template -inline T blend(T from, T to, double progress) -{ - COMPILE_ASSERT(WTF::IsInteger::value, BlendForUnsignedTypes); - return clampTo(round(to > from ? from + (to - from) * progress : from - (from - to) * progress)); +inline T blend(T from, T to, double progress) { + COMPILE_ASSERT(WTF::IsInteger::value, BlendForUnsignedTypes); + return clampTo(round(to > from ? from + (to - from) * progress + : from - (from - to) * progress)); } -inline double blend(double from, double to, double progress) -{ - return from + (to - from) * progress; +inline double blend(double from, double to, double progress) { + return from + (to - from) * progress; } -inline float blend(float from, float to, double progress) -{ - return static_cast(from + (to - from) * progress); +inline float blend(float from, float to, double progress) { + return static_cast(from + (to - from) * progress); } -inline LayoutUnit blend(LayoutUnit from, LayoutUnit to, double progress) -{ - return from + (to - from) * progress; +inline LayoutUnit blend(LayoutUnit from, LayoutUnit to, double progress) { + return from + (to - from) * progress; } -inline IntPoint blend(const IntPoint& from, const IntPoint& to, double progress) -{ - return IntPoint(blend(from.x(), to.x(), progress), blend(from.y(), to.y(), progress)); +inline IntPoint blend(const IntPoint& from, + const IntPoint& to, + double progress) { + return IntPoint(blend(from.x(), to.x(), progress), + blend(from.y(), to.y(), progress)); } -inline FloatPoint blend(const FloatPoint& from, const FloatPoint& to, double progress) -{ - return FloatPoint(blend(from.x(), to.x(), progress), blend(from.y(), to.y(), progress)); +inline FloatPoint blend(const FloatPoint& from, + const FloatPoint& to, + double progress) { + return FloatPoint(blend(from.x(), to.x(), progress), + blend(from.y(), to.y(), progress)); } -// Calculates the accuracy for evaluating a timing function for an animation with the specified duration. -inline double accuracyForDuration(double duration) -{ - return 1.0 / (200.0 * duration); +// Calculates the accuracy for evaluating a timing function for an animation +// with the specified duration. +inline double accuracyForDuration(double duration) { + return 1.0 / (200.0 * duration); } -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_ANIMATION_ANIMATIONUTILITIES_H_ diff --git a/sky/engine/platform/animation/AnimationValue.h b/sky/engine/platform/animation/AnimationValue.h index 5a28874ac8961..c18c61307e4b1 100644 --- a/sky/engine/platform/animation/AnimationValue.h +++ b/sky/engine/platform/animation/AnimationValue.h @@ -39,58 +39,64 @@ namespace blink { // represent values for properties being animated via the GraphicsLayer, // without pulling in style-related data from outside of the platform directory. class AnimationValue { - WTF_MAKE_FAST_ALLOCATED; -public: - explicit AnimationValue(double keyTime, PassRefPtr timingFunction = nullptr) - : m_keyTime(keyTime) - , m_timingFunction(timingFunction) - { - } - - virtual ~AnimationValue() { } - - double keyTime() const { return m_keyTime; } - const TimingFunction* timingFunction() const { return m_timingFunction.get(); } - virtual PassOwnPtr clone() const = 0; - -private: - double m_keyTime; - RefPtr m_timingFunction; + WTF_MAKE_FAST_ALLOCATED; + + public: + explicit AnimationValue(double keyTime, + PassRefPtr timingFunction = nullptr) + : m_keyTime(keyTime), m_timingFunction(timingFunction) {} + + virtual ~AnimationValue() {} + + double keyTime() const { return m_keyTime; } + const TimingFunction* timingFunction() const { + return m_timingFunction.get(); + } + virtual PassOwnPtr clone() const = 0; + + private: + double m_keyTime; + RefPtr m_timingFunction; }; // Used to store one float value of an animation. class FloatAnimationValue final : public AnimationValue { -public: - FloatAnimationValue(double keyTime, float value, PassRefPtr timingFunction = nullptr) - : AnimationValue(keyTime, timingFunction) - , m_value(value) - { - } - virtual PassOwnPtr clone() const override { return adoptPtr(new FloatAnimationValue(*this)); } - - float value() const { return m_value; } - -private: - float m_value; + public: + FloatAnimationValue(double keyTime, + float value, + PassRefPtr timingFunction = nullptr) + : AnimationValue(keyTime, timingFunction), m_value(value) {} + virtual PassOwnPtr clone() const override { + return adoptPtr(new FloatAnimationValue(*this)); + } + + float value() const { return m_value; } + + private: + float m_value; }; // Used to store one transform value in a keyframe list. class TransformAnimationValue final : public AnimationValue { -public: - explicit TransformAnimationValue(double keyTime, const TransformOperations* value = 0, PassRefPtr timingFunction = nullptr) - : AnimationValue(keyTime, timingFunction) - { - if (value) - m_value = *value; - } - virtual PassOwnPtr clone() const override { return adoptPtr(new TransformAnimationValue(*this)); } - - const TransformOperations* value() const { return &m_value; } - -private: - TransformOperations m_value; + public: + explicit TransformAnimationValue( + double keyTime, + const TransformOperations* value = 0, + PassRefPtr timingFunction = nullptr) + : AnimationValue(keyTime, timingFunction) { + if (value) + m_value = *value; + } + virtual PassOwnPtr clone() const override { + return adoptPtr(new TransformAnimationValue(*this)); + } + + const TransformOperations* value() const { return &m_value; } + + private: + TransformOperations m_value; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_ANIMATION_ANIMATIONVALUE_H_ diff --git a/sky/engine/platform/animation/KeyframeValueList.cpp b/sky/engine/platform/animation/KeyframeValueList.cpp index 3e9ed26714024..d08375cfc1c92 100644 --- a/sky/engine/platform/animation/KeyframeValueList.cpp +++ b/sky/engine/platform/animation/KeyframeValueList.cpp @@ -28,24 +28,23 @@ namespace blink { -void KeyframeValueList::insert(PassOwnPtr value) -{ - for (size_t i = 0; i < m_values.size(); ++i) { - const AnimationValue* curValue = m_values[i].get(); - if (curValue->keyTime() == value->keyTime()) { - ASSERT_NOT_REACHED(); - // insert after - m_values.insert(i + 1, value); - return; - } - if (curValue->keyTime() > value->keyTime()) { - // insert before - m_values.insert(i, value); - return; - } +void KeyframeValueList::insert(PassOwnPtr value) { + for (size_t i = 0; i < m_values.size(); ++i) { + const AnimationValue* curValue = m_values[i].get(); + if (curValue->keyTime() == value->keyTime()) { + ASSERT_NOT_REACHED(); + // insert after + m_values.insert(i + 1, value); + return; } + if (curValue->keyTime() > value->keyTime()) { + // insert before + m_values.insert(i, value); + return; + } + } - m_values.append(value); + m_values.append(value); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/animation/KeyframeValueList.h b/sky/engine/platform/animation/KeyframeValueList.h index a3a038433ee4d..830567ef07a21 100644 --- a/sky/engine/platform/animation/KeyframeValueList.h +++ b/sky/engine/platform/animation/KeyframeValueList.h @@ -36,55 +36,50 @@ namespace blink { enum AnimatedPropertyID { - AnimatedPropertyInvalid, - AnimatedPropertyTransform, - AnimatedPropertyOpacity, - AnimatedPropertyBackgroundColor, - AnimatedPropertyFilter + AnimatedPropertyInvalid, + AnimatedPropertyTransform, + AnimatedPropertyOpacity, + AnimatedPropertyBackgroundColor, + AnimatedPropertyFilter }; // Used to store a series of values in a keyframe list. // Values will all be of the same type, which can be inferred from the property. class PLATFORM_EXPORT KeyframeValueList { -public: - explicit KeyframeValueList(AnimatedPropertyID property) - : m_property(property) - { - } + public: + explicit KeyframeValueList(AnimatedPropertyID property) + : m_property(property) {} - KeyframeValueList(const KeyframeValueList& other) - : m_property(other.property()) - { - for (size_t i = 0; i < other.m_values.size(); ++i) - m_values.append(other.m_values[i]->clone()); - } + KeyframeValueList(const KeyframeValueList& other) + : m_property(other.property()) { + for (size_t i = 0; i < other.m_values.size(); ++i) + m_values.append(other.m_values[i]->clone()); + } - KeyframeValueList& operator=(const KeyframeValueList& other) - { - KeyframeValueList copy(other); - swap(copy); - return *this; - } + KeyframeValueList& operator=(const KeyframeValueList& other) { + KeyframeValueList copy(other); + swap(copy); + return *this; + } - void swap(KeyframeValueList& other) - { - std::swap(m_property, other.m_property); - m_values.swap(other.m_values); - } + void swap(KeyframeValueList& other) { + std::swap(m_property, other.m_property); + m_values.swap(other.m_values); + } - AnimatedPropertyID property() const { return m_property; } + AnimatedPropertyID property() const { return m_property; } - size_t size() const { return m_values.size(); } - const AnimationValue* at(size_t i) const { return m_values.at(i).get(); } + size_t size() const { return m_values.size(); } + const AnimationValue* at(size_t i) const { return m_values.at(i).get(); } - // Insert, sorted by keyTime. - void insert(PassOwnPtr); + // Insert, sorted by keyTime. + void insert(PassOwnPtr); -protected: - Vector > m_values; - AnimatedPropertyID m_property; + protected: + Vector> m_values; + AnimatedPropertyID m_property; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_ANIMATION_KEYFRAMEVALUELIST_H_ diff --git a/sky/engine/platform/animation/TimingFunction.cpp b/sky/engine/platform/animation/TimingFunction.cpp index c6b32e339fde9..dad8e400eb167 100644 --- a/sky/engine/platform/animation/TimingFunction.cpp +++ b/sky/engine/platform/animation/TimingFunction.cpp @@ -8,221 +8,218 @@ namespace blink { -String LinearTimingFunction::toString() const -{ - return "linear"; +String LinearTimingFunction::toString() const { + return "linear"; } -double LinearTimingFunction::evaluate(double fraction, double) const -{ - return fraction; +double LinearTimingFunction::evaluate(double fraction, double) const { + return fraction; } -void LinearTimingFunction::range(double* minValue, double* maxValue) const -{ -} +void LinearTimingFunction::range(double* minValue, double* maxValue) const {} -String CubicBezierTimingFunction::toString() const -{ - switch (this->subType()) { +String CubicBezierTimingFunction::toString() const { + switch (this->subType()) { case CubicBezierTimingFunction::Ease: - return "ease"; + return "ease"; case CubicBezierTimingFunction::EaseIn: - return "ease-in"; + return "ease-in"; case CubicBezierTimingFunction::EaseOut: - return "ease-out"; + return "ease-out"; case CubicBezierTimingFunction::EaseInOut: - return "ease-in-out"; + return "ease-in-out"; case CubicBezierTimingFunction::Custom: - return "cubic-bezier(" + String::numberToStringECMAScript(this->x1()) + ", " + - String::numberToStringECMAScript(this->y1()) + ", " + String::numberToStringECMAScript(this->x2()) + - ", " + String::numberToStringECMAScript(this->y2()) + ")"; + return "cubic-bezier(" + String::numberToStringECMAScript(this->x1()) + + ", " + String::numberToStringECMAScript(this->y1()) + ", " + + String::numberToStringECMAScript(this->x2()) + ", " + + String::numberToStringECMAScript(this->y2()) + ")"; default: - ASSERT_NOT_REACHED(); - } - return ""; + ASSERT_NOT_REACHED(); + } + return ""; } -double CubicBezierTimingFunction::evaluate(double fraction, double accuracy) const -{ - if (!m_bezier) - m_bezier = adoptPtr(new UnitBezier(m_x1, m_y1, m_x2, m_y2)); - return m_bezier->solve(fraction, accuracy); +double CubicBezierTimingFunction::evaluate(double fraction, + double accuracy) const { + if (!m_bezier) + m_bezier = adoptPtr(new UnitBezier(m_x1, m_y1, m_x2, m_y2)); + return m_bezier->solve(fraction, accuracy); } // This works by taking taking the derivative of the cubic bezier, on the y // axis. We can then solve for where the derivative is zero to find the min // and max distace along the line. We the have to solve those in terms of time // rather than distance on the x-axis -void CubicBezierTimingFunction::range(double* minValue, double* maxValue) const -{ - if (0 <= m_y1 && m_y2 < 1 && 0 <= m_y2 && m_y2 <= 1) { - return; - } - - double a = 3.0 * (m_y1 - m_y2) + 1.0; - double b = 2.0 * (m_y2 - 2.0 * m_y1); - double c = m_y1; - - if (std::abs(a) < std::numeric_limits::epsilon() - && std::abs(b) < std::numeric_limits::epsilon()) { - return; - } - - double t1 = 0.0; - double t2 = 0.0; - - if (std::abs(a) < std::numeric_limits::epsilon()) { - t1 = -c / b; - } else { - double discriminant = b * b - 4 * a * c; - if (discriminant < 0) - return; - double discriminantSqrt = sqrt(discriminant); - t1 = (-b + discriminantSqrt) / (2 * a); - t2 = (-b - discriminantSqrt) / (2 * a); - } - - double solution1 = 0.0; - double solution2 = 0.0; - - // If the solution is in the range [0,1] then we include it, otherwise we - // ignore it. - if (!m_bezier) - m_bezier = adoptPtr(new UnitBezier(m_x1, m_y1, m_x2, m_y2)); - - // An interesting fact about these beziers is that they are only - // actually evaluated in [0,1]. After that we take the tangent at that point - // and linearly project it out. - if (0 < t1 && t1 < 1) - solution1= m_bezier->sampleCurveY(t1); - - if (0 < t2 && t2 < 1) - solution2 = m_bezier->sampleCurveY(t2); - - // Since our input values can be out of the range 0->1 so we must also - // consider the minimum and maximum points. - double solutionMin = m_bezier->solve(*minValue, std::numeric_limits::epsilon()); - double solutionMax = m_bezier->solve(*maxValue, std::numeric_limits::epsilon()); - *minValue = std::min(std::min(solutionMin, solutionMax), 0.0); - *maxValue = std::max(std::max(solutionMin, solutionMax), 1.0); - *minValue = std::min(std::min(*minValue, solution1), solution2); - *maxValue = std::max(std::max(*maxValue, solution1), solution2); +void CubicBezierTimingFunction::range(double* minValue, + double* maxValue) const { + if (0 <= m_y1 && m_y2 < 1 && 0 <= m_y2 && m_y2 <= 1) { + return; + } + + double a = 3.0 * (m_y1 - m_y2) + 1.0; + double b = 2.0 * (m_y2 - 2.0 * m_y1); + double c = m_y1; + + if (std::abs(a) < std::numeric_limits::epsilon() && + std::abs(b) < std::numeric_limits::epsilon()) { + return; + } + + double t1 = 0.0; + double t2 = 0.0; + + if (std::abs(a) < std::numeric_limits::epsilon()) { + t1 = -c / b; + } else { + double discriminant = b * b - 4 * a * c; + if (discriminant < 0) + return; + double discriminantSqrt = sqrt(discriminant); + t1 = (-b + discriminantSqrt) / (2 * a); + t2 = (-b - discriminantSqrt) / (2 * a); + } + + double solution1 = 0.0; + double solution2 = 0.0; + + // If the solution is in the range [0,1] then we include it, otherwise we + // ignore it. + if (!m_bezier) + m_bezier = adoptPtr(new UnitBezier(m_x1, m_y1, m_x2, m_y2)); + + // An interesting fact about these beziers is that they are only + // actually evaluated in [0,1]. After that we take the tangent at that point + // and linearly project it out. + if (0 < t1 && t1 < 1) + solution1 = m_bezier->sampleCurveY(t1); + + if (0 < t2 && t2 < 1) + solution2 = m_bezier->sampleCurveY(t2); + + // Since our input values can be out of the range 0->1 so we must also + // consider the minimum and maximum points. + double solutionMin = + m_bezier->solve(*minValue, std::numeric_limits::epsilon()); + double solutionMax = + m_bezier->solve(*maxValue, std::numeric_limits::epsilon()); + *minValue = std::min(std::min(solutionMin, solutionMax), 0.0); + *maxValue = std::max(std::max(solutionMin, solutionMax), 1.0); + *minValue = std::min(std::min(*minValue, solution1), solution2); + *maxValue = std::max(std::max(*maxValue, solution1), solution2); } -String StepsTimingFunction::toString() const -{ - StringBuilder builder; - switch (this->subType()) { +String StepsTimingFunction::toString() const { + StringBuilder builder; + switch (this->subType()) { case StepsTimingFunction::Start: - return "step-start"; + return "step-start"; case StepsTimingFunction::Middle: - return "step-middle"; + return "step-middle"; case StepsTimingFunction::End: - return "step-end"; + return "step-end"; case StepsTimingFunction::Custom: - builder.append("steps(" + String::numberToStringECMAScript(this->numberOfSteps()) + ", "); - - if (this->stepAtPosition() == StepsTimingFunction::StepAtStart) - builder.appendLiteral("start"); - else if (this->stepAtPosition() == StepsTimingFunction::StepAtMiddle) - builder.appendLiteral("middle"); - else if (this->stepAtPosition() == StepsTimingFunction::StepAtEnd) - builder.appendLiteral("end"); - else - ASSERT_NOT_REACHED(); - - builder.append(')'); - break; - default: + builder.append("steps(" + + String::numberToStringECMAScript(this->numberOfSteps()) + + ", "); + + if (this->stepAtPosition() == StepsTimingFunction::StepAtStart) + builder.appendLiteral("start"); + else if (this->stepAtPosition() == StepsTimingFunction::StepAtMiddle) + builder.appendLiteral("middle"); + else if (this->stepAtPosition() == StepsTimingFunction::StepAtEnd) + builder.appendLiteral("end"); + else ASSERT_NOT_REACHED(); - } - return builder.toString(); + + builder.append(')'); + break; + default: + ASSERT_NOT_REACHED(); + } + return builder.toString(); } -void StepsTimingFunction::range(double* minValue, double* maxValue) const -{ - *minValue = 0; - *maxValue = 1; +void StepsTimingFunction::range(double* minValue, double* maxValue) const { + *minValue = 0; + *maxValue = 1; } -double StepsTimingFunction::evaluate(double fraction, double) const -{ - double startOffset = 0; - switch (m_stepAtPosition) { +double StepsTimingFunction::evaluate(double fraction, double) const { + double startOffset = 0; + switch (m_stepAtPosition) { case StepAtStart: - startOffset = 1; - break; + startOffset = 1; + break; case StepAtMiddle: - startOffset = 0.5; - break; + startOffset = 0.5; + break; case StepAtEnd: - startOffset = 0; - break; + startOffset = 0; + break; default: - ASSERT_NOT_REACHED(); - break; - } - return clampTo(floor((m_steps * fraction) + startOffset) / m_steps, 0.0, 1.0); + ASSERT_NOT_REACHED(); + break; + } + return clampTo(floor((m_steps * fraction) + startOffset) / m_steps, 0.0, 1.0); } // Equals operators -bool operator==(const LinearTimingFunction& lhs, const TimingFunction& rhs) -{ - return rhs.type() == TimingFunction::LinearFunction; +bool operator==(const LinearTimingFunction& lhs, const TimingFunction& rhs) { + return rhs.type() == TimingFunction::LinearFunction; } -bool operator==(const CubicBezierTimingFunction& lhs, const TimingFunction& rhs) -{ - if (rhs.type() != TimingFunction::CubicBezierFunction) - return false; +bool operator==(const CubicBezierTimingFunction& lhs, + const TimingFunction& rhs) { + if (rhs.type() != TimingFunction::CubicBezierFunction) + return false; - const CubicBezierTimingFunction& ctf = toCubicBezierTimingFunction(rhs); - if ((lhs.subType() == CubicBezierTimingFunction::Custom) && (ctf.subType() == CubicBezierTimingFunction::Custom)) - return (lhs.x1() == ctf.x1()) && (lhs.y1() == ctf.y1()) && (lhs.x2() == ctf.x2()) && (lhs.y2() == ctf.y2()); + const CubicBezierTimingFunction& ctf = toCubicBezierTimingFunction(rhs); + if ((lhs.subType() == CubicBezierTimingFunction::Custom) && + (ctf.subType() == CubicBezierTimingFunction::Custom)) + return (lhs.x1() == ctf.x1()) && (lhs.y1() == ctf.y1()) && + (lhs.x2() == ctf.x2()) && (lhs.y2() == ctf.y2()); - return lhs.subType() == ctf.subType(); + return lhs.subType() == ctf.subType(); } -bool operator==(const StepsTimingFunction& lhs, const TimingFunction& rhs) -{ - if (rhs.type() != TimingFunction::StepsFunction) - return false; +bool operator==(const StepsTimingFunction& lhs, const TimingFunction& rhs) { + if (rhs.type() != TimingFunction::StepsFunction) + return false; - const StepsTimingFunction& stf = toStepsTimingFunction(rhs); - if ((lhs.subType() == StepsTimingFunction::Custom) && (stf.subType() == StepsTimingFunction::Custom)) - return (lhs.numberOfSteps() == stf.numberOfSteps()) && (lhs.stepAtPosition() == stf.stepAtPosition()); + const StepsTimingFunction& stf = toStepsTimingFunction(rhs); + if ((lhs.subType() == StepsTimingFunction::Custom) && + (stf.subType() == StepsTimingFunction::Custom)) + return (lhs.numberOfSteps() == stf.numberOfSteps()) && + (lhs.stepAtPosition() == stf.stepAtPosition()); - return lhs.subType() == stf.subType(); + return lhs.subType() == stf.subType(); } // The generic operator== *must* come after the // non-generic operator== otherwise it will end up calling itself. -bool operator==(const TimingFunction& lhs, const TimingFunction& rhs) -{ - switch (lhs.type()) { +bool operator==(const TimingFunction& lhs, const TimingFunction& rhs) { + switch (lhs.type()) { case TimingFunction::LinearFunction: { - const LinearTimingFunction& linear = toLinearTimingFunction(lhs); - return (linear == rhs); + const LinearTimingFunction& linear = toLinearTimingFunction(lhs); + return (linear == rhs); } case TimingFunction::CubicBezierFunction: { - const CubicBezierTimingFunction& cubic = toCubicBezierTimingFunction(lhs); - return (cubic == rhs); + const CubicBezierTimingFunction& cubic = toCubicBezierTimingFunction(lhs); + return (cubic == rhs); } case TimingFunction::StepsFunction: { - const StepsTimingFunction& step = toStepsTimingFunction(lhs); - return (step == rhs); + const StepsTimingFunction& step = toStepsTimingFunction(lhs); + return (step == rhs); } default: - ASSERT_NOT_REACHED(); - } - return false; + ASSERT_NOT_REACHED(); + } + return false; } // No need to define specific operator!= as they can all come via this function. -bool operator!=(const TimingFunction& lhs, const TimingFunction& rhs) -{ - return !(lhs == rhs); +bool operator!=(const TimingFunction& lhs, const TimingFunction& rhs) { + return !(lhs == rhs); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/animation/TimingFunction.h b/sky/engine/platform/animation/TimingFunction.h index 6329e6b943306..d3c635a4d0689 100644 --- a/sky/engine/platform/animation/TimingFunction.h +++ b/sky/engine/platform/animation/TimingFunction.h @@ -39,223 +39,208 @@ namespace blink { class PLATFORM_EXPORT TimingFunction : public RefCounted { -public: + public: + enum Type { LinearFunction, CubicBezierFunction, StepsFunction }; - enum Type { - LinearFunction, CubicBezierFunction, StepsFunction - }; + virtual ~TimingFunction() {} - virtual ~TimingFunction() { } + Type type() const { return m_type; } - Type type() const { return m_type; } + virtual String toString() const = 0; - virtual String toString() const = 0; + // Evaluates the timing function at the given fraction. The accuracy parameter + // provides a hint as to the required accuracy and is not guaranteed. + virtual double evaluate(double fraction, double accuracy) const = 0; - // Evaluates the timing function at the given fraction. The accuracy parameter provides a hint as to the required - // accuracy and is not guaranteed. - virtual double evaluate(double fraction, double accuracy) const = 0; + // This function returns the minimum and maximum values obtainable when + // calling evaluate(); + virtual void range(double* minValue, double* maxValue) const = 0; - // This function returns the minimum and maximum values obtainable when - // calling evaluate(); - virtual void range(double* minValue, double* maxValue) const = 0; + protected: + TimingFunction(Type type) : m_type(type) {} -protected: - TimingFunction(Type type) - : m_type(type) - { - } - -private: - Type m_type; + private: + Type m_type; }; class PLATFORM_EXPORT LinearTimingFunction final : public TimingFunction { -public: - static LinearTimingFunction* shared() - { - DEFINE_STATIC_REF(LinearTimingFunction, linear, (adoptRef(new LinearTimingFunction()))); - return linear; - } + public: + static LinearTimingFunction* shared() { + DEFINE_STATIC_REF(LinearTimingFunction, linear, + (adoptRef(new LinearTimingFunction()))); + return linear; + } - virtual ~LinearTimingFunction() { } + virtual ~LinearTimingFunction() {} - virtual String toString() const override; + virtual String toString() const override; - virtual double evaluate(double fraction, double) const override; + virtual double evaluate(double fraction, double) const override; - virtual void range(double* minValue, double* maxValue) const override; -private: - LinearTimingFunction() - : TimingFunction(LinearFunction) - { - } + virtual void range(double* minValue, double* maxValue) const override; + + private: + LinearTimingFunction() : TimingFunction(LinearFunction) {} }; class PLATFORM_EXPORT CubicBezierTimingFunction final : public TimingFunction { -public: - enum SubType { - Ease, - EaseIn, - EaseOut, - EaseInOut, - Custom - }; - - static PassRefPtr create(double x1, double y1, double x2, double y2) - { - return adoptRef(new CubicBezierTimingFunction(Custom, x1, y1, x2, y2)); + public: + enum SubType { Ease, EaseIn, EaseOut, EaseInOut, Custom }; + + static PassRefPtr create(double x1, + double y1, + double x2, + double y2) { + return adoptRef(new CubicBezierTimingFunction(Custom, x1, y1, x2, y2)); + } + + static CubicBezierTimingFunction* preset(SubType subType) { + switch (subType) { + case Ease: { + DEFINE_STATIC_REF(CubicBezierTimingFunction, ease, + (adoptRef(new CubicBezierTimingFunction( + Ease, 0.25, 0.1, 0.25, 1.0)))); + return ease; + } + case EaseIn: { + DEFINE_STATIC_REF(CubicBezierTimingFunction, easeIn, + (adoptRef(new CubicBezierTimingFunction( + EaseIn, 0.42, 0.0, 1.0, 1.0)))); + return easeIn; + } + case EaseOut: { + DEFINE_STATIC_REF(CubicBezierTimingFunction, easeOut, + (adoptRef(new CubicBezierTimingFunction( + EaseOut, 0.0, 0.0, 0.58, 1.0)))); + return easeOut; + } + case EaseInOut: { + DEFINE_STATIC_REF(CubicBezierTimingFunction, easeInOut, + (adoptRef(new CubicBezierTimingFunction( + EaseInOut, 0.42, 0.0, 0.58, 1.0)))); + return easeInOut; + } + default: + ASSERT_NOT_REACHED(); + return 0; } - - static CubicBezierTimingFunction* preset(SubType subType) - { - switch (subType) { - case Ease: - { - DEFINE_STATIC_REF(CubicBezierTimingFunction, ease, (adoptRef(new CubicBezierTimingFunction(Ease, 0.25, 0.1, 0.25, 1.0)))); - return ease; - } - case EaseIn: - { - DEFINE_STATIC_REF(CubicBezierTimingFunction, easeIn, (adoptRef(new CubicBezierTimingFunction(EaseIn, 0.42, 0.0, 1.0, 1.0)))); - return easeIn; - } - case EaseOut: - { - DEFINE_STATIC_REF(CubicBezierTimingFunction, easeOut, (adoptRef(new CubicBezierTimingFunction(EaseOut, 0.0, 0.0, 0.58, 1.0)))); - return easeOut; - } - case EaseInOut: - { - DEFINE_STATIC_REF(CubicBezierTimingFunction, easeInOut, (adoptRef(new CubicBezierTimingFunction(EaseInOut, 0.42, 0.0, 0.58, 1.0)))); - return easeInOut; - } - default: - ASSERT_NOT_REACHED(); - return 0; - } - } - - virtual ~CubicBezierTimingFunction() { } - - virtual String toString() const override; - - virtual double evaluate(double fraction, double accuracy) const override; - virtual void range(double* minValue, double* maxValue) const override; - - double x1() const { return m_x1; } - double y1() const { return m_y1; } - double x2() const { return m_x2; } - double y2() const { return m_y2; } - - SubType subType() const { return m_subType; } - -private: - explicit CubicBezierTimingFunction(SubType subType, double x1, double y1, double x2, double y2) - : TimingFunction(CubicBezierFunction) - , m_x1(x1) - , m_y1(y1) - , m_x2(x2) - , m_y2(y2) - , m_subType(subType) - { - } - - double m_x1; - double m_y1; - double m_x2; - double m_y2; - SubType m_subType; - mutable OwnPtr m_bezier; + } + + virtual ~CubicBezierTimingFunction() {} + + virtual String toString() const override; + + virtual double evaluate(double fraction, double accuracy) const override; + virtual void range(double* minValue, double* maxValue) const override; + + double x1() const { return m_x1; } + double y1() const { return m_y1; } + double x2() const { return m_x2; } + double y2() const { return m_y2; } + + SubType subType() const { return m_subType; } + + private: + explicit CubicBezierTimingFunction(SubType subType, + double x1, + double y1, + double x2, + double y2) + : TimingFunction(CubicBezierFunction), + m_x1(x1), + m_y1(y1), + m_x2(x2), + m_y2(y2), + m_subType(subType) {} + + double m_x1; + double m_y1; + double m_x2; + double m_y2; + SubType m_subType; + mutable OwnPtr m_bezier; }; class PLATFORM_EXPORT StepsTimingFunction final : public TimingFunction { -public: - enum SubType { - Start, - End, - Middle, - Custom - }; - - enum StepAtPosition { - StepAtStart, - StepAtMiddle, - StepAtEnd - }; - - static PassRefPtr create(int steps, StepAtPosition stepAtPosition) - { - return adoptRef(new StepsTimingFunction(Custom, steps, stepAtPosition)); - } - - static StepsTimingFunction* preset(SubType subType) - { - switch (subType) { - case Start: - { - DEFINE_STATIC_REF(StepsTimingFunction, start, (adoptRef(new StepsTimingFunction(Start, 1, StepAtStart)))); - return start; - } - case Middle: - { - DEFINE_STATIC_REF(StepsTimingFunction, middle, (adoptRef(new StepsTimingFunction(Middle, 1, StepAtMiddle)))); - return middle; - } - case End: - { - DEFINE_STATIC_REF(StepsTimingFunction, end, (adoptRef(new StepsTimingFunction(End, 1, StepAtEnd)))); - return end; - } - default: - ASSERT_NOT_REACHED(); - return 0; - } + public: + enum SubType { Start, End, Middle, Custom }; + + enum StepAtPosition { StepAtStart, StepAtMiddle, StepAtEnd }; + + static PassRefPtr create(int steps, + StepAtPosition stepAtPosition) { + return adoptRef(new StepsTimingFunction(Custom, steps, stepAtPosition)); + } + + static StepsTimingFunction* preset(SubType subType) { + switch (subType) { + case Start: { + DEFINE_STATIC_REF( + StepsTimingFunction, start, + (adoptRef(new StepsTimingFunction(Start, 1, StepAtStart)))); + return start; + } + case Middle: { + DEFINE_STATIC_REF( + StepsTimingFunction, middle, + (adoptRef(new StepsTimingFunction(Middle, 1, StepAtMiddle)))); + return middle; + } + case End: { + DEFINE_STATIC_REF( + StepsTimingFunction, end, + (adoptRef(new StepsTimingFunction(End, 1, StepAtEnd)))); + return end; + } + default: + ASSERT_NOT_REACHED(); + return 0; } + } + virtual ~StepsTimingFunction() {} - virtual ~StepsTimingFunction() { } + virtual String toString() const override; - virtual String toString() const override; + virtual double evaluate(double fraction, double) const override; - virtual double evaluate(double fraction, double) const override; + virtual void range(double* minValue, double* maxValue) const override; + int numberOfSteps() const { return m_steps; } + StepAtPosition stepAtPosition() const { return m_stepAtPosition; } - virtual void range(double* minValue, double* maxValue) const override; - int numberOfSteps() const { return m_steps; } - StepAtPosition stepAtPosition() const { return m_stepAtPosition; } + SubType subType() const { return m_subType; } - SubType subType() const { return m_subType; } - -private: - StepsTimingFunction(SubType subType, int steps, StepAtPosition stepAtPosition) - : TimingFunction(StepsFunction) - , m_steps(steps) - , m_stepAtPosition(stepAtPosition) - , m_subType(subType) - { - } + private: + StepsTimingFunction(SubType subType, int steps, StepAtPosition stepAtPosition) + : TimingFunction(StepsFunction), + m_steps(steps), + m_stepAtPosition(stepAtPosition), + m_subType(subType) {} - int m_steps; - StepAtPosition m_stepAtPosition; - SubType m_subType; + int m_steps; + StepAtPosition m_stepAtPosition; + SubType m_subType; }; -PLATFORM_EXPORT bool operator==(const LinearTimingFunction&, const TimingFunction&); -PLATFORM_EXPORT bool operator==(const CubicBezierTimingFunction&, const TimingFunction&); -PLATFORM_EXPORT bool operator==(const StepsTimingFunction&, const TimingFunction&); +PLATFORM_EXPORT bool operator==(const LinearTimingFunction&, + const TimingFunction&); +PLATFORM_EXPORT bool operator==(const CubicBezierTimingFunction&, + const TimingFunction&); +PLATFORM_EXPORT bool operator==(const StepsTimingFunction&, + const TimingFunction&); PLATFORM_EXPORT bool operator==(const TimingFunction&, const TimingFunction&); PLATFORM_EXPORT bool operator!=(const TimingFunction&, const TimingFunction&); -#define DEFINE_TIMING_FUNCTION_TYPE_CASTS(typeName) \ - DEFINE_TYPE_CASTS( \ - typeName##TimingFunction, TimingFunction, value, \ - value->type() == TimingFunction::typeName##Function, \ - value.type() == TimingFunction::typeName##Function) +#define DEFINE_TIMING_FUNCTION_TYPE_CASTS(typeName) \ + DEFINE_TYPE_CASTS(typeName##TimingFunction, TimingFunction, value, \ + value->type() == TimingFunction::typeName##Function, \ + value.type() == TimingFunction::typeName##Function) DEFINE_TIMING_FUNCTION_TYPE_CASTS(Linear); DEFINE_TIMING_FUNCTION_TYPE_CASTS(CubicBezier); DEFINE_TIMING_FUNCTION_TYPE_CASTS(Steps); -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_ANIMATION_TIMINGFUNCTION_H_ diff --git a/sky/engine/platform/animation/TimingFunctionTest.cpp b/sky/engine/platform/animation/TimingFunctionTest.cpp index 1a6c1c88e2387..dea32efa0644a 100644 --- a/sky/engine/platform/animation/TimingFunctionTest.cpp +++ b/sky/engine/platform/animation/TimingFunctionTest.cpp @@ -38,381 +38,424 @@ // Macro is only used to allow the use of streams. // Can be removed if a pretty failure message isn't needed. -#define EXPECT_NE_WITH_MESSAGE(a, b) \ - EXPECT_NE(*a.second, *b.second) \ - << a.first \ - << " (" << a.second->toString().latin1().data() << ")" \ - << " == " \ - << b.first \ - << " (" << b.second->toString().latin1().data() << ")" \ - << "\n"; +#define EXPECT_NE_WITH_MESSAGE(a, b) \ + EXPECT_NE(*a.second, *b.second) \ + << a.first << " (" << a.second->toString().latin1().data() << ")" \ + << " == " << b.first << " (" << b.second->toString().latin1().data() \ + << ")" \ + << "\n"; namespace blink { namespace { class TimingFunctionTest : public ::testing::Test { -public: - void notEqualHelperLoop(Vector > >& v) - { - for (size_t i = 0; i < v.size(); ++i) { - for (size_t j = 0; j < v.size(); ++j) { - if (i == j) - continue; - EXPECT_NE_WITH_MESSAGE(v[i], v[j]); - } - } + public: + void notEqualHelperLoop( + Vector>>& v) { + for (size_t i = 0; i < v.size(); ++i) { + for (size_t j = 0; j < v.size(); ++j) { + if (i == j) + continue; + EXPECT_NE_WITH_MESSAGE(v[i], v[j]); + } } + } }; -TEST_F(TimingFunctionTest, LinearToString) -{ - RefPtr linearTiming = LinearTimingFunction::shared(); - EXPECT_EQ(linearTiming->toString(), "linear"); +TEST_F(TimingFunctionTest, LinearToString) { + RefPtr linearTiming = LinearTimingFunction::shared(); + EXPECT_EQ(linearTiming->toString(), "linear"); } -TEST_F(TimingFunctionTest, CubicToString) -{ - RefPtr cubicEaseTiming = CubicBezierTimingFunction::preset(CubicBezierTimingFunction::Ease); - EXPECT_EQ("ease", cubicEaseTiming->toString()); - RefPtr cubicEaseInTiming = CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseIn); - EXPECT_EQ("ease-in", cubicEaseInTiming->toString()); - RefPtr cubicEaseOutTiming = CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseOut); - EXPECT_EQ("ease-out", cubicEaseOutTiming->toString()); - RefPtr cubicEaseInOutTiming = CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseInOut); - EXPECT_EQ("ease-in-out", cubicEaseInOutTiming->toString()); - - RefPtr cubicCustomTiming = CubicBezierTimingFunction::create(0.17, 0.67, 1, -1.73); - EXPECT_EQ("cubic-bezier(0.17, 0.67, 1, -1.73)", cubicCustomTiming->toString()); +TEST_F(TimingFunctionTest, CubicToString) { + RefPtr cubicEaseTiming = + CubicBezierTimingFunction::preset(CubicBezierTimingFunction::Ease); + EXPECT_EQ("ease", cubicEaseTiming->toString()); + RefPtr cubicEaseInTiming = + CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseIn); + EXPECT_EQ("ease-in", cubicEaseInTiming->toString()); + RefPtr cubicEaseOutTiming = + CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseOut); + EXPECT_EQ("ease-out", cubicEaseOutTiming->toString()); + RefPtr cubicEaseInOutTiming = + CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseInOut); + EXPECT_EQ("ease-in-out", cubicEaseInOutTiming->toString()); + + RefPtr cubicCustomTiming = + CubicBezierTimingFunction::create(0.17, 0.67, 1, -1.73); + EXPECT_EQ("cubic-bezier(0.17, 0.67, 1, -1.73)", + cubicCustomTiming->toString()); } -TEST_F(TimingFunctionTest, StepToString) -{ - RefPtr stepTimingStart = StepsTimingFunction::preset(StepsTimingFunction::Start); - EXPECT_EQ("step-start", stepTimingStart->toString()); +TEST_F(TimingFunctionTest, StepToString) { + RefPtr stepTimingStart = + StepsTimingFunction::preset(StepsTimingFunction::Start); + EXPECT_EQ("step-start", stepTimingStart->toString()); - RefPtr stepTimingMiddle = StepsTimingFunction::preset(StepsTimingFunction::Middle); - EXPECT_EQ("step-middle", stepTimingMiddle->toString()); + RefPtr stepTimingMiddle = + StepsTimingFunction::preset(StepsTimingFunction::Middle); + EXPECT_EQ("step-middle", stepTimingMiddle->toString()); - RefPtr stepTimingEnd = StepsTimingFunction::preset(StepsTimingFunction::End); - EXPECT_EQ("step-end", stepTimingEnd->toString()); + RefPtr stepTimingEnd = + StepsTimingFunction::preset(StepsTimingFunction::End); + EXPECT_EQ("step-end", stepTimingEnd->toString()); - RefPtr stepTimingCustomStart = StepsTimingFunction::create(3, StepsTimingFunction::StepAtStart); - EXPECT_EQ("steps(3, start)", stepTimingCustomStart->toString()); + RefPtr stepTimingCustomStart = + StepsTimingFunction::create(3, StepsTimingFunction::StepAtStart); + EXPECT_EQ("steps(3, start)", stepTimingCustomStart->toString()); - RefPtr stepTimingCustomMiddle = StepsTimingFunction::create(4, StepsTimingFunction::StepAtMiddle); - EXPECT_EQ("steps(4, middle)", stepTimingCustomMiddle->toString()); + RefPtr stepTimingCustomMiddle = + StepsTimingFunction::create(4, StepsTimingFunction::StepAtMiddle); + EXPECT_EQ("steps(4, middle)", stepTimingCustomMiddle->toString()); - RefPtr stepTimingCustomEnd = StepsTimingFunction::create(5, StepsTimingFunction::StepAtEnd); - EXPECT_EQ("steps(5, end)", stepTimingCustomEnd->toString()); + RefPtr stepTimingCustomEnd = + StepsTimingFunction::create(5, StepsTimingFunction::StepAtEnd); + EXPECT_EQ("steps(5, end)", stepTimingCustomEnd->toString()); } -TEST_F(TimingFunctionTest, BaseOperatorEq) -{ - RefPtr linearTiming = LinearTimingFunction::shared(); - RefPtr cubicTiming1 = CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseIn); - RefPtr cubicTiming2 = CubicBezierTimingFunction::create(0.17, 0.67, 1, -1.73); - RefPtr stepsTiming1 = StepsTimingFunction::preset(StepsTimingFunction::End); - RefPtr stepsTiming2 = StepsTimingFunction::create(5, StepsTimingFunction::StepAtStart); - - Vector > > v; - v.append(std::make_pair("linearTiming", linearTiming)); - v.append(std::make_pair("cubicTiming1", cubicTiming1)); - v.append(std::make_pair("cubicTiming2", cubicTiming2)); - v.append(std::make_pair("stepsTiming1", stepsTiming1)); - v.append(std::make_pair("stepsTiming2", stepsTiming2)); - notEqualHelperLoop(v); +TEST_F(TimingFunctionTest, BaseOperatorEq) { + RefPtr linearTiming = LinearTimingFunction::shared(); + RefPtr cubicTiming1 = + CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseIn); + RefPtr cubicTiming2 = + CubicBezierTimingFunction::create(0.17, 0.67, 1, -1.73); + RefPtr stepsTiming1 = + StepsTimingFunction::preset(StepsTimingFunction::End); + RefPtr stepsTiming2 = + StepsTimingFunction::create(5, StepsTimingFunction::StepAtStart); + + Vector>> v; + v.append(std::make_pair("linearTiming", linearTiming)); + v.append(std::make_pair("cubicTiming1", cubicTiming1)); + v.append(std::make_pair("cubicTiming2", cubicTiming2)); + v.append(std::make_pair("stepsTiming1", stepsTiming1)); + v.append(std::make_pair("stepsTiming2", stepsTiming2)); + notEqualHelperLoop(v); } -TEST_F(TimingFunctionTest, LinearOperatorEq) -{ - RefPtr linearTiming1 = LinearTimingFunction::shared(); - RefPtr linearTiming2 = LinearTimingFunction::shared(); - EXPECT_EQ(*linearTiming1, *linearTiming1); - EXPECT_EQ(*linearTiming1, *linearTiming2); +TEST_F(TimingFunctionTest, LinearOperatorEq) { + RefPtr linearTiming1 = LinearTimingFunction::shared(); + RefPtr linearTiming2 = LinearTimingFunction::shared(); + EXPECT_EQ(*linearTiming1, *linearTiming1); + EXPECT_EQ(*linearTiming1, *linearTiming2); } -TEST_F(TimingFunctionTest, CubicOperatorEq) -{ - RefPtr cubicEaseInTiming1 = CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseIn); - RefPtr cubicEaseInTiming2 = CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseIn); - EXPECT_EQ(*cubicEaseInTiming1, *cubicEaseInTiming1); - EXPECT_EQ(*cubicEaseInTiming1, *cubicEaseInTiming2); - - RefPtr cubicEaseOutTiming1 = CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseOut); - RefPtr cubicEaseOutTiming2 = CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseOut); - EXPECT_EQ(*cubicEaseOutTiming1, *cubicEaseOutTiming1); - EXPECT_EQ(*cubicEaseOutTiming1, *cubicEaseOutTiming2); - - RefPtr cubicEaseInOutTiming1 = CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseInOut); - RefPtr cubicEaseInOutTiming2 = CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseInOut); - EXPECT_EQ(*cubicEaseInOutTiming1, *cubicEaseInOutTiming1); - EXPECT_EQ(*cubicEaseInOutTiming1, *cubicEaseInOutTiming2); - - RefPtr cubicCustomTiming1 = CubicBezierTimingFunction::create(0.17, 0.67, 1, -1.73); - RefPtr cubicCustomTiming2 = CubicBezierTimingFunction::create(0.17, 0.67, 1, -1.73); - EXPECT_EQ(*cubicCustomTiming1, *cubicCustomTiming1); - EXPECT_EQ(*cubicCustomTiming1, *cubicCustomTiming2); - - Vector > > v; - v.append(std::make_pair("cubicEaseInTiming1", cubicEaseInTiming1)); - v.append(std::make_pair("cubicEaseOutTiming1", cubicEaseOutTiming1)); - v.append(std::make_pair("cubicEaseInOutTiming1", cubicEaseInOutTiming1)); - v.append(std::make_pair("cubicCustomTiming1", cubicCustomTiming1)); - notEqualHelperLoop(v); +TEST_F(TimingFunctionTest, CubicOperatorEq) { + RefPtr cubicEaseInTiming1 = + CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseIn); + RefPtr cubicEaseInTiming2 = + CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseIn); + EXPECT_EQ(*cubicEaseInTiming1, *cubicEaseInTiming1); + EXPECT_EQ(*cubicEaseInTiming1, *cubicEaseInTiming2); + + RefPtr cubicEaseOutTiming1 = + CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseOut); + RefPtr cubicEaseOutTiming2 = + CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseOut); + EXPECT_EQ(*cubicEaseOutTiming1, *cubicEaseOutTiming1); + EXPECT_EQ(*cubicEaseOutTiming1, *cubicEaseOutTiming2); + + RefPtr cubicEaseInOutTiming1 = + CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseInOut); + RefPtr cubicEaseInOutTiming2 = + CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseInOut); + EXPECT_EQ(*cubicEaseInOutTiming1, *cubicEaseInOutTiming1); + EXPECT_EQ(*cubicEaseInOutTiming1, *cubicEaseInOutTiming2); + + RefPtr cubicCustomTiming1 = + CubicBezierTimingFunction::create(0.17, 0.67, 1, -1.73); + RefPtr cubicCustomTiming2 = + CubicBezierTimingFunction::create(0.17, 0.67, 1, -1.73); + EXPECT_EQ(*cubicCustomTiming1, *cubicCustomTiming1); + EXPECT_EQ(*cubicCustomTiming1, *cubicCustomTiming2); + + Vector>> v; + v.append(std::make_pair("cubicEaseInTiming1", cubicEaseInTiming1)); + v.append(std::make_pair("cubicEaseOutTiming1", cubicEaseOutTiming1)); + v.append(std::make_pair("cubicEaseInOutTiming1", cubicEaseInOutTiming1)); + v.append(std::make_pair("cubicCustomTiming1", cubicCustomTiming1)); + notEqualHelperLoop(v); } -TEST_F(TimingFunctionTest, CubicOperatorEqReflectivity) -{ - RefPtr cubicA = CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseIn); - RefPtr cubicB = CubicBezierTimingFunction::create(0.42, 0.0, 1.0, 1.0); - EXPECT_NE(*cubicA, *cubicB); - EXPECT_NE(*cubicB, *cubicA); +TEST_F(TimingFunctionTest, CubicOperatorEqReflectivity) { + RefPtr cubicA = + CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseIn); + RefPtr cubicB = + CubicBezierTimingFunction::create(0.42, 0.0, 1.0, 1.0); + EXPECT_NE(*cubicA, *cubicB); + EXPECT_NE(*cubicB, *cubicA); } -TEST_F(TimingFunctionTest, StepsOperatorEq) -{ - RefPtr stepsTimingStart1 = StepsTimingFunction::preset(StepsTimingFunction::Start); - RefPtr stepsTimingStart2 = StepsTimingFunction::preset(StepsTimingFunction::Start); - EXPECT_EQ(*stepsTimingStart1, *stepsTimingStart1); - EXPECT_EQ(*stepsTimingStart1, *stepsTimingStart2); - - RefPtr stepsTimingEnd1 = StepsTimingFunction::preset(StepsTimingFunction::End); - RefPtr stepsTimingEnd2 = StepsTimingFunction::preset(StepsTimingFunction::End); - EXPECT_EQ(*stepsTimingEnd1, *stepsTimingEnd1); - EXPECT_EQ(*stepsTimingEnd1, *stepsTimingEnd2); - - RefPtr stepsTimingCustom1 = StepsTimingFunction::create(5, StepsTimingFunction::StepAtStart); - RefPtr stepsTimingCustom2 = StepsTimingFunction::create(5, StepsTimingFunction::StepAtEnd); - RefPtr stepsTimingCustom3 = StepsTimingFunction::create(7, StepsTimingFunction::StepAtStart); - RefPtr stepsTimingCustom4 = StepsTimingFunction::create(7, StepsTimingFunction::StepAtEnd); - - EXPECT_EQ(*StepsTimingFunction::create(5, StepsTimingFunction::StepAtStart), *stepsTimingCustom1); - EXPECT_EQ(*StepsTimingFunction::create(5, StepsTimingFunction::StepAtEnd), *stepsTimingCustom2); - EXPECT_EQ(*StepsTimingFunction::create(7, StepsTimingFunction::StepAtStart), *stepsTimingCustom3); - EXPECT_EQ(*StepsTimingFunction::create(7, StepsTimingFunction::StepAtEnd), *stepsTimingCustom4); - - Vector > > v; - v.append(std::make_pair("stepsTimingStart1", stepsTimingStart1)); - v.append(std::make_pair("stepsTimingEnd1", stepsTimingEnd1)); - v.append(std::make_pair("stepsTimingCustom1", stepsTimingCustom1)); - v.append(std::make_pair("stepsTimingCustom2", stepsTimingCustom2)); - v.append(std::make_pair("stepsTimingCustom3", stepsTimingCustom3)); - v.append(std::make_pair("stepsTimingCustom4", stepsTimingCustom4)); - notEqualHelperLoop(v); +TEST_F(TimingFunctionTest, StepsOperatorEq) { + RefPtr stepsTimingStart1 = + StepsTimingFunction::preset(StepsTimingFunction::Start); + RefPtr stepsTimingStart2 = + StepsTimingFunction::preset(StepsTimingFunction::Start); + EXPECT_EQ(*stepsTimingStart1, *stepsTimingStart1); + EXPECT_EQ(*stepsTimingStart1, *stepsTimingStart2); + + RefPtr stepsTimingEnd1 = + StepsTimingFunction::preset(StepsTimingFunction::End); + RefPtr stepsTimingEnd2 = + StepsTimingFunction::preset(StepsTimingFunction::End); + EXPECT_EQ(*stepsTimingEnd1, *stepsTimingEnd1); + EXPECT_EQ(*stepsTimingEnd1, *stepsTimingEnd2); + + RefPtr stepsTimingCustom1 = + StepsTimingFunction::create(5, StepsTimingFunction::StepAtStart); + RefPtr stepsTimingCustom2 = + StepsTimingFunction::create(5, StepsTimingFunction::StepAtEnd); + RefPtr stepsTimingCustom3 = + StepsTimingFunction::create(7, StepsTimingFunction::StepAtStart); + RefPtr stepsTimingCustom4 = + StepsTimingFunction::create(7, StepsTimingFunction::StepAtEnd); + + EXPECT_EQ(*StepsTimingFunction::create(5, StepsTimingFunction::StepAtStart), + *stepsTimingCustom1); + EXPECT_EQ(*StepsTimingFunction::create(5, StepsTimingFunction::StepAtEnd), + *stepsTimingCustom2); + EXPECT_EQ(*StepsTimingFunction::create(7, StepsTimingFunction::StepAtStart), + *stepsTimingCustom3); + EXPECT_EQ(*StepsTimingFunction::create(7, StepsTimingFunction::StepAtEnd), + *stepsTimingCustom4); + + Vector>> v; + v.append(std::make_pair("stepsTimingStart1", stepsTimingStart1)); + v.append(std::make_pair("stepsTimingEnd1", stepsTimingEnd1)); + v.append(std::make_pair("stepsTimingCustom1", stepsTimingCustom1)); + v.append(std::make_pair("stepsTimingCustom2", stepsTimingCustom2)); + v.append(std::make_pair("stepsTimingCustom3", stepsTimingCustom3)); + v.append(std::make_pair("stepsTimingCustom4", stepsTimingCustom4)); + notEqualHelperLoop(v); } -TEST_F(TimingFunctionTest, StepsOperatorEqReflectivity) -{ - RefPtr stepsA = StepsTimingFunction::preset(StepsTimingFunction::Start); - RefPtr stepsB = StepsTimingFunction::create(1, StepsTimingFunction::StepAtStart); - EXPECT_NE(*stepsA, *stepsB); - EXPECT_NE(*stepsB, *stepsA); +TEST_F(TimingFunctionTest, StepsOperatorEqReflectivity) { + RefPtr stepsA = + StepsTimingFunction::preset(StepsTimingFunction::Start); + RefPtr stepsB = + StepsTimingFunction::create(1, StepsTimingFunction::StepAtStart); + EXPECT_NE(*stepsA, *stepsB); + EXPECT_NE(*stepsB, *stepsA); } -TEST_F(TimingFunctionTest, LinearEvaluate) -{ - RefPtr linearTiming = LinearTimingFunction::shared(); - EXPECT_EQ(0.2, linearTiming->evaluate(0.2, 0)); - EXPECT_EQ(0.6, linearTiming->evaluate(0.6, 0)); - EXPECT_EQ(-0.2, linearTiming->evaluate(-0.2, 0)); - EXPECT_EQ(1.6, linearTiming->evaluate(1.6, 0)); +TEST_F(TimingFunctionTest, LinearEvaluate) { + RefPtr linearTiming = LinearTimingFunction::shared(); + EXPECT_EQ(0.2, linearTiming->evaluate(0.2, 0)); + EXPECT_EQ(0.6, linearTiming->evaluate(0.6, 0)); + EXPECT_EQ(-0.2, linearTiming->evaluate(-0.2, 0)); + EXPECT_EQ(1.6, linearTiming->evaluate(1.6, 0)); } -TEST_F(TimingFunctionTest, LinearRange) -{ - double start = 0; - double end = 1; - RefPtr linearTiming = LinearTimingFunction::shared(); - linearTiming->range(&start, &end); - EXPECT_NEAR(0, start, 0.01); - EXPECT_NEAR(1, end, 0.01); - start = -1; - end = 10; - linearTiming->range(&start, &end); - EXPECT_NEAR(-1, start, 0.01); - EXPECT_NEAR(10, end, 0.01); +TEST_F(TimingFunctionTest, LinearRange) { + double start = 0; + double end = 1; + RefPtr linearTiming = LinearTimingFunction::shared(); + linearTiming->range(&start, &end); + EXPECT_NEAR(0, start, 0.01); + EXPECT_NEAR(1, end, 0.01); + start = -1; + end = 10; + linearTiming->range(&start, &end); + EXPECT_NEAR(-1, start, 0.01); + EXPECT_NEAR(10, end, 0.01); } -TEST_F(TimingFunctionTest, StepRange) -{ - double start = 0; - double end = 1; - RefPtr steps = StepsTimingFunction::preset(StepsTimingFunction::Start); - steps->range(&start, &end); - EXPECT_NEAR(0, start, 0.01); - EXPECT_NEAR(1, end, 0.01); - - start = -1; - end = 10; - steps->range(&start, &end); - EXPECT_NEAR(0, start, 0.01); - EXPECT_NEAR(1, end, 0.01); +TEST_F(TimingFunctionTest, StepRange) { + double start = 0; + double end = 1; + RefPtr steps = + StepsTimingFunction::preset(StepsTimingFunction::Start); + steps->range(&start, &end); + EXPECT_NEAR(0, start, 0.01); + EXPECT_NEAR(1, end, 0.01); + + start = -1; + end = 10; + steps->range(&start, &end); + EXPECT_NEAR(0, start, 0.01); + EXPECT_NEAR(1, end, 0.01); } -TEST_F(TimingFunctionTest, CubicRange) -{ - double start = 0; - double end = 1; - - RefPtr cubicEaseTiming = CubicBezierTimingFunction::preset(CubicBezierTimingFunction::Ease); - start = 0; - end = 1; - cubicEaseTiming->range(&start, &end); - EXPECT_NEAR(0, start, 0.01); - EXPECT_NEAR(1, end, 0.01); - start = -1; - end = 10; - cubicEaseTiming->range(&start, &end); - EXPECT_NEAR(-0.4, start, 0.01); - EXPECT_NEAR(1, end, 0.01); - - RefPtr cubicEaseInTiming = CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseIn); - start = 0; - end = 1; - cubicEaseInTiming->range(&start, &end); - EXPECT_NEAR(0, start, 0.01); - EXPECT_NEAR(1, end, 0.01); - start = -1; - end = 10; - cubicEaseInTiming->range(&start, &end); - EXPECT_NEAR(0.0, start, 0.01); - EXPECT_NEAR(16.51, end, 0.01); - - RefPtr cubicEaseOutTiming = CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseOut); - start = 0; - end = 1; - cubicEaseOutTiming->range(&start, &end); - EXPECT_NEAR(0, start, 0.01); - EXPECT_NEAR(1, end, 0.01); - start = -1; - end = 10; - cubicEaseOutTiming->range(&start, &end); - EXPECT_NEAR(-1.72, start, 0.01); - EXPECT_NEAR(1.0, end, 0.01); - - RefPtr cubicEaseInOutTiming = CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseInOut); - start = 0; - end = 1; - cubicEaseInOutTiming->range(&start, &end); - EXPECT_NEAR(0, start, 0.01); - EXPECT_NEAR(1, end, 0.01); - start = -1; - end = 10; - cubicEaseInOutTiming->range(&start, &end); - EXPECT_NEAR(0.0, start, 0.01); - EXPECT_NEAR(1.0, end, 0.01); - - RefPtr cubicCustomTiming = CubicBezierTimingFunction::create(0.17, 0.67, 1.0, -1.73); - start = 0; - end = 1; - cubicCustomTiming->range(&start, &end); - EXPECT_NEAR(-0.33, start, 0.01); - EXPECT_NEAR(1.0, end, 0.01); - - start = -1; - end = 10; - cubicCustomTiming->range(&start, &end); - EXPECT_NEAR(-3.94, start, 0.01); - EXPECT_NEAR(4.578, end, 0.01); +TEST_F(TimingFunctionTest, CubicRange) { + double start = 0; + double end = 1; + + RefPtr cubicEaseTiming = + CubicBezierTimingFunction::preset(CubicBezierTimingFunction::Ease); + start = 0; + end = 1; + cubicEaseTiming->range(&start, &end); + EXPECT_NEAR(0, start, 0.01); + EXPECT_NEAR(1, end, 0.01); + start = -1; + end = 10; + cubicEaseTiming->range(&start, &end); + EXPECT_NEAR(-0.4, start, 0.01); + EXPECT_NEAR(1, end, 0.01); + + RefPtr cubicEaseInTiming = + CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseIn); + start = 0; + end = 1; + cubicEaseInTiming->range(&start, &end); + EXPECT_NEAR(0, start, 0.01); + EXPECT_NEAR(1, end, 0.01); + start = -1; + end = 10; + cubicEaseInTiming->range(&start, &end); + EXPECT_NEAR(0.0, start, 0.01); + EXPECT_NEAR(16.51, end, 0.01); + + RefPtr cubicEaseOutTiming = + CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseOut); + start = 0; + end = 1; + cubicEaseOutTiming->range(&start, &end); + EXPECT_NEAR(0, start, 0.01); + EXPECT_NEAR(1, end, 0.01); + start = -1; + end = 10; + cubicEaseOutTiming->range(&start, &end); + EXPECT_NEAR(-1.72, start, 0.01); + EXPECT_NEAR(1.0, end, 0.01); + + RefPtr cubicEaseInOutTiming = + CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseInOut); + start = 0; + end = 1; + cubicEaseInOutTiming->range(&start, &end); + EXPECT_NEAR(0, start, 0.01); + EXPECT_NEAR(1, end, 0.01); + start = -1; + end = 10; + cubicEaseInOutTiming->range(&start, &end); + EXPECT_NEAR(0.0, start, 0.01); + EXPECT_NEAR(1.0, end, 0.01); + + RefPtr cubicCustomTiming = + CubicBezierTimingFunction::create(0.17, 0.67, 1.0, -1.73); + start = 0; + end = 1; + cubicCustomTiming->range(&start, &end); + EXPECT_NEAR(-0.33, start, 0.01); + EXPECT_NEAR(1.0, end, 0.01); + + start = -1; + end = 10; + cubicCustomTiming->range(&start, &end); + EXPECT_NEAR(-3.94, start, 0.01); + EXPECT_NEAR(4.578, end, 0.01); } -TEST_F(TimingFunctionTest, CubicEvaluate) -{ - double tolerance = 0.01; - RefPtr cubicEaseTiming = CubicBezierTimingFunction::preset(CubicBezierTimingFunction::Ease); - EXPECT_NEAR(0.418, cubicEaseTiming->evaluate(0.25, tolerance), tolerance); - EXPECT_NEAR(0.805, cubicEaseTiming->evaluate(0.50, tolerance), tolerance); - EXPECT_NEAR(0.960, cubicEaseTiming->evaluate(0.75, tolerance), tolerance); - - RefPtr cubicEaseInTiming = CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseIn); - EXPECT_NEAR(0.093, cubicEaseInTiming->evaluate(0.25, tolerance), tolerance); - EXPECT_NEAR(0.305, cubicEaseInTiming->evaluate(0.50, tolerance), tolerance); - EXPECT_NEAR(0.620, cubicEaseInTiming->evaluate(0.75, tolerance), tolerance); - - RefPtr cubicEaseOutTiming = CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseOut); - EXPECT_NEAR(0.379, cubicEaseOutTiming->evaluate(0.25, tolerance), tolerance); - EXPECT_NEAR(0.694, cubicEaseOutTiming->evaluate(0.50, tolerance), tolerance); - EXPECT_NEAR(0.906, cubicEaseOutTiming->evaluate(0.75, tolerance), tolerance); - - RefPtr cubicEaseInOutTiming = CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseInOut); - EXPECT_NEAR(0.128, cubicEaseInOutTiming->evaluate(0.25, tolerance), tolerance); - EXPECT_NEAR(0.500, cubicEaseInOutTiming->evaluate(0.50, tolerance), tolerance); - EXPECT_NEAR(0.871, cubicEaseInOutTiming->evaluate(0.75, tolerance), tolerance); - - RefPtr cubicCustomTiming = CubicBezierTimingFunction::create(0.17, 0.67, 1, -1.73); - EXPECT_NEAR(0.034, cubicCustomTiming->evaluate(0.25, tolerance), tolerance); - EXPECT_NEAR(-0.217, cubicCustomTiming->evaluate(0.50, tolerance), tolerance); - EXPECT_NEAR(-0.335, cubicCustomTiming->evaluate(0.75, tolerance), tolerance); +TEST_F(TimingFunctionTest, CubicEvaluate) { + double tolerance = 0.01; + RefPtr cubicEaseTiming = + CubicBezierTimingFunction::preset(CubicBezierTimingFunction::Ease); + EXPECT_NEAR(0.418, cubicEaseTiming->evaluate(0.25, tolerance), tolerance); + EXPECT_NEAR(0.805, cubicEaseTiming->evaluate(0.50, tolerance), tolerance); + EXPECT_NEAR(0.960, cubicEaseTiming->evaluate(0.75, tolerance), tolerance); + + RefPtr cubicEaseInTiming = + CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseIn); + EXPECT_NEAR(0.093, cubicEaseInTiming->evaluate(0.25, tolerance), tolerance); + EXPECT_NEAR(0.305, cubicEaseInTiming->evaluate(0.50, tolerance), tolerance); + EXPECT_NEAR(0.620, cubicEaseInTiming->evaluate(0.75, tolerance), tolerance); + + RefPtr cubicEaseOutTiming = + CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseOut); + EXPECT_NEAR(0.379, cubicEaseOutTiming->evaluate(0.25, tolerance), tolerance); + EXPECT_NEAR(0.694, cubicEaseOutTiming->evaluate(0.50, tolerance), tolerance); + EXPECT_NEAR(0.906, cubicEaseOutTiming->evaluate(0.75, tolerance), tolerance); + + RefPtr cubicEaseInOutTiming = + CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseInOut); + EXPECT_NEAR(0.128, cubicEaseInOutTiming->evaluate(0.25, tolerance), + tolerance); + EXPECT_NEAR(0.500, cubicEaseInOutTiming->evaluate(0.50, tolerance), + tolerance); + EXPECT_NEAR(0.871, cubicEaseInOutTiming->evaluate(0.75, tolerance), + tolerance); + + RefPtr cubicCustomTiming = + CubicBezierTimingFunction::create(0.17, 0.67, 1, -1.73); + EXPECT_NEAR(0.034, cubicCustomTiming->evaluate(0.25, tolerance), tolerance); + EXPECT_NEAR(-0.217, cubicCustomTiming->evaluate(0.50, tolerance), tolerance); + EXPECT_NEAR(-0.335, cubicCustomTiming->evaluate(0.75, tolerance), tolerance); } -TEST_F(TimingFunctionTest, StepsEvaluate) -{ - RefPtr stepsTimingStart = StepsTimingFunction::preset(StepsTimingFunction::Start); - EXPECT_EQ(0.00, stepsTimingStart->evaluate(-1.10, 0)); - EXPECT_EQ(0.00, stepsTimingStart->evaluate(-0.10, 0)); - EXPECT_EQ(1.00, stepsTimingStart->evaluate(0.00, 0)); - EXPECT_EQ(1.00, stepsTimingStart->evaluate(0.20, 0)); - EXPECT_EQ(1.00, stepsTimingStart->evaluate(0.60, 0)); - EXPECT_EQ(1.00, stepsTimingStart->evaluate(1.00, 0)); - EXPECT_EQ(1.00, stepsTimingStart->evaluate(2.00, 0)); - - RefPtr stepsTimingMiddle = StepsTimingFunction::preset(StepsTimingFunction::Middle); - EXPECT_EQ(0.00, stepsTimingMiddle->evaluate(-2.50, 0)); - EXPECT_EQ(0.00, stepsTimingMiddle->evaluate(0.00, 0)); - EXPECT_EQ(0.00, stepsTimingMiddle->evaluate(0.49, 0)); - EXPECT_EQ(1.00, stepsTimingMiddle->evaluate(0.50, 0)); - EXPECT_EQ(1.00, stepsTimingMiddle->evaluate(1.00, 0)); - EXPECT_EQ(1.00, stepsTimingMiddle->evaluate(2.50, 0)); - - RefPtr stepsTimingEnd = StepsTimingFunction::preset(StepsTimingFunction::End); - EXPECT_EQ(0.00, stepsTimingEnd->evaluate(-2.00, 0)); - EXPECT_EQ(0.00, stepsTimingEnd->evaluate(0.00, 0)); - EXPECT_EQ(0.00, stepsTimingEnd->evaluate(0.20, 0)); - EXPECT_EQ(0.00, stepsTimingEnd->evaluate(0.60, 0)); - EXPECT_EQ(1.00, stepsTimingEnd->evaluate(1.00, 0)); - EXPECT_EQ(1.00, stepsTimingEnd->evaluate(2.00, 0)); - - RefPtr stepsTimingCustomStart = StepsTimingFunction::create(4, StepsTimingFunction::StepAtStart); - EXPECT_EQ(0.00, stepsTimingCustomStart->evaluate(-0.50, 0)); - EXPECT_EQ(0.25, stepsTimingCustomStart->evaluate(0.00, 0)); - EXPECT_EQ(0.25, stepsTimingCustomStart->evaluate(0.24, 0)); - EXPECT_EQ(0.50, stepsTimingCustomStart->evaluate(0.25, 0)); - EXPECT_EQ(0.50, stepsTimingCustomStart->evaluate(0.49, 0)); - EXPECT_EQ(0.75, stepsTimingCustomStart->evaluate(0.50, 0)); - EXPECT_EQ(0.75, stepsTimingCustomStart->evaluate(0.74, 0)); - EXPECT_EQ(1.00, stepsTimingCustomStart->evaluate(0.75, 0)); - EXPECT_EQ(1.00, stepsTimingCustomStart->evaluate(1.00, 0)); - EXPECT_EQ(1.00, stepsTimingCustomStart->evaluate(1.50, 0)); - - RefPtr stepsTimingCustomMiddle = StepsTimingFunction::create(4, StepsTimingFunction::StepAtMiddle); - EXPECT_EQ(0.00, stepsTimingCustomMiddle->evaluate(-2.00, 0)); - EXPECT_EQ(0.00, stepsTimingCustomMiddle->evaluate(0.00, 0)); - EXPECT_EQ(0.00, stepsTimingCustomMiddle->evaluate(0.12, 0)); - EXPECT_EQ(0.25, stepsTimingCustomMiddle->evaluate(0.13, 0)); - EXPECT_EQ(0.25, stepsTimingCustomMiddle->evaluate(0.37, 0)); - EXPECT_EQ(0.50, stepsTimingCustomMiddle->evaluate(0.38, 0)); - EXPECT_EQ(0.50, stepsTimingCustomMiddle->evaluate(0.62, 0)); - EXPECT_EQ(0.75, stepsTimingCustomMiddle->evaluate(0.63, 0)); - EXPECT_EQ(0.75, stepsTimingCustomMiddle->evaluate(0.87, 0)); - EXPECT_EQ(1.00, stepsTimingCustomMiddle->evaluate(0.88, 0)); - EXPECT_EQ(1.00, stepsTimingCustomMiddle->evaluate(1.00, 0)); - EXPECT_EQ(1.00, stepsTimingCustomMiddle->evaluate(3.00, 0)); - - RefPtr stepsTimingCustomEnd = StepsTimingFunction::create(4, StepsTimingFunction::StepAtEnd); - EXPECT_EQ(0.00, stepsTimingCustomEnd->evaluate(-2.00, 0)); - EXPECT_EQ(0.00, stepsTimingCustomEnd->evaluate(0.00, 0)); - EXPECT_EQ(0.00, stepsTimingCustomEnd->evaluate(0.24, 0)); - EXPECT_EQ(0.25, stepsTimingCustomEnd->evaluate(0.25, 0)); - EXPECT_EQ(0.25, stepsTimingCustomEnd->evaluate(0.49, 0)); - EXPECT_EQ(0.50, stepsTimingCustomEnd->evaluate(0.50, 0)); - EXPECT_EQ(0.50, stepsTimingCustomEnd->evaluate(0.74, 0)); - EXPECT_EQ(0.75, stepsTimingCustomEnd->evaluate(0.75, 0)); - EXPECT_EQ(0.75, stepsTimingCustomEnd->evaluate(0.99, 0)); - EXPECT_EQ(1.00, stepsTimingCustomEnd->evaluate(1.00, 0)); - EXPECT_EQ(1.00, stepsTimingCustomEnd->evaluate(2.00, 0)); +TEST_F(TimingFunctionTest, StepsEvaluate) { + RefPtr stepsTimingStart = + StepsTimingFunction::preset(StepsTimingFunction::Start); + EXPECT_EQ(0.00, stepsTimingStart->evaluate(-1.10, 0)); + EXPECT_EQ(0.00, stepsTimingStart->evaluate(-0.10, 0)); + EXPECT_EQ(1.00, stepsTimingStart->evaluate(0.00, 0)); + EXPECT_EQ(1.00, stepsTimingStart->evaluate(0.20, 0)); + EXPECT_EQ(1.00, stepsTimingStart->evaluate(0.60, 0)); + EXPECT_EQ(1.00, stepsTimingStart->evaluate(1.00, 0)); + EXPECT_EQ(1.00, stepsTimingStart->evaluate(2.00, 0)); + + RefPtr stepsTimingMiddle = + StepsTimingFunction::preset(StepsTimingFunction::Middle); + EXPECT_EQ(0.00, stepsTimingMiddle->evaluate(-2.50, 0)); + EXPECT_EQ(0.00, stepsTimingMiddle->evaluate(0.00, 0)); + EXPECT_EQ(0.00, stepsTimingMiddle->evaluate(0.49, 0)); + EXPECT_EQ(1.00, stepsTimingMiddle->evaluate(0.50, 0)); + EXPECT_EQ(1.00, stepsTimingMiddle->evaluate(1.00, 0)); + EXPECT_EQ(1.00, stepsTimingMiddle->evaluate(2.50, 0)); + + RefPtr stepsTimingEnd = + StepsTimingFunction::preset(StepsTimingFunction::End); + EXPECT_EQ(0.00, stepsTimingEnd->evaluate(-2.00, 0)); + EXPECT_EQ(0.00, stepsTimingEnd->evaluate(0.00, 0)); + EXPECT_EQ(0.00, stepsTimingEnd->evaluate(0.20, 0)); + EXPECT_EQ(0.00, stepsTimingEnd->evaluate(0.60, 0)); + EXPECT_EQ(1.00, stepsTimingEnd->evaluate(1.00, 0)); + EXPECT_EQ(1.00, stepsTimingEnd->evaluate(2.00, 0)); + + RefPtr stepsTimingCustomStart = + StepsTimingFunction::create(4, StepsTimingFunction::StepAtStart); + EXPECT_EQ(0.00, stepsTimingCustomStart->evaluate(-0.50, 0)); + EXPECT_EQ(0.25, stepsTimingCustomStart->evaluate(0.00, 0)); + EXPECT_EQ(0.25, stepsTimingCustomStart->evaluate(0.24, 0)); + EXPECT_EQ(0.50, stepsTimingCustomStart->evaluate(0.25, 0)); + EXPECT_EQ(0.50, stepsTimingCustomStart->evaluate(0.49, 0)); + EXPECT_EQ(0.75, stepsTimingCustomStart->evaluate(0.50, 0)); + EXPECT_EQ(0.75, stepsTimingCustomStart->evaluate(0.74, 0)); + EXPECT_EQ(1.00, stepsTimingCustomStart->evaluate(0.75, 0)); + EXPECT_EQ(1.00, stepsTimingCustomStart->evaluate(1.00, 0)); + EXPECT_EQ(1.00, stepsTimingCustomStart->evaluate(1.50, 0)); + + RefPtr stepsTimingCustomMiddle = + StepsTimingFunction::create(4, StepsTimingFunction::StepAtMiddle); + EXPECT_EQ(0.00, stepsTimingCustomMiddle->evaluate(-2.00, 0)); + EXPECT_EQ(0.00, stepsTimingCustomMiddle->evaluate(0.00, 0)); + EXPECT_EQ(0.00, stepsTimingCustomMiddle->evaluate(0.12, 0)); + EXPECT_EQ(0.25, stepsTimingCustomMiddle->evaluate(0.13, 0)); + EXPECT_EQ(0.25, stepsTimingCustomMiddle->evaluate(0.37, 0)); + EXPECT_EQ(0.50, stepsTimingCustomMiddle->evaluate(0.38, 0)); + EXPECT_EQ(0.50, stepsTimingCustomMiddle->evaluate(0.62, 0)); + EXPECT_EQ(0.75, stepsTimingCustomMiddle->evaluate(0.63, 0)); + EXPECT_EQ(0.75, stepsTimingCustomMiddle->evaluate(0.87, 0)); + EXPECT_EQ(1.00, stepsTimingCustomMiddle->evaluate(0.88, 0)); + EXPECT_EQ(1.00, stepsTimingCustomMiddle->evaluate(1.00, 0)); + EXPECT_EQ(1.00, stepsTimingCustomMiddle->evaluate(3.00, 0)); + + RefPtr stepsTimingCustomEnd = + StepsTimingFunction::create(4, StepsTimingFunction::StepAtEnd); + EXPECT_EQ(0.00, stepsTimingCustomEnd->evaluate(-2.00, 0)); + EXPECT_EQ(0.00, stepsTimingCustomEnd->evaluate(0.00, 0)); + EXPECT_EQ(0.00, stepsTimingCustomEnd->evaluate(0.24, 0)); + EXPECT_EQ(0.25, stepsTimingCustomEnd->evaluate(0.25, 0)); + EXPECT_EQ(0.25, stepsTimingCustomEnd->evaluate(0.49, 0)); + EXPECT_EQ(0.50, stepsTimingCustomEnd->evaluate(0.50, 0)); + EXPECT_EQ(0.50, stepsTimingCustomEnd->evaluate(0.74, 0)); + EXPECT_EQ(0.75, stepsTimingCustomEnd->evaluate(0.75, 0)); + EXPECT_EQ(0.75, stepsTimingCustomEnd->evaluate(0.99, 0)); + EXPECT_EQ(1.00, stepsTimingCustomEnd->evaluate(1.00, 0)); + EXPECT_EQ(1.00, stepsTimingCustomEnd->evaluate(2.00, 0)); } -} // namespace +} // namespace -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/animation/UnitBezier.h b/sky/engine/platform/animation/UnitBezier.h index 7957e6d24b8b3..ceb122158a45d 100644 --- a/sky/engine/platform/animation/UnitBezier.h +++ b/sky/engine/platform/animation/UnitBezier.h @@ -33,132 +33,126 @@ namespace blink { struct UnitBezier { - UnitBezier(double p1x, double p1y, double p2x, double p2y) - { - // Calculate the polynomial coefficients, implicit first and last control points are (0,0) and (1,1). - cx = 3.0 * p1x; - bx = 3.0 * (p2x - p1x) - cx; - ax = 1.0 - cx -bx; - - cy = 3.0 * p1y; - by = 3.0 * (p2y - p1y) - cy; - ay = 1.0 - cy - by; - - // End-point gradients are used to calculate timing function results - // outside the range [0, 1]. - // - // There are three possibilities for the gradient at each end: - // (1) the closest control point is not horizontally coincident with regard to - // (0, 0) or (1, 1). In this case the line between the end point and - // the control point is tangent to the bezier at the end point. - // (2) the closest control point is coincident with the end point. In - // this case the line between the end point and the far control - // point is tangent to the bezier at the end point. - // (3) the closest control point is horizontally coincident with the end - // point, but vertically distinct. In this case the gradient at the - // end point is Infinite. However, this causes issues when - // interpolating. As a result, we break down to a simple case of - // 0 gradient under these conditions. - - if (p1x > 0) - m_startGradient = p1y / p1x; - else if (!p1y && p2x > 0) - m_startGradient = p2y / p2x; - else - m_startGradient = 0; - - if (p2x < 1) - m_endGradient = (p2y - 1) / (p2x - 1); - else if (p2x == 1 && p1x < 1) - m_endGradient = (p1y - 1) / (p1x - 1); - else - m_endGradient = 0; - } - - double sampleCurveX(double t) - { - // `ax t^3 + bx t^2 + cx t' expanded using Horner's rule. - return ((ax * t + bx) * t + cx) * t; - } - - double sampleCurveY(double t) - { - return ((ay * t + by) * t + cy) * t; + UnitBezier(double p1x, double p1y, double p2x, double p2y) { + // Calculate the polynomial coefficients, implicit first and last control + // points are (0,0) and (1,1). + cx = 3.0 * p1x; + bx = 3.0 * (p2x - p1x) - cx; + ax = 1.0 - cx - bx; + + cy = 3.0 * p1y; + by = 3.0 * (p2y - p1y) - cy; + ay = 1.0 - cy - by; + + // End-point gradients are used to calculate timing function results + // outside the range [0, 1]. + // + // There are three possibilities for the gradient at each end: + // (1) the closest control point is not horizontally coincident with regard + // to + // (0, 0) or (1, 1). In this case the line between the end point and + // the control point is tangent to the bezier at the end point. + // (2) the closest control point is coincident with the end point. In + // this case the line between the end point and the far control + // point is tangent to the bezier at the end point. + // (3) the closest control point is horizontally coincident with the end + // point, but vertically distinct. In this case the gradient at the + // end point is Infinite. However, this causes issues when + // interpolating. As a result, we break down to a simple case of + // 0 gradient under these conditions. + + if (p1x > 0) + m_startGradient = p1y / p1x; + else if (!p1y && p2x > 0) + m_startGradient = p2y / p2x; + else + m_startGradient = 0; + + if (p2x < 1) + m_endGradient = (p2y - 1) / (p2x - 1); + else if (p2x == 1 && p1x < 1) + m_endGradient = (p1y - 1) / (p1x - 1); + else + m_endGradient = 0; + } + + double sampleCurveX(double t) { + // `ax t^3 + bx t^2 + cx t' expanded using Horner's rule. + return ((ax * t + bx) * t + cx) * t; + } + + double sampleCurveY(double t) { return ((ay * t + by) * t + cy) * t; } + + double sampleCurveDerivativeX(double t) { + return (3.0 * ax * t + 2.0 * bx) * t + cx; + } + + // Given an x value, find a parametric value it came from. + double solveCurveX(double x, double epsilon) { + ASSERT(x >= 0.0); + ASSERT(x <= 1.0); + + double t0; + double t1; + double t2; + double x2; + double d2; + int i; + + // First try a few iterations of Newton's method -- normally very fast. + for (t2 = x, i = 0; i < 8; i++) { + x2 = sampleCurveX(t2) - x; + if (fabs(x2) < epsilon) + return t2; + d2 = sampleCurveDerivativeX(t2); + if (fabs(d2) < 1e-6) + break; + t2 = t2 - x2 / d2; } - double sampleCurveDerivativeX(double t) - { - return (3.0 * ax * t + 2.0 * bx) * t + cx; - } + // Fall back to the bisection method for reliability. + t0 = 0.0; + t1 = 1.0; + t2 = x; - // Given an x value, find a parametric value it came from. - double solveCurveX(double x, double epsilon) - { - ASSERT(x >= 0.0); - ASSERT(x <= 1.0); - - double t0; - double t1; - double t2; - double x2; - double d2; - int i; - - // First try a few iterations of Newton's method -- normally very fast. - for (t2 = x, i = 0; i < 8; i++) { - x2 = sampleCurveX(t2) - x; - if (fabs (x2) < epsilon) - return t2; - d2 = sampleCurveDerivativeX(t2); - if (fabs(d2) < 1e-6) - break; - t2 = t2 - x2 / d2; - } - - // Fall back to the bisection method for reliability. - t0 = 0.0; - t1 = 1.0; - t2 = x; - - while (t0 < t1) { - x2 = sampleCurveX(t2); - if (fabs(x2 - x) < epsilon) - return t2; - if (x > x2) - t0 = t2; - else - t1 = t2; - t2 = (t1 - t0) * .5 + t0; - } - - // Failure. + while (t0 < t1) { + x2 = sampleCurveX(t2); + if (fabs(x2 - x) < epsilon) return t2; + if (x > x2) + t0 = t2; + else + t1 = t2; + t2 = (t1 - t0) * .5 + t0; } - // Evaluates y at the given x. The epsilon parameter provides a hint as to the required - // accuracy and is not guaranteed. - double solve(double x, double epsilon) - { - if (x < 0.0) - return 0.0 + m_startGradient * x; - if (x > 1.0) - return 1.0 + m_endGradient * (x - 1.0); - return sampleCurveY(solveCurveX(x, epsilon)); - } - -private: - double ax; - double bx; - double cx; - - double ay; - double by; - double cy; - - double m_startGradient; - double m_endGradient; + // Failure. + return t2; + } + + // Evaluates y at the given x. The epsilon parameter provides a hint as to the + // required accuracy and is not guaranteed. + double solve(double x, double epsilon) { + if (x < 0.0) + return 0.0 + m_startGradient * x; + if (x > 1.0) + return 1.0 + m_endGradient * (x - 1.0); + return sampleCurveY(solveCurveX(x, epsilon)); + } + + private: + double ax; + double bx; + double cx; + + double ay; + double by; + double cy; + + double m_startGradient; + double m_endGradient; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_ANIMATION_UNITBEZIER_H_ diff --git a/sky/engine/platform/animation/UnitBezierTest.cpp b/sky/engine/platform/animation/UnitBezierTest.cpp index bf2263f218256..19d4367827ef5 100644 --- a/sky/engine/platform/animation/UnitBezierTest.cpp +++ b/sky/engine/platform/animation/UnitBezierTest.cpp @@ -10,19 +10,18 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #include "flutter/sky/engine/platform/animation/UnitBezier.h" #include @@ -31,64 +30,55 @@ using namespace blink; namespace { -TEST(UnitBezierTest, BasicUse) -{ - UnitBezier bezier(0.5, 1.0, 0.5, 1.0); - EXPECT_EQ(0.875, bezier.solve(0.5, 0.005)); +TEST(UnitBezierTest, BasicUse) { + UnitBezier bezier(0.5, 1.0, 0.5, 1.0); + EXPECT_EQ(0.875, bezier.solve(0.5, 0.005)); } -TEST(UnitBezierTest, Overshoot) -{ - UnitBezier bezier(0.5, 2.0, 0.5, 2.0); - EXPECT_EQ(1.625, bezier.solve(0.5, 0.005)); +TEST(UnitBezierTest, Overshoot) { + UnitBezier bezier(0.5, 2.0, 0.5, 2.0); + EXPECT_EQ(1.625, bezier.solve(0.5, 0.005)); } -TEST(UnitBezierTest, Undershoot) -{ - UnitBezier bezier(0.5, -1.0, 0.5, -1.0); - EXPECT_EQ(-0.625, bezier.solve(0.5, 0.005)); +TEST(UnitBezierTest, Undershoot) { + UnitBezier bezier(0.5, -1.0, 0.5, -1.0); + EXPECT_EQ(-0.625, bezier.solve(0.5, 0.005)); } -TEST(UnitBezierTest, InputAtEdgeOfRange) -{ - UnitBezier bezier(0.5, 1.0, 0.5, 1.0); - EXPECT_EQ(0.0, bezier.solve(0.0, 0.005)); - EXPECT_EQ(1.0, bezier.solve(1.0, 0.005)); +TEST(UnitBezierTest, InputAtEdgeOfRange) { + UnitBezier bezier(0.5, 1.0, 0.5, 1.0); + EXPECT_EQ(0.0, bezier.solve(0.0, 0.005)); + EXPECT_EQ(1.0, bezier.solve(1.0, 0.005)); } -TEST(UnitBezierTest, InputOutOfRange) -{ - UnitBezier bezier(0.5, 1.0, 0.5, 1.0); - EXPECT_EQ(-2.0, bezier.solve(-1.0, 0.005)); - EXPECT_EQ(1.0, bezier.solve(2.0, 0.005)); +TEST(UnitBezierTest, InputOutOfRange) { + UnitBezier bezier(0.5, 1.0, 0.5, 1.0); + EXPECT_EQ(-2.0, bezier.solve(-1.0, 0.005)); + EXPECT_EQ(1.0, bezier.solve(2.0, 0.005)); } -TEST(UnitBezierTest, InputOutOfRangeLargeEpsilon) -{ - UnitBezier bezier(0.5, 1.0, 0.5, 1.0); - EXPECT_EQ(-2.0, bezier.solve(-1.0, 1.0)); - EXPECT_EQ(1.0, bezier.solve(2.0, 1.0)); +TEST(UnitBezierTest, InputOutOfRangeLargeEpsilon) { + UnitBezier bezier(0.5, 1.0, 0.5, 1.0); + EXPECT_EQ(-2.0, bezier.solve(-1.0, 1.0)); + EXPECT_EQ(1.0, bezier.solve(2.0, 1.0)); } -TEST(UnitBezierTest, InputOutOfRangeCoincidentEndpoints) -{ - UnitBezier bezier(0.0, 0.0, 1.0, 1.0); - EXPECT_EQ(-1.0, bezier.solve(-1.0, 0.005)); - EXPECT_EQ(2.0, bezier.solve(2.0, 0.005)); +TEST(UnitBezierTest, InputOutOfRangeCoincidentEndpoints) { + UnitBezier bezier(0.0, 0.0, 1.0, 1.0); + EXPECT_EQ(-1.0, bezier.solve(-1.0, 0.005)); + EXPECT_EQ(2.0, bezier.solve(2.0, 0.005)); } -TEST(UnitBezierTest, InputOutOfRangeVerticalGradient) -{ - UnitBezier bezier(0.0, 1.0, 1.0, 0.0); - EXPECT_EQ(0.0, bezier.solve(-1.0, 0.005)); - EXPECT_EQ(1.0, bezier.solve(2.0, 0.005)); +TEST(UnitBezierTest, InputOutOfRangeVerticalGradient) { + UnitBezier bezier(0.0, 1.0, 1.0, 0.0); + EXPECT_EQ(0.0, bezier.solve(-1.0, 0.005)); + EXPECT_EQ(1.0, bezier.solve(2.0, 0.005)); } -TEST(UnitBezierTest, InputOutOfRangeDistinctEndpoints) -{ - UnitBezier bezier(0.1, 0.2, 0.8, 0.8); - EXPECT_EQ(-2.0, bezier.solve(-1.0, 0.005)); - EXPECT_EQ(2.0, bezier.solve(2.0, 0.005)); +TEST(UnitBezierTest, InputOutOfRangeDistinctEndpoints) { + UnitBezier bezier(0.1, 0.2, 0.8, 0.8); + EXPECT_EQ(-2.0, bezier.solve(-1.0, 0.005)); + EXPECT_EQ(2.0, bezier.solve(2.0, 0.005)); } -} // namespace +} // namespace diff --git a/sky/engine/platform/exported/Platform.cpp b/sky/engine/platform/exported/Platform.cpp index 503a8fcf248ba..1050fd9ee2ecd 100644 --- a/sky/engine/platform/exported/Platform.cpp +++ b/sky/engine/platform/exported/Platform.cpp @@ -34,19 +34,16 @@ namespace blink { static Platform* s_platform = 0; -void Platform::initialize(Platform* platform) -{ - s_platform = platform; +void Platform::initialize(Platform* platform) { + s_platform = platform; } -void Platform::shutdown() -{ - s_platform = 0; +void Platform::shutdown() { + s_platform = 0; } -Platform* Platform::current() -{ - return s_platform; +Platform* Platform::current() { + return s_platform; } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/exported/WebCommon.cpp b/sky/engine/platform/exported/WebCommon.cpp index af0ad64dc008b..d081ee306fdd8 100644 --- a/sky/engine/platform/exported/WebCommon.cpp +++ b/sky/engine/platform/exported/WebCommon.cpp @@ -34,10 +34,12 @@ namespace blink { -void failedAssertion(const char* file, int line, const char* function, const char* assertion) -{ - WTFReportAssertionFailure(file, line, function, assertion); - CRASH(); +void failedAssertion(const char* file, + int line, + const char* function, + const char* assertion) { + WTFReportAssertionFailure(file, line, function, assertion); + CRASH(); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/fonts/AlternateFontFamily.h b/sky/engine/platform/fonts/AlternateFontFamily.h index 8bd0a547d318b..587120ff2ddd2 100644 --- a/sky/engine/platform/fonts/AlternateFontFamily.h +++ b/sky/engine/platform/fonts/AlternateFontFamily.h @@ -37,71 +37,80 @@ namespace blink { // We currently do not support bitmap fonts on windows. -// Instead of trying to construct a bitmap font and then going down the fallback path map -// certain common bitmap fonts to their truetype equivalent up front. -inline const AtomicString& adjustFamilyNameToAvoidUnsupportedFonts(const AtomicString& familyName) -{ - return familyName; +// Instead of trying to construct a bitmap font and then going down the fallback +// path map certain common bitmap fonts to their truetype equivalent up front. +inline const AtomicString& adjustFamilyNameToAvoidUnsupportedFonts( + const AtomicString& familyName) { + return familyName; } -inline const AtomicString& alternateFamilyName(const AtomicString& familyName) -{ - // Alias Courier <-> Courier New - DEFINE_STATIC_LOCAL(AtomicString, courier, ("Courier", AtomicString::ConstructFromLiteral)); - DEFINE_STATIC_LOCAL(AtomicString, courierNew, ("Courier New", AtomicString::ConstructFromLiteral)); - if (equalIgnoringCase(familyName, courier)) - return courierNew; - // On Windows, Courier New (truetype font) is always present and - // Courier is a bitmap font. So, we don't want to map Courier New to - // Courier. - if (equalIgnoringCase(familyName, courierNew)) - return courier; +inline const AtomicString& alternateFamilyName(const AtomicString& familyName) { + // Alias Courier <-> Courier New + DEFINE_STATIC_LOCAL(AtomicString, courier, + ("Courier", AtomicString::ConstructFromLiteral)); + DEFINE_STATIC_LOCAL(AtomicString, courierNew, + ("Courier New", AtomicString::ConstructFromLiteral)); + if (equalIgnoringCase(familyName, courier)) + return courierNew; + // On Windows, Courier New (truetype font) is always present and + // Courier is a bitmap font. So, we don't want to map Courier New to + // Courier. + if (equalIgnoringCase(familyName, courierNew)) + return courier; - // Alias Times and Times New Roman. - DEFINE_STATIC_LOCAL(AtomicString, times, ("Times", AtomicString::ConstructFromLiteral)); - DEFINE_STATIC_LOCAL(AtomicString, timesNewRoman, ("Times New Roman", AtomicString::ConstructFromLiteral)); - if (equalIgnoringCase(familyName, times)) - return timesNewRoman; - if (equalIgnoringCase(familyName, timesNewRoman)) - return times; + // Alias Times and Times New Roman. + DEFINE_STATIC_LOCAL(AtomicString, times, + ("Times", AtomicString::ConstructFromLiteral)); + DEFINE_STATIC_LOCAL(AtomicString, timesNewRoman, + ("Times New Roman", AtomicString::ConstructFromLiteral)); + if (equalIgnoringCase(familyName, times)) + return timesNewRoman; + if (equalIgnoringCase(familyName, timesNewRoman)) + return times; - // Alias Arial and Helvetica - DEFINE_STATIC_LOCAL(AtomicString, arial, ("Arial", AtomicString::ConstructFromLiteral)); - DEFINE_STATIC_LOCAL(AtomicString, helvetica, ("Helvetica", AtomicString::ConstructFromLiteral)); - if (equalIgnoringCase(familyName, arial)) - return helvetica; - if (equalIgnoringCase(familyName, helvetica)) - return arial; + // Alias Arial and Helvetica + DEFINE_STATIC_LOCAL(AtomicString, arial, + ("Arial", AtomicString::ConstructFromLiteral)); + DEFINE_STATIC_LOCAL(AtomicString, helvetica, + ("Helvetica", AtomicString::ConstructFromLiteral)); + if (equalIgnoringCase(familyName, arial)) + return helvetica; + if (equalIgnoringCase(familyName, helvetica)) + return arial; - return emptyAtom; + return emptyAtom; } +inline const AtomicString getFallbackFontFamily( + const FontDescription& description) { + DEFINE_STATIC_LOCAL(const AtomicString, sansStr, + ("sans-serif", AtomicString::ConstructFromLiteral)); + DEFINE_STATIC_LOCAL(const AtomicString, serifStr, + ("serif", AtomicString::ConstructFromLiteral)); + DEFINE_STATIC_LOCAL(const AtomicString, monospaceStr, + ("monospace", AtomicString::ConstructFromLiteral)); + DEFINE_STATIC_LOCAL(const AtomicString, cursiveStr, + ("cursive", AtomicString::ConstructFromLiteral)); + DEFINE_STATIC_LOCAL(const AtomicString, fantasyStr, + ("fantasy", AtomicString::ConstructFromLiteral)); -inline const AtomicString getFallbackFontFamily(const FontDescription& description) -{ - DEFINE_STATIC_LOCAL(const AtomicString, sansStr, ("sans-serif", AtomicString::ConstructFromLiteral)); - DEFINE_STATIC_LOCAL(const AtomicString, serifStr, ("serif", AtomicString::ConstructFromLiteral)); - DEFINE_STATIC_LOCAL(const AtomicString, monospaceStr, ("monospace", AtomicString::ConstructFromLiteral)); - DEFINE_STATIC_LOCAL(const AtomicString, cursiveStr, ("cursive", AtomicString::ConstructFromLiteral)); - DEFINE_STATIC_LOCAL(const AtomicString, fantasyStr, ("fantasy", AtomicString::ConstructFromLiteral)); - - switch (description.genericFamily()) { + switch (description.genericFamily()) { case FontDescription::SansSerifFamily: - return sansStr; + return sansStr; case FontDescription::SerifFamily: - return serifStr; + return serifStr; case FontDescription::MonospaceFamily: - return monospaceStr; + return monospaceStr; case FontDescription::CursiveFamily: - return cursiveStr; + return cursiveStr; case FontDescription::FantasyFamily: - return fantasyStr; + return fantasyStr; default: - // Let the caller use the system default font. - return emptyAtom; - } + // Let the caller use the system default font. + return emptyAtom; + } } -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_FONTS_ALTERNATEFONTFAMILY_H_ diff --git a/sky/engine/platform/fonts/Character.cpp b/sky/engine/platform/fonts/Character.cpp index 0d2ae59473646..23a700197bb60 100644 --- a/sky/engine/platform/fonts/Character.cpp +++ b/sky/engine/platform/fonts/Character.cpp @@ -47,328 +47,320 @@ static const UChar32 cjkIsolatedSymbolsArray[] = { // 0x2CB Modifier Letter Grave Access, Mandarin Chinese 4th Tone 0x2CB, // 0x2D9 Dot Above, Mandarin Chinese 5th Tone - 0x2D9, - 0x2020, 0x2021, 0x2030, 0x203B, 0x203C, 0x2042, 0x2047, 0x2048, 0x2049, 0x2051, - 0x20DD, 0x20DE, 0x2100, 0x2103, 0x2105, 0x2109, 0x210A, 0x2113, 0x2116, 0x2121, - 0x212B, 0x213B, 0x2150, 0x2151, 0x2152, 0x217F, 0x2189, 0x2307, 0x2312, 0x23CE, - 0x2423, 0x25A0, 0x25A1, 0x25A2, 0x25AA, 0x25AB, 0x25B1, 0x25B2, 0x25B3, 0x25B6, - 0x25B7, 0x25BC, 0x25BD, 0x25C0, 0x25C1, 0x25C6, 0x25C7, 0x25C9, 0x25CB, 0x25CC, - 0x25EF, 0x2605, 0x2606, 0x260E, 0x2616, 0x2617, 0x2640, 0x2642, 0x26A0, 0x26BD, - 0x26BE, 0x2713, 0x271A, 0x273F, 0x2740, 0x2756, 0x2B1A, 0xFE10, 0xFE11, 0xFE12, - 0xFE19, 0xFF1D, + 0x2D9, 0x2020, 0x2021, 0x2030, 0x203B, 0x203C, 0x2042, 0x2047, 0x2048, + 0x2049, 0x2051, 0x20DD, 0x20DE, 0x2100, 0x2103, 0x2105, 0x2109, 0x210A, + 0x2113, 0x2116, 0x2121, 0x212B, 0x213B, 0x2150, 0x2151, 0x2152, 0x217F, + 0x2189, 0x2307, 0x2312, 0x23CE, 0x2423, 0x25A0, 0x25A1, 0x25A2, 0x25AA, + 0x25AB, 0x25B1, 0x25B2, 0x25B3, 0x25B6, 0x25B7, 0x25BC, 0x25BD, 0x25C0, + 0x25C1, 0x25C6, 0x25C7, 0x25C9, 0x25CB, 0x25CC, 0x25EF, 0x2605, 0x2606, + 0x260E, 0x2616, 0x2617, 0x2640, 0x2642, 0x26A0, 0x26BD, 0x26BE, 0x2713, + 0x271A, 0x273F, 0x2740, 0x2756, 0x2B1A, 0xFE10, 0xFE11, 0xFE12, 0xFE19, + 0xFF1D, // Emoji. - 0x1F100 -}; + 0x1F100}; // Takes a flattened list of closed intervals template -bool valueInIntervalList(const T (&intervalList)[size], const T& value) -{ - const T* bound = std::upper_bound(&intervalList[0], &intervalList[size], value); - if ((bound - intervalList) % 2 == 1) - return true; - return bound > intervalList && *(bound - 1) == value; +bool valueInIntervalList(const T (&intervalList)[size], const T& value) { + const T* bound = + std::upper_bound(&intervalList[0], &intervalList[size], value); + if ((bound - intervalList) % 2 == 1) + return true; + return bound > intervalList && *(bound - 1) == value; } -CodePath Character::characterRangeCodePath(const UChar* characters, unsigned len) -{ - static const UChar complexCodePathRanges[] = { - // U+02E5 through U+02E9 (Modifier Letters : Tone letters) - 0x2E5, 0x2E9, - // U+0300 through U+036F Combining diacritical marks - 0x300, 0x36F, - // U+0591 through U+05CF excluding U+05BE Hebrew combining marks, ... - 0x0591, 0x05BD, - // ... Hebrew punctuation Paseq, Sof Pasuq and Nun Hafukha - 0x05BF, 0x05CF, - // U+0600 through U+109F Arabic, Syriac, Thaana, NKo, Samaritan, Mandaic, - // Devanagari, Bengali, Gurmukhi, Gujarati, Oriya, Tamil, Telugu, Kannada, - // Malayalam, Sinhala, Thai, Lao, Tibetan, Myanmar - 0x0600, 0x109F, - // U+1100 through U+11FF Hangul Jamo (only Ancient Korean should be left - // here if you precompose; Modern Korean will be precomposed as a result of step A) - 0x1100, 0x11FF, - // U+135D through U+135F Ethiopic combining marks - 0x135D, 0x135F, - // U+1780 through U+18AF Tagalog, Hanunoo, Buhid, Taghanwa,Khmer, Mongolian - 0x1700, 0x18AF, - // U+1900 through U+194F Limbu (Unicode 4.0) - 0x1900, 0x194F, - // U+1980 through U+19DF New Tai Lue - 0x1980, 0x19DF, - // U+1A00 through U+1CFF Buginese, Tai Tham, Balinese, Batak, Lepcha, Vedic - 0x1A00, 0x1CFF, - // U+1DC0 through U+1DFF Comining diacritical mark supplement - 0x1DC0, 0x1DFF, - // U+20D0 through U+20FF Combining marks for symbols - 0x20D0, 0x20FF, - // U+2CEF through U+2CF1 Combining marks for Coptic - 0x2CEF, 0x2CF1, - // U+302A through U+302F Ideographic and Hangul Tone marks - 0x302A, 0x302F, - // U+A67C through U+A67D Combining marks for old Cyrillic - 0xA67C, 0xA67D, - // U+A6F0 through U+A6F1 Combining mark for Bamum - 0xA6F0, 0xA6F1, - // U+A800 through U+ABFF Nagri, Phags-pa, Saurashtra, Devanagari Extended, - // Hangul Jamo Ext. A, Javanese, Myanmar Extended A, Tai Viet, Meetei Mayek - 0xA800, 0xABFF, - // U+D7B0 through U+D7FF Hangul Jamo Ext. B - 0xD7B0, 0xD7FF, - // U+FE00 through U+FE0F Unicode variation selectors - 0xFE00, 0xFE0F, - // U+FE20 through U+FE2F Combining half marks - 0xFE20, 0xFE2F - }; - - CodePath result = SimplePath; - for (unsigned i = 0; i < len; i++) { - const UChar c = characters[i]; - - // Shortcut for common case - if (c < 0x2E5) - continue; - - // U+1E00 through U+2000 characters with diacritics and stacked diacritics - if (c >= 0x1E00 && c <= 0x2000) { - result = SimpleWithGlyphOverflowPath; - continue; - } - - // Surrogate pairs - if (c > 0xD7FF && c <= 0xDBFF) { - if (i == len - 1) - continue; - - UChar next = characters[++i]; - if (!U16_IS_TRAIL(next)) - continue; - - UChar32 supplementaryCharacter = U16_GET_SUPPLEMENTARY(c, next); - - if (supplementaryCharacter < 0x1F1E6) // U+1F1E6 through U+1F1FF Regional Indicator Symbols - continue; - if (supplementaryCharacter <= 0x1F1FF) - return ComplexPath; - - if (supplementaryCharacter < 0xE0100) // U+E0100 through U+E01EF Unicode variation selectors. - continue; - if (supplementaryCharacter <= 0xE01EF) - return ComplexPath; - - // FIXME: Check for Brahmi (U+11000 block), Kaithi (U+11080 block) and other complex scripts - // in plane 1 or higher. - - continue; - } - - // Search for other Complex cases - if (valueInIntervalList(complexCodePathRanges, c)) - return ComplexPath; +CodePath Character::characterRangeCodePath(const UChar* characters, + unsigned len) { + static const UChar complexCodePathRanges[] = { + // U+02E5 through U+02E9 (Modifier Letters : Tone letters) + 0x2E5, 0x2E9, + // U+0300 through U+036F Combining diacritical marks + 0x300, 0x36F, + // U+0591 through U+05CF excluding U+05BE Hebrew combining marks, ... + 0x0591, 0x05BD, + // ... Hebrew punctuation Paseq, Sof Pasuq and Nun Hafukha + 0x05BF, 0x05CF, + // U+0600 through U+109F Arabic, Syriac, Thaana, NKo, Samaritan, Mandaic, + // Devanagari, Bengali, Gurmukhi, Gujarati, Oriya, Tamil, Telugu, Kannada, + // Malayalam, Sinhala, Thai, Lao, Tibetan, Myanmar + 0x0600, 0x109F, + // U+1100 through U+11FF Hangul Jamo (only Ancient Korean should be left + // here if you precompose; Modern Korean will be precomposed as a result + // of step A) + 0x1100, 0x11FF, + // U+135D through U+135F Ethiopic combining marks + 0x135D, 0x135F, + // U+1780 through U+18AF Tagalog, Hanunoo, Buhid, Taghanwa,Khmer, + // Mongolian + 0x1700, 0x18AF, + // U+1900 through U+194F Limbu (Unicode 4.0) + 0x1900, 0x194F, + // U+1980 through U+19DF New Tai Lue + 0x1980, 0x19DF, + // U+1A00 through U+1CFF Buginese, Tai Tham, Balinese, Batak, Lepcha, + // Vedic + 0x1A00, 0x1CFF, + // U+1DC0 through U+1DFF Comining diacritical mark supplement + 0x1DC0, 0x1DFF, + // U+20D0 through U+20FF Combining marks for symbols + 0x20D0, 0x20FF, + // U+2CEF through U+2CF1 Combining marks for Coptic + 0x2CEF, 0x2CF1, + // U+302A through U+302F Ideographic and Hangul Tone marks + 0x302A, 0x302F, + // U+A67C through U+A67D Combining marks for old Cyrillic + 0xA67C, 0xA67D, + // U+A6F0 through U+A6F1 Combining mark for Bamum + 0xA6F0, 0xA6F1, + // U+A800 through U+ABFF Nagri, Phags-pa, Saurashtra, Devanagari Extended, + // Hangul Jamo Ext. A, Javanese, Myanmar Extended A, Tai Viet, Meetei + // Mayek + 0xA800, 0xABFF, + // U+D7B0 through U+D7FF Hangul Jamo Ext. B + 0xD7B0, 0xD7FF, + // U+FE00 through U+FE0F Unicode variation selectors + 0xFE00, 0xFE0F, + // U+FE20 through U+FE2F Combining half marks + 0xFE20, 0xFE2F}; + + CodePath result = SimplePath; + for (unsigned i = 0; i < len; i++) { + const UChar c = characters[i]; + + // Shortcut for common case + if (c < 0x2E5) + continue; + + // U+1E00 through U+2000 characters with diacritics and stacked diacritics + if (c >= 0x1E00 && c <= 0x2000) { + result = SimpleWithGlyphOverflowPath; + continue; + } + + // Surrogate pairs + if (c > 0xD7FF && c <= 0xDBFF) { + if (i == len - 1) + continue; + + UChar next = characters[++i]; + if (!U16_IS_TRAIL(next)) + continue; + + UChar32 supplementaryCharacter = U16_GET_SUPPLEMENTARY(c, next); + + if (supplementaryCharacter < + 0x1F1E6) // U+1F1E6 through U+1F1FF Regional Indicator Symbols + continue; + if (supplementaryCharacter <= 0x1F1FF) + return ComplexPath; + + if (supplementaryCharacter < + 0xE0100) // U+E0100 through U+E01EF Unicode variation selectors. + continue; + if (supplementaryCharacter <= 0xE01EF) + return ComplexPath; + + // FIXME: Check for Brahmi (U+11000 block), Kaithi (U+11080 block) and + // other complex scripts in plane 1 or higher. + + continue; } - return result; + // Search for other Complex cases + if (valueInIntervalList(complexCodePathRanges, c)) + return ComplexPath; + } + + return result; } -bool Character::isCJKIdeograph(UChar32 c) -{ - static const UChar32 cjkIdeographRanges[] = { - // CJK Radicals Supplement and Kangxi Radicals. - 0x2E80, 0x2FDF, - // CJK Strokes. - 0x31C0, 0x31EF, - // CJK Unified Ideographs Extension A. - 0x3400, 0x4DBF, - // The basic CJK Unified Ideographs block. - 0x4E00, 0x9FFF, - // CJK Compatibility Ideographs. - 0xF900, 0xFAFF, - // CJK Unified Ideographs Extension B. - 0x20000, 0x2A6DF, - // CJK Unified Ideographs Extension C. - // CJK Unified Ideographs Extension D. - 0x2A700, 0x2B81F, - // CJK Compatibility Ideographs Supplement. - 0x2F800, 0x2FA1F - }; - static size_t cjkIdeographRangesCount = WTF_ARRAY_LENGTH(cjkIdeographRanges); - - // Early out - if (c < cjkIdeographRanges[0] || c > cjkIdeographRanges[cjkIdeographRangesCount - 1]) - return false; - - return valueInIntervalList(cjkIdeographRanges, c); +bool Character::isCJKIdeograph(UChar32 c) { + static const UChar32 cjkIdeographRanges[] = { + // CJK Radicals Supplement and Kangxi Radicals. + 0x2E80, 0x2FDF, + // CJK Strokes. + 0x31C0, 0x31EF, + // CJK Unified Ideographs Extension A. + 0x3400, 0x4DBF, + // The basic CJK Unified Ideographs block. + 0x4E00, 0x9FFF, + // CJK Compatibility Ideographs. + 0xF900, 0xFAFF, + // CJK Unified Ideographs Extension B. + 0x20000, 0x2A6DF, + // CJK Unified Ideographs Extension C. + // CJK Unified Ideographs Extension D. + 0x2A700, 0x2B81F, + // CJK Compatibility Ideographs Supplement. + 0x2F800, 0x2FA1F}; + static size_t cjkIdeographRangesCount = WTF_ARRAY_LENGTH(cjkIdeographRanges); + + // Early out + if (c < cjkIdeographRanges[0] || + c > cjkIdeographRanges[cjkIdeographRangesCount - 1]) + return false; + + return valueInIntervalList(cjkIdeographRanges, c); } -bool Character::isCJKIdeographOrSymbol(UChar32 c) -{ - // Likely common case - if (c < 0x2C7) - return false; - - // Hash lookup for isolated symbols (those not part of a contiguous range) - static HashSet* cjkIsolatedSymbols = 0; - if (!cjkIsolatedSymbols) { - cjkIsolatedSymbols = new HashSet(); - for (size_t i = 0; i < WTF_ARRAY_LENGTH(cjkIsolatedSymbolsArray); ++i) - cjkIsolatedSymbols->add(cjkIsolatedSymbolsArray[i]); - } - if (cjkIsolatedSymbols->contains(c)) - return true; - - if (isCJKIdeograph(c)) - return true; - - static const UChar32 cjkSymbolRanges[] = { - 0x2156, 0x215A, - 0x2160, 0x216B, - 0x2170, 0x217B, - 0x23BE, 0x23CC, - 0x2460, 0x2492, - 0x249C, 0x24FF, - 0x25CE, 0x25D3, - 0x25E2, 0x25E6, - 0x2600, 0x2603, - 0x2660, 0x266F, - 0x2672, 0x267D, - 0x2776, 0x277F, - // Ideographic Description Characters, with CJK Symbols and Punctuation, excluding 0x3030. - // Then Hiragana 0x3040 .. 0x309F, Katakana 0x30A0 .. 0x30FF, Bopomofo 0x3100 .. 0x312F - 0x2FF0, 0x302F, - 0x3031, 0x312F, - // More Bopomofo and Bopomofo Extended 0x31A0 .. 0x31BF - 0x3190, 0x31BF, - // Enclosed CJK Letters and Months (0x3200 .. 0x32FF). - // CJK Compatibility (0x3300 .. 0x33FF). - 0x3200, 0x33FF, - 0xF860, 0xF862, - // CJK Compatibility Forms. - 0xFE30, 0xFE4F, - // Halfwidth and Fullwidth Forms - // Usually only used in CJK - 0xFF00, 0xFF0C, - 0xFF0E, 0xFF1A, - 0xFF1F, 0xFFEF, - // Emoji. - 0x1F110, 0x1F129, - 0x1F130, 0x1F149, - 0x1F150, 0x1F169, - 0x1F170, 0x1F189, - 0x1F200, 0x1F6FF - }; - - return valueInIntervalList(cjkSymbolRanges, c); +bool Character::isCJKIdeographOrSymbol(UChar32 c) { + // Likely common case + if (c < 0x2C7) + return false; + + // Hash lookup for isolated symbols (those not part of a contiguous range) + static HashSet* cjkIsolatedSymbols = 0; + if (!cjkIsolatedSymbols) { + cjkIsolatedSymbols = new HashSet(); + for (size_t i = 0; i < WTF_ARRAY_LENGTH(cjkIsolatedSymbolsArray); ++i) + cjkIsolatedSymbols->add(cjkIsolatedSymbolsArray[i]); + } + if (cjkIsolatedSymbols->contains(c)) + return true; + + if (isCJKIdeograph(c)) + return true; + + static const UChar32 cjkSymbolRanges[] = { + 0x2156, 0x215A, 0x2160, 0x216B, 0x2170, 0x217B, 0x23BE, 0x23CC, 0x2460, + 0x2492, 0x249C, 0x24FF, 0x25CE, 0x25D3, 0x25E2, 0x25E6, 0x2600, 0x2603, + 0x2660, 0x266F, 0x2672, 0x267D, 0x2776, 0x277F, + // Ideographic Description Characters, with CJK Symbols and Punctuation, + // excluding 0x3030. Then Hiragana 0x3040 .. 0x309F, Katakana 0x30A0 .. + // 0x30FF, Bopomofo 0x3100 .. 0x312F + 0x2FF0, 0x302F, 0x3031, 0x312F, + // More Bopomofo and Bopomofo Extended 0x31A0 .. 0x31BF + 0x3190, 0x31BF, + // Enclosed CJK Letters and Months (0x3200 .. 0x32FF). + // CJK Compatibility (0x3300 .. 0x33FF). + 0x3200, 0x33FF, 0xF860, 0xF862, + // CJK Compatibility Forms. + 0xFE30, 0xFE4F, + // Halfwidth and Fullwidth Forms + // Usually only used in CJK + 0xFF00, 0xFF0C, 0xFF0E, 0xFF1A, 0xFF1F, 0xFFEF, + // Emoji. + 0x1F110, 0x1F129, 0x1F130, 0x1F149, 0x1F150, 0x1F169, 0x1F170, 0x1F189, + 0x1F200, 0x1F6FF}; + + return valueInIntervalList(cjkSymbolRanges, c); } -unsigned Character::expansionOpportunityCount(const LChar* characters, size_t length, TextDirection direction, bool& isAfterExpansion) -{ - unsigned count = 0; - if (direction == LTR) { - for (size_t i = 0; i < length; ++i) { - if (treatAsSpace(characters[i])) { - count++; - isAfterExpansion = true; - } else { - isAfterExpansion = false; - } - } - } else { - for (size_t i = length; i > 0; --i) { - if (treatAsSpace(characters[i - 1])) { - count++; - isAfterExpansion = true; - } else { - isAfterExpansion = false; - } - } +unsigned Character::expansionOpportunityCount(const LChar* characters, + size_t length, + TextDirection direction, + bool& isAfterExpansion) { + unsigned count = 0; + if (direction == LTR) { + for (size_t i = 0; i < length; ++i) { + if (treatAsSpace(characters[i])) { + count++; + isAfterExpansion = true; + } else { + isAfterExpansion = false; + } + } + } else { + for (size_t i = length; i > 0; --i) { + if (treatAsSpace(characters[i - 1])) { + count++; + isAfterExpansion = true; + } else { + isAfterExpansion = false; + } } - return count; + } + return count; } -unsigned Character::expansionOpportunityCount(const UChar* characters, size_t length, TextDirection direction, bool& isAfterExpansion) -{ - static bool expandAroundIdeographs = FontPlatformFeatures::canExpandAroundIdeographsInComplexText(); - unsigned count = 0; - if (direction == LTR) { - for (size_t i = 0; i < length; ++i) { - UChar32 character = characters[i]; - if (treatAsSpace(character)) { - count++; - isAfterExpansion = true; - continue; - } - if (U16_IS_LEAD(character) && i + 1 < length && U16_IS_TRAIL(characters[i + 1])) { - character = U16_GET_SUPPLEMENTARY(character, characters[i + 1]); - i++; - } - if (expandAroundIdeographs && isCJKIdeographOrSymbol(character)) { - if (!isAfterExpansion) - count++; - count++; - isAfterExpansion = true; - continue; - } - isAfterExpansion = false; - } - } else { - for (size_t i = length; i > 0; --i) { - UChar32 character = characters[i - 1]; - if (treatAsSpace(character)) { - count++; - isAfterExpansion = true; - continue; - } - if (U16_IS_TRAIL(character) && i > 1 && U16_IS_LEAD(characters[i - 2])) { - character = U16_GET_SUPPLEMENTARY(characters[i - 2], character); - i--; - } - if (expandAroundIdeographs && isCJKIdeographOrSymbol(character)) { - if (!isAfterExpansion) - count++; - count++; - isAfterExpansion = true; - continue; - } - isAfterExpansion = false; - } +unsigned Character::expansionOpportunityCount(const UChar* characters, + size_t length, + TextDirection direction, + bool& isAfterExpansion) { + static bool expandAroundIdeographs = + FontPlatformFeatures::canExpandAroundIdeographsInComplexText(); + unsigned count = 0; + if (direction == LTR) { + for (size_t i = 0; i < length; ++i) { + UChar32 character = characters[i]; + if (treatAsSpace(character)) { + count++; + isAfterExpansion = true; + continue; + } + if (U16_IS_LEAD(character) && i + 1 < length && + U16_IS_TRAIL(characters[i + 1])) { + character = U16_GET_SUPPLEMENTARY(character, characters[i + 1]); + i++; + } + if (expandAroundIdeographs && isCJKIdeographOrSymbol(character)) { + if (!isAfterExpansion) + count++; + count++; + isAfterExpansion = true; + continue; + } + isAfterExpansion = false; } - return count; + } else { + for (size_t i = length; i > 0; --i) { + UChar32 character = characters[i - 1]; + if (treatAsSpace(character)) { + count++; + isAfterExpansion = true; + continue; + } + if (U16_IS_TRAIL(character) && i > 1 && U16_IS_LEAD(characters[i - 2])) { + character = U16_GET_SUPPLEMENTARY(characters[i - 2], character); + i--; + } + if (expandAroundIdeographs && isCJKIdeographOrSymbol(character)) { + if (!isAfterExpansion) + count++; + count++; + isAfterExpansion = true; + continue; + } + isAfterExpansion = false; + } + } + return count; } -bool Character::canReceiveTextEmphasis(UChar32 c) -{ - CharCategory category = Unicode::category(c); - if (category & (Separator_Space | Separator_Line | Separator_Paragraph | Other_NotAssigned | Other_Control | Other_Format)) - return false; +bool Character::canReceiveTextEmphasis(UChar32 c) { + CharCategory category = Unicode::category(c); + if (category & (Separator_Space | Separator_Line | Separator_Paragraph | + Other_NotAssigned | Other_Control | Other_Format)) + return false; - // Additional word-separator characters listed in CSS Text Level 3 Editor's Draft 3 November 2010. - if (c == ethiopicWordspace || c == aegeanWordSeparatorLine || c == aegeanWordSeparatorDot - || c == ugariticWordDivider || c == tibetanMarkIntersyllabicTsheg || c == tibetanMarkDelimiterTshegBstar) - return false; + // Additional word-separator characters listed in CSS Text Level 3 Editor's + // Draft 3 November 2010. + if (c == ethiopicWordspace || c == aegeanWordSeparatorLine || + c == aegeanWordSeparatorDot || c == ugariticWordDivider || + c == tibetanMarkIntersyllabicTsheg || c == tibetanMarkDelimiterTshegBstar) + return false; - return true; + return true; } template -static inline String normalizeSpacesInternal(const CharacterType* characters, unsigned length) -{ - StringBuilder normalized; - normalized.reserveCapacity(length); +static inline String normalizeSpacesInternal(const CharacterType* characters, + unsigned length) { + StringBuilder normalized; + normalized.reserveCapacity(length); - for (unsigned i = 0; i < length; ++i) - normalized.append(Character::normalizeSpaces(characters[i])); + for (unsigned i = 0; i < length; ++i) + normalized.append(Character::normalizeSpaces(characters[i])); - return normalized.toString(); + return normalized.toString(); } -String Character::normalizeSpaces(const LChar* characters, unsigned length) -{ - return normalizeSpacesInternal(characters, length); +String Character::normalizeSpaces(const LChar* characters, unsigned length) { + return normalizeSpacesInternal(characters, length); } -String Character::normalizeSpaces(const UChar* characters, unsigned length) -{ - return normalizeSpacesInternal(characters, length); +String Character::normalizeSpaces(const UChar* characters, unsigned length) { + return normalizeSpacesInternal(characters, length); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/fonts/Character.h b/sky/engine/platform/fonts/Character.h index 5634b320394f6..72adc88d0f8e9 100644 --- a/sky/engine/platform/fonts/Character.h +++ b/sky/engine/platform/fonts/Character.h @@ -41,39 +41,56 @@ namespace blink { class PLATFORM_EXPORT Character { -public: - static CodePath characterRangeCodePath(const LChar*, unsigned) { return SimplePath; } - static CodePath characterRangeCodePath(const UChar*, unsigned len); + public: + static CodePath characterRangeCodePath(const LChar*, unsigned) { + return SimplePath; + } + static CodePath characterRangeCodePath(const UChar*, unsigned len); - static bool isCJKIdeograph(UChar32); - static bool isCJKIdeographOrSymbol(UChar32); + static bool isCJKIdeograph(UChar32); + static bool isCJKIdeographOrSymbol(UChar32); - static unsigned expansionOpportunityCount(const LChar*, size_t length, TextDirection, bool& isAfterExpansion); - static unsigned expansionOpportunityCount(const UChar*, size_t length, TextDirection, bool& isAfterExpansion); + static unsigned expansionOpportunityCount(const LChar*, + size_t length, + TextDirection, + bool& isAfterExpansion); + static unsigned expansionOpportunityCount(const UChar*, + size_t length, + TextDirection, + bool& isAfterExpansion); - static bool treatAsSpace(UChar c) { return c == ' ' || c == '\t' || c == '\n' || c == noBreakSpace; } - static bool treatAsZeroWidthSpace(UChar c) { return treatAsZeroWidthSpaceInComplexScript(c) || c == 0x200c || c == 0x200d; } - static bool treatAsZeroWidthSpaceInComplexScript(UChar c) { return c < 0x20 || (c >= 0x7F && c < 0xA0) || c == softHyphen || c == zeroWidthSpace || (c >= 0x200e && c <= 0x200f) || (c >= 0x202a && c <= 0x202e) || c == zeroWidthNoBreakSpace || c == objectReplacementCharacter; } - static bool canReceiveTextEmphasis(UChar32); + static bool treatAsSpace(UChar c) { + return c == ' ' || c == '\t' || c == '\n' || c == noBreakSpace; + } + static bool treatAsZeroWidthSpace(UChar c) { + return treatAsZeroWidthSpaceInComplexScript(c) || c == 0x200c || + c == 0x200d; + } + static bool treatAsZeroWidthSpaceInComplexScript(UChar c) { + return c < 0x20 || (c >= 0x7F && c < 0xA0) || c == softHyphen || + c == zeroWidthSpace || (c >= 0x200e && c <= 0x200f) || + (c >= 0x202a && c <= 0x202e) || c == zeroWidthNoBreakSpace || + c == objectReplacementCharacter; + } + static bool canReceiveTextEmphasis(UChar32); - static inline UChar normalizeSpaces(UChar character) - { - if (treatAsSpace(character)) - return space; + static inline UChar normalizeSpaces(UChar character) { + if (treatAsSpace(character)) + return space; - if (treatAsZeroWidthSpace(character)) - return zeroWidthSpace; + if (treatAsZeroWidthSpace(character)) + return zeroWidthSpace; - return character; - } + return character; + } - static String normalizeSpaces(const LChar*, unsigned length); - static String normalizeSpaces(const UChar*, unsigned length); + static String normalizeSpaces(const LChar*, unsigned length); + static String normalizeSpaces(const UChar*, unsigned length); -private: - Character(); + private: + Character(); }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_FONTS_CHARACTER_H_ diff --git a/sky/engine/platform/fonts/CustomFontData.h b/sky/engine/platform/fonts/CustomFontData.h index 8816e4f2eba66..3b9ad4a5b96d4 100644 --- a/sky/engine/platform/fonts/CustomFontData.h +++ b/sky/engine/platform/fonts/CustomFontData.h @@ -35,28 +35,34 @@ class SimpleFontData; struct WidthIterator; class PLATFORM_EXPORT CustomFontData : public RefCounted { -public: - static PassRefPtr create() { return adoptRef(new CustomFontData()); } + public: + static PassRefPtr create() { + return adoptRef(new CustomFontData()); + } - virtual ~CustomFontData() { } + virtual ~CustomFontData() {} - virtual void beginLoadIfNeeded() const { }; - virtual bool isLoading() const { return false; } - virtual bool isLoadingFallback() const { return false; } - virtual bool shouldSkipDrawing() const { return false; } - virtual void clearFontFaceSource() { } + virtual void beginLoadIfNeeded() const {}; + virtual bool isLoading() const { return false; } + virtual bool isLoadingFallback() const { return false; } + virtual bool shouldSkipDrawing() const { return false; } + virtual void clearFontFaceSource() {} - virtual bool isSVGFont() const { return false; } - virtual void initializeFontData(SimpleFontData*, float) { } + virtual bool isSVGFont() const { return false; } + virtual void initializeFontData(SimpleFontData*, float) {} -protected: - CustomFontData() { } + protected: + CustomFontData() {} }; -#define DEFINE_CUSTOM_FONT_DATA_TYPE_CASTS(thisType, predicate) \ - template inline thisType* to##thisType(const RefPtr& customFontData) { return to##thisType(customFontData.get()); } \ - DEFINE_TYPE_CASTS(thisType, CustomFontData, customFontData, customFontData->predicate, customFontData.predicate) +#define DEFINE_CUSTOM_FONT_DATA_TYPE_CASTS(thisType, predicate) \ + template \ + inline thisType* to##thisType(const RefPtr& customFontData) { \ + return to##thisType(customFontData.get()); \ + } \ + DEFINE_TYPE_CASTS(thisType, CustomFontData, customFontData, \ + customFontData->predicate, customFontData.predicate) -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_FONTS_CUSTOMFONTDATA_H_ diff --git a/sky/engine/platform/fonts/FixedPitchFontType.h b/sky/engine/platform/fonts/FixedPitchFontType.h index 830c679c1eb8e..b207172e86b85 100644 --- a/sky/engine/platform/fonts/FixedPitchFontType.h +++ b/sky/engine/platform/fonts/FixedPitchFontType.h @@ -7,11 +7,8 @@ namespace blink { -enum FixedPitchFontType { - FixedPitchFont, - NonFixedPitchFont -}; +enum FixedPitchFontType { FixedPitchFont, NonFixedPitchFont }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_FONTS_FIXEDPITCHFONTTYPE_H_ diff --git a/sky/engine/platform/fonts/Font.cpp b/sky/engine/platform/fonts/Font.cpp index f0e88150dd5fa..01e3b7c051dce 100644 --- a/sky/engine/platform/fonts/Font.cpp +++ b/sky/engine/platform/fonts/Font.cpp @@ -52,788 +52,923 @@ CodePath Font::s_codePath = AutoPath; // Font Implementation (Cross-Platform Portion) // ============================================================================================ -Font::Font() -{ -} +Font::Font() {} -Font::Font(const FontDescription& fd) - : m_fontDescription(fd) -{ -} +Font::Font(const FontDescription& fd) : m_fontDescription(fd) {} Font::Font(const Font& other) - : m_fontDescription(other.m_fontDescription) - , m_fontFallbackList(other.m_fontFallbackList) -{ -} - -Font& Font::operator=(const Font& other) -{ - m_fontDescription = other.m_fontDescription; - m_fontFallbackList = other.m_fontFallbackList; - return *this; -} - -bool Font::operator==(const Font& other) const -{ - // Our FontData don't have to be checked, since checking the font description will be fine. - // FIXME: This does not work if the font was made with the FontPlatformData constructor. - if (loadingCustomFonts() || other.loadingCustomFonts()) - return false; - - FontSelector* first = m_fontFallbackList ? m_fontFallbackList->fontSelector() : 0; - FontSelector* second = other.m_fontFallbackList ? other.m_fontFallbackList->fontSelector() : 0; - - return first == second - && m_fontDescription == other.m_fontDescription - && (m_fontFallbackList ? m_fontFallbackList->fontSelectorVersion() : 0) == (other.m_fontFallbackList ? other.m_fontFallbackList->fontSelectorVersion() : 0) - && (m_fontFallbackList ? m_fontFallbackList->generation() : 0) == (other.m_fontFallbackList ? other.m_fontFallbackList->generation() : 0); -} - -void Font::update(PassRefPtr fontSelector) const -{ - // FIXME: It is pretty crazy that we are willing to just poke into a RefPtr, but it ends up - // being reasonably safe (because inherited fonts in the render tree pick up the new - // style anyway. Other copies are transient, e.g., the state in the GraphicsContext, and - // won't stick around long enough to get you in trouble). Still, this is pretty disgusting, - // and could eventually be rectified by using RefPtrs for Fonts themselves. - if (!m_fontFallbackList) - m_fontFallbackList = FontFallbackList::create(); - m_fontFallbackList->invalidate(fontSelector); -} - -float Font::buildGlyphBuffer(const TextRunPaintInfo& runInfo, GlyphBuffer& glyphBuffer, - ForTextEmphasisOrNot forTextEmphasis) const -{ - if (codePath(runInfo.run) == ComplexPath) { - HarfBuzzShaper shaper(this, runInfo.run, (forTextEmphasis == ForTextEmphasis) - ? HarfBuzzShaper::ForTextEmphasis : HarfBuzzShaper::NotForTextEmphasis); - shaper.setDrawRange(runInfo.from, runInfo.to); - shaper.shape(&glyphBuffer); - - return 0; - } - - WidthIterator it(this, runInfo.run, nullptr, false, forTextEmphasis); - it.advance(runInfo.from); - float beforeWidth = it.runWidthSoFar(); - it.advance(runInfo.to, &glyphBuffer); + : m_fontDescription(other.m_fontDescription), + m_fontFallbackList(other.m_fontFallbackList) {} - if (runInfo.run.ltr()) - return beforeWidth; - - // RTL - float afterWidth = it.runWidthSoFar(); - it.advance(runInfo.run.length()); - glyphBuffer.reverse(); - - return it.runWidthSoFar() - afterWidth; +Font& Font::operator=(const Font& other) { + m_fontDescription = other.m_fontDescription; + m_fontFallbackList = other.m_fontFallbackList; + return *this; } -void Font::drawText(GraphicsContext* context, const TextRunPaintInfo& runInfo, - const FloatPoint& point) const -{ - // Don't draw anything while we are using custom fonts that are in the process of loading. - if (shouldSkipDrawing()) - return; - - TextDrawingModeFlags textMode = context->textDrawingMode(); - if (!(textMode & TextModeFill) && !((textMode & TextModeStroke) && context->hasStroke())) - return; - - if (runInfo.cachedTextBlob && runInfo.cachedTextBlob->get()) { - // we have a pre-cached blob -- happy joy! - drawTextBlob(context, runInfo.cachedTextBlob->get(), point.data()); - return; - } - - { - FontCachePurgePreventer preventer; - GlyphBuffer glyphBuffer; - float initialAdvance = buildGlyphBuffer(runInfo, glyphBuffer); - - if (glyphBuffer.isEmpty()) - return; - - // Enabling text-blobs forces the blob rendering path even for uncacheable blobs. - TextBlobPtr uncacheableTextBlob; - TextBlobPtr& textBlob = runInfo.cachedTextBlob ? *runInfo.cachedTextBlob : uncacheableTextBlob; - FloatRect blobBounds = runInfo.bounds; - blobBounds.moveBy(-point); - - textBlob = buildTextBlob(glyphBuffer, initialAdvance, blobBounds); - if (textBlob) { - drawTextBlob(context, textBlob.get(), point.data()); - return; - } - - drawGlyphBuffer(context, runInfo, glyphBuffer, FloatPoint(point.x() + initialAdvance, point.y())); - } -} - -float Font::drawUncachedText(GraphicsContext* context, const TextRunPaintInfo& runInfo, - const FloatPoint& point, CustomFontNotReadyAction customFontNotReadyAction) const -{ - // Don't draw anything while we are using custom fonts that are in the process of loading, - // except if the 'force' argument is set to true (in which case it will use a fallback - // font). - if (shouldSkipDrawing() && customFontNotReadyAction == DoNotPaintIfFontNotReady) - return 0; - - TextDrawingModeFlags textMode = context->textDrawingMode(); - if (!(textMode & TextModeFill) && !((textMode & TextModeStroke) && context->hasStroke())) - return 0; +bool Font::operator==(const Font& other) const { + // Our FontData don't have to be checked, since checking the font description + // will be fine. + // FIXME: This does not work if the font was made with the FontPlatformData + // constructor. + if (loadingCustomFonts() || other.loadingCustomFonts()) + return false; + FontSelector* first = + m_fontFallbackList ? m_fontFallbackList->fontSelector() : 0; + FontSelector* second = + other.m_fontFallbackList ? other.m_fontFallbackList->fontSelector() : 0; + + return first == second && m_fontDescription == other.m_fontDescription && + (m_fontFallbackList ? m_fontFallbackList->fontSelectorVersion() : 0) == + (other.m_fontFallbackList + ? other.m_fontFallbackList->fontSelectorVersion() + : 0) && + (m_fontFallbackList ? m_fontFallbackList->generation() : 0) == + (other.m_fontFallbackList ? other.m_fontFallbackList->generation() + : 0); +} + +void Font::update(PassRefPtr fontSelector) const { + // FIXME: It is pretty crazy that we are willing to just poke into a RefPtr, + // but it ends up being reasonably safe (because inherited fonts in the render + // tree pick up the new style anyway. Other copies are transient, e.g., the + // state in the GraphicsContext, and won't stick around long enough to get you + // in trouble). Still, this is pretty disgusting, and could eventually be + // rectified by using RefPtrs for Fonts themselves. + if (!m_fontFallbackList) + m_fontFallbackList = FontFallbackList::create(); + m_fontFallbackList->invalidate(fontSelector); +} + +float Font::buildGlyphBuffer(const TextRunPaintInfo& runInfo, + GlyphBuffer& glyphBuffer, + ForTextEmphasisOrNot forTextEmphasis) const { + if (codePath(runInfo.run) == ComplexPath) { + HarfBuzzShaper shaper(this, runInfo.run, + (forTextEmphasis == ForTextEmphasis) + ? HarfBuzzShaper::ForTextEmphasis + : HarfBuzzShaper::NotForTextEmphasis); + shaper.setDrawRange(runInfo.from, runInfo.to); + shaper.shape(&glyphBuffer); + + return 0; + } + + WidthIterator it(this, runInfo.run, nullptr, false, forTextEmphasis); + it.advance(runInfo.from); + float beforeWidth = it.runWidthSoFar(); + it.advance(runInfo.to, &glyphBuffer); + + if (runInfo.run.ltr()) + return beforeWidth; + + // RTL + float afterWidth = it.runWidthSoFar(); + it.advance(runInfo.run.length()); + glyphBuffer.reverse(); + + return it.runWidthSoFar() - afterWidth; +} + +void Font::drawText(GraphicsContext* context, + const TextRunPaintInfo& runInfo, + const FloatPoint& point) const { + // Don't draw anything while we are using custom fonts that are in the process + // of loading. + if (shouldSkipDrawing()) + return; + + TextDrawingModeFlags textMode = context->textDrawingMode(); + if (!(textMode & TextModeFill) && + !((textMode & TextModeStroke) && context->hasStroke())) + return; + + if (runInfo.cachedTextBlob && runInfo.cachedTextBlob->get()) { + // we have a pre-cached blob -- happy joy! + drawTextBlob(context, runInfo.cachedTextBlob->get(), point.data()); + return; + } + + { + FontCachePurgePreventer preventer; GlyphBuffer glyphBuffer; float initialAdvance = buildGlyphBuffer(runInfo, glyphBuffer); if (glyphBuffer.isEmpty()) - return 0; - - return drawGlyphBuffer(context, runInfo, glyphBuffer, FloatPoint(point.x() + initialAdvance, point.y())); -} - -void Font::drawEmphasisMarks(GraphicsContext* context, const TextRunPaintInfo& runInfo, const AtomicString& mark, const FloatPoint& point) const -{ - if (shouldSkipDrawing()) - return; - - GlyphBuffer glyphBuffer; - float initialAdvance = buildGlyphBuffer(runInfo, glyphBuffer, ForTextEmphasis); - - if (glyphBuffer.isEmpty()) - return; - - drawEmphasisMarks(context, runInfo, glyphBuffer, mark, FloatPoint(point.x() + initialAdvance, point.y())); -} - -static inline void updateGlyphOverflowFromBounds(const IntRectExtent& glyphBounds, - const FontMetrics& fontMetrics, GlyphOverflow* glyphOverflow) -{ - glyphOverflow->top = std::max(glyphOverflow->top, - glyphBounds.top() - (glyphOverflow->computeBounds ? 0 : fontMetrics.ascent())); - glyphOverflow->bottom = std::max(glyphOverflow->bottom, - glyphBounds.bottom() - (glyphOverflow->computeBounds ? 0 : fontMetrics.descent())); - glyphOverflow->left = glyphBounds.left(); - glyphOverflow->right = glyphBounds.right(); -} - -float Font::width(const TextRun& run, HashSet* fallbackFonts, GlyphOverflow* glyphOverflow) const -{ - CodePath codePathToUse = codePath(run); - if (codePathToUse != ComplexPath) { - // The simple path can optimize the case where glyph overflow is not observable. - if (codePathToUse != SimpleWithGlyphOverflowPath && (glyphOverflow && !glyphOverflow->computeBounds)) - glyphOverflow = 0; + return; + + // Enabling text-blobs forces the blob rendering path even for uncacheable + // blobs. + TextBlobPtr uncacheableTextBlob; + TextBlobPtr& textBlob = + runInfo.cachedTextBlob ? *runInfo.cachedTextBlob : uncacheableTextBlob; + FloatRect blobBounds = runInfo.bounds; + blobBounds.moveBy(-point); + + textBlob = buildTextBlob(glyphBuffer, initialAdvance, blobBounds); + if (textBlob) { + drawTextBlob(context, textBlob.get(), point.data()); + return; } - bool hasWordSpacingOrLetterSpacing = fontDescription().wordSpacing() || fontDescription().letterSpacing(); - bool isCacheable = codePathToUse == ComplexPath - && !hasWordSpacingOrLetterSpacing // Word spacing and letter spacing can change the width of a word. - && !run.allowTabs(); // If we allow tabs and a tab occurs inside a word, the width of the word varies based on its position on the line. - - WidthCacheEntry* cacheEntry = isCacheable - ? m_fontFallbackList->widthCache().add(run, WidthCacheEntry()) - : 0; - if (cacheEntry && cacheEntry->isValid()) { - if (glyphOverflow) - updateGlyphOverflowFromBounds(cacheEntry->glyphBounds, fontMetrics(), glyphOverflow); - return cacheEntry->width; - } + drawGlyphBuffer(context, runInfo, glyphBuffer, + FloatPoint(point.x() + initialAdvance, point.y())); + } +} + +float Font::drawUncachedText( + GraphicsContext* context, + const TextRunPaintInfo& runInfo, + const FloatPoint& point, + CustomFontNotReadyAction customFontNotReadyAction) const { + // Don't draw anything while we are using custom fonts that are in the process + // of loading, except if the 'force' argument is set to true (in which case it + // will use a fallback font). + if (shouldSkipDrawing() && + customFontNotReadyAction == DoNotPaintIfFontNotReady) + return 0; + + TextDrawingModeFlags textMode = context->textDrawingMode(); + if (!(textMode & TextModeFill) && + !((textMode & TextModeStroke) && context->hasStroke())) + return 0; + + GlyphBuffer glyphBuffer; + float initialAdvance = buildGlyphBuffer(runInfo, glyphBuffer); + + if (glyphBuffer.isEmpty()) + return 0; + + return drawGlyphBuffer(context, runInfo, glyphBuffer, + FloatPoint(point.x() + initialAdvance, point.y())); +} + +void Font::drawEmphasisMarks(GraphicsContext* context, + const TextRunPaintInfo& runInfo, + const AtomicString& mark, + const FloatPoint& point) const { + if (shouldSkipDrawing()) + return; + + GlyphBuffer glyphBuffer; + float initialAdvance = + buildGlyphBuffer(runInfo, glyphBuffer, ForTextEmphasis); + + if (glyphBuffer.isEmpty()) + return; + + drawEmphasisMarks(context, runInfo, glyphBuffer, mark, + FloatPoint(point.x() + initialAdvance, point.y())); +} + +static inline void updateGlyphOverflowFromBounds( + const IntRectExtent& glyphBounds, + const FontMetrics& fontMetrics, + GlyphOverflow* glyphOverflow) { + glyphOverflow->top = std::max( + glyphOverflow->top, + glyphBounds.top() - + (glyphOverflow->computeBounds ? 0 : fontMetrics.ascent())); + glyphOverflow->bottom = std::max( + glyphOverflow->bottom, + glyphBounds.bottom() - + (glyphOverflow->computeBounds ? 0 : fontMetrics.descent())); + glyphOverflow->left = glyphBounds.left(); + glyphOverflow->right = glyphBounds.right(); +} + +float Font::width(const TextRun& run, + HashSet* fallbackFonts, + GlyphOverflow* glyphOverflow) const { + CodePath codePathToUse = codePath(run); + if (codePathToUse != ComplexPath) { + // The simple path can optimize the case where glyph overflow is not + // observable. + if (codePathToUse != SimpleWithGlyphOverflowPath && + (glyphOverflow && !glyphOverflow->computeBounds)) + glyphOverflow = 0; + } + + bool hasWordSpacingOrLetterSpacing = + fontDescription().wordSpacing() || fontDescription().letterSpacing(); + bool isCacheable = + codePathToUse == ComplexPath && + !hasWordSpacingOrLetterSpacing // Word spacing and letter spacing can + // change the width of a word. + && !run.allowTabs(); // If we allow tabs and a tab occurs inside a word, + // the width of the word varies based on its + // position on the line. + + WidthCacheEntry* cacheEntry = + isCacheable ? m_fontFallbackList->widthCache().add(run, WidthCacheEntry()) + : 0; + if (cacheEntry && cacheEntry->isValid()) { + if (glyphOverflow) + updateGlyphOverflowFromBounds(cacheEntry->glyphBounds, fontMetrics(), + glyphOverflow); + return cacheEntry->width; + } - float result; - IntRectExtent glyphBounds; - if (codePathToUse == ComplexPath) { - result = floatWidthForComplexText(run, fallbackFonts, &glyphBounds); - } else { - ASSERT(!isCacheable); - result = floatWidthForSimpleText(run, fallbackFonts, glyphOverflow ? &glyphBounds : 0); - } + float result; + IntRectExtent glyphBounds; + if (codePathToUse == ComplexPath) { + result = floatWidthForComplexText(run, fallbackFonts, &glyphBounds); + } else { + ASSERT(!isCacheable); + result = floatWidthForSimpleText(run, fallbackFonts, + glyphOverflow ? &glyphBounds : 0); + } - if (cacheEntry && (!fallbackFonts || fallbackFonts->isEmpty())) { - cacheEntry->glyphBounds = glyphBounds; - cacheEntry->width = result; - } + if (cacheEntry && (!fallbackFonts || fallbackFonts->isEmpty())) { + cacheEntry->glyphBounds = glyphBounds; + cacheEntry->width = result; + } - if (glyphOverflow) - updateGlyphOverflowFromBounds(glyphBounds, fontMetrics(), glyphOverflow); - return result; + if (glyphOverflow) + updateGlyphOverflowFromBounds(glyphBounds, fontMetrics(), glyphOverflow); + return result; } -float Font::width(const TextRun& run, int& charsConsumed, Glyph& glyphId) const -{ - charsConsumed = run.length(); - glyphId = 0; - return width(run); +float Font::width(const TextRun& run, + int& charsConsumed, + Glyph& glyphId) const { + charsConsumed = run.length(); + glyphId = 0; + return width(run); } -FloatRect Font::selectionRectForText(const TextRun& run, const FloatPoint& point, int h, int from, int to, bool accountForGlyphBounds) const -{ - to = (to == -1 ? run.length() : to); +FloatRect Font::selectionRectForText(const TextRun& run, + const FloatPoint& point, + int h, + int from, + int to, + bool accountForGlyphBounds) const { + to = (to == -1 ? run.length() : to); - CodePath codePathToUse = codePath(run); - // FIXME: Use the fast code path once it handles partial runs with kerning and ligatures. See http://webkit.org/b/100050 - if (codePathToUse != ComplexPath && fontDescription().typesettingFeatures() && (from || to != run.length())) - codePathToUse = ComplexPath; + CodePath codePathToUse = codePath(run); + // FIXME: Use the fast code path once it handles partial runs with kerning and + // ligatures. See http://webkit.org/b/100050 + if (codePathToUse != ComplexPath && fontDescription().typesettingFeatures() && + (from || to != run.length())) + codePathToUse = ComplexPath; - if (codePathToUse != ComplexPath) - return selectionRectForSimpleText(run, point, h, from, to, accountForGlyphBounds); + if (codePathToUse != ComplexPath) + return selectionRectForSimpleText(run, point, h, from, to, + accountForGlyphBounds); - return selectionRectForComplexText(run, point, h, from, to); + return selectionRectForComplexText(run, point, h, from, to); } -int Font::offsetForPosition(const TextRun& run, float x, bool includePartialGlyphs) const -{ - // FIXME: Use the fast code path once it handles partial runs with kerning and ligatures. See http://webkit.org/b/100050 - if (codePath(run) != ComplexPath && !fontDescription().typesettingFeatures()) - return offsetForPositionForSimpleText(run, x, includePartialGlyphs); +int Font::offsetForPosition(const TextRun& run, + float x, + bool includePartialGlyphs) const { + // FIXME: Use the fast code path once it handles partial runs with kerning and + // ligatures. See http://webkit.org/b/100050 + if (codePath(run) != ComplexPath && !fontDescription().typesettingFeatures()) + return offsetForPositionForSimpleText(run, x, includePartialGlyphs); - return offsetForPositionForComplexText(run, x, includePartialGlyphs); + return offsetForPositionForComplexText(run, x, includePartialGlyphs); } -void Font::setCodePath(CodePath p) -{ - s_codePath = p; +void Font::setCodePath(CodePath p) { + s_codePath = p; } -CodePath Font::codePath() -{ - return s_codePath; +CodePath Font::codePath() { + return s_codePath; } -CodePath Font::codePath(const TextRun& run) const -{ - if (s_codePath != AutoPath) - return s_codePath; +CodePath Font::codePath(const TextRun& run) const { + if (s_codePath != AutoPath) + return s_codePath; - if (m_fontDescription.featureSettings() && m_fontDescription.featureSettings()->size() > 0 && m_fontDescription.letterSpacing() == 0) - return ComplexPath; + if (m_fontDescription.featureSettings() && + m_fontDescription.featureSettings()->size() > 0 && + m_fontDescription.letterSpacing() == 0) + return ComplexPath; - if (m_fontDescription.widthVariant() != RegularWidth) - return ComplexPath; + if (m_fontDescription.widthVariant() != RegularWidth) + return ComplexPath; - if (run.length() > 1 && fontDescription().typesettingFeatures()) - return ComplexPath; + if (run.length() > 1 && fontDescription().typesettingFeatures()) + return ComplexPath; - if (!run.characterScanForCodePath()) - return SimplePath; + if (!run.characterScanForCodePath()) + return SimplePath; - if (run.is8Bit()) - return SimplePath; + if (run.is8Bit()) + return SimplePath; - // Start from 0 since drawing and highlighting also measure the characters before run->from. - return Character::characterRangeCodePath(run.characters16(), run.length()); + // Start from 0 since drawing and highlighting also measure the characters + // before run->from. + return Character::characterRangeCodePath(run.characters16(), run.length()); } -void Font::willUseFontData(UChar32 character) const -{ - const FontFamily& family = fontDescription().family(); - if (m_fontFallbackList && m_fontFallbackList->fontSelector() && !family.familyIsEmpty()) - m_fontFallbackList->fontSelector()->willUseFontData(fontDescription(), family.family(), character); +void Font::willUseFontData(UChar32 character) const { + const FontFamily& family = fontDescription().family(); + if (m_fontFallbackList && m_fontFallbackList->fontSelector() && + !family.familyIsEmpty()) + m_fontFallbackList->fontSelector()->willUseFontData( + fontDescription(), family.family(), character); } -static inline bool isInRange(UChar32 character, UChar32 lowerBound, UChar32 upperBound) -{ - return character >= lowerBound && character <= upperBound; +static inline bool isInRange(UChar32 character, + UChar32 lowerBound, + UChar32 upperBound) { + return character >= lowerBound && character <= upperBound; } -static bool shouldIgnoreRotation(UChar32 character) -{ - if (character == 0x000A7 || character == 0x000A9 || character == 0x000AE) - return true; - - if (character == 0x000B6 || character == 0x000BC || character == 0x000BD || character == 0x000BE) - return true; +static bool shouldIgnoreRotation(UChar32 character) { + if (character == 0x000A7 || character == 0x000A9 || character == 0x000AE) + return true; - if (isInRange(character, 0x002E5, 0x002EB)) - return true; + if (character == 0x000B6 || character == 0x000BC || character == 0x000BD || + character == 0x000BE) + return true; - if (isInRange(character, 0x01100, 0x011FF) || isInRange(character, 0x01401, 0x0167F) || isInRange(character, 0x01800, 0x018FF)) - return true; + if (isInRange(character, 0x002E5, 0x002EB)) + return true; - if (character == 0x02016 || character == 0x02018 || character == 0x02019 || character == 0x02020 || character == 0x02021 - || character == 0x2030 || character == 0x02031) - return true; + if (isInRange(character, 0x01100, 0x011FF) || + isInRange(character, 0x01401, 0x0167F) || + isInRange(character, 0x01800, 0x018FF)) + return true; - if (isInRange(character, 0x0203B, 0x0203D) || character == 0x02042 || character == 0x02044 || character == 0x02047 - || character == 0x02048 || character == 0x02049 || character == 0x2051) - return true; + if (character == 0x02016 || character == 0x02018 || character == 0x02019 || + character == 0x02020 || character == 0x02021 || character == 0x2030 || + character == 0x02031) + return true; - if (isInRange(character, 0x02065, 0x02069) || isInRange(character, 0x020DD, 0x020E0) - || isInRange(character, 0x020E2, 0x020E4) || isInRange(character, 0x02100, 0x02117) - || isInRange(character, 0x02119, 0x02131) || isInRange(character, 0x02133, 0x0213F)) - return true; + if (isInRange(character, 0x0203B, 0x0203D) || character == 0x02042 || + character == 0x02044 || character == 0x02047 || character == 0x02048 || + character == 0x02049 || character == 0x2051) + return true; - if (isInRange(character, 0x02145, 0x0214A) || character == 0x0214C || character == 0x0214D - || isInRange(character, 0x0214F, 0x0218F)) - return true; + if (isInRange(character, 0x02065, 0x02069) || + isInRange(character, 0x020DD, 0x020E0) || + isInRange(character, 0x020E2, 0x020E4) || + isInRange(character, 0x02100, 0x02117) || + isInRange(character, 0x02119, 0x02131) || + isInRange(character, 0x02133, 0x0213F)) + return true; - if (isInRange(character, 0x02300, 0x02307) || isInRange(character, 0x0230C, 0x0231F) - || isInRange(character, 0x02322, 0x0232B) || isInRange(character, 0x0237D, 0x0239A) - || isInRange(character, 0x023B4, 0x023B6) || isInRange(character, 0x023BA, 0x023CF) - || isInRange(character, 0x023D1, 0x023DB) || isInRange(character, 0x023E2, 0x024FF)) - return true; + if (isInRange(character, 0x02145, 0x0214A) || character == 0x0214C || + character == 0x0214D || isInRange(character, 0x0214F, 0x0218F)) + return true; - if (isInRange(character, 0x025A0, 0x02619) || isInRange(character, 0x02620, 0x02767) - || isInRange(character, 0x02776, 0x02793) || isInRange(character, 0x02B12, 0x02B2F) - || isInRange(character, 0x02B4D, 0x02BFF) || isInRange(character, 0x02E80, 0x03007)) - return true; + if (isInRange(character, 0x02300, 0x02307) || + isInRange(character, 0x0230C, 0x0231F) || + isInRange(character, 0x02322, 0x0232B) || + isInRange(character, 0x0237D, 0x0239A) || + isInRange(character, 0x023B4, 0x023B6) || + isInRange(character, 0x023BA, 0x023CF) || + isInRange(character, 0x023D1, 0x023DB) || + isInRange(character, 0x023E2, 0x024FF)) + return true; - if (character == 0x03012 || character == 0x03013 || isInRange(character, 0x03020, 0x0302F) - || isInRange(character, 0x03031, 0x0309F) || isInRange(character, 0x030A1, 0x030FB) - || isInRange(character, 0x030FD, 0x0A4CF)) - return true; + if (isInRange(character, 0x025A0, 0x02619) || + isInRange(character, 0x02620, 0x02767) || + isInRange(character, 0x02776, 0x02793) || + isInRange(character, 0x02B12, 0x02B2F) || + isInRange(character, 0x02B4D, 0x02BFF) || + isInRange(character, 0x02E80, 0x03007)) + return true; - if (isInRange(character, 0x0A840, 0x0A87F) || isInRange(character, 0x0A960, 0x0A97F) - || isInRange(character, 0x0AC00, 0x0D7FF) || isInRange(character, 0x0E000, 0x0FAFF)) - return true; + if (character == 0x03012 || character == 0x03013 || + isInRange(character, 0x03020, 0x0302F) || + isInRange(character, 0x03031, 0x0309F) || + isInRange(character, 0x030A1, 0x030FB) || + isInRange(character, 0x030FD, 0x0A4CF)) + return true; - if (isInRange(character, 0x0FE10, 0x0FE1F) || isInRange(character, 0x0FE30, 0x0FE48) - || isInRange(character, 0x0FE50, 0x0FE57) || isInRange(character, 0x0FE5F, 0x0FE62) - || isInRange(character, 0x0FE67, 0x0FE6F)) - return true; + if (isInRange(character, 0x0A840, 0x0A87F) || + isInRange(character, 0x0A960, 0x0A97F) || + isInRange(character, 0x0AC00, 0x0D7FF) || + isInRange(character, 0x0E000, 0x0FAFF)) + return true; - if (isInRange(character, 0x0FF01, 0x0FF07) || isInRange(character, 0x0FF0A, 0x0FF0C) - || isInRange(character, 0x0FF0E, 0x0FF19) || isInRange(character, 0x0FF1F, 0x0FF3A)) - return true; + if (isInRange(character, 0x0FE10, 0x0FE1F) || + isInRange(character, 0x0FE30, 0x0FE48) || + isInRange(character, 0x0FE50, 0x0FE57) || + isInRange(character, 0x0FE5F, 0x0FE62) || + isInRange(character, 0x0FE67, 0x0FE6F)) + return true; - if (character == 0x0FF3C || character == 0x0FF3E) - return true; + if (isInRange(character, 0x0FF01, 0x0FF07) || + isInRange(character, 0x0FF0A, 0x0FF0C) || + isInRange(character, 0x0FF0E, 0x0FF19) || + isInRange(character, 0x0FF1F, 0x0FF3A)) + return true; - if (isInRange(character, 0x0FF40, 0x0FF5A) || isInRange(character, 0x0FFE0, 0x0FFE2) - || isInRange(character, 0x0FFE4, 0x0FFE7) || isInRange(character, 0x0FFF0, 0x0FFF8) - || character == 0x0FFFD) - return true; + if (character == 0x0FF3C || character == 0x0FF3E) + return true; - if (isInRange(character, 0x13000, 0x1342F) || isInRange(character, 0x1B000, 0x1B0FF) - || isInRange(character, 0x1D000, 0x1D1FF) || isInRange(character, 0x1D300, 0x1D37F) - || isInRange(character, 0x1F000, 0x1F64F) || isInRange(character, 0x1F680, 0x1F77F)) - return true; + if (isInRange(character, 0x0FF40, 0x0FF5A) || + isInRange(character, 0x0FFE0, 0x0FFE2) || + isInRange(character, 0x0FFE4, 0x0FFE7) || + isInRange(character, 0x0FFF0, 0x0FFF8) || character == 0x0FFFD) + return true; - if (isInRange(character, 0x20000, 0x2FFFD) || isInRange(character, 0x30000, 0x3FFFD)) - return true; + if (isInRange(character, 0x13000, 0x1342F) || + isInRange(character, 0x1B000, 0x1B0FF) || + isInRange(character, 0x1D000, 0x1D1FF) || + isInRange(character, 0x1D300, 0x1D37F) || + isInRange(character, 0x1F000, 0x1F64F) || + isInRange(character, 0x1F680, 0x1F77F)) + return true; - return false; -} + if (isInRange(character, 0x20000, 0x2FFFD) || + isInRange(character, 0x30000, 0x3FFFD)) + return true; -static inline std::pair glyphDataAndPageForNonCJKCharacterWithGlyphOrientation(UChar32 character, NonCJKGlyphOrientation orientation, GlyphData& data, GlyphPage* page, unsigned pageNumber) -{ - if (orientation == NonCJKGlyphOrientationUpright || shouldIgnoreRotation(character)) { - RefPtr uprightFontData = data.fontData->uprightOrientationFontData(); - GlyphPageTreeNode* uprightNode = GlyphPageTreeNode::getRootChild(uprightFontData.get(), pageNumber); - GlyphPage* uprightPage = uprightNode->page(); - if (uprightPage) { - GlyphData uprightData = uprightPage->glyphDataForCharacter(character); - // If the glyphs are the same, then we know we can just use the horizontal glyph rotated vertically to be upright. - if (data.glyph == uprightData.glyph) - return std::make_pair(data, page); - // The glyphs are distinct, meaning that the font has a vertical-right glyph baked into it. We can't use that - // glyph, so we fall back to the upright data and use the horizontal glyph. - if (uprightData.fontData) - return std::make_pair(uprightData, uprightPage); - } - } else if (orientation == NonCJKGlyphOrientationVerticalRight) { - RefPtr verticalRightFontData = data.fontData->verticalRightOrientationFontData(); - GlyphPageTreeNode* verticalRightNode = GlyphPageTreeNode::getRootChild(verticalRightFontData.get(), pageNumber); - GlyphPage* verticalRightPage = verticalRightNode->page(); - if (verticalRightPage) { - GlyphData verticalRightData = verticalRightPage->glyphDataForCharacter(character); - // If the glyphs are distinct, we will make the assumption that the font has a vertical-right glyph baked - // into it. - if (data.glyph != verticalRightData.glyph) - return std::make_pair(data, page); - // The glyphs are identical, meaning that we should just use the horizontal glyph. - if (verticalRightData.fontData) - return std::make_pair(verticalRightData, verticalRightPage); - } + return false; +} + +static inline std::pair +glyphDataAndPageForNonCJKCharacterWithGlyphOrientation( + UChar32 character, + NonCJKGlyphOrientation orientation, + GlyphData& data, + GlyphPage* page, + unsigned pageNumber) { + if (orientation == NonCJKGlyphOrientationUpright || + shouldIgnoreRotation(character)) { + RefPtr uprightFontData = + data.fontData->uprightOrientationFontData(); + GlyphPageTreeNode* uprightNode = + GlyphPageTreeNode::getRootChild(uprightFontData.get(), pageNumber); + GlyphPage* uprightPage = uprightNode->page(); + if (uprightPage) { + GlyphData uprightData = uprightPage->glyphDataForCharacter(character); + // If the glyphs are the same, then we know we can just use the horizontal + // glyph rotated vertically to be upright. + if (data.glyph == uprightData.glyph) + return std::make_pair(data, page); + // The glyphs are distinct, meaning that the font has a vertical-right + // glyph baked into it. We can't use that glyph, so we fall back to the + // upright data and use the horizontal glyph. + if (uprightData.fontData) + return std::make_pair(uprightData, uprightPage); } - return std::make_pair(data, page); -} - -std::pair Font::glyphDataAndPageForCharacter(UChar32 c, bool mirror, FontDataVariant variant) const -{ - ASSERT(isMainThread()); - - if (variant == AutoVariant) { - if (m_fontDescription.variant() == FontVariantSmallCaps && !primaryFont()->isSVGFont()) { - UChar32 upperC = toUpper(c); - if (upperC != c) { - c = upperC; - variant = SmallCapsVariant; - } else { - variant = NormalVariant; - } - } else { - variant = NormalVariant; - } + } else if (orientation == NonCJKGlyphOrientationVerticalRight) { + RefPtr verticalRightFontData = + data.fontData->verticalRightOrientationFontData(); + GlyphPageTreeNode* verticalRightNode = GlyphPageTreeNode::getRootChild( + verticalRightFontData.get(), pageNumber); + GlyphPage* verticalRightPage = verticalRightNode->page(); + if (verticalRightPage) { + GlyphData verticalRightData = + verticalRightPage->glyphDataForCharacter(character); + // If the glyphs are distinct, we will make the assumption that the font + // has a vertical-right glyph baked into it. + if (data.glyph != verticalRightData.glyph) + return std::make_pair(data, page); + // The glyphs are identical, meaning that we should just use the + // horizontal glyph. + if (verticalRightData.fontData) + return std::make_pair(verticalRightData, verticalRightPage); } - - if (mirror) - c = mirroredChar(c); - - unsigned pageNumber = (c / GlyphPage::size); - - GlyphPageTreeNode* node = m_fontFallbackList->getPageNode(pageNumber); - if (!node) { - node = GlyphPageTreeNode::getRootChild(fontDataAt(0), pageNumber); - m_fontFallbackList->setPageNode(pageNumber, node); + } + return std::make_pair(data, page); +} + +std::pair Font::glyphDataAndPageForCharacter( + UChar32 c, + bool mirror, + FontDataVariant variant) const { + ASSERT(isMainThread()); + + if (variant == AutoVariant) { + if (m_fontDescription.variant() == FontVariantSmallCaps && + !primaryFont()->isSVGFont()) { + UChar32 upperC = toUpper(c); + if (upperC != c) { + c = upperC; + variant = SmallCapsVariant; + } else { + variant = NormalVariant; + } + } else { + variant = NormalVariant; } - - GlyphPage* page = 0; - if (variant == NormalVariant) { - // Fastest loop, for the common case (normal variant). - while (true) { - page = node->page(); - if (page) { - GlyphData data = page->glyphDataForCharacter(c); - if (data.fontData && (data.fontData->platformData().orientation() == Horizontal || data.fontData->isTextOrientationFallback())) - return std::make_pair(data, page); - - if (data.fontData) { - if (Character::isCJKIdeographOrSymbol(c)) { - if (!data.fontData->hasVerticalGlyphs()) { - // Use the broken ideograph font data. The broken ideograph font will use the horizontal width of glyphs - // to make sure you get a square (even for broken glyphs like symbols used for punctuation). - variant = BrokenIdeographVariant; - break; - } - } else { - return glyphDataAndPageForNonCJKCharacterWithGlyphOrientation(c, m_fontDescription.nonCJKGlyphOrientation(), data, page, pageNumber); - } - - return std::make_pair(data, page); - } - - if (node->isSystemFallback()) - break; + } + + if (mirror) + c = mirroredChar(c); + + unsigned pageNumber = (c / GlyphPage::size); + + GlyphPageTreeNode* node = m_fontFallbackList->getPageNode(pageNumber); + if (!node) { + node = GlyphPageTreeNode::getRootChild(fontDataAt(0), pageNumber); + m_fontFallbackList->setPageNode(pageNumber, node); + } + + GlyphPage* page = 0; + if (variant == NormalVariant) { + // Fastest loop, for the common case (normal variant). + while (true) { + page = node->page(); + if (page) { + GlyphData data = page->glyphDataForCharacter(c); + if (data.fontData && + (data.fontData->platformData().orientation() == Horizontal || + data.fontData->isTextOrientationFallback())) + return std::make_pair(data, page); + + if (data.fontData) { + if (Character::isCJKIdeographOrSymbol(c)) { + if (!data.fontData->hasVerticalGlyphs()) { + // Use the broken ideograph font data. The broken ideograph font + // will use the horizontal width of glyphs to make sure you get a + // square (even for broken glyphs like symbols used for + // punctuation). + variant = BrokenIdeographVariant; + break; } + } else { + return glyphDataAndPageForNonCJKCharacterWithGlyphOrientation( + c, m_fontDescription.nonCJKGlyphOrientation(), data, page, + pageNumber); + } - // Proceed with the fallback list. - node = node->getChild(fontDataAt(node->level()), pageNumber); - m_fontFallbackList->setPageNode(pageNumber, node); + return std::make_pair(data, page); } - } - if (variant != NormalVariant) { - while (true) { - page = node->page(); - if (page) { - GlyphData data = page->glyphDataForCharacter(c); - if (data.fontData) { - // The variantFontData function should not normally return 0. - // But if it does, we will just render the capital letter big. - RefPtr variantFontData = data.fontData->variantFontData(m_fontDescription, variant); - if (!variantFontData) - return std::make_pair(data, page); - - GlyphPageTreeNode* variantNode = GlyphPageTreeNode::getRootChild(variantFontData.get(), pageNumber); - GlyphPage* variantPage = variantNode->page(); - if (variantPage) { - GlyphData data = variantPage->glyphDataForCharacter(c); - if (data.fontData) - return std::make_pair(data, variantPage); - } - - // Do not attempt system fallback off the variantFontData. This is the very unlikely case that - // a font has the lowercase character but the small caps font does not have its uppercase version. - return std::make_pair(variantFontData->missingGlyphData(), page); - } - - if (node->isSystemFallback()) - break; - } - // Proceed with the fallback list. - node = node->getChild(fontDataAt(node->level()), pageNumber); - m_fontFallbackList->setPageNode(pageNumber, node); - } - } + if (node->isSystemFallback()) + break; + } - ASSERT(page); - ASSERT(node->isSystemFallback()); - - // System fallback is character-dependent. When we get here, we - // know that the character in question isn't in the system fallback - // font's glyph page. Try to lazily create it here. - - // FIXME: Unclear if this should normalizeSpaces above 0xFFFF. - // Doing so changes fast/text/international/plane2-diffs.html - UChar32 characterToRender = c; - if (characterToRender <= 0xFFFF) - characterToRender = Character::normalizeSpaces(characterToRender); - const SimpleFontData* fontDataToSubstitute = fontDataAt(0)->fontDataForCharacter(characterToRender); - RefPtr characterFontData = FontCache::fontCache()->fallbackFontForCharacter(m_fontDescription, characterToRender, fontDataToSubstitute); - if (characterFontData) { - if (characterFontData->platformData().orientation() == Vertical && !characterFontData->hasVerticalGlyphs() && Character::isCJKIdeographOrSymbol(c)) - variant = BrokenIdeographVariant; - if (variant != NormalVariant) - characterFontData = characterFontData->variantFontData(m_fontDescription, variant); + // Proceed with the fallback list. + node = node->getChild(fontDataAt(node->level()), pageNumber); + m_fontFallbackList->setPageNode(pageNumber, node); } - if (characterFontData) { - // Got the fallback glyph and font. - GlyphPage* fallbackPage = GlyphPageTreeNode::getRootChild(characterFontData.get(), pageNumber)->page(); - GlyphData data = fallbackPage && fallbackPage->glyphForCharacter(c) ? fallbackPage->glyphDataForCharacter(c) : characterFontData->missingGlyphData(); - // Cache it so we don't have to do system fallback again next time. - if (variant == NormalVariant) { - page->setGlyphDataForCharacter(c, data.glyph, data.fontData); - data.fontData->setMaxGlyphPageTreeLevel(std::max(data.fontData->maxGlyphPageTreeLevel(), node->level())); - if (!Character::isCJKIdeographOrSymbol(c) && data.fontData->platformData().orientation() != Horizontal && !data.fontData->isTextOrientationFallback()) - return glyphDataAndPageForNonCJKCharacterWithGlyphOrientation(c, m_fontDescription.nonCJKGlyphOrientation(), data, page, pageNumber); + } + if (variant != NormalVariant) { + while (true) { + page = node->page(); + if (page) { + GlyphData data = page->glyphDataForCharacter(c); + if (data.fontData) { + // The variantFontData function should not normally return 0. + // But if it does, we will just render the capital letter big. + RefPtr variantFontData = + data.fontData->variantFontData(m_fontDescription, variant); + if (!variantFontData) + return std::make_pair(data, page); + + GlyphPageTreeNode* variantNode = GlyphPageTreeNode::getRootChild( + variantFontData.get(), pageNumber); + GlyphPage* variantPage = variantNode->page(); + if (variantPage) { + GlyphData data = variantPage->glyphDataForCharacter(c); + if (data.fontData) + return std::make_pair(data, variantPage); + } + + // Do not attempt system fallback off the variantFontData. This is the + // very unlikely case that a font has the lowercase character but the + // small caps font does not have its uppercase version. + return std::make_pair(variantFontData->missingGlyphData(), page); } - return std::make_pair(data, page); - } - // Even system fallback can fail; use the missing glyph in that case. - // FIXME: It would be nicer to use the missing glyph from the last resort font instead. - GlyphData data = primaryFont()->missingGlyphData(); + if (node->isSystemFallback()) + break; + } + + // Proceed with the fallback list. + node = node->getChild(fontDataAt(node->level()), pageNumber); + m_fontFallbackList->setPageNode(pageNumber, node); + } + } + + ASSERT(page); + ASSERT(node->isSystemFallback()); + + // System fallback is character-dependent. When we get here, we + // know that the character in question isn't in the system fallback + // font's glyph page. Try to lazily create it here. + + // FIXME: Unclear if this should normalizeSpaces above 0xFFFF. + // Doing so changes fast/text/international/plane2-diffs.html + UChar32 characterToRender = c; + if (characterToRender <= 0xFFFF) + characterToRender = Character::normalizeSpaces(characterToRender); + const SimpleFontData* fontDataToSubstitute = + fontDataAt(0)->fontDataForCharacter(characterToRender); + RefPtr characterFontData = + FontCache::fontCache()->fallbackFontForCharacter( + m_fontDescription, characterToRender, fontDataToSubstitute); + if (characterFontData) { + if (characterFontData->platformData().orientation() == Vertical && + !characterFontData->hasVerticalGlyphs() && + Character::isCJKIdeographOrSymbol(c)) + variant = BrokenIdeographVariant; + if (variant != NormalVariant) + characterFontData = + characterFontData->variantFontData(m_fontDescription, variant); + } + if (characterFontData) { + // Got the fallback glyph and font. + GlyphPage* fallbackPage = + GlyphPageTreeNode::getRootChild(characterFontData.get(), pageNumber) + ->page(); + GlyphData data = fallbackPage && fallbackPage->glyphForCharacter(c) + ? fallbackPage->glyphDataForCharacter(c) + : characterFontData->missingGlyphData(); + // Cache it so we don't have to do system fallback again next time. if (variant == NormalVariant) { - page->setGlyphDataForCharacter(c, data.glyph, data.fontData); - data.fontData->setMaxGlyphPageTreeLevel(std::max(data.fontData->maxGlyphPageTreeLevel(), node->level())); + page->setGlyphDataForCharacter(c, data.glyph, data.fontData); + data.fontData->setMaxGlyphPageTreeLevel( + std::max(data.fontData->maxGlyphPageTreeLevel(), node->level())); + if (!Character::isCJKIdeographOrSymbol(c) && + data.fontData->platformData().orientation() != Horizontal && + !data.fontData->isTextOrientationFallback()) + return glyphDataAndPageForNonCJKCharacterWithGlyphOrientation( + c, m_fontDescription.nonCJKGlyphOrientation(), data, page, + pageNumber); } return std::make_pair(data, page); -} - -bool Font::primaryFontHasGlyphForCharacter(UChar32 character) const -{ - unsigned pageNumber = (character / GlyphPage::size); - - GlyphPageTreeNode* node = GlyphPageTreeNode::getRootChild(primaryFont(), pageNumber); - GlyphPage* page = node->page(); + } - return page && page->glyphForCharacter(character); + // Even system fallback can fail; use the missing glyph in that case. + // FIXME: It would be nicer to use the missing glyph from the last resort font + // instead. + GlyphData data = primaryFont()->missingGlyphData(); + if (variant == NormalVariant) { + page->setGlyphDataForCharacter(c, data.glyph, data.fontData); + data.fontData->setMaxGlyphPageTreeLevel( + std::max(data.fontData->maxGlyphPageTreeLevel(), node->level())); + } + return std::make_pair(data, page); } -// FIXME: This function may not work if the emphasis mark uses a complex script, but none of the -// standard emphasis marks do so. -bool Font::getEmphasisMarkGlyphData(const AtomicString& mark, GlyphData& glyphData) const -{ - if (mark.isEmpty()) - return false; - - UChar32 character = mark[0]; - - if (U16_IS_SURROGATE(character)) { - if (!U16_IS_SURROGATE_LEAD(character)) - return false; - - if (mark.length() < 2) - return false; +bool Font::primaryFontHasGlyphForCharacter(UChar32 character) const { + unsigned pageNumber = (character / GlyphPage::size); - UChar low = mark[1]; - if (!U16_IS_TRAIL(low)) - return false; + GlyphPageTreeNode* node = + GlyphPageTreeNode::getRootChild(primaryFont(), pageNumber); + GlyphPage* page = node->page(); - character = U16_GET_SUPPLEMENTARY(character, low); - } - - glyphData = glyphDataForCharacter(character, false, EmphasisMarkVariant); - return true; + return page && page->glyphForCharacter(character); } -int Font::emphasisMarkAscent(const AtomicString& mark) const -{ - FontCachePurgePreventer purgePreventer; - - GlyphData markGlyphData; - if (!getEmphasisMarkGlyphData(mark, markGlyphData)) - return 0; +// FIXME: This function may not work if the emphasis mark uses a complex script, +// but none of the standard emphasis marks do so. +bool Font::getEmphasisMarkGlyphData(const AtomicString& mark, + GlyphData& glyphData) const { + if (mark.isEmpty()) + return false; - const SimpleFontData* markFontData = markGlyphData.fontData; - ASSERT(markFontData); - if (!markFontData) - return 0; + UChar32 character = mark[0]; - return markFontData->fontMetrics().ascent(); -} + if (U16_IS_SURROGATE(character)) { + if (!U16_IS_SURROGATE_LEAD(character)) + return false; -int Font::emphasisMarkDescent(const AtomicString& mark) const -{ - FontCachePurgePreventer purgePreventer; + if (mark.length() < 2) + return false; - GlyphData markGlyphData; - if (!getEmphasisMarkGlyphData(mark, markGlyphData)) - return 0; + UChar low = mark[1]; + if (!U16_IS_TRAIL(low)) + return false; - const SimpleFontData* markFontData = markGlyphData.fontData; - ASSERT(markFontData); - if (!markFontData) - return 0; + character = U16_GET_SUPPLEMENTARY(character, low); + } - return markFontData->fontMetrics().descent(); + glyphData = glyphDataForCharacter(character, false, EmphasisMarkVariant); + return true; } -int Font::emphasisMarkHeight(const AtomicString& mark) const -{ - FontCachePurgePreventer purgePreventer; +int Font::emphasisMarkAscent(const AtomicString& mark) const { + FontCachePurgePreventer purgePreventer; - GlyphData markGlyphData; - if (!getEmphasisMarkGlyphData(mark, markGlyphData)) - return 0; + GlyphData markGlyphData; + if (!getEmphasisMarkGlyphData(mark, markGlyphData)) + return 0; - const SimpleFontData* markFontData = markGlyphData.fontData; - ASSERT(markFontData); - if (!markFontData) - return 0; + const SimpleFontData* markFontData = markGlyphData.fontData; + ASSERT(markFontData); + if (!markFontData) + return 0; - return markFontData->fontMetrics().height(); + return markFontData->fontMetrics().ascent(); } -float Font::drawGlyphBuffer(GraphicsContext* context, const TextRunPaintInfo& runInfo, const GlyphBuffer& glyphBuffer, const FloatPoint& point) const -{ - // Draw each contiguous run of glyphs that use the same font data. - const SimpleFontData* fontData = glyphBuffer.fontDataAt(0); - FloatPoint startPoint(point); - float advanceSoFar = 0; - unsigned lastFrom = 0; - unsigned nextGlyph = 0; - while (nextGlyph < glyphBuffer.size()) { - const SimpleFontData* nextFontData = glyphBuffer.fontDataAt(nextGlyph); - if (nextFontData != fontData) { - drawGlyphs(context, fontData, glyphBuffer, lastFrom, nextGlyph - lastFrom, startPoint, runInfo.bounds); - lastFrom = nextGlyph; - fontData = nextFontData; - startPoint += FloatSize(advanceSoFar, 0); - advanceSoFar = 0; - } - advanceSoFar += glyphBuffer.advanceAt(nextGlyph); - nextGlyph++; - } - drawGlyphs(context, fontData, glyphBuffer, lastFrom, nextGlyph - lastFrom, startPoint, runInfo.bounds); - startPoint += FloatSize(advanceSoFar, 0); - return startPoint.x() - point.x(); -} +int Font::emphasisMarkDescent(const AtomicString& mark) const { + FontCachePurgePreventer purgePreventer; + GlyphData markGlyphData; + if (!getEmphasisMarkGlyphData(mark, markGlyphData)) + return 0; -inline static float offsetToMiddleOfGlyph(const SimpleFontData* fontData, Glyph glyph) -{ - if (fontData->platformData().orientation() == Horizontal) { - FloatRect bounds = fontData->boundsForGlyph(glyph); - return bounds.x() + bounds.width() / 2; - } - // FIXME: Use glyph bounds once they make sense for vertical fonts. - return fontData->widthForGlyph(glyph) / 2; -} + const SimpleFontData* markFontData = markGlyphData.fontData; + ASSERT(markFontData); + if (!markFontData) + return 0; -inline static float offsetToMiddleOfAdvanceAtIndex(const GlyphBuffer& glyphBuffer, size_t i) -{ - return glyphBuffer.advanceAt(i) / 2; + return markFontData->fontMetrics().descent(); } -void Font::drawEmphasisMarks(GraphicsContext* context, const TextRunPaintInfo& runInfo, const GlyphBuffer& glyphBuffer, const AtomicString& mark, const FloatPoint& point) const -{ - FontCachePurgePreventer purgePreventer; - - GlyphData markGlyphData; - if (!getEmphasisMarkGlyphData(mark, markGlyphData)) - return; +int Font::emphasisMarkHeight(const AtomicString& mark) const { + FontCachePurgePreventer purgePreventer; - const SimpleFontData* markFontData = markGlyphData.fontData; - ASSERT(markFontData); - if (!markFontData) - return; + GlyphData markGlyphData; + if (!getEmphasisMarkGlyphData(mark, markGlyphData)) + return 0; - Glyph markGlyph = markGlyphData.glyph; - Glyph spaceGlyph = markFontData->spaceGlyph(); - - float middleOfLastGlyph = offsetToMiddleOfAdvanceAtIndex(glyphBuffer, 0); - FloatPoint startPoint(point.x() + middleOfLastGlyph - offsetToMiddleOfGlyph(markFontData, markGlyph), point.y()); - - GlyphBuffer markBuffer; - for (unsigned i = 0; i + 1 < glyphBuffer.size(); ++i) { - float middleOfNextGlyph = offsetToMiddleOfAdvanceAtIndex(glyphBuffer, i + 1); - float advance = glyphBuffer.advanceAt(i) - middleOfLastGlyph + middleOfNextGlyph; - markBuffer.add(glyphBuffer.glyphAt(i) ? markGlyph : spaceGlyph, markFontData, advance); - middleOfLastGlyph = middleOfNextGlyph; - } - markBuffer.add(glyphBuffer.glyphAt(glyphBuffer.size() - 1) ? markGlyph : spaceGlyph, markFontData, 0); + const SimpleFontData* markFontData = markGlyphData.fontData; + ASSERT(markFontData); + if (!markFontData) + return 0; - drawGlyphBuffer(context, runInfo, markBuffer, startPoint); + return markFontData->fontMetrics().height(); } -float Font::floatWidthForSimpleText(const TextRun& run, HashSet* fallbackFonts, IntRectExtent* glyphBounds) const -{ - WidthIterator it(this, run, fallbackFonts, glyphBounds); - it.advance(run.length()); - - if (glyphBounds) { - glyphBounds->setTop(floorf(-it.minGlyphBoundingBoxY())); - glyphBounds->setBottom(ceilf(it.maxGlyphBoundingBoxY())); - glyphBounds->setLeft(floorf(it.firstGlyphOverflow())); - glyphBounds->setRight(ceilf(it.lastGlyphOverflow())); +float Font::drawGlyphBuffer(GraphicsContext* context, + const TextRunPaintInfo& runInfo, + const GlyphBuffer& glyphBuffer, + const FloatPoint& point) const { + // Draw each contiguous run of glyphs that use the same font data. + const SimpleFontData* fontData = glyphBuffer.fontDataAt(0); + FloatPoint startPoint(point); + float advanceSoFar = 0; + unsigned lastFrom = 0; + unsigned nextGlyph = 0; + while (nextGlyph < glyphBuffer.size()) { + const SimpleFontData* nextFontData = glyphBuffer.fontDataAt(nextGlyph); + if (nextFontData != fontData) { + drawGlyphs(context, fontData, glyphBuffer, lastFrom, nextGlyph - lastFrom, + startPoint, runInfo.bounds); + lastFrom = nextGlyph; + fontData = nextFontData; + startPoint += FloatSize(advanceSoFar, 0); + advanceSoFar = 0; } - - return it.m_runWidthSoFar; -} - -FloatRect Font::pixelSnappedSelectionRect(float fromX, float toX, float y, float height) -{ - // Using roundf() rather than ceilf() for the right edge as a compromise to - // ensure correct caret positioning. - float roundedX = roundf(fromX); - return FloatRect(roundedX, y, roundf(toX - roundedX), height); -} - -FloatRect Font::selectionRectForSimpleText(const TextRun& run, const FloatPoint& point, int h, int from, int to, bool accountForGlyphBounds) const -{ - WidthIterator it(this, run, 0, accountForGlyphBounds); - it.advance(from); - float fromX = it.m_runWidthSoFar; - it.advance(to); - float toX = it.m_runWidthSoFar; - - if (run.rtl()) { - it.advance(run.length()); - float totalWidth = it.m_runWidthSoFar; - float beforeWidth = fromX; - float afterWidth = toX; - fromX = totalWidth - afterWidth; - toX = totalWidth - beforeWidth; + advanceSoFar += glyphBuffer.advanceAt(nextGlyph); + nextGlyph++; + } + drawGlyphs(context, fontData, glyphBuffer, lastFrom, nextGlyph - lastFrom, + startPoint, runInfo.bounds); + startPoint += FloatSize(advanceSoFar, 0); + return startPoint.x() - point.x(); +} + +inline static float offsetToMiddleOfGlyph(const SimpleFontData* fontData, + Glyph glyph) { + if (fontData->platformData().orientation() == Horizontal) { + FloatRect bounds = fontData->boundsForGlyph(glyph); + return bounds.x() + bounds.width() / 2; + } + // FIXME: Use glyph bounds once they make sense for vertical fonts. + return fontData->widthForGlyph(glyph) / 2; +} + +inline static float offsetToMiddleOfAdvanceAtIndex( + const GlyphBuffer& glyphBuffer, + size_t i) { + return glyphBuffer.advanceAt(i) / 2; +} + +void Font::drawEmphasisMarks(GraphicsContext* context, + const TextRunPaintInfo& runInfo, + const GlyphBuffer& glyphBuffer, + const AtomicString& mark, + const FloatPoint& point) const { + FontCachePurgePreventer purgePreventer; + + GlyphData markGlyphData; + if (!getEmphasisMarkGlyphData(mark, markGlyphData)) + return; + + const SimpleFontData* markFontData = markGlyphData.fontData; + ASSERT(markFontData); + if (!markFontData) + return; + + Glyph markGlyph = markGlyphData.glyph; + Glyph spaceGlyph = markFontData->spaceGlyph(); + + float middleOfLastGlyph = offsetToMiddleOfAdvanceAtIndex(glyphBuffer, 0); + FloatPoint startPoint(point.x() + middleOfLastGlyph - + offsetToMiddleOfGlyph(markFontData, markGlyph), + point.y()); + + GlyphBuffer markBuffer; + for (unsigned i = 0; i + 1 < glyphBuffer.size(); ++i) { + float middleOfNextGlyph = + offsetToMiddleOfAdvanceAtIndex(glyphBuffer, i + 1); + float advance = + glyphBuffer.advanceAt(i) - middleOfLastGlyph + middleOfNextGlyph; + markBuffer.add(glyphBuffer.glyphAt(i) ? markGlyph : spaceGlyph, + markFontData, advance); + middleOfLastGlyph = middleOfNextGlyph; + } + markBuffer.add( + glyphBuffer.glyphAt(glyphBuffer.size() - 1) ? markGlyph : spaceGlyph, + markFontData, 0); + + drawGlyphBuffer(context, runInfo, markBuffer, startPoint); +} + +float Font::floatWidthForSimpleText( + const TextRun& run, + HashSet* fallbackFonts, + IntRectExtent* glyphBounds) const { + WidthIterator it(this, run, fallbackFonts, glyphBounds); + it.advance(run.length()); + + if (glyphBounds) { + glyphBounds->setTop(floorf(-it.minGlyphBoundingBoxY())); + glyphBounds->setBottom(ceilf(it.maxGlyphBoundingBoxY())); + glyphBounds->setLeft(floorf(it.firstGlyphOverflow())); + glyphBounds->setRight(ceilf(it.lastGlyphOverflow())); + } + + return it.m_runWidthSoFar; +} + +FloatRect Font::pixelSnappedSelectionRect(float fromX, + float toX, + float y, + float height) { + // Using roundf() rather than ceilf() for the right edge as a compromise to + // ensure correct caret positioning. + float roundedX = roundf(fromX); + return FloatRect(roundedX, y, roundf(toX - roundedX), height); +} + +FloatRect Font::selectionRectForSimpleText(const TextRun& run, + const FloatPoint& point, + int h, + int from, + int to, + bool accountForGlyphBounds) const { + WidthIterator it(this, run, 0, accountForGlyphBounds); + it.advance(from); + float fromX = it.m_runWidthSoFar; + it.advance(to); + float toX = it.m_runWidthSoFar; + + if (run.rtl()) { + it.advance(run.length()); + float totalWidth = it.m_runWidthSoFar; + float beforeWidth = fromX; + float afterWidth = toX; + fromX = totalWidth - afterWidth; + toX = totalWidth - beforeWidth; + } + + return pixelSnappedSelectionRect( + point.x() + fromX, point.x() + toX, + accountForGlyphBounds ? it.minGlyphBoundingBoxY() : point.y(), + accountForGlyphBounds + ? it.maxGlyphBoundingBoxY() - it.minGlyphBoundingBoxY() + : h); +} + +int Font::offsetForPositionForSimpleText(const TextRun& run, + float x, + bool includePartialGlyphs) const { + float delta = x; + + WidthIterator it(this, run); + unsigned offset; + if (run.rtl()) { + delta -= floatWidthForSimpleText(run); + while (1) { + offset = it.m_currentCharacter; + float w; + if (!it.advanceOneCharacter(w)) + break; + delta += w; + if (includePartialGlyphs) { + if (delta - w / 2 >= 0) + break; + } else { + if (delta >= 0) + break; + } } - - return pixelSnappedSelectionRect(point.x() + fromX, point.x() + toX, - accountForGlyphBounds ? it.minGlyphBoundingBoxY() : point.y(), - accountForGlyphBounds ? it.maxGlyphBoundingBoxY() - it.minGlyphBoundingBoxY() : h); -} - -int Font::offsetForPositionForSimpleText(const TextRun& run, float x, bool includePartialGlyphs) const -{ - float delta = x; - - WidthIterator it(this, run); - unsigned offset; - if (run.rtl()) { - delta -= floatWidthForSimpleText(run); - while (1) { - offset = it.m_currentCharacter; - float w; - if (!it.advanceOneCharacter(w)) - break; - delta += w; - if (includePartialGlyphs) { - if (delta - w / 2 >= 0) - break; - } else { - if (delta >= 0) - break; - } - } - } else { - while (1) { - offset = it.m_currentCharacter; - float w; - if (!it.advanceOneCharacter(w)) - break; - delta -= w; - if (includePartialGlyphs) { - if (delta + w / 2 <= 0) - break; - } else { - if (delta <= 0) - break; - } - } + } else { + while (1) { + offset = it.m_currentCharacter; + float w; + if (!it.advanceOneCharacter(w)) + break; + delta -= w; + if (includePartialGlyphs) { + if (delta + w / 2 <= 0) + break; + } else { + if (delta <= 0) + break; + } } + } - return offset; + return offset; } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/fonts/Font.h b/sky/engine/platform/fonts/Font.h index 45de7cd03fba1..3f37f19df201a 100644 --- a/sky/engine/platform/fonts/Font.h +++ b/sky/engine/platform/fonts/Font.h @@ -61,163 +61,219 @@ struct TextRunPaintInfo; struct GlyphData; struct GlyphOverflow { - GlyphOverflow() - : left(0) - , right(0) - , top(0) - , bottom(0) - , computeBounds(false) - { - } - - bool isZero() const - { - return !left && !right && !top && !bottom; - } - - int left; - int right; - int top; - int bottom; - bool computeBounds; -}; - - -class PLATFORM_EXPORT Font { -public: - Font(); - Font(const FontDescription&); - ~Font(); - - Font(const Font&); - Font& operator=(const Font&); - - bool operator==(const Font& other) const; - bool operator!=(const Font& other) const { return !(*this == other); } - - const FontDescription& fontDescription() const { return m_fontDescription; } - - void update(PassRefPtr) const; - - enum CustomFontNotReadyAction { DoNotPaintIfFontNotReady, UseFallbackIfFontNotReady }; - void drawText(GraphicsContext*, const TextRunPaintInfo&, const FloatPoint&) const; - float drawUncachedText(GraphicsContext*, const TextRunPaintInfo&, const FloatPoint&, CustomFontNotReadyAction) const; - void drawEmphasisMarks(GraphicsContext*, const TextRunPaintInfo&, const AtomicString& mark, const FloatPoint&) const; - - float width(const TextRun&, HashSet* fallbackFonts = 0, GlyphOverflow* = 0) const; - float width(const TextRun&, int& charsConsumed, Glyph& glyphId) const; - - int offsetForPosition(const TextRun&, float position, bool includePartialGlyphs) const; - FloatRect selectionRectForText(const TextRun&, const FloatPoint&, int h, int from = 0, int to = -1, bool accountForGlyphBounds = false) const; - - bool isFixedPitch() const; - - // Metrics that we query the FontFallbackList for. - const FontMetrics& fontMetrics() const { return primaryFont()->fontMetrics(); } - float spaceWidth() const { return primaryFont()->spaceWidth() + fontDescription().letterSpacing(); } - float tabWidth(const SimpleFontData&, unsigned tabSize, float position) const; - float tabWidth(unsigned tabSize, float position) const { return tabWidth(*primaryFont(), tabSize, position); } - - int emphasisMarkAscent(const AtomicString&) const; - int emphasisMarkDescent(const AtomicString&) const; - int emphasisMarkHeight(const AtomicString&) const; + GlyphOverflow() + : left(0), right(0), top(0), bottom(0), computeBounds(false) {} - const SimpleFontData* primaryFont() const; - const FontData* fontDataAt(unsigned) const; - inline GlyphData glyphDataForCharacter(UChar32 c, bool mirror, FontDataVariant variant = AutoVariant) const - { - return glyphDataAndPageForCharacter(c, mirror, variant).first; - } - std::pair glyphDataAndPageForCharacter(UChar32, bool mirror, FontDataVariant = AutoVariant) const; - bool primaryFontHasGlyphForCharacter(UChar32) const; + bool isZero() const { return !left && !right && !top && !bottom; } - CodePath codePath(const TextRun&) const; - -private: - enum ForTextEmphasisOrNot { NotForTextEmphasis, ForTextEmphasis }; - - // Returns the initial in-stream advance. - float buildGlyphBuffer(const TextRunPaintInfo&, GlyphBuffer&, ForTextEmphasisOrNot = NotForTextEmphasis) const; - TextBlobPtr buildTextBlob(const GlyphBuffer&, float initialAdvance, const FloatRect& bounds) const; - void drawGlyphs(GraphicsContext*, const SimpleFontData*, const GlyphBuffer&, unsigned from, unsigned numGlyphs, const FloatPoint&, const FloatRect& textRect) const; - void drawTextBlob(GraphicsContext*, const SkTextBlob*, const SkPoint& origin) const; - float drawGlyphBuffer(GraphicsContext*, const TextRunPaintInfo&, const GlyphBuffer&, const FloatPoint&) const; - void drawEmphasisMarks(GraphicsContext*, const TextRunPaintInfo&, const GlyphBuffer&, const AtomicString&, const FloatPoint&) const; - float floatWidthForSimpleText(const TextRun&, HashSet* fallbackFonts = 0, IntRectExtent* glyphBounds = 0) const; - int offsetForPositionForSimpleText(const TextRun&, float position, bool includePartialGlyphs) const; - FloatRect selectionRectForSimpleText(const TextRun&, const FloatPoint&, int h, int from, int to, bool accountForGlyphBounds) const; - - bool getEmphasisMarkGlyphData(const AtomicString&, GlyphData&) const; - - float floatWidthForComplexText(const TextRun&, HashSet* fallbackFonts, IntRectExtent* glyphBounds) const; - int offsetForPositionForComplexText(const TextRun&, float position, bool includePartialGlyphs) const; - FloatRect selectionRectForComplexText(const TextRun&, const FloatPoint&, int h, int from, int to) const; - - friend struct WidthIterator; - -public: - // Useful for debugging the different font rendering code paths. - static void setCodePath(CodePath); - static CodePath codePath(); - static CodePath s_codePath; - - FontSelector* fontSelector() const; - - FontFallbackList* fontList() const { return m_fontFallbackList.get(); } - - void willUseFontData(UChar32) const; - - static FloatRect pixelSnappedSelectionRect(float fromX, float toX, float y, float height); -private: - bool loadingCustomFonts() const - { - return m_fontFallbackList && m_fontFallbackList->loadingCustomFonts(); - } - - bool shouldSkipDrawing() const - { - return m_fontFallbackList && m_fontFallbackList->shouldSkipDrawing(); - } + int left; + int right; + int top; + int bottom; + bool computeBounds; +}; - FontDescription m_fontDescription; - mutable RefPtr m_fontFallbackList; +class PLATFORM_EXPORT Font { + public: + Font(); + Font(const FontDescription&); + ~Font(); + + Font(const Font&); + Font& operator=(const Font&); + + bool operator==(const Font& other) const; + bool operator!=(const Font& other) const { return !(*this == other); } + + const FontDescription& fontDescription() const { return m_fontDescription; } + + void update(PassRefPtr) const; + + enum CustomFontNotReadyAction { + DoNotPaintIfFontNotReady, + UseFallbackIfFontNotReady + }; + void drawText(GraphicsContext*, + const TextRunPaintInfo&, + const FloatPoint&) const; + float drawUncachedText(GraphicsContext*, + const TextRunPaintInfo&, + const FloatPoint&, + CustomFontNotReadyAction) const; + void drawEmphasisMarks(GraphicsContext*, + const TextRunPaintInfo&, + const AtomicString& mark, + const FloatPoint&) const; + + float width(const TextRun&, + HashSet* fallbackFonts = 0, + GlyphOverflow* = 0) const; + float width(const TextRun&, int& charsConsumed, Glyph& glyphId) const; + + int offsetForPosition(const TextRun&, + float position, + bool includePartialGlyphs) const; + FloatRect selectionRectForText(const TextRun&, + const FloatPoint&, + int h, + int from = 0, + int to = -1, + bool accountForGlyphBounds = false) const; + + bool isFixedPitch() const; + + // Metrics that we query the FontFallbackList for. + const FontMetrics& fontMetrics() const { + return primaryFont()->fontMetrics(); + } + float spaceWidth() const { + return primaryFont()->spaceWidth() + fontDescription().letterSpacing(); + } + float tabWidth(const SimpleFontData&, unsigned tabSize, float position) const; + float tabWidth(unsigned tabSize, float position) const { + return tabWidth(*primaryFont(), tabSize, position); + } + + int emphasisMarkAscent(const AtomicString&) const; + int emphasisMarkDescent(const AtomicString&) const; + int emphasisMarkHeight(const AtomicString&) const; + + const SimpleFontData* primaryFont() const; + const FontData* fontDataAt(unsigned) const; + inline GlyphData glyphDataForCharacter( + UChar32 c, + bool mirror, + FontDataVariant variant = AutoVariant) const { + return glyphDataAndPageForCharacter(c, mirror, variant).first; + } + std::pair glyphDataAndPageForCharacter( + UChar32, + bool mirror, + FontDataVariant = AutoVariant) const; + bool primaryFontHasGlyphForCharacter(UChar32) const; + + CodePath codePath(const TextRun&) const; + + private: + enum ForTextEmphasisOrNot { NotForTextEmphasis, ForTextEmphasis }; + + // Returns the initial in-stream advance. + float buildGlyphBuffer(const TextRunPaintInfo&, + GlyphBuffer&, + ForTextEmphasisOrNot = NotForTextEmphasis) const; + TextBlobPtr buildTextBlob(const GlyphBuffer&, + float initialAdvance, + const FloatRect& bounds) const; + void drawGlyphs(GraphicsContext*, + const SimpleFontData*, + const GlyphBuffer&, + unsigned from, + unsigned numGlyphs, + const FloatPoint&, + const FloatRect& textRect) const; + void drawTextBlob(GraphicsContext*, + const SkTextBlob*, + const SkPoint& origin) const; + float drawGlyphBuffer(GraphicsContext*, + const TextRunPaintInfo&, + const GlyphBuffer&, + const FloatPoint&) const; + void drawEmphasisMarks(GraphicsContext*, + const TextRunPaintInfo&, + const GlyphBuffer&, + const AtomicString&, + const FloatPoint&) const; + float floatWidthForSimpleText( + const TextRun&, + HashSet* fallbackFonts = 0, + IntRectExtent* glyphBounds = 0) const; + int offsetForPositionForSimpleText(const TextRun&, + float position, + bool includePartialGlyphs) const; + FloatRect selectionRectForSimpleText(const TextRun&, + const FloatPoint&, + int h, + int from, + int to, + bool accountForGlyphBounds) const; + + bool getEmphasisMarkGlyphData(const AtomicString&, GlyphData&) const; + + float floatWidthForComplexText(const TextRun&, + HashSet* fallbackFonts, + IntRectExtent* glyphBounds) const; + int offsetForPositionForComplexText(const TextRun&, + float position, + bool includePartialGlyphs) const; + FloatRect selectionRectForComplexText(const TextRun&, + const FloatPoint&, + int h, + int from, + int to) const; + + friend struct WidthIterator; + + public: + // Useful for debugging the different font rendering code paths. + static void setCodePath(CodePath); + static CodePath codePath(); + static CodePath s_codePath; + + FontSelector* fontSelector() const; + + FontFallbackList* fontList() const { return m_fontFallbackList.get(); } + + void willUseFontData(UChar32) const; + + static FloatRect pixelSnappedSelectionRect(float fromX, + float toX, + float y, + float height); + + private: + bool loadingCustomFonts() const { + return m_fontFallbackList && m_fontFallbackList->loadingCustomFonts(); + } + + bool shouldSkipDrawing() const { + return m_fontFallbackList && m_fontFallbackList->shouldSkipDrawing(); + } + + FontDescription m_fontDescription; + mutable RefPtr m_fontFallbackList; }; -inline Font::~Font() -{ -} +inline Font::~Font() {} -inline const SimpleFontData* Font::primaryFont() const -{ - ASSERT(m_fontFallbackList); - return m_fontFallbackList->primarySimpleFontData(m_fontDescription); +inline const SimpleFontData* Font::primaryFont() const { + ASSERT(m_fontFallbackList); + return m_fontFallbackList->primarySimpleFontData(m_fontDescription); } -inline const FontData* Font::fontDataAt(unsigned index) const -{ - ASSERT(m_fontFallbackList); - return m_fontFallbackList->fontDataAt(m_fontDescription, index); +inline const FontData* Font::fontDataAt(unsigned index) const { + ASSERT(m_fontFallbackList); + return m_fontFallbackList->fontDataAt(m_fontDescription, index); } -inline bool Font::isFixedPitch() const -{ - ASSERT(m_fontFallbackList); - return m_fontFallbackList->isFixedPitch(m_fontDescription); +inline bool Font::isFixedPitch() const { + ASSERT(m_fontFallbackList); + return m_fontFallbackList->isFixedPitch(m_fontDescription); } -inline FontSelector* Font::fontSelector() const -{ - return m_fontFallbackList ? m_fontFallbackList->fontSelector() : 0; +inline FontSelector* Font::fontSelector() const { + return m_fontFallbackList ? m_fontFallbackList->fontSelector() : 0; } -inline float Font::tabWidth(const SimpleFontData& fontData, unsigned tabSize, float position) const -{ - if (!tabSize) - return fontDescription().letterSpacing(); - float tabWidth = tabSize * fontData.spaceWidth() + fontDescription().letterSpacing(); - return tabWidth - fmodf(position, tabWidth); +inline float Font::tabWidth(const SimpleFontData& fontData, + unsigned tabSize, + float position) const { + if (!tabSize) + return fontDescription().letterSpacing(); + float tabWidth = + tabSize * fontData.spaceWidth() + fontDescription().letterSpacing(); + return tabWidth - fmodf(position, tabWidth); } -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_FONTS_FONT_H_ diff --git a/sky/engine/platform/fonts/FontBaseline.h b/sky/engine/platform/fonts/FontBaseline.h index b2d0ecade9b5e..12e799a2afad6 100644 --- a/sky/engine/platform/fonts/FontBaseline.h +++ b/sky/engine/platform/fonts/FontBaseline.h @@ -30,6 +30,6 @@ namespace blink { enum FontBaseline { AlphabeticBaseline, IdeographicBaseline }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_FONTS_FONTBASELINE_H_ diff --git a/sky/engine/platform/fonts/FontCache.cpp b/sky/engine/platform/fonts/FontCache.cpp index a3d7448aaecb8..4f96e9d0e343b 100644 --- a/sky/engine/platform/fonts/FontCache.cpp +++ b/sky/engine/platform/fonts/FontCache.cpp @@ -50,232 +50,262 @@ using namespace WTF; namespace blink { -FontCache::FontCache() - : m_purgePreventCount(0) -{ -} +FontCache::FontCache() : m_purgePreventCount(0) {} -typedef HashMap, FontCacheKeyHash, FontCacheKeyTraits> FontPlatformDataCache; +typedef HashMap, + FontCacheKeyHash, + FontCacheKeyTraits> + FontPlatformDataCache; static FontPlatformDataCache* gFontPlatformDataCache = 0; -FontCache* FontCache::fontCache() -{ - DEFINE_STATIC_LOCAL(FontCache, globalFontCache, ()); - return &globalFontCache; +FontCache* FontCache::fontCache() { + DEFINE_STATIC_LOCAL(FontCache, globalFontCache, ()); + return &globalFontCache; } -FontPlatformData* FontCache::getFontPlatformData(const FontDescription& fontDescription, - const FontFaceCreationParams& creationParams, bool checkingAlternateName) -{ - if (!gFontPlatformDataCache) { - gFontPlatformDataCache = new FontPlatformDataCache; - platformInit(); - } - - FontCacheKey key = fontDescription.cacheKey(creationParams); - FontPlatformData* result = 0; - bool foundResult; - FontPlatformDataCache::iterator it = gFontPlatformDataCache->find(key); - if (it == gFontPlatformDataCache->end()) { - result = createFontPlatformData(fontDescription, creationParams, fontDescription.effectiveFontSize()); - gFontPlatformDataCache->set(key, adoptPtr(result)); - foundResult = result; - } else { - result = it->value.get(); - foundResult = true; - } - - if (!foundResult && !checkingAlternateName && creationParams.creationType() == CreateFontByFamily) { - // We were unable to find a font. We have a small set of fonts that we alias to other names, - // e.g., Arial/Helvetica, Courier/Courier New, etc. Try looking up the font under the aliased name. - const AtomicString& alternateName = alternateFamilyName(creationParams.family()); - if (!alternateName.isEmpty()) { - FontFaceCreationParams createByAlternateFamily(alternateName); - result = getFontPlatformData(fontDescription, createByAlternateFamily, true); - } - if (result) - gFontPlatformDataCache->set(key, adoptPtr(new FontPlatformData(*result))); // Cache the result under the old name. +FontPlatformData* FontCache::getFontPlatformData( + const FontDescription& fontDescription, + const FontFaceCreationParams& creationParams, + bool checkingAlternateName) { + if (!gFontPlatformDataCache) { + gFontPlatformDataCache = new FontPlatformDataCache; + platformInit(); + } + + FontCacheKey key = fontDescription.cacheKey(creationParams); + FontPlatformData* result = 0; + bool foundResult; + FontPlatformDataCache::iterator it = gFontPlatformDataCache->find(key); + if (it == gFontPlatformDataCache->end()) { + result = createFontPlatformData(fontDescription, creationParams, + fontDescription.effectiveFontSize()); + gFontPlatformDataCache->set(key, adoptPtr(result)); + foundResult = result; + } else { + result = it->value.get(); + foundResult = true; + } + + if (!foundResult && !checkingAlternateName && + creationParams.creationType() == CreateFontByFamily) { + // We were unable to find a font. We have a small set of fonts that we alias + // to other names, e.g., Arial/Helvetica, Courier/Courier New, etc. Try + // looking up the font under the aliased name. + const AtomicString& alternateName = + alternateFamilyName(creationParams.family()); + if (!alternateName.isEmpty()) { + FontFaceCreationParams createByAlternateFamily(alternateName); + result = + getFontPlatformData(fontDescription, createByAlternateFamily, true); } + if (result) + gFontPlatformDataCache->set( + key, adoptPtr(new FontPlatformData( + *result))); // Cache the result under the old name. + } - return result; + return result; } #if ENABLE(OPENTYPE_VERTICAL) -typedef HashMap, IntHash, UnsignedWithZeroKeyHashTraits > FontVerticalDataCache; - -FontVerticalDataCache& fontVerticalDataCacheInstance() -{ - DEFINE_STATIC_LOCAL(FontVerticalDataCache, fontVerticalDataCache, ()); - return fontVerticalDataCache; +typedef HashMap, + IntHash, + UnsignedWithZeroKeyHashTraits> + FontVerticalDataCache; + +FontVerticalDataCache& fontVerticalDataCacheInstance() { + DEFINE_STATIC_LOCAL(FontVerticalDataCache, fontVerticalDataCache, ()); + return fontVerticalDataCache; } -PassRefPtr FontCache::getVerticalData(const FontFileKey& key, const FontPlatformData& platformData) -{ - FontVerticalDataCache& fontVerticalDataCache = fontVerticalDataCacheInstance(); - FontVerticalDataCache::iterator result = fontVerticalDataCache.find(key); - if (result != fontVerticalDataCache.end()) - return result.get()->value; - - RefPtr verticalData = OpenTypeVerticalData::create(platformData); - if (!verticalData->isOpenType()) - verticalData.clear(); - fontVerticalDataCache.set(key, verticalData); - return verticalData; +PassRefPtr FontCache::getVerticalData( + const FontFileKey& key, + const FontPlatformData& platformData) { + FontVerticalDataCache& fontVerticalDataCache = + fontVerticalDataCacheInstance(); + FontVerticalDataCache::iterator result = fontVerticalDataCache.find(key); + if (result != fontVerticalDataCache.end()) + return result.get()->value; + + RefPtr verticalData = + OpenTypeVerticalData::create(platformData); + if (!verticalData->isOpenType()) + verticalData.clear(); + fontVerticalDataCache.set(key, verticalData); + return verticalData; } #endif static FontDataCache* gFontDataCache = 0; -PassRefPtr FontCache::getFontData(const FontDescription& fontDescription, const AtomicString& family, bool checkingAlternateName, ShouldRetain shouldRetain) -{ - if (FontPlatformData* platformData = getFontPlatformData(fontDescription, FontFaceCreationParams(adjustFamilyNameToAvoidUnsupportedFonts(family)), checkingAlternateName)) - return fontDataFromFontPlatformData(platformData, shouldRetain); - - return nullptr; +PassRefPtr FontCache::getFontData( + const FontDescription& fontDescription, + const AtomicString& family, + bool checkingAlternateName, + ShouldRetain shouldRetain) { + if (FontPlatformData* platformData = getFontPlatformData( + fontDescription, + FontFaceCreationParams( + adjustFamilyNameToAvoidUnsupportedFonts(family)), + checkingAlternateName)) + return fontDataFromFontPlatformData(platformData, shouldRetain); + + return nullptr; } -PassRefPtr FontCache::fontDataFromFontPlatformData(const FontPlatformData* platformData, ShouldRetain shouldRetain) -{ - if (!gFontDataCache) - gFontDataCache = new FontDataCache; +PassRefPtr FontCache::fontDataFromFontPlatformData( + const FontPlatformData* platformData, + ShouldRetain shouldRetain) { + if (!gFontDataCache) + gFontDataCache = new FontDataCache; #if ENABLE(ASSERT) - if (shouldRetain == DoNotRetain) - ASSERT(m_purgePreventCount); + if (shouldRetain == DoNotRetain) + ASSERT(m_purgePreventCount); #endif - return gFontDataCache->get(platformData, shouldRetain); + return gFontDataCache->get(platformData, shouldRetain); } -bool FontCache::isPlatformFontAvailable(const FontDescription& fontDescription, const AtomicString& family) -{ - bool checkingAlternateName = true; - return getFontPlatformData(fontDescription, FontFaceCreationParams(adjustFamilyNameToAvoidUnsupportedFonts(family)), checkingAlternateName); +bool FontCache::isPlatformFontAvailable(const FontDescription& fontDescription, + const AtomicString& family) { + bool checkingAlternateName = true; + return getFontPlatformData( + fontDescription, + FontFaceCreationParams(adjustFamilyNameToAvoidUnsupportedFonts(family)), + checkingAlternateName); } -SimpleFontData* FontCache::getNonRetainedLastResortFallbackFont(const FontDescription& fontDescription) -{ - return getLastResortFallbackFont(fontDescription, DoNotRetain).leakRef(); +SimpleFontData* FontCache::getNonRetainedLastResortFallbackFont( + const FontDescription& fontDescription) { + return getLastResortFallbackFont(fontDescription, DoNotRetain).leakRef(); } -void FontCache::releaseFontData(const SimpleFontData* fontData) -{ - ASSERT(gFontDataCache); +void FontCache::releaseFontData(const SimpleFontData* fontData) { + ASSERT(gFontDataCache); - gFontDataCache->release(fontData); + gFontDataCache->release(fontData); } -static inline void purgePlatformFontDataCache() -{ - if (!gFontPlatformDataCache) - return; - - Vector keysToRemove; - keysToRemove.reserveInitialCapacity(gFontPlatformDataCache->size()); - FontPlatformDataCache::iterator platformDataEnd = gFontPlatformDataCache->end(); - for (FontPlatformDataCache::iterator platformData = gFontPlatformDataCache->begin(); platformData != platformDataEnd; ++platformData) { - if (platformData->value && !gFontDataCache->contains(platformData->value.get())) - keysToRemove.append(platformData->key); - } - gFontPlatformDataCache->removeAll(keysToRemove); +static inline void purgePlatformFontDataCache() { + if (!gFontPlatformDataCache) + return; + + Vector keysToRemove; + keysToRemove.reserveInitialCapacity(gFontPlatformDataCache->size()); + FontPlatformDataCache::iterator platformDataEnd = + gFontPlatformDataCache->end(); + for (FontPlatformDataCache::iterator platformData = + gFontPlatformDataCache->begin(); + platformData != platformDataEnd; ++platformData) { + if (platformData->value && + !gFontDataCache->contains(platformData->value.get())) + keysToRemove.append(platformData->key); + } + gFontPlatformDataCache->removeAll(keysToRemove); } -static inline void purgeFontVerticalDataCache() -{ +static inline void purgeFontVerticalDataCache() { #if ENABLE(OPENTYPE_VERTICAL) - FontVerticalDataCache& fontVerticalDataCache = fontVerticalDataCacheInstance(); - if (!fontVerticalDataCache.isEmpty()) { - // Mark & sweep unused verticalData - FontVerticalDataCache::iterator verticalDataEnd = fontVerticalDataCache.end(); - for (FontVerticalDataCache::iterator verticalData = fontVerticalDataCache.begin(); verticalData != verticalDataEnd; ++verticalData) { - if (verticalData->value) - verticalData->value->setInFontCache(false); - } - - gFontDataCache->markAllVerticalData(); - - Vector keysToRemove; - keysToRemove.reserveInitialCapacity(fontVerticalDataCache.size()); - for (FontVerticalDataCache::iterator verticalData = fontVerticalDataCache.begin(); verticalData != verticalDataEnd; ++verticalData) { - if (!verticalData->value || !verticalData->value->inFontCache()) - keysToRemove.append(verticalData->key); - } - fontVerticalDataCache.removeAll(keysToRemove); + FontVerticalDataCache& fontVerticalDataCache = + fontVerticalDataCacheInstance(); + if (!fontVerticalDataCache.isEmpty()) { + // Mark & sweep unused verticalData + FontVerticalDataCache::iterator verticalDataEnd = + fontVerticalDataCache.end(); + for (FontVerticalDataCache::iterator verticalData = + fontVerticalDataCache.begin(); + verticalData != verticalDataEnd; ++verticalData) { + if (verticalData->value) + verticalData->value->setInFontCache(false); + } + + gFontDataCache->markAllVerticalData(); + + Vector keysToRemove; + keysToRemove.reserveInitialCapacity(fontVerticalDataCache.size()); + for (FontVerticalDataCache::iterator verticalData = + fontVerticalDataCache.begin(); + verticalData != verticalDataEnd; ++verticalData) { + if (!verticalData->value || !verticalData->value->inFontCache()) + keysToRemove.append(verticalData->key); } + fontVerticalDataCache.removeAll(keysToRemove); + } #endif } -void FontCache::purge(PurgeSeverity PurgeSeverity) -{ - // We should never be forcing the purge while the FontCachePurgePreventer is in scope. - ASSERT(!m_purgePreventCount || PurgeSeverity == PurgeIfNeeded); - if (m_purgePreventCount) - return; +void FontCache::purge(PurgeSeverity PurgeSeverity) { + // We should never be forcing the purge while the FontCachePurgePreventer is + // in scope. + ASSERT(!m_purgePreventCount || PurgeSeverity == PurgeIfNeeded); + if (m_purgePreventCount) + return; - if (!gFontDataCache || !gFontDataCache->purge(PurgeSeverity)) - return; + if (!gFontDataCache || !gFontDataCache->purge(PurgeSeverity)) + return; - purgePlatformFontDataCache(); - purgeFontVerticalDataCache(); + purgePlatformFontDataCache(); + purgeFontVerticalDataCache(); } static bool invalidateFontCache = false; -HashSet >& fontCacheClients() -{ - DEFINE_STATIC_LOCAL(OwnPtr > >, clients, (adoptPtr(new HashSet >()))); - invalidateFontCache = true; - return *clients; +HashSet>& fontCacheClients() { + DEFINE_STATIC_LOCAL(OwnPtr>>, clients, + (adoptPtr(new HashSet>()))); + invalidateFontCache = true; + return *clients; } -void FontCache::addClient(FontCacheClient* client) -{ - ASSERT(!fontCacheClients().contains(client)); - fontCacheClients().add(client); +void FontCache::addClient(FontCacheClient* client) { + ASSERT(!fontCacheClients().contains(client)); + fontCacheClients().add(client); } #if !ENABLE(OILPAN) -void FontCache::removeClient(FontCacheClient* client) -{ - ASSERT(fontCacheClients().contains(client)); - fontCacheClients().remove(client); +void FontCache::removeClient(FontCacheClient* client) { + ASSERT(fontCacheClients().contains(client)); + fontCacheClients().remove(client); } #endif static unsigned short gGeneration = 0; -unsigned short FontCache::generation() -{ - return gGeneration; +unsigned short FontCache::generation() { + return gGeneration; } -void FontCache::invalidate() -{ - if (!invalidateFontCache) { - ASSERT(!gFontPlatformDataCache); - return; - } - - if (gFontPlatformDataCache) { - delete gFontPlatformDataCache; - gFontPlatformDataCache = new FontPlatformDataCache; - } - - gGeneration++; - - Vector > clients; - size_t numClients = fontCacheClients().size(); - clients.reserveInitialCapacity(numClients); - HashSet >::iterator end = fontCacheClients().end(); - for (HashSet >::iterator it = fontCacheClients().begin(); it != end; ++it) - clients.append(*it); - - ASSERT(numClients == clients.size()); - for (size_t i = 0; i < numClients; ++i) - clients[i]->fontCacheInvalidated(); - - purge(ForcePurge); +void FontCache::invalidate() { + if (!invalidateFontCache) { + ASSERT(!gFontPlatformDataCache); + return; + } + + if (gFontPlatformDataCache) { + delete gFontPlatformDataCache; + gFontPlatformDataCache = new FontPlatformDataCache; + } + + gGeneration++; + + Vector> clients; + size_t numClients = fontCacheClients().size(); + clients.reserveInitialCapacity(numClients); + HashSet>::iterator end = fontCacheClients().end(); + for (HashSet>::iterator it = + fontCacheClients().begin(); + it != end; ++it) + clients.append(*it); + + ASSERT(numClients == clients.size()); + for (size_t i = 0; i < numClients; ++i) + clients[i]->fontCacheInvalidated(); + + purge(ForcePurge); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/fonts/FontCache.h b/sky/engine/platform/fonts/FontCache.h index 28523ffb4398d..e3c32df287ae6 100644 --- a/sky/engine/platform/fonts/FontCache.h +++ b/sky/engine/platform/fonts/FontCache.h @@ -61,93 +61,113 @@ enum ShouldRetain { Retain, DoNotRetain }; enum PurgeSeverity { PurgeIfNeeded, ForcePurge }; class PLATFORM_EXPORT FontCache { - friend class FontCachePurgePreventer; + friend class FontCachePurgePreventer; - WTF_MAKE_NONCOPYABLE(FontCache); WTF_MAKE_FAST_ALLOCATED; -public: - static FontCache* fontCache(); + WTF_MAKE_NONCOPYABLE(FontCache); + WTF_MAKE_FAST_ALLOCATED; - void releaseFontData(const SimpleFontData*); + public: + static FontCache* fontCache(); - // This method is implemented by the plaform and used by - // FontFastPath to lookup the font for a given character. - PassRefPtr fallbackFontForCharacter(const FontDescription&, UChar32, const SimpleFontData* fontDataToSubstitute); + void releaseFontData(const SimpleFontData*); - // Also implemented by the platform. - void platformInit(); + // This method is implemented by the plaform and used by + // FontFastPath to lookup the font for a given character. + PassRefPtr fallbackFontForCharacter( + const FontDescription&, + UChar32, + const SimpleFontData* fontDataToSubstitute); - PassRefPtr getFontData(const FontDescription&, const AtomicString&, bool checkingAlternateName = false, ShouldRetain = Retain); - PassRefPtr getLastResortFallbackFont(const FontDescription&, ShouldRetain = Retain); - SimpleFontData* getNonRetainedLastResortFallbackFont(const FontDescription&); - bool isPlatformFontAvailable(const FontDescription&, const AtomicString&); + // Also implemented by the platform. + void platformInit(); - void addClient(FontCacheClient*); + PassRefPtr getFontData(const FontDescription&, + const AtomicString&, + bool checkingAlternateName = false, + ShouldRetain = Retain); + PassRefPtr getLastResortFallbackFont(const FontDescription&, + ShouldRetain = Retain); + SimpleFontData* getNonRetainedLastResortFallbackFont(const FontDescription&); + bool isPlatformFontAvailable(const FontDescription&, const AtomicString&); + + void addClient(FontCacheClient*); #if !ENABLE(OILPAN) - void removeClient(FontCacheClient*); + void removeClient(FontCacheClient*); #endif - unsigned short generation(); - void invalidate(); + unsigned short generation(); + void invalidate(); #if ENABLE(OPENTYPE_VERTICAL) - typedef uint32_t FontFileKey; - PassRefPtr getVerticalData(const FontFileKey&, const FontPlatformData&); + typedef uint32_t FontFileKey; + PassRefPtr getVerticalData(const FontFileKey&, + const FontPlatformData&); #endif #if !OS(ANDROID) && !OS(IOS) - struct PlatformFallbackFont { - String name; - CString filename; - int fontconfigInterfaceId; - int ttcIndex; - bool isBold; - bool isItalic; - }; - static void getFontForCharacter(UChar32, const char* preferredLocale, PlatformFallbackFont*); + struct PlatformFallbackFont { + String name; + CString filename; + int fontconfigInterfaceId; + int ttcIndex; + bool isBold; + bool isItalic; + }; + static void getFontForCharacter(UChar32, + const char* preferredLocale, + PlatformFallbackFont*); #endif -private: - FontCache(); - ~FontCache(); + private: + FontCache(); + ~FontCache(); - void purge(PurgeSeverity = PurgeIfNeeded); + void purge(PurgeSeverity = PurgeIfNeeded); - void disablePurging() { m_purgePreventCount++; } - void enablePurging() - { - ASSERT(m_purgePreventCount); - if (!--m_purgePreventCount) - purge(PurgeIfNeeded); - } + void disablePurging() { m_purgePreventCount++; } + void enablePurging() { + ASSERT(m_purgePreventCount); + if (!--m_purgePreventCount) + purge(PurgeIfNeeded); + } - // FIXME: This method should eventually be removed. - FontPlatformData* getFontPlatformData(const FontDescription&, const FontFaceCreationParams&, bool checkingAlternateName = false); + // FIXME: This method should eventually be removed. + FontPlatformData* getFontPlatformData(const FontDescription&, + const FontFaceCreationParams&, + bool checkingAlternateName = false); - // These methods are implemented by each platform. - FontPlatformData* createFontPlatformData(const FontDescription&, const FontFaceCreationParams&, float fontSize); + // These methods are implemented by each platform. + FontPlatformData* createFontPlatformData(const FontDescription&, + const FontFaceCreationParams&, + float fontSize); - // Implemented on skia platforms. - sk_sp createTypeface(const FontDescription&, const FontFaceCreationParams&, CString& name); + // Implemented on skia platforms. + sk_sp createTypeface(const FontDescription&, + const FontFaceCreationParams&, + CString& name); - PassRefPtr fontDataFromFontPlatformData(const FontPlatformData*, ShouldRetain = Retain); - PassRefPtr fallbackOnStandardFontStyle(const FontDescription&, UChar32); + PassRefPtr fontDataFromFontPlatformData( + const FontPlatformData*, + ShouldRetain = Retain); + PassRefPtr fallbackOnStandardFontStyle(const FontDescription&, + UChar32); - // Don't purge if this count is > 0; - int m_purgePreventCount; + // Don't purge if this count is > 0; + int m_purgePreventCount; #if OS(ANDROID) - friend class ComplexTextController; + friend class ComplexTextController; #endif - friend class SimpleFontData; // For fontDataFromFontPlatformData - friend class FontFallbackList; + friend class SimpleFontData; // For fontDataFromFontPlatformData + friend class FontFallbackList; }; class PLATFORM_EXPORT FontCachePurgePreventer { -public: - FontCachePurgePreventer() { FontCache::fontCache()->disablePurging(); } - ~FontCachePurgePreventer() { FontCache::fontCache()->enablePurging(); } + public: + FontCachePurgePreventer() { FontCache::fontCache()->disablePurging(); } + ~FontCachePurgePreventer() { FontCache::fontCache()->enablePurging(); } }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_FONTS_FONTCACHE_H_ diff --git a/sky/engine/platform/fonts/FontCacheClient.h b/sky/engine/platform/fonts/FontCacheClient.h index 32fa7e534e0a2..2fb49c8fea34a 100644 --- a/sky/engine/platform/fonts/FontCacheClient.h +++ b/sky/engine/platform/fonts/FontCacheClient.h @@ -38,12 +38,12 @@ namespace blink { class PLATFORM_EXPORT FontCacheClient : public RefCounted { -public: - virtual ~FontCacheClient() { } + public: + virtual ~FontCacheClient() {} - virtual void fontCacheInvalidated() = 0; + virtual void fontCacheInvalidated() = 0; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_FONTS_FONTCACHECLIENT_H_ diff --git a/sky/engine/platform/fonts/FontCacheKey.h b/sky/engine/platform/fonts/FontCacheKey.h index 3319589936b9d..b3d952f466aab 100644 --- a/sky/engine/platform/fonts/FontCacheKey.h +++ b/sky/engine/platform/fonts/FontCacheKey.h @@ -44,73 +44,57 @@ namespace blink { static const unsigned s_fontSizePrecisionMultiplier = 100; struct FontCacheKey { - WTF_MAKE_FAST_ALLOCATED; -public: - FontCacheKey() - : m_creationParams() - , m_fontSize(0) - , m_options(0) { } - FontCacheKey(FontFaceCreationParams creationParams, float fontSize, unsigned options) - : m_creationParams(creationParams) - , m_fontSize(fontSize * s_fontSizePrecisionMultiplier) - , m_options(options) { } - FontCacheKey(WTF::HashTableDeletedValueType) - : m_fontSize(hashTableDeletedSize()) { } - - unsigned hash() const - { - unsigned hashCodes[3] = { - m_creationParams.hash(), - m_fontSize, - m_options - }; - return StringHasher::hashMemory(hashCodes); - } - - bool operator==(const FontCacheKey& other) const - { - return m_creationParams == other.m_creationParams - && m_fontSize == other.m_fontSize - && m_options == other.m_options; - } - - bool isHashTableDeletedValue() const - { - return m_fontSize == hashTableDeletedSize(); - } - - static unsigned precisionMultiplier() - { - return s_fontSizePrecisionMultiplier; - } - -private: - static unsigned hashTableDeletedSize() - { - return 0xFFFFFFFFU; - } - - FontFaceCreationParams m_creationParams; - unsigned m_fontSize; - unsigned m_options; + WTF_MAKE_FAST_ALLOCATED; + + public: + FontCacheKey() : m_creationParams(), m_fontSize(0), m_options(0) {} + FontCacheKey(FontFaceCreationParams creationParams, + float fontSize, + unsigned options) + : m_creationParams(creationParams), + m_fontSize(fontSize * s_fontSizePrecisionMultiplier), + m_options(options) {} + FontCacheKey(WTF::HashTableDeletedValueType) + : m_fontSize(hashTableDeletedSize()) {} + + unsigned hash() const { + unsigned hashCodes[3] = {m_creationParams.hash(), m_fontSize, m_options}; + return StringHasher::hashMemory(hashCodes); + } + + bool operator==(const FontCacheKey& other) const { + return m_creationParams == other.m_creationParams && + m_fontSize == other.m_fontSize && m_options == other.m_options; + } + + bool isHashTableDeletedValue() const { + return m_fontSize == hashTableDeletedSize(); + } + + static unsigned precisionMultiplier() { + return s_fontSizePrecisionMultiplier; + } + + private: + static unsigned hashTableDeletedSize() { return 0xFFFFFFFFU; } + + FontFaceCreationParams m_creationParams; + unsigned m_fontSize; + unsigned m_options; }; struct FontCacheKeyHash { - static unsigned hash(const FontCacheKey& key) - { - return key.hash(); - } + static unsigned hash(const FontCacheKey& key) { return key.hash(); } - static bool equal(const FontCacheKey& a, const FontCacheKey& b) - { - return a == b; - } + static bool equal(const FontCacheKey& a, const FontCacheKey& b) { + return a == b; + } - static const bool safeToCompareToEmptyOrDeleted = true; + static const bool safeToCompareToEmptyOrDeleted = true; }; -struct FontCacheKeyTraits : WTF::SimpleClassHashTraits { }; +struct FontCacheKeyTraits : WTF::SimpleClassHashTraits {}; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_FONTS_FONTCACHEKEY_H_ diff --git a/sky/engine/platform/fonts/FontCacheTest.cpp b/sky/engine/platform/fonts/FontCacheTest.cpp index 8cfb84352df57..5ca51515ed732 100644 --- a/sky/engine/platform/fonts/FontCacheTest.cpp +++ b/sky/engine/platform/fonts/FontCacheTest.cpp @@ -12,30 +12,30 @@ namespace blink { class EmptyPlatform : public Platform { -public: - EmptyPlatform() { } - virtual ~EmptyPlatform() { } + public: + EmptyPlatform() {} + virtual ~EmptyPlatform() {} }; -TEST(FontCache, getLastResortFallbackFont) -{ - FontCache* fontCache = FontCache::fontCache(); - ASSERT_TRUE(fontCache); +TEST(FontCache, getLastResortFallbackFont) { + FontCache* fontCache = FontCache::fontCache(); + ASSERT_TRUE(fontCache); - Platform* oldPlatform = Platform::current(); - OwnPtr platform = adoptPtr(new EmptyPlatform); - Platform::initialize(platform.get()); + Platform* oldPlatform = Platform::current(); + OwnPtr platform = adoptPtr(new EmptyPlatform); + Platform::initialize(platform.get()); - FontDescription fontDescription; - fontDescription.setGenericFamily(FontDescription::StandardFamily); - RefPtr fontData = fontCache->getLastResortFallbackFont(fontDescription, Retain); - EXPECT_TRUE(fontData); + FontDescription fontDescription; + fontDescription.setGenericFamily(FontDescription::StandardFamily); + RefPtr fontData = + fontCache->getLastResortFallbackFont(fontDescription, Retain); + EXPECT_TRUE(fontData); - fontDescription.setGenericFamily(FontDescription::SansSerifFamily); - fontData = fontCache->getLastResortFallbackFont(fontDescription, Retain); - EXPECT_TRUE(fontData); + fontDescription.setGenericFamily(FontDescription::SansSerifFamily); + fontData = fontCache->getLastResortFallbackFont(fontDescription, Retain); + EXPECT_TRUE(fontData); - Platform::initialize(oldPlatform); + Platform::initialize(oldPlatform); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/fonts/FontCustomPlatformData.h b/sky/engine/platform/fonts/FontCustomPlatformData.h index 655dc6c6aeb57..a0f6cf23e1767 100644 --- a/sky/engine/platform/fonts/FontCustomPlatformData.h +++ b/sky/engine/platform/fonts/FontCustomPlatformData.h @@ -49,20 +49,25 @@ class FontPlatformData; class SharedBuffer; class PLATFORM_EXPORT FontCustomPlatformData { - WTF_MAKE_NONCOPYABLE(FontCustomPlatformData); -public: - static PassOwnPtr create(SharedBuffer*); - ~FontCustomPlatformData(); + WTF_MAKE_NONCOPYABLE(FontCustomPlatformData); - FontPlatformData fontPlatformData(float size, bool bold, bool italic, FontOrientation = Horizontal, FontWidthVariant = RegularWidth); + public: + static PassOwnPtr create(SharedBuffer*); + ~FontCustomPlatformData(); - static bool supportsFormat(const String&); + FontPlatformData fontPlatformData(float size, + bool bold, + bool italic, + FontOrientation = Horizontal, + FontWidthVariant = RegularWidth); -private: - explicit FontCustomPlatformData(sk_sp); - sk_sp m_typeface; + static bool supportsFormat(const String&); + + private: + explicit FontCustomPlatformData(sk_sp); + sk_sp m_typeface; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_FONTS_FONTCUSTOMPLATFORMDATA_H_ diff --git a/sky/engine/platform/fonts/FontData.cpp b/sky/engine/platform/fonts/FontData.cpp index 1fa14c06ad494..9e66f5ae82860 100644 --- a/sky/engine/platform/fonts/FontData.cpp +++ b/sky/engine/platform/fonts/FontData.cpp @@ -27,8 +27,6 @@ namespace blink { -FontData::~FontData() -{ -} +FontData::~FontData() {} -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/fonts/FontData.h b/sky/engine/platform/fonts/FontData.h index 8b74c181717bd..3805253b7edb9 100644 --- a/sky/engine/platform/fonts/FontData.h +++ b/sky/engine/platform/fonts/FontData.h @@ -39,38 +39,45 @@ namespace blink { class SimpleFontData; class PLATFORM_EXPORT FontData : public RefCounted { - WTF_MAKE_NONCOPYABLE(FontData); WTF_MAKE_FAST_ALLOCATED; -public: - FontData() - : m_maxGlyphPageTreeLevel(0) - { - } + WTF_MAKE_NONCOPYABLE(FontData); + WTF_MAKE_FAST_ALLOCATED; - virtual ~FontData(); + public: + FontData() : m_maxGlyphPageTreeLevel(0) {} - virtual const SimpleFontData* fontDataForCharacter(UChar32) const = 0; - virtual bool isCustomFont() const = 0; - virtual bool isLoading() const = 0; - // Returns whether this is a temporary font data for a custom font which is not yet loaded. - virtual bool isLoadingFallback() const = 0; - virtual bool isSegmented() const = 0; - virtual bool shouldSkipDrawing() const = 0; + virtual ~FontData(); - void setMaxGlyphPageTreeLevel(unsigned level) const { m_maxGlyphPageTreeLevel = level; } - unsigned maxGlyphPageTreeLevel() const { return m_maxGlyphPageTreeLevel; } + virtual const SimpleFontData* fontDataForCharacter(UChar32) const = 0; + virtual bool isCustomFont() const = 0; + virtual bool isLoading() const = 0; + // Returns whether this is a temporary font data for a custom font which is + // not yet loaded. + virtual bool isLoadingFallback() const = 0; + virtual bool isSegmented() const = 0; + virtual bool shouldSkipDrawing() const = 0; + + void setMaxGlyphPageTreeLevel(unsigned level) const { + m_maxGlyphPageTreeLevel = level; + } + unsigned maxGlyphPageTreeLevel() const { return m_maxGlyphPageTreeLevel; } #ifndef NDEBUG - virtual String description() const = 0; + virtual String description() const = 0; #endif -private: - mutable unsigned m_maxGlyphPageTreeLevel; + private: + mutable unsigned m_maxGlyphPageTreeLevel; }; -#define DEFINE_FONT_DATA_TYPE_CASTS(thisType, predicate) \ - template inline thisType* to##thisType(const RefPtr& fontData) { return to##thisType(fontData.get()); } \ - DEFINE_TYPE_CASTS(thisType, FontData, fontData, fontData->isSegmented() == predicate, fontData.isSegmented() == predicate) +#define DEFINE_FONT_DATA_TYPE_CASTS(thisType, predicate) \ + template \ + inline thisType* to##thisType(const RefPtr& fontData) { \ + return to##thisType(fontData.get()); \ + } \ + DEFINE_TYPE_CASTS(thisType, FontData, fontData, \ + fontData->isSegmented() == predicate, \ + fontData.isSegmented() == predicate) -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_FONTS_FONTDATA_H_ diff --git a/sky/engine/platform/fonts/FontDataCache.cpp b/sky/engine/platform/fonts/FontDataCache.cpp index 85ef1d7175a88..a9f6d8f2ad281 100644 --- a/sky/engine/platform/fonts/FontDataCache.cpp +++ b/sky/engine/platform/fonts/FontDataCache.cpp @@ -44,111 +44,114 @@ const unsigned cMaxInactiveFontData = 225; const unsigned cTargetInactiveFontData = 200; #endif -PassRefPtr FontDataCache::get(const FontPlatformData* platformData, ShouldRetain shouldRetain) -{ - if (!platformData) - return nullptr; - - Cache::iterator result = m_cache.find(*platformData); - if (result == m_cache.end()) { - pair, unsigned> newValue(SimpleFontData::create(*platformData), shouldRetain == Retain ? 1 : 0); - m_cache.set(*platformData, newValue); - if (shouldRetain == DoNotRetain) - m_inactiveFontData.add(newValue.first); - return newValue.first.release(); - } - - if (!result.get()->value.second) { - ASSERT(m_inactiveFontData.contains(result.get()->value.first)); - m_inactiveFontData.remove(result.get()->value.first); - } - - if (shouldRetain == Retain) { - result.get()->value.second++; - } else if (!result.get()->value.second) { - // If shouldRetain is DoNotRetain and count is 0, we want to remove the fontData from - // m_inactiveFontData (above) and re-add here to update LRU position. - m_inactiveFontData.add(result.get()->value.first); - } - - return result.get()->value.first; +PassRefPtr FontDataCache::get( + const FontPlatformData* platformData, + ShouldRetain shouldRetain) { + if (!platformData) + return nullptr; + + Cache::iterator result = m_cache.find(*platformData); + if (result == m_cache.end()) { + pair, unsigned> newValue( + SimpleFontData::create(*platformData), shouldRetain == Retain ? 1 : 0); + m_cache.set(*platformData, newValue); + if (shouldRetain == DoNotRetain) + m_inactiveFontData.add(newValue.first); + return newValue.first.release(); + } + + if (!result.get()->value.second) { + ASSERT(m_inactiveFontData.contains(result.get()->value.first)); + m_inactiveFontData.remove(result.get()->value.first); + } + + if (shouldRetain == Retain) { + result.get()->value.second++; + } else if (!result.get()->value.second) { + // If shouldRetain is DoNotRetain and count is 0, we want to remove the + // fontData from m_inactiveFontData (above) and re-add here to update LRU + // position. + m_inactiveFontData.add(result.get()->value.first); + } + + return result.get()->value.first; } -bool FontDataCache::contains(const FontPlatformData* fontPlatformData) const -{ - return m_cache.contains(*fontPlatformData); +bool FontDataCache::contains(const FontPlatformData* fontPlatformData) const { + return m_cache.contains(*fontPlatformData); } -void FontDataCache::release(const SimpleFontData* fontData) -{ - ASSERT(!fontData->isCustomFont()); +void FontDataCache::release(const SimpleFontData* fontData) { + ASSERT(!fontData->isCustomFont()); - Cache::iterator it = m_cache.find(fontData->platformData()); - ASSERT(it != m_cache.end()); - if (it == m_cache.end()) - return; + Cache::iterator it = m_cache.find(fontData->platformData()); + ASSERT(it != m_cache.end()); + if (it == m_cache.end()) + return; - ASSERT(it->value.second); - if (!--it->value.second) - m_inactiveFontData.add(it->value.first); + ASSERT(it->value.second); + if (!--it->value.second) + m_inactiveFontData.add(it->value.first); } -void FontDataCache::markAllVerticalData() -{ +void FontDataCache::markAllVerticalData() { #if ENABLE(OPENTYPE_VERTICAL) - Cache::iterator end = m_cache.end(); - for (Cache::iterator fontData = m_cache.begin(); fontData != end; ++fontData) { - OpenTypeVerticalData* verticalData = const_cast(fontData->value.first->verticalData()); - if (verticalData) - verticalData->setInFontCache(true); - } + Cache::iterator end = m_cache.end(); + for (Cache::iterator fontData = m_cache.begin(); fontData != end; + ++fontData) { + OpenTypeVerticalData* verticalData = const_cast( + fontData->value.first->verticalData()); + if (verticalData) + verticalData->setInFontCache(true); + } #endif } -bool FontDataCache::purge(PurgeSeverity PurgeSeverity) -{ - if (PurgeSeverity == ForcePurge) - return purgeLeastRecentlyUsed(INT_MAX); +bool FontDataCache::purge(PurgeSeverity PurgeSeverity) { + if (PurgeSeverity == ForcePurge) + return purgeLeastRecentlyUsed(INT_MAX); - if (m_inactiveFontData.size() > cMaxInactiveFontData) - return purgeLeastRecentlyUsed(m_inactiveFontData.size() - cTargetInactiveFontData); + if (m_inactiveFontData.size() > cMaxInactiveFontData) + return purgeLeastRecentlyUsed(m_inactiveFontData.size() - + cTargetInactiveFontData); - return false; + return false; } -bool FontDataCache::purgeLeastRecentlyUsed(int count) -{ - static bool isPurging; // Guard against reentry when e.g. a deleted FontData releases its small caps FontData. - if (isPurging) - return false; +bool FontDataCache::purgeLeastRecentlyUsed(int count) { + static bool isPurging; // Guard against reentry when e.g. a deleted FontData + // releases its small caps FontData. + if (isPurging) + return false; - isPurging = true; + isPurging = true; - Vector, 20> fontDataToDelete; - ListHashSet >::iterator end = m_inactiveFontData.end(); - ListHashSet >::iterator it = m_inactiveFontData.begin(); - for (int i = 0; i < count && it != end; ++it, ++i) { - RefPtr& fontData = *it.get(); - m_cache.remove(fontData->platformData()); - // We should not delete SimpleFontData here because deletion can modify m_inactiveFontData. See http://trac.webkit.org/changeset/44011 - fontDataToDelete.append(fontData); - } + Vector, 20> fontDataToDelete; + ListHashSet>::iterator end = m_inactiveFontData.end(); + ListHashSet>::iterator it = m_inactiveFontData.begin(); + for (int i = 0; i < count && it != end; ++it, ++i) { + RefPtr& fontData = *it.get(); + m_cache.remove(fontData->platformData()); + // We should not delete SimpleFontData here because deletion can modify + // m_inactiveFontData. See http://trac.webkit.org/changeset/44011 + fontDataToDelete.append(fontData); + } - if (it == end) { - // Removed everything - m_inactiveFontData.clear(); - } else { - for (int i = 0; i < count; ++i) - m_inactiveFontData.remove(m_inactiveFontData.begin()); - } + if (it == end) { + // Removed everything + m_inactiveFontData.clear(); + } else { + for (int i = 0; i < count; ++i) + m_inactiveFontData.remove(m_inactiveFontData.begin()); + } - bool didWork = fontDataToDelete.size(); + bool didWork = fontDataToDelete.size(); - fontDataToDelete.clear(); + fontDataToDelete.clear(); - isPurging = false; + isPurging = false; - return didWork; + return didWork; } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/fonts/FontDataCache.h b/sky/engine/platform/fonts/FontDataCache.h index c0d61ed03a940..cf879410670a6 100644 --- a/sky/engine/platform/fonts/FontDataCache.h +++ b/sky/engine/platform/fonts/FontDataCache.h @@ -41,59 +41,60 @@ namespace blink { class SimpleFontData; struct FontDataCacheKeyHash { - static unsigned hash(const FontPlatformData& platformData) - { - return platformData.hash(); - } + static unsigned hash(const FontPlatformData& platformData) { + return platformData.hash(); + } - static bool equal(const FontPlatformData& a, const FontPlatformData& b) - { - return a == b; - } + static bool equal(const FontPlatformData& a, const FontPlatformData& b) { + return a == b; + } - static const bool safeToCompareToEmptyOrDeleted = true; + static const bool safeToCompareToEmptyOrDeleted = true; }; struct FontDataCacheKeyTraits : WTF::GenericHashTraits { - static const bool emptyValueIsZero = true; - static const bool needsDestruction = true; - static const FontPlatformData& emptyValue() - { - DEFINE_STATIC_LOCAL(FontPlatformData, key, (0.f, false, false)); - return key; - } - static void constructDeletedValue(FontPlatformData& slot, bool) - { - new (NotNull, &slot) FontPlatformData(WTF::HashTableDeletedValue); - } - static bool isDeletedValue(const FontPlatformData& value) - { - return value.isHashTableDeletedValue(); - } + static const bool emptyValueIsZero = true; + static const bool needsDestruction = true; + static const FontPlatformData& emptyValue() { + DEFINE_STATIC_LOCAL(FontPlatformData, key, (0.f, false, false)); + return key; + } + static void constructDeletedValue(FontPlatformData& slot, bool) { + new (NotNull, &slot) FontPlatformData(WTF::HashTableDeletedValue); + } + static bool isDeletedValue(const FontPlatformData& value) { + return value.isHashTableDeletedValue(); + } }; class FontDataCache { -public: - PassRefPtr get(const FontPlatformData*, ShouldRetain = Retain); - bool contains(const FontPlatformData*) const; - void release(const SimpleFontData*); + public: + PassRefPtr get(const FontPlatformData*, + ShouldRetain = Retain); + bool contains(const FontPlatformData*) const; + void release(const SimpleFontData*); - // This is used by FontVerticalDataCache to mark all items with vertical data - // that are currently in cache as "in cache", which is later used to sweep the FontVerticalDataCache. - void markAllVerticalData(); + // This is used by FontVerticalDataCache to mark all items with vertical data + // that are currently in cache as "in cache", which is later used to sweep the + // FontVerticalDataCache. + void markAllVerticalData(); - // Purges items in FontDataCache according to provided severity. - // Returns true if any removal of cache items actually occurred. - bool purge(PurgeSeverity); + // Purges items in FontDataCache according to provided severity. + // Returns true if any removal of cache items actually occurred. + bool purge(PurgeSeverity); -private: - bool purgeLeastRecentlyUsed(int count); + private: + bool purgeLeastRecentlyUsed(int count); - typedef HashMap, unsigned>, FontDataCacheKeyHash, FontDataCacheKeyTraits> Cache; - Cache m_cache; - ListHashSet > m_inactiveFontData; + typedef HashMap, unsigned>, + FontDataCacheKeyHash, + FontDataCacheKeyTraits> + Cache; + Cache m_cache; + ListHashSet> m_inactiveFontData; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_FONTS_FONTDATACACHE_H_ diff --git a/sky/engine/platform/fonts/FontDescription.cpp b/sky/engine/platform/fonts/FontDescription.cpp index fc4119d9881a2..67581bad7a5c7 100644 --- a/sky/engine/platform/fonts/FontDescription.cpp +++ b/sky/engine/platform/fonts/FontDescription.cpp @@ -35,184 +35,178 @@ namespace blink { struct SameSizeAsFontDescription { - FontFamily familyList; - RefPtr m_featureSettings; - String locale; - float sizes[4]; - // FXIME: Make them fit into one word. - uint32_t bitfields; - uint32_t bitfields2 : 7; + FontFamily familyList; + RefPtr m_featureSettings; + String locale; + float sizes[4]; + // FXIME: Make them fit into one word. + uint32_t bitfields; + uint32_t bitfields2 : 7; }; -COMPILE_ASSERT(sizeof(FontDescription) == sizeof(SameSizeAsFontDescription), FontDescription_should_stay_small); +COMPILE_ASSERT(sizeof(FontDescription) == sizeof(SameSizeAsFontDescription), + FontDescription_should_stay_small); TypesettingFeatures FontDescription::s_defaultTypesettingFeatures = 0; bool FontDescription::s_useSubpixelTextPositioning = false; -FontWeight FontDescription::lighterWeight(FontWeight weight) -{ - switch (weight) { - case FontWeight100: - case FontWeight200: - case FontWeight300: - case FontWeight400: - case FontWeight500: - return FontWeight100; - - case FontWeight600: - case FontWeight700: - return FontWeight400; - - case FontWeight800: - case FontWeight900: - return FontWeight700; - } - ASSERT_NOT_REACHED(); - return FontWeightNormal; +FontWeight FontDescription::lighterWeight(FontWeight weight) { + switch (weight) { + case FontWeight100: + case FontWeight200: + case FontWeight300: + case FontWeight400: + case FontWeight500: + return FontWeight100; + + case FontWeight600: + case FontWeight700: + return FontWeight400; + + case FontWeight800: + case FontWeight900: + return FontWeight700; + } + ASSERT_NOT_REACHED(); + return FontWeightNormal; } -FontWeight FontDescription::bolderWeight(FontWeight weight) -{ - switch (weight) { - case FontWeight100: - case FontWeight200: - case FontWeight300: - return FontWeight400; - - case FontWeight400: - case FontWeight500: - return FontWeight700; - - case FontWeight600: - case FontWeight700: - case FontWeight800: - case FontWeight900: - return FontWeight900; - } - ASSERT_NOT_REACHED(); - return FontWeightNormal; +FontWeight FontDescription::bolderWeight(FontWeight weight) { + switch (weight) { + case FontWeight100: + case FontWeight200: + case FontWeight300: + return FontWeight400; + + case FontWeight400: + case FontWeight500: + return FontWeight700; + + case FontWeight600: + case FontWeight700: + case FontWeight800: + case FontWeight900: + return FontWeight900; + } + ASSERT_NOT_REACHED(); + return FontWeightNormal; } -FontTraits FontDescription::traits() const -{ - return FontTraits(style(), variant(), weight(), stretch()); +FontTraits FontDescription::traits() const { + return FontTraits(style(), variant(), weight(), stretch()); } -FontDescription::VariantLigatures FontDescription::variantLigatures() const -{ - VariantLigatures ligatures; +FontDescription::VariantLigatures FontDescription::variantLigatures() const { + VariantLigatures ligatures; - ligatures.common = commonLigaturesState(); - ligatures.discretionary = discretionaryLigaturesState(); - ligatures.historical = historicalLigaturesState(); - ligatures.contextual = contextualLigaturesState(); + ligatures.common = commonLigaturesState(); + ligatures.discretionary = discretionaryLigaturesState(); + ligatures.historical = historicalLigaturesState(); + ligatures.contextual = contextualLigaturesState(); - return ligatures; + return ligatures; } -void FontDescription::setTraits(FontTraits traits) -{ - setStyle(traits.style()); - setVariant(traits.variant()); - setWeight(traits.weight()); - setStretch(traits.stretch()); +void FontDescription::setTraits(FontTraits traits) { + setStyle(traits.style()); + setVariant(traits.variant()); + setWeight(traits.weight()); + setStretch(traits.stretch()); } -void FontDescription::setVariantLigatures(const VariantLigatures& ligatures) -{ - m_commonLigaturesState = ligatures.common; - m_discretionaryLigaturesState = ligatures.discretionary; - m_historicalLigaturesState = ligatures.historical; - m_contextualLigaturesState = ligatures.contextual; +void FontDescription::setVariantLigatures(const VariantLigatures& ligatures) { + m_commonLigaturesState = ligatures.common; + m_discretionaryLigaturesState = ligatures.discretionary; + m_historicalLigaturesState = ligatures.historical; + m_contextualLigaturesState = ligatures.contextual; - updateTypesettingFeatures(); + updateTypesettingFeatures(); } -float FontDescription::effectiveFontSize() const -{ - float size = computedSize(); +float FontDescription::effectiveFontSize() const { + float size = computedSize(); - // Ensure that the effective precision matches the font-cache precision. - // This guarantees that the same precision is used regardless of cache status. - return floorf(size * FontCacheKey::precisionMultiplier()) / FontCacheKey::precisionMultiplier(); + // Ensure that the effective precision matches the font-cache precision. + // This guarantees that the same precision is used regardless of cache status. + return floorf(size * FontCacheKey::precisionMultiplier()) / + FontCacheKey::precisionMultiplier(); } -FontCacheKey FontDescription::cacheKey(const FontFaceCreationParams& creationParams, FontTraits desiredTraits) const -{ - FontTraits fontTraits = desiredTraits.bitfield() ? desiredTraits : traits(); +FontCacheKey FontDescription::cacheKey( + const FontFaceCreationParams& creationParams, + FontTraits desiredTraits) const { + FontTraits fontTraits = desiredTraits.bitfield() ? desiredTraits : traits(); - unsigned options = - static_cast(m_syntheticItalic) << 7 | // bit 8 - static_cast(m_syntheticBold) << 6 | // bit 7 - static_cast(m_fontSmoothing) << 4 | // bits 5-6 - static_cast(m_textRendering) << 2 | // bits 3-4 - static_cast(m_orientation) << 1 | // bit 2 - static_cast(m_subpixelTextPosition); // bit 1 + unsigned options = static_cast(m_syntheticItalic) << 7 | // bit 8 + static_cast(m_syntheticBold) << 6 | // bit 7 + static_cast(m_fontSmoothing) << 4 | // bits 5-6 + static_cast(m_textRendering) << 2 | // bits 3-4 + static_cast(m_orientation) << 1 | // bit 2 + static_cast(m_subpixelTextPosition); // bit 1 - return FontCacheKey(creationParams, effectiveFontSize(), options | fontTraits.bitfield() << 8); + return FontCacheKey(creationParams, effectiveFontSize(), + options | fontTraits.bitfield() << 8); } - -void FontDescription::setDefaultTypesettingFeatures(TypesettingFeatures typesettingFeatures) -{ - s_defaultTypesettingFeatures = typesettingFeatures; +void FontDescription::setDefaultTypesettingFeatures( + TypesettingFeatures typesettingFeatures) { + s_defaultTypesettingFeatures = typesettingFeatures; } -TypesettingFeatures FontDescription::defaultTypesettingFeatures() -{ - return s_defaultTypesettingFeatures; +TypesettingFeatures FontDescription::defaultTypesettingFeatures() { + return s_defaultTypesettingFeatures; } -void FontDescription::updateTypesettingFeatures() const -{ - m_typesettingFeatures = s_defaultTypesettingFeatures; +void FontDescription::updateTypesettingFeatures() const { + m_typesettingFeatures = s_defaultTypesettingFeatures; - switch (textRendering()) { + switch (textRendering()) { case AutoTextRendering: - break; + break; case OptimizeSpeed: - m_typesettingFeatures &= ~(blink::Kerning | Ligatures); - break; + m_typesettingFeatures &= ~(blink::Kerning | Ligatures); + break; case GeometricPrecision: case OptimizeLegibility: - m_typesettingFeatures |= blink::Kerning | Ligatures; - break; - } + m_typesettingFeatures |= blink::Kerning | Ligatures; + break; + } - switch (kerning()) { + switch (kerning()) { case FontDescription::NoneKerning: - m_typesettingFeatures &= ~blink::Kerning; - break; + m_typesettingFeatures &= ~blink::Kerning; + break; case FontDescription::NormalKerning: - m_typesettingFeatures |= blink::Kerning; - break; + m_typesettingFeatures |= blink::Kerning; + break; case FontDescription::AutoKerning: + break; + } + + // As per CSS (http://dev.w3.org/csswg/css-text-3/#letter-spacing-property), + // When the effective letter-spacing between two characters is not zero (due + // to either justification or non-zero computed letter-spacing), user agents + // should not apply optional ligatures. + if (m_letterSpacing == 0) { + switch (commonLigaturesState()) { + case FontDescription::DisabledLigaturesState: + m_typesettingFeatures &= ~Ligatures; + break; + case FontDescription::EnabledLigaturesState: + m_typesettingFeatures |= Ligatures; + break; + case FontDescription::NormalLigaturesState: break; } - // As per CSS (http://dev.w3.org/csswg/css-text-3/#letter-spacing-property), - // When the effective letter-spacing between two characters is not zero (due to - // either justification or non-zero computed letter-spacing), user agents should - // not apply optional ligatures. - if (m_letterSpacing == 0) { - switch (commonLigaturesState()) { - case FontDescription::DisabledLigaturesState: - m_typesettingFeatures &= ~Ligatures; - break; - case FontDescription::EnabledLigaturesState: - m_typesettingFeatures |= Ligatures; - break; - case FontDescription::NormalLigaturesState: - break; - } - - if (discretionaryLigaturesState() == FontDescription::EnabledLigaturesState - || historicalLigaturesState() == FontDescription::EnabledLigaturesState - || contextualLigaturesState() == FontDescription::EnabledLigaturesState) { - m_typesettingFeatures |= blink::Ligatures; - } + if (discretionaryLigaturesState() == + FontDescription::EnabledLigaturesState || + historicalLigaturesState() == FontDescription::EnabledLigaturesState || + contextualLigaturesState() == FontDescription::EnabledLigaturesState) { + m_typesettingFeatures |= blink::Ligatures; } + } } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/fonts/FontDescription.h b/sky/engine/platform/fonts/FontDescription.h index f3a898c99950c..a3086cf593ff6 100644 --- a/sky/engine/platform/fonts/FontDescription.h +++ b/sky/engine/platform/fonts/FontDescription.h @@ -2,7 +2,8 @@ * Copyright (C) 2000 Lars Knoll (knoll@kde.org) * (C) 2000 Antti Koivisto (koivisto@kde.org) * (C) 2000 Dirk Mueller (mueller@kde.org) - * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. + * All rights reserved. * Copyright (C) 2007 Nicholas Shanks * * This library is free software; you can redistribute it and/or @@ -45,212 +46,274 @@ namespace blink { class PLATFORM_EXPORT FontDescription { -public: - enum GenericFamilyType { NoFamily, StandardFamily, SerifFamily, SansSerifFamily, - MonospaceFamily, CursiveFamily, FantasyFamily, PictographFamily }; - - enum Kerning { AutoKerning, NormalKerning, NoneKerning }; - - enum LigaturesState { NormalLigaturesState, DisabledLigaturesState, EnabledLigaturesState }; - - FontDescription() - : m_locale("en") - , m_specifiedSize(0) - , m_computedSize(0) - , m_letterSpacing(0) - , m_wordSpacing(0) - , m_orientation(Horizontal) - , m_nonCJKGlyphOrientation(NonCJKGlyphOrientationVerticalRight) - , m_widthVariant(RegularWidth) - , m_style(FontStyleNormal) - , m_variant(FontVariantNormal) - , m_isAbsoluteSize(false) - , m_weight(FontWeightNormal) - , m_stretch(FontStretchNormal) - , m_genericFamily(NoFamily) - , m_kerning(AutoKerning) - , m_commonLigaturesState(NormalLigaturesState) - , m_discretionaryLigaturesState(NormalLigaturesState) - , m_historicalLigaturesState(NormalLigaturesState) - , m_contextualLigaturesState(NormalLigaturesState) - , m_fontSmoothing(AutoSmoothing) - , m_textRendering(AutoTextRendering) - , m_script(USCRIPT_COMMON) - , m_syntheticBold(false) - , m_syntheticItalic(false) - , m_subpixelTextPosition(s_useSubpixelTextPositioning) - , m_typesettingFeatures(s_defaultTypesettingFeatures) - { - } - - bool operator==(const FontDescription&) const; - bool operator!=(const FontDescription& other) const { return !(*this == other); } - - struct VariantLigatures { - VariantLigatures() - : common(NormalLigaturesState) - , discretionary(NormalLigaturesState) - , historical(NormalLigaturesState) - , contextual(NormalLigaturesState) - { - } - - unsigned common : 2; - unsigned discretionary : 2; - unsigned historical : 2; - unsigned contextual : 2; - }; - - const FontFamily& family() const { return m_familyList; } - FontFamily& firstFamily() { return m_familyList; } - float specifiedSize() const { return m_specifiedSize; } - float computedSize() const { return m_computedSize; } - FontStyle style() const { return static_cast(m_style); } - int computedPixelSize() const { return int(m_computedSize + 0.5f); } - FontVariant variant() const { return static_cast(m_variant); } - bool isAbsoluteSize() const { return m_isAbsoluteSize; } - FontWeight weight() const { return static_cast(m_weight); } - FontStretch stretch() const { return static_cast(m_stretch); } - static FontWeight lighterWeight(FontWeight); - static FontWeight bolderWeight(FontWeight); - GenericFamilyType genericFamily() const { return static_cast(m_genericFamily); } - - Kerning kerning() const { return static_cast(m_kerning); } - VariantLigatures variantLigatures() const; - LigaturesState commonLigaturesState() const { return static_cast(m_commonLigaturesState); } - LigaturesState discretionaryLigaturesState() const { return static_cast(m_discretionaryLigaturesState); } - LigaturesState historicalLigaturesState() const { return static_cast(m_historicalLigaturesState); } - LigaturesState contextualLigaturesState() const { return static_cast(m_contextualLigaturesState); } - FontSmoothingMode fontSmoothing() const { return static_cast(m_fontSmoothing); } - TextRenderingMode textRendering() const { return static_cast(m_textRendering); } - UScriptCode script() const { return static_cast(m_script); } - const String& locale() const { return m_locale; } - bool isSyntheticBold() const { return m_syntheticBold; } - bool isSyntheticItalic() const { return m_syntheticItalic; } - bool useSubpixelPositioning() const { return m_subpixelTextPosition; } - - FontTraits traits() const; - float wordSpacing() const { return m_wordSpacing; } - float letterSpacing() const { return m_letterSpacing; } - FontOrientation orientation() const { return static_cast(m_orientation); } - NonCJKGlyphOrientation nonCJKGlyphOrientation() const { return static_cast(m_nonCJKGlyphOrientation); } - FontWidthVariant widthVariant() const { return static_cast(m_widthVariant); } - FontFeatureSettings* featureSettings() const { return m_featureSettings.get(); } - - float effectiveFontSize() const; // Returns either the computedSize or the computedPixelSize - FontCacheKey cacheKey(const FontFaceCreationParams&, FontTraits desiredTraits = FontTraits(0)) const; - - void setFamily(const FontFamily& family) { m_familyList = family; } - void setComputedSize(float s) { m_computedSize = clampToFloat(s); } - void setSpecifiedSize(float s) { m_specifiedSize = clampToFloat(s); } - void setStyle(FontStyle i) { m_style = i; } - void setVariant(FontVariant c) { m_variant = c; } - void setVariantLigatures(const VariantLigatures&); - void setIsAbsoluteSize(bool s) { m_isAbsoluteSize = s; } - void setWeight(FontWeight w) { m_weight = w; } - void setStretch(FontStretch s) { m_stretch = s; } - void setGenericFamily(GenericFamilyType genericFamily) { m_genericFamily = genericFamily; } - void setKerning(Kerning kerning) { m_kerning = kerning; updateTypesettingFeatures(); } - void setFontSmoothing(FontSmoothingMode smoothing) { m_fontSmoothing = smoothing; } - void setTextRendering(TextRenderingMode rendering) { m_textRendering = rendering; updateTypesettingFeatures(); } - void setOrientation(FontOrientation orientation) { m_orientation = orientation; } - void setNonCJKGlyphOrientation(NonCJKGlyphOrientation orientation) { m_nonCJKGlyphOrientation = orientation; } - void setWidthVariant(FontWidthVariant widthVariant) { m_widthVariant = widthVariant; } - void setScript(UScriptCode s) { m_script = s; } - void setLocale(const String& locale) { m_locale = locale; } - void setSyntheticBold(bool syntheticBold) { m_syntheticBold = syntheticBold; } - void setSyntheticItalic(bool syntheticItalic) { m_syntheticItalic = syntheticItalic; } - void setFeatureSettings(PassRefPtr settings) { m_featureSettings = settings; } - void setTraits(FontTraits); - void setWordSpacing(float s) { m_wordSpacing = s; } - void setLetterSpacing(float s) { m_letterSpacing = s; } - - TypesettingFeatures typesettingFeatures() const { return static_cast(m_typesettingFeatures); } - - static void setSubpixelPositioning(bool b) { s_useSubpixelTextPositioning = b; } - static bool subpixelPositioning() { return s_useSubpixelTextPositioning; } - - static void setDefaultTypesettingFeatures(TypesettingFeatures); - static TypesettingFeatures defaultTypesettingFeatures(); - -private: - FontFamily m_familyList; // The list of font families to be used. - RefPtr m_featureSettings; - String m_locale; - - void updateTypesettingFeatures() const; - - float m_specifiedSize; // Specified CSS value. Independent of rendering issues such as integer - // rounding, minimum font sizes. - float m_computedSize; // Computed size adjusted for the minimum font size. - - float m_letterSpacing; - float m_wordSpacing; - - unsigned m_orientation : 1; // FontOrientation - Whether the font is rendering on a horizontal line or a vertical line. - unsigned m_nonCJKGlyphOrientation : 1; // NonCJKGlyphOrientation - Only used by vertical text. Determines the default orientation for non-ideograph glyphs. - - unsigned m_widthVariant : 2; // FontWidthVariant - - unsigned m_style : 1; // FontStyle - unsigned m_variant : 1; // FontVariant - unsigned m_isAbsoluteSize : 1; // Whether or not CSS specified an explicit size - // (logical sizes like "medium" don't count). - unsigned m_weight : 4; // FontWeight - unsigned m_stretch : 4; // FontStretch - unsigned m_genericFamily : 3; // GenericFamilyType - - unsigned m_kerning : 2; // Kerning - - unsigned m_commonLigaturesState : 2; - unsigned m_discretionaryLigaturesState : 2; - unsigned m_historicalLigaturesState : 2; - unsigned m_contextualLigaturesState : 2; - - unsigned m_fontSmoothing : 2; // FontSmoothingMode - unsigned m_textRendering : 2; // TextRenderingMode - unsigned m_script : 7; // Used to help choose an appropriate font for generic font families. - unsigned m_syntheticBold : 1; - unsigned m_syntheticItalic : 1; - unsigned m_subpixelTextPosition : 1; - - mutable unsigned m_typesettingFeatures : 2; // TypesettingFeatures - - static TypesettingFeatures s_defaultTypesettingFeatures; - - static bool s_useSubpixelTextPositioning; + public: + enum GenericFamilyType { + NoFamily, + StandardFamily, + SerifFamily, + SansSerifFamily, + MonospaceFamily, + CursiveFamily, + FantasyFamily, + PictographFamily + }; + + enum Kerning { AutoKerning, NormalKerning, NoneKerning }; + + enum LigaturesState { + NormalLigaturesState, + DisabledLigaturesState, + EnabledLigaturesState + }; + + FontDescription() + : m_locale("en"), + m_specifiedSize(0), + m_computedSize(0), + m_letterSpacing(0), + m_wordSpacing(0), + m_orientation(Horizontal), + m_nonCJKGlyphOrientation(NonCJKGlyphOrientationVerticalRight), + m_widthVariant(RegularWidth), + m_style(FontStyleNormal), + m_variant(FontVariantNormal), + m_isAbsoluteSize(false), + m_weight(FontWeightNormal), + m_stretch(FontStretchNormal), + m_genericFamily(NoFamily), + m_kerning(AutoKerning), + m_commonLigaturesState(NormalLigaturesState), + m_discretionaryLigaturesState(NormalLigaturesState), + m_historicalLigaturesState(NormalLigaturesState), + m_contextualLigaturesState(NormalLigaturesState), + m_fontSmoothing(AutoSmoothing), + m_textRendering(AutoTextRendering), + m_script(USCRIPT_COMMON), + m_syntheticBold(false), + m_syntheticItalic(false), + m_subpixelTextPosition(s_useSubpixelTextPositioning), + m_typesettingFeatures(s_defaultTypesettingFeatures) {} + + bool operator==(const FontDescription&) const; + bool operator!=(const FontDescription& other) const { + return !(*this == other); + } + + struct VariantLigatures { + VariantLigatures() + : common(NormalLigaturesState), + discretionary(NormalLigaturesState), + historical(NormalLigaturesState), + contextual(NormalLigaturesState) {} + + unsigned common : 2; + unsigned discretionary : 2; + unsigned historical : 2; + unsigned contextual : 2; + }; + + const FontFamily& family() const { return m_familyList; } + FontFamily& firstFamily() { return m_familyList; } + float specifiedSize() const { return m_specifiedSize; } + float computedSize() const { return m_computedSize; } + FontStyle style() const { return static_cast(m_style); } + int computedPixelSize() const { return int(m_computedSize + 0.5f); } + FontVariant variant() const { return static_cast(m_variant); } + bool isAbsoluteSize() const { return m_isAbsoluteSize; } + FontWeight weight() const { return static_cast(m_weight); } + FontStretch stretch() const { return static_cast(m_stretch); } + static FontWeight lighterWeight(FontWeight); + static FontWeight bolderWeight(FontWeight); + GenericFamilyType genericFamily() const { + return static_cast(m_genericFamily); + } + + Kerning kerning() const { return static_cast(m_kerning); } + VariantLigatures variantLigatures() const; + LigaturesState commonLigaturesState() const { + return static_cast(m_commonLigaturesState); + } + LigaturesState discretionaryLigaturesState() const { + return static_cast(m_discretionaryLigaturesState); + } + LigaturesState historicalLigaturesState() const { + return static_cast(m_historicalLigaturesState); + } + LigaturesState contextualLigaturesState() const { + return static_cast(m_contextualLigaturesState); + } + FontSmoothingMode fontSmoothing() const { + return static_cast(m_fontSmoothing); + } + TextRenderingMode textRendering() const { + return static_cast(m_textRendering); + } + UScriptCode script() const { return static_cast(m_script); } + const String& locale() const { return m_locale; } + bool isSyntheticBold() const { return m_syntheticBold; } + bool isSyntheticItalic() const { return m_syntheticItalic; } + bool useSubpixelPositioning() const { return m_subpixelTextPosition; } + + FontTraits traits() const; + float wordSpacing() const { return m_wordSpacing; } + float letterSpacing() const { return m_letterSpacing; } + FontOrientation orientation() const { + return static_cast(m_orientation); + } + NonCJKGlyphOrientation nonCJKGlyphOrientation() const { + return static_cast(m_nonCJKGlyphOrientation); + } + FontWidthVariant widthVariant() const { + return static_cast(m_widthVariant); + } + FontFeatureSettings* featureSettings() const { + return m_featureSettings.get(); + } + + float effectiveFontSize() + const; // Returns either the computedSize or the computedPixelSize + FontCacheKey cacheKey(const FontFaceCreationParams&, + FontTraits desiredTraits = FontTraits(0)) const; + + void setFamily(const FontFamily& family) { m_familyList = family; } + void setComputedSize(float s) { m_computedSize = clampToFloat(s); } + void setSpecifiedSize(float s) { m_specifiedSize = clampToFloat(s); } + void setStyle(FontStyle i) { m_style = i; } + void setVariant(FontVariant c) { m_variant = c; } + void setVariantLigatures(const VariantLigatures&); + void setIsAbsoluteSize(bool s) { m_isAbsoluteSize = s; } + void setWeight(FontWeight w) { m_weight = w; } + void setStretch(FontStretch s) { m_stretch = s; } + void setGenericFamily(GenericFamilyType genericFamily) { + m_genericFamily = genericFamily; + } + void setKerning(Kerning kerning) { + m_kerning = kerning; + updateTypesettingFeatures(); + } + void setFontSmoothing(FontSmoothingMode smoothing) { + m_fontSmoothing = smoothing; + } + void setTextRendering(TextRenderingMode rendering) { + m_textRendering = rendering; + updateTypesettingFeatures(); + } + void setOrientation(FontOrientation orientation) { + m_orientation = orientation; + } + void setNonCJKGlyphOrientation(NonCJKGlyphOrientation orientation) { + m_nonCJKGlyphOrientation = orientation; + } + void setWidthVariant(FontWidthVariant widthVariant) { + m_widthVariant = widthVariant; + } + void setScript(UScriptCode s) { m_script = s; } + void setLocale(const String& locale) { m_locale = locale; } + void setSyntheticBold(bool syntheticBold) { m_syntheticBold = syntheticBold; } + void setSyntheticItalic(bool syntheticItalic) { + m_syntheticItalic = syntheticItalic; + } + void setFeatureSettings(PassRefPtr settings) { + m_featureSettings = settings; + } + void setTraits(FontTraits); + void setWordSpacing(float s) { m_wordSpacing = s; } + void setLetterSpacing(float s) { m_letterSpacing = s; } + + TypesettingFeatures typesettingFeatures() const { + return static_cast(m_typesettingFeatures); + } + + static void setSubpixelPositioning(bool b) { + s_useSubpixelTextPositioning = b; + } + static bool subpixelPositioning() { return s_useSubpixelTextPositioning; } + + static void setDefaultTypesettingFeatures(TypesettingFeatures); + static TypesettingFeatures defaultTypesettingFeatures(); + + private: + FontFamily m_familyList; // The list of font families to be used. + RefPtr m_featureSettings; + String m_locale; + + void updateTypesettingFeatures() const; + + float m_specifiedSize; // Specified CSS value. Independent of rendering + // issues such as integer rounding, minimum font + // sizes. + float m_computedSize; // Computed size adjusted for the minimum font size. + + float m_letterSpacing; + float m_wordSpacing; + + unsigned m_orientation : 1; // FontOrientation - Whether the font is + // rendering on a horizontal line or a vertical + // line. + unsigned m_nonCJKGlyphOrientation : 1; // NonCJKGlyphOrientation - Only used + // by vertical text. Determines the + // default orientation for + // non-ideograph glyphs. + + unsigned m_widthVariant : 2; // FontWidthVariant + + unsigned m_style : 1; // FontStyle + unsigned m_variant : 1; // FontVariant + unsigned + m_isAbsoluteSize : 1; // Whether or not CSS specified an explicit size + // (logical sizes like "medium" don't count). + unsigned m_weight : 4; // FontWeight + unsigned m_stretch : 4; // FontStretch + unsigned m_genericFamily : 3; // GenericFamilyType + + unsigned m_kerning : 2; // Kerning + + unsigned m_commonLigaturesState : 2; + unsigned m_discretionaryLigaturesState : 2; + unsigned m_historicalLigaturesState : 2; + unsigned m_contextualLigaturesState : 2; + + unsigned m_fontSmoothing : 2; // FontSmoothingMode + unsigned m_textRendering : 2; // TextRenderingMode + unsigned m_script : 7; // Used to help choose an appropriate font for generic + // font families. + unsigned m_syntheticBold : 1; + unsigned m_syntheticItalic : 1; + unsigned m_subpixelTextPosition : 1; + + mutable unsigned m_typesettingFeatures : 2; // TypesettingFeatures + + static TypesettingFeatures s_defaultTypesettingFeatures; + + static bool s_useSubpixelTextPositioning; }; -inline bool FontDescription::operator==(const FontDescription& other) const -{ - return m_familyList == other.m_familyList - && m_specifiedSize == other.m_specifiedSize - && m_computedSize == other.m_computedSize - && m_letterSpacing == other.m_letterSpacing - && m_wordSpacing == other.m_wordSpacing - && m_style == other.m_style - && m_variant == other.m_variant - && m_isAbsoluteSize == other.m_isAbsoluteSize - && m_weight == other.m_weight - && m_stretch == other.m_stretch - && m_genericFamily == other.m_genericFamily - && m_kerning == other.m_kerning - && m_commonLigaturesState == other.m_commonLigaturesState - && m_discretionaryLigaturesState == other.m_discretionaryLigaturesState - && m_historicalLigaturesState == other.m_historicalLigaturesState - && m_contextualLigaturesState == other.m_contextualLigaturesState - && m_fontSmoothing == other.m_fontSmoothing - && m_textRendering == other.m_textRendering - && m_orientation == other.m_orientation - && m_nonCJKGlyphOrientation == other.m_nonCJKGlyphOrientation - && m_widthVariant == other.m_widthVariant - && m_script == other.m_script - && m_syntheticBold == other.m_syntheticBold - && m_syntheticItalic == other.m_syntheticItalic - && m_featureSettings == other.m_featureSettings - && m_subpixelTextPosition == other.m_subpixelTextPosition; +inline bool FontDescription::operator==(const FontDescription& other) const { + return m_familyList == other.m_familyList && + m_specifiedSize == other.m_specifiedSize && + m_computedSize == other.m_computedSize && + m_letterSpacing == other.m_letterSpacing && + m_wordSpacing == other.m_wordSpacing && m_style == other.m_style && + m_variant == other.m_variant && + m_isAbsoluteSize == other.m_isAbsoluteSize && + m_weight == other.m_weight && m_stretch == other.m_stretch && + m_genericFamily == other.m_genericFamily && + m_kerning == other.m_kerning && + m_commonLigaturesState == other.m_commonLigaturesState && + m_discretionaryLigaturesState == other.m_discretionaryLigaturesState && + m_historicalLigaturesState == other.m_historicalLigaturesState && + m_contextualLigaturesState == other.m_contextualLigaturesState && + m_fontSmoothing == other.m_fontSmoothing && + m_textRendering == other.m_textRendering && + m_orientation == other.m_orientation && + m_nonCJKGlyphOrientation == other.m_nonCJKGlyphOrientation && + m_widthVariant == other.m_widthVariant && m_script == other.m_script && + m_syntheticBold == other.m_syntheticBold && + m_syntheticItalic == other.m_syntheticItalic && + m_featureSettings == other.m_featureSettings && + m_subpixelTextPosition == other.m_subpixelTextPosition; } -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_FONTS_FONTDESCRIPTION_H_ diff --git a/sky/engine/platform/fonts/FontDescriptionTest.cpp b/sky/engine/platform/fonts/FontDescriptionTest.cpp index dd81b5941a9ab..412873a7203be 100644 --- a/sky/engine/platform/fonts/FontDescriptionTest.cpp +++ b/sky/engine/platform/fonts/FontDescriptionTest.cpp @@ -23,98 +23,95 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #include "flutter/sky/engine/platform/fonts/FontDescription.h" #include namespace blink { - -static inline void assertDescriptionMatchesMask(FontDescription& source, FontTraitsBitfield bitfield) -{ - FontDescription target; - target.setTraits(FontTraits(bitfield)); - EXPECT_EQ(source.style(), target.style()); - EXPECT_EQ(source.variant(), target.variant()); - EXPECT_EQ(source.weight(), target.weight()); - EXPECT_EQ(source.stretch(), target.stretch()); +static inline void assertDescriptionMatchesMask(FontDescription& source, + FontTraitsBitfield bitfield) { + FontDescription target; + target.setTraits(FontTraits(bitfield)); + EXPECT_EQ(source.style(), target.style()); + EXPECT_EQ(source.variant(), target.variant()); + EXPECT_EQ(source.weight(), target.weight()); + EXPECT_EQ(source.stretch(), target.stretch()); } -TEST(FontDescriptionTest, TestFontTraits) -{ - FontDescription source; - source.setStyle(FontStyleNormal); - source.setVariant(FontVariantNormal); - source.setWeight(FontWeightNormal); - source.setStretch(FontStretchNormal); - assertDescriptionMatchesMask(source, source.traits().bitfield()); - - source.setStyle(FontStyleNormal); - source.setVariant(FontVariantNormal); - source.setWeight(FontWeightNormal); - source.setStretch(FontStretchExtraCondensed); - assertDescriptionMatchesMask(source, source.traits().bitfield()); - - source.setStyle(FontStyleItalic); - source.setVariant(FontVariantNormal); - source.setWeight(FontWeight900); - source.setStretch(FontStretchUltraExpanded); - assertDescriptionMatchesMask(source, source.traits().bitfield()); - - source.setStyle(FontStyleItalic); - source.setVariant(FontVariantSmallCaps); - source.setWeight(FontWeight100); - source.setStretch(FontStretchExtraExpanded); - assertDescriptionMatchesMask(source, source.traits().bitfield()); - - source.setStyle(FontStyleItalic); - source.setVariant(FontVariantNormal); - source.setWeight(FontWeight900); - source.setStretch(FontStretchNormal); - assertDescriptionMatchesMask(source, source.traits().bitfield()); - - source.setStyle(FontStyleItalic); - source.setVariant(FontVariantNormal); - source.setWeight(FontWeight800); - source.setStretch(FontStretchNormal); - assertDescriptionMatchesMask(source, source.traits().bitfield()); - - source.setStyle(FontStyleItalic); - source.setVariant(FontVariantNormal); - source.setWeight(FontWeight700); - source.setStretch(FontStretchNormal); - assertDescriptionMatchesMask(source, source.traits().bitfield()); - - source.setStyle(FontStyleItalic); - source.setVariant(FontVariantNormal); - source.setWeight(FontWeight600); - source.setStretch(FontStretchNormal); - assertDescriptionMatchesMask(source, source.traits().bitfield()); - - source.setStyle(FontStyleItalic); - source.setVariant(FontVariantNormal); - source.setWeight(FontWeight500); - source.setStretch(FontStretchNormal); - assertDescriptionMatchesMask(source, source.traits().bitfield()); - - source.setStyle(FontStyleItalic); - source.setVariant(FontVariantNormal); - source.setWeight(FontWeight400); - source.setStretch(FontStretchNormal); - assertDescriptionMatchesMask(source, source.traits().bitfield()); - - source.setStyle(FontStyleItalic); - source.setVariant(FontVariantNormal); - source.setWeight(FontWeight300); - source.setStretch(FontStretchUltraExpanded); - assertDescriptionMatchesMask(source, source.traits().bitfield()); - - source.setStyle(FontStyleItalic); - source.setVariant(FontVariantNormal); - source.setWeight(FontWeight200); - source.setStretch(FontStretchNormal); - assertDescriptionMatchesMask(source, source.traits().bitfield()); +TEST(FontDescriptionTest, TestFontTraits) { + FontDescription source; + source.setStyle(FontStyleNormal); + source.setVariant(FontVariantNormal); + source.setWeight(FontWeightNormal); + source.setStretch(FontStretchNormal); + assertDescriptionMatchesMask(source, source.traits().bitfield()); + + source.setStyle(FontStyleNormal); + source.setVariant(FontVariantNormal); + source.setWeight(FontWeightNormal); + source.setStretch(FontStretchExtraCondensed); + assertDescriptionMatchesMask(source, source.traits().bitfield()); + + source.setStyle(FontStyleItalic); + source.setVariant(FontVariantNormal); + source.setWeight(FontWeight900); + source.setStretch(FontStretchUltraExpanded); + assertDescriptionMatchesMask(source, source.traits().bitfield()); + + source.setStyle(FontStyleItalic); + source.setVariant(FontVariantSmallCaps); + source.setWeight(FontWeight100); + source.setStretch(FontStretchExtraExpanded); + assertDescriptionMatchesMask(source, source.traits().bitfield()); + + source.setStyle(FontStyleItalic); + source.setVariant(FontVariantNormal); + source.setWeight(FontWeight900); + source.setStretch(FontStretchNormal); + assertDescriptionMatchesMask(source, source.traits().bitfield()); + + source.setStyle(FontStyleItalic); + source.setVariant(FontVariantNormal); + source.setWeight(FontWeight800); + source.setStretch(FontStretchNormal); + assertDescriptionMatchesMask(source, source.traits().bitfield()); + + source.setStyle(FontStyleItalic); + source.setVariant(FontVariantNormal); + source.setWeight(FontWeight700); + source.setStretch(FontStretchNormal); + assertDescriptionMatchesMask(source, source.traits().bitfield()); + + source.setStyle(FontStyleItalic); + source.setVariant(FontVariantNormal); + source.setWeight(FontWeight600); + source.setStretch(FontStretchNormal); + assertDescriptionMatchesMask(source, source.traits().bitfield()); + + source.setStyle(FontStyleItalic); + source.setVariant(FontVariantNormal); + source.setWeight(FontWeight500); + source.setStretch(FontStretchNormal); + assertDescriptionMatchesMask(source, source.traits().bitfield()); + + source.setStyle(FontStyleItalic); + source.setVariant(FontVariantNormal); + source.setWeight(FontWeight400); + source.setStretch(FontStretchNormal); + assertDescriptionMatchesMask(source, source.traits().bitfield()); + + source.setStyle(FontStyleItalic); + source.setVariant(FontVariantNormal); + source.setWeight(FontWeight300); + source.setStretch(FontStretchUltraExpanded); + assertDescriptionMatchesMask(source, source.traits().bitfield()); + + source.setStyle(FontStyleItalic); + source.setVariant(FontVariantNormal); + source.setWeight(FontWeight200); + source.setStretch(FontStretchNormal); + assertDescriptionMatchesMask(source, source.traits().bitfield()); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/fonts/FontFaceCreationParams.h b/sky/engine/platform/fonts/FontFaceCreationParams.h index b6376d07ced19..047fa05b30f51 100644 --- a/sky/engine/platform/fonts/FontFaceCreationParams.h +++ b/sky/engine/platform/fonts/FontFaceCreationParams.h @@ -38,83 +38,84 @@ namespace blink { -enum FontFaceCreationType { - CreateFontByFamily, - CreateFontByFciIdAndTtcIndex -}; +enum FontFaceCreationType { CreateFontByFamily, CreateFontByFciIdAndTtcIndex }; class FontFaceCreationParams { - FontFaceCreationType m_creationType; - AtomicString m_family; - CString m_filename; - int m_fontconfigInterfaceId; - int m_ttcIndex; + FontFaceCreationType m_creationType; + AtomicString m_family; + CString m_filename; + int m_fontconfigInterfaceId; + int m_ttcIndex; -public: - FontFaceCreationParams() - : m_creationType(CreateFontByFamily), m_family(AtomicString()), m_filename(CString()), m_fontconfigInterfaceId(0), m_ttcIndex(0) - { - } + public: + FontFaceCreationParams() + : m_creationType(CreateFontByFamily), + m_family(AtomicString()), + m_filename(CString()), + m_fontconfigInterfaceId(0), + m_ttcIndex(0) {} - explicit FontFaceCreationParams(AtomicString family) - : m_creationType(CreateFontByFamily), m_family(family), m_filename(CString()), m_fontconfigInterfaceId(0), m_ttcIndex(0) - { - } - - FontFaceCreationParams(CString filename, int fontconfigInterfaceId, int ttcIndex = 0) - : m_creationType(CreateFontByFciIdAndTtcIndex), m_filename(filename), m_fontconfigInterfaceId(fontconfigInterfaceId), m_ttcIndex(ttcIndex) - { - } + explicit FontFaceCreationParams(AtomicString family) + : m_creationType(CreateFontByFamily), + m_family(family), + m_filename(CString()), + m_fontconfigInterfaceId(0), + m_ttcIndex(0) {} - FontFaceCreationType creationType() const { return m_creationType; } - AtomicString family() const - { - ASSERT(m_creationType == CreateFontByFamily); - return m_family; - } - CString filename() const - { - ASSERT(m_creationType == CreateFontByFciIdAndTtcIndex); - return m_filename; - } - int fontconfigInterfaceId() const - { - ASSERT(m_creationType == CreateFontByFciIdAndTtcIndex); - return m_fontconfigInterfaceId; - } - int ttcIndex() const - { - ASSERT(m_creationType == CreateFontByFciIdAndTtcIndex); - return m_ttcIndex; - } + FontFaceCreationParams(CString filename, + int fontconfigInterfaceId, + int ttcIndex = 0) + : m_creationType(CreateFontByFciIdAndTtcIndex), + m_filename(filename), + m_fontconfigInterfaceId(fontconfigInterfaceId), + m_ttcIndex(ttcIndex) {} - unsigned hash() const - { - if (m_creationType == CreateFontByFciIdAndTtcIndex) { - StringHasher hasher; - // Hashing the filename and ints in this way is sensitive to character encoding - // and endianness. However, since the hash is not transferred over a network - // or permanently stored and only used for the runtime of Chromium, - // this is not a concern. - hasher.addCharacters(reinterpret_cast(m_filename.data()), m_filename.length()); - hasher.addCharacters(reinterpret_cast(&m_ttcIndex), sizeof(m_ttcIndex)); - hasher.addCharacters(reinterpret_cast(&m_fontconfigInterfaceId), sizeof(m_fontconfigInterfaceId)); - return hasher.hash(); - } - return CaseFoldingHash::hash(m_family.isEmpty() ? "" : m_family); - } + FontFaceCreationType creationType() const { return m_creationType; } + AtomicString family() const { + ASSERT(m_creationType == CreateFontByFamily); + return m_family; + } + CString filename() const { + ASSERT(m_creationType == CreateFontByFciIdAndTtcIndex); + return m_filename; + } + int fontconfigInterfaceId() const { + ASSERT(m_creationType == CreateFontByFciIdAndTtcIndex); + return m_fontconfigInterfaceId; + } + int ttcIndex() const { + ASSERT(m_creationType == CreateFontByFciIdAndTtcIndex); + return m_ttcIndex; + } - bool operator==(const FontFaceCreationParams& other) const - { - return m_creationType == other.m_creationType - && equalIgnoringCase(m_family, other.m_family) - && m_filename == other.m_filename - && m_fontconfigInterfaceId == other.m_fontconfigInterfaceId - && m_ttcIndex == other.m_ttcIndex; + unsigned hash() const { + if (m_creationType == CreateFontByFciIdAndTtcIndex) { + StringHasher hasher; + // Hashing the filename and ints in this way is sensitive to character + // encoding and endianness. However, since the hash is not transferred + // over a network or permanently stored and only used for the runtime of + // Chromium, this is not a concern. + hasher.addCharacters(reinterpret_cast(m_filename.data()), + m_filename.length()); + hasher.addCharacters(reinterpret_cast(&m_ttcIndex), + sizeof(m_ttcIndex)); + hasher.addCharacters( + reinterpret_cast(&m_fontconfigInterfaceId), + sizeof(m_fontconfigInterfaceId)); + return hasher.hash(); } + return CaseFoldingHash::hash(m_family.isEmpty() ? "" : m_family); + } + bool operator==(const FontFaceCreationParams& other) const { + return m_creationType == other.m_creationType && + equalIgnoringCase(m_family, other.m_family) && + m_filename == other.m_filename && + m_fontconfigInterfaceId == other.m_fontconfigInterfaceId && + m_ttcIndex == other.m_ttcIndex; + } }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_FONTS_FONTFACECREATIONPARAMS_H_ diff --git a/sky/engine/platform/fonts/FontFallbackList.cpp b/sky/engine/platform/fonts/FontFallbackList.cpp index f10cb3a52028c..177f161adaa7e 100644 --- a/sky/engine/platform/fonts/FontFallbackList.cpp +++ b/sky/engine/platform/fonts/FontFallbackList.cpp @@ -38,200 +38,209 @@ namespace blink { FontFallbackList::FontFallbackList() - : m_pageZero(0) - , m_cachedPrimarySimpleFontData(0) - , m_fontSelector(nullptr) - , m_fontSelectorVersion(0) - , m_familyIndex(0) - , m_generation(FontCache::fontCache()->generation()) - , m_pitch(UnknownPitch) - , m_hasLoadingFallback(false) -{ + : m_pageZero(0), + m_cachedPrimarySimpleFontData(0), + m_fontSelector(nullptr), + m_fontSelectorVersion(0), + m_familyIndex(0), + m_generation(FontCache::fontCache()->generation()), + m_pitch(UnknownPitch), + m_hasLoadingFallback(false) {} + +void FontFallbackList::invalidate(PassRefPtr fontSelector) { + releaseFontData(); + m_fontList.clear(); + m_pageZero = 0; + m_pages.clear(); + m_cachedPrimarySimpleFontData = 0; + m_familyIndex = 0; + m_pitch = UnknownPitch; + m_hasLoadingFallback = false; + m_fontSelector = fontSelector; + m_fontSelectorVersion = m_fontSelector ? m_fontSelector->version() : 0; + m_generation = FontCache::fontCache()->generation(); + m_widthCache.clear(); } -void FontFallbackList::invalidate(PassRefPtr fontSelector) -{ - releaseFontData(); - m_fontList.clear(); - m_pageZero = 0; - m_pages.clear(); - m_cachedPrimarySimpleFontData = 0; - m_familyIndex = 0; - m_pitch = UnknownPitch; - m_hasLoadingFallback = false; - m_fontSelector = fontSelector; - m_fontSelectorVersion = m_fontSelector ? m_fontSelector->version() : 0; - m_generation = FontCache::fontCache()->generation(); - m_widthCache.clear(); +void FontFallbackList::releaseFontData() { + unsigned numFonts = m_fontList.size(); + for (unsigned i = 0; i < numFonts; ++i) { + if (!m_fontList[i]->isCustomFont()) { + ASSERT(!m_fontList[i]->isSegmented()); + FontCache::fontCache()->releaseFontData(toSimpleFontData(m_fontList[i])); + } + } } -void FontFallbackList::releaseFontData() -{ - unsigned numFonts = m_fontList.size(); - for (unsigned i = 0; i < numFonts; ++i) { - if (!m_fontList[i]->isCustomFont()) { - ASSERT(!m_fontList[i]->isSegmented()); - FontCache::fontCache()->releaseFontData(toSimpleFontData(m_fontList[i])); - } +void FontFallbackList::determinePitch( + const FontDescription& fontDescription) const { + for (unsigned fontIndex = 0;; ++fontIndex) { + const FontData* fontData = fontDataAt(fontDescription, fontIndex); + if (!fontData) { + // All fonts are custom fonts and are loading. Fallback should be variable + // pitch. + m_pitch = VariablePitch; + break; } -} -void FontFallbackList::determinePitch(const FontDescription& fontDescription) const -{ - for (unsigned fontIndex = 0; ; ++fontIndex) { - const FontData* fontData = fontDataAt(fontDescription, fontIndex); - if (!fontData) { - // All fonts are custom fonts and are loading. Fallback should be variable pitch. - m_pitch = VariablePitch; - break; - } - - const SimpleFontData* simpleFontData; - if (fontData->isSegmented()) { - const SegmentedFontData* segmentedFontData = toSegmentedFontData(fontData); - if (segmentedFontData->numRanges() != 1 || !segmentedFontData->rangeAt(0).isEntireRange()) { - m_pitch = VariablePitch; - break; - } - simpleFontData = segmentedFontData->rangeAt(0).fontData().get(); - } else { - simpleFontData = toSimpleFontData(fontData); - } - if (!fontData->isLoadingFallback()) { - m_pitch = simpleFontData->pitch(); - break; - } + const SimpleFontData* simpleFontData; + if (fontData->isSegmented()) { + const SegmentedFontData* segmentedFontData = + toSegmentedFontData(fontData); + if (segmentedFontData->numRanges() != 1 || + !segmentedFontData->rangeAt(0).isEntireRange()) { + m_pitch = VariablePitch; + break; + } + simpleFontData = segmentedFontData->rangeAt(0).fontData().get(); + } else { + simpleFontData = toSimpleFontData(fontData); } + if (!fontData->isLoadingFallback()) { + m_pitch = simpleFontData->pitch(); + break; + } + } } -bool FontFallbackList::loadingCustomFonts() const -{ - if (!m_hasLoadingFallback) - return false; - - unsigned numFonts = m_fontList.size(); - for (unsigned i = 0; i < numFonts; ++i) { - if (m_fontList[i]->isLoading()) - return true; - } +bool FontFallbackList::loadingCustomFonts() const { + if (!m_hasLoadingFallback) return false; -} -bool FontFallbackList::shouldSkipDrawing() const -{ - if (!m_hasLoadingFallback) - return false; + unsigned numFonts = m_fontList.size(); + for (unsigned i = 0; i < numFonts; ++i) { + if (m_fontList[i]->isLoading()) + return true; + } + return false; +} - unsigned numFonts = m_fontList.size(); - for (unsigned i = 0; i < numFonts; ++i) { - if (m_fontList[i]->shouldSkipDrawing()) - return true; - } +bool FontFallbackList::shouldSkipDrawing() const { + if (!m_hasLoadingFallback) return false; -} -const SimpleFontData* FontFallbackList::determinePrimarySimpleFontData(const FontDescription& fontDescription) const -{ - bool shouldLoadCustomFont = true; - - for (unsigned fontIndex = 0; ; ++fontIndex) { - const FontData* fontData = fontDataAt(fontDescription, fontIndex); - if (!fontData) { - // All fonts are custom fonts and are loading. Return the first FontData. - fontData = fontDataAt(fontDescription, 0); - if (fontData) - return fontData->fontDataForCharacter(space); - - SimpleFontData* lastResortFallback = FontCache::fontCache()->getLastResortFallbackFont(fontDescription).get(); - ASSERT(lastResortFallback); - return lastResortFallback; - } - - if (fontData->isSegmented() && !toSegmentedFontData(fontData)->containsCharacter(space)) - continue; - - const SimpleFontData* fontDataForSpace = fontData->fontDataForCharacter(space); - ASSERT(fontDataForSpace); - - // When a custom font is loading, we should use the correct fallback font to layout the text. - // Here skip the temporary font for the loading custom font which may not act as the correct fallback font. - if (!fontDataForSpace->isLoadingFallback()) - return fontDataForSpace; - - if (fontData->isSegmented()) { - const SegmentedFontData* segmented = toSegmentedFontData(fontData); - for (unsigned i = 0; i < segmented->numRanges(); i++) { - const SimpleFontData* rangeFontData = segmented->rangeAt(i).fontData().get(); - if (!rangeFontData->isLoadingFallback()) - return rangeFontData; - } - if (fontData->isLoading()) - shouldLoadCustomFont = false; - } - - // Begin to load the first custom font if needed. - if (shouldLoadCustomFont) { - shouldLoadCustomFont = false; - fontDataForSpace->customFontData()->beginLoadIfNeeded(); - } - } + unsigned numFonts = m_fontList.size(); + for (unsigned i = 0; i < numFonts; ++i) { + if (m_fontList[i]->shouldSkipDrawing()) + return true; + } + return false; } -PassRefPtr FontFallbackList::getFontData(const FontDescription& fontDescription, int& familyIndex) const -{ - RefPtr result; - - int startIndex = familyIndex; - const FontFamily* startFamily = &fontDescription.family(); - for (int i = 0; startFamily && i < startIndex; i++) - startFamily = startFamily->next(); - const FontFamily* currFamily = startFamily; - while (currFamily && !result) { - familyIndex++; - if (currFamily->family().length() || Settings::Get().use_test_fonts) { - if (m_fontSelector) - result = m_fontSelector->getFontData(fontDescription, currFamily->family()); - - if (!result) - result = FontCache::fontCache()->getFontData(fontDescription, currFamily->family()); - } - currFamily = currFamily->next(); +const SimpleFontData* FontFallbackList::determinePrimarySimpleFontData( + const FontDescription& fontDescription) const { + bool shouldLoadCustomFont = true; + + for (unsigned fontIndex = 0;; ++fontIndex) { + const FontData* fontData = fontDataAt(fontDescription, fontIndex); + if (!fontData) { + // All fonts are custom fonts and are loading. Return the first FontData. + fontData = fontDataAt(fontDescription, 0); + if (fontData) + return fontData->fontDataForCharacter(space); + + SimpleFontData* lastResortFallback = + FontCache::fontCache() + ->getLastResortFallbackFont(fontDescription) + .get(); + ASSERT(lastResortFallback); + return lastResortFallback; } - if (!currFamily) - familyIndex = cAllFamiliesScanned; - - if (result || startIndex) - return result.release(); + if (fontData->isSegmented() && + !toSegmentedFontData(fontData)->containsCharacter(space)) + continue; + + const SimpleFontData* fontDataForSpace = + fontData->fontDataForCharacter(space); + ASSERT(fontDataForSpace); + + // When a custom font is loading, we should use the correct fallback font to + // layout the text. Here skip the temporary font for the loading custom font + // which may not act as the correct fallback font. + if (!fontDataForSpace->isLoadingFallback()) + return fontDataForSpace; + + if (fontData->isSegmented()) { + const SegmentedFontData* segmented = toSegmentedFontData(fontData); + for (unsigned i = 0; i < segmented->numRanges(); i++) { + const SimpleFontData* rangeFontData = + segmented->rangeAt(i).fontData().get(); + if (!rangeFontData->isLoadingFallback()) + return rangeFontData; + } + if (fontData->isLoading()) + shouldLoadCustomFont = false; + } - // Still no result. Hand back our last resort fallback font. - return FontCache::fontCache()->getLastResortFallbackFont(fontDescription); + // Begin to load the first custom font if needed. + if (shouldLoadCustomFont) { + shouldLoadCustomFont = false; + fontDataForSpace->customFontData()->beginLoadIfNeeded(); + } + } } +PassRefPtr FontFallbackList::getFontData( + const FontDescription& fontDescription, + int& familyIndex) const { + RefPtr result; + + int startIndex = familyIndex; + const FontFamily* startFamily = &fontDescription.family(); + for (int i = 0; startFamily && i < startIndex; i++) + startFamily = startFamily->next(); + const FontFamily* currFamily = startFamily; + while (currFamily && !result) { + familyIndex++; + if (currFamily->family().length() || Settings::Get().use_test_fonts) { + if (m_fontSelector) + result = + m_fontSelector->getFontData(fontDescription, currFamily->family()); + + if (!result) + result = FontCache::fontCache()->getFontData(fontDescription, + currFamily->family()); + } + currFamily = currFamily->next(); + } -const FontData* FontFallbackList::fontDataAt(const FontDescription& fontDescription, unsigned realizedFontIndex) const -{ - if (realizedFontIndex < m_fontList.size()) - return m_fontList[realizedFontIndex].get(); // This fallback font is already in our list. + if (!currFamily) + familyIndex = cAllFamiliesScanned; - // Make sure we're not passing in some crazy value here. - ASSERT(realizedFontIndex == m_fontList.size()); + if (result || startIndex) + return result.release(); - if (m_familyIndex == cAllFamiliesScanned) - return 0; + // Still no result. Hand back our last resort fallback font. + return FontCache::fontCache()->getLastResortFallbackFont(fontDescription); +} - // Ask the font cache for the font data. - // We are obtaining this font for the first time. We keep track of the families we've looked at before - // in |m_familyIndex|, so that we never scan the same spot in the list twice. getFontData will adjust our - // |m_familyIndex| as it scans for the right font to make. - ASSERT(FontCache::fontCache()->generation() == m_generation); - RefPtr result = getFontData(fontDescription, m_familyIndex); - if (result) { - m_fontList.append(result); - if (result->isLoadingFallback()) - m_hasLoadingFallback = true; - } - return result.get(); +const FontData* FontFallbackList::fontDataAt( + const FontDescription& fontDescription, + unsigned realizedFontIndex) const { + if (realizedFontIndex < m_fontList.size()) + return m_fontList[realizedFontIndex] + .get(); // This fallback font is already in our list. + + // Make sure we're not passing in some crazy value here. + ASSERT(realizedFontIndex == m_fontList.size()); + + if (m_familyIndex == cAllFamiliesScanned) + return 0; + + // Ask the font cache for the font data. + // We are obtaining this font for the first time. We keep track of the + // families we've looked at before in |m_familyIndex|, so that we never scan + // the same spot in the list twice. getFontData will adjust our + // |m_familyIndex| as it scans for the right font to make. + ASSERT(FontCache::fontCache()->generation() == m_generation); + RefPtr result = getFontData(fontDescription, m_familyIndex); + if (result) { + m_fontList.append(result); + if (result->isLoadingFallback()) + m_hasLoadingFallback = true; + } + return result.get(); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/fonts/FontFallbackList.h b/sky/engine/platform/fonts/FontFallbackList.h index 5e07b2ee71b43..fdac27e5013bd 100644 --- a/sky/engine/platform/fonts/FontFallbackList.h +++ b/sky/engine/platform/fonts/FontFallbackList.h @@ -35,98 +35,98 @@ class FontDescription; const int cAllFamiliesScanned = -1; class PLATFORM_EXPORT FontFallbackList : public RefCounted { - WTF_MAKE_NONCOPYABLE(FontFallbackList); -public: - typedef HashMap::Hash> GlyphPages; - - class GlyphPagesStateSaver { - public: - GlyphPagesStateSaver(FontFallbackList& fallbackList) - : m_fallbackList(fallbackList) - , m_pages(fallbackList.m_pages) - , m_pageZero(fallbackList.m_pageZero) - { - } - - ~GlyphPagesStateSaver() - { - m_fallbackList.m_pages = m_pages; - m_fallbackList.m_pageZero = m_pageZero; - } - - private: - FontFallbackList& m_fallbackList; - GlyphPages& m_pages; - GlyphPageTreeNode* m_pageZero; - }; - - static PassRefPtr create() { return adoptRef(new FontFallbackList()); } - - ~FontFallbackList() { releaseFontData(); } - void invalidate(PassRefPtr); - - bool isFixedPitch(const FontDescription& fontDescription) const - { - if (m_pitch == UnknownPitch) - determinePitch(fontDescription); - return m_pitch == FixedPitch; - } - void determinePitch(const FontDescription&) const; - - bool loadingCustomFonts() const; - bool shouldSkipDrawing() const; - - FontSelector* fontSelector() const { return m_fontSelector.get(); } - // FIXME: It should be possible to combine fontSelectorVersion and generation. - unsigned fontSelectorVersion() const { return m_fontSelectorVersion; } - unsigned generation() const { return m_generation; } + WTF_MAKE_NONCOPYABLE(FontFallbackList); - WidthCache& widthCache() const { return m_widthCache; } - - const SimpleFontData* primarySimpleFontData(const FontDescription& fontDescription) - { - ASSERT(isMainThread()); - if (!m_cachedPrimarySimpleFontData) - m_cachedPrimarySimpleFontData = determinePrimarySimpleFontData(fontDescription); - return m_cachedPrimarySimpleFontData; - } - const FontData* fontDataAt(const FontDescription&, unsigned index) const; + public: + typedef HashMap::Hash> GlyphPages; - GlyphPageTreeNode* getPageNode(unsigned pageNumber) const - { - return pageNumber ? m_pages.get(pageNumber) : m_pageZero; - } + class GlyphPagesStateSaver { + public: + GlyphPagesStateSaver(FontFallbackList& fallbackList) + : m_fallbackList(fallbackList), + m_pages(fallbackList.m_pages), + m_pageZero(fallbackList.m_pageZero) {} - void setPageNode(unsigned pageNumber, GlyphPageTreeNode* node) - { - if (pageNumber) - m_pages.set(pageNumber, node); - else - m_pageZero = node; + ~GlyphPagesStateSaver() { + m_fallbackList.m_pages = m_pages; + m_fallbackList.m_pageZero = m_pageZero; } -private: - FontFallbackList(); - - PassRefPtr getFontData(const FontDescription&, int& familyIndex) const; - - const SimpleFontData* determinePrimarySimpleFontData(const FontDescription&) const; - - void releaseFontData(); - - mutable Vector, 1> m_fontList; - GlyphPages m_pages; + private: + FontFallbackList& m_fallbackList; + GlyphPages& m_pages; GlyphPageTreeNode* m_pageZero; - mutable const SimpleFontData* m_cachedPrimarySimpleFontData; - RefPtr m_fontSelector; - mutable WidthCache m_widthCache; - unsigned m_fontSelectorVersion; - mutable int m_familyIndex; - unsigned short m_generation; - mutable unsigned m_pitch : 3; // Pitch - mutable bool m_hasLoadingFallback : 1; + }; + + static PassRefPtr create() { + return adoptRef(new FontFallbackList()); + } + + ~FontFallbackList() { releaseFontData(); } + void invalidate(PassRefPtr); + + bool isFixedPitch(const FontDescription& fontDescription) const { + if (m_pitch == UnknownPitch) + determinePitch(fontDescription); + return m_pitch == FixedPitch; + } + void determinePitch(const FontDescription&) const; + + bool loadingCustomFonts() const; + bool shouldSkipDrawing() const; + + FontSelector* fontSelector() const { return m_fontSelector.get(); } + // FIXME: It should be possible to combine fontSelectorVersion and generation. + unsigned fontSelectorVersion() const { return m_fontSelectorVersion; } + unsigned generation() const { return m_generation; } + + WidthCache& widthCache() const { return m_widthCache; } + + const SimpleFontData* primarySimpleFontData( + const FontDescription& fontDescription) { + ASSERT(isMainThread()); + if (!m_cachedPrimarySimpleFontData) + m_cachedPrimarySimpleFontData = + determinePrimarySimpleFontData(fontDescription); + return m_cachedPrimarySimpleFontData; + } + const FontData* fontDataAt(const FontDescription&, unsigned index) const; + + GlyphPageTreeNode* getPageNode(unsigned pageNumber) const { + return pageNumber ? m_pages.get(pageNumber) : m_pageZero; + } + + void setPageNode(unsigned pageNumber, GlyphPageTreeNode* node) { + if (pageNumber) + m_pages.set(pageNumber, node); + else + m_pageZero = node; + } + + private: + FontFallbackList(); + + PassRefPtr getFontData(const FontDescription&, + int& familyIndex) const; + + const SimpleFontData* determinePrimarySimpleFontData( + const FontDescription&) const; + + void releaseFontData(); + + mutable Vector, 1> m_fontList; + GlyphPages m_pages; + GlyphPageTreeNode* m_pageZero; + mutable const SimpleFontData* m_cachedPrimarySimpleFontData; + RefPtr m_fontSelector; + mutable WidthCache m_widthCache; + unsigned m_fontSelectorVersion; + mutable int m_familyIndex; + unsigned short m_generation; + mutable unsigned m_pitch : 3; // Pitch + mutable bool m_hasLoadingFallback : 1; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_FONTS_FONTFALLBACKLIST_H_ diff --git a/sky/engine/platform/fonts/FontFamily.cpp b/sky/engine/platform/fonts/FontFamily.cpp index 192b1c355aea6..728899c1cca9f 100644 --- a/sky/engine/platform/fonts/FontFamily.cpp +++ b/sky/engine/platform/fonts/FontFamily.cpp @@ -27,19 +27,19 @@ namespace blink { -bool operator==(const FontFamily& a, const FontFamily& b) -{ - if (a.family() != b.family()) - return false; - const FontFamily* ap; - const FontFamily* bp; - for (ap = a.next(), bp = b.next(); ap != bp; ap = ap->next(), bp = bp->next()) { - if (!ap || !bp) - return false; - if (ap->family() != bp->family()) - return false; - } - return true; +bool operator==(const FontFamily& a, const FontFamily& b) { + if (a.family() != b.family()) + return false; + const FontFamily* ap; + const FontFamily* bp; + for (ap = a.next(), bp = b.next(); ap != bp; + ap = ap->next(), bp = bp->next()) { + if (!ap || !bp) + return false; + if (ap->family() != bp->family()) + return false; + } + return true; } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/fonts/FontFamily.h b/sky/engine/platform/fonts/FontFamily.h index 5bf564244b58f..f2ebe66cdd66e 100644 --- a/sky/engine/platform/fonts/FontFamily.h +++ b/sky/engine/platform/fonts/FontFamily.h @@ -36,60 +36,59 @@ namespace blink { class SharedFontFamily; class PLATFORM_EXPORT FontFamily { -public: - FontFamily() { } - ~FontFamily(); + public: + FontFamily() {} + ~FontFamily(); - void setFamily(const AtomicString& family) { m_family = family; } - const AtomicString& family() const { return m_family; } - bool familyIsEmpty() const { return m_family.isEmpty(); } + void setFamily(const AtomicString& family) { m_family = family; } + const AtomicString& family() const { return m_family; } + bool familyIsEmpty() const { return m_family.isEmpty(); } - const FontFamily* next() const; + const FontFamily* next() const; - void appendFamily(PassRefPtr); - PassRefPtr releaseNext(); + void appendFamily(PassRefPtr); + PassRefPtr releaseNext(); -private: - AtomicString m_family; - RefPtr m_next; + private: + AtomicString m_family; + RefPtr m_next; }; -class PLATFORM_EXPORT SharedFontFamily : public FontFamily, public RefCounted { -public: - static PassRefPtr create() - { - return adoptRef(new SharedFontFamily); - } +class PLATFORM_EXPORT SharedFontFamily : public FontFamily, + public RefCounted { + public: + static PassRefPtr create() { + return adoptRef(new SharedFontFamily); + } -private: - SharedFontFamily() { } + private: + SharedFontFamily() {} }; PLATFORM_EXPORT bool operator==(const FontFamily&, const FontFamily&); -inline bool operator!=(const FontFamily& a, const FontFamily& b) { return !(a == b); } +inline bool operator!=(const FontFamily& a, const FontFamily& b) { + return !(a == b); +} -inline FontFamily::~FontFamily() -{ - RefPtr reaper = m_next.release(); - while (reaper && reaper->hasOneRef()) - reaper = reaper->releaseNext(); // implicitly protects reaper->next, then derefs reaper +inline FontFamily::~FontFamily() { + RefPtr reaper = m_next.release(); + while (reaper && reaper->hasOneRef()) + reaper = reaper->releaseNext(); // implicitly protects reaper->next, then + // derefs reaper } -inline const FontFamily* FontFamily::next() const -{ - return m_next.get(); +inline const FontFamily* FontFamily::next() const { + return m_next.get(); } -inline void FontFamily::appendFamily(PassRefPtr family) -{ - m_next = family; +inline void FontFamily::appendFamily(PassRefPtr family) { + m_next = family; } -inline PassRefPtr FontFamily::releaseNext() -{ - return m_next.release(); +inline PassRefPtr FontFamily::releaseNext() { + return m_next.release(); } -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_FONTS_FONTFAMILY_H_ diff --git a/sky/engine/platform/fonts/FontFeatureSettings.cpp b/sky/engine/platform/fonts/FontFeatureSettings.cpp index 6712dde4ffeb2..2ff6975f98753 100644 --- a/sky/engine/platform/fonts/FontFeatureSettings.cpp +++ b/sky/engine/platform/fonts/FontFeatureSettings.cpp @@ -28,18 +28,12 @@ namespace blink { FontFeature::FontFeature(const AtomicString& tag, int value) - : m_tag(tag) - , m_value(value) -{ -} + : m_tag(tag), m_value(value) {} -bool FontFeature::operator==(const FontFeature& other) -{ - return m_tag == other.m_tag && m_value == other.m_value; +bool FontFeature::operator==(const FontFeature& other) { + return m_tag == other.m_tag && m_value == other.m_value; } -FontFeatureSettings::FontFeatureSettings() -{ -} +FontFeatureSettings::FontFeatureSettings() {} -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/fonts/FontFeatureSettings.h b/sky/engine/platform/fonts/FontFeatureSettings.h index 43d47e1c997ea..7de8f7074db27 100644 --- a/sky/engine/platform/fonts/FontFeatureSettings.h +++ b/sky/engine/platform/fonts/FontFeatureSettings.h @@ -36,34 +36,34 @@ namespace blink { class PLATFORM_EXPORT FontFeature { -public: - FontFeature(const AtomicString& tag, int value); - bool operator==(const FontFeature&); + public: + FontFeature(const AtomicString& tag, int value); + bool operator==(const FontFeature&); - const AtomicString& tag() const { return m_tag; } - int value() const { return m_value; } + const AtomicString& tag() const { return m_tag; } + int value() const { return m_value; } -private: - AtomicString m_tag; - const int m_value; + private: + AtomicString m_tag; + const int m_value; }; -class PLATFORM_EXPORT FontFeatureSettings : public RefCounted { -public: - static PassRefPtr create() - { - return adoptRef(new FontFeatureSettings()); - } - void append(const FontFeature& feature) { m_list.append(feature); } - size_t size() const { return m_list.size(); } - const FontFeature& operator[](int index) const { return m_list[index]; } - const FontFeature& at(size_t index) const { return m_list.at(index); } +class PLATFORM_EXPORT FontFeatureSettings + : public RefCounted { + public: + static PassRefPtr create() { + return adoptRef(new FontFeatureSettings()); + } + void append(const FontFeature& feature) { m_list.append(feature); } + size_t size() const { return m_list.size(); } + const FontFeature& operator[](int index) const { return m_list[index]; } + const FontFeature& at(size_t index) const { return m_list.at(index); } -private: - FontFeatureSettings(); - Vector m_list; + private: + FontFeatureSettings(); + Vector m_list; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_FONTS_FONTFEATURESETTINGS_H_ diff --git a/sky/engine/platform/fonts/FontMetrics.h b/sky/engine/platform/fonts/FontMetrics.h index 25ad47441291d..28f8b77bec8d2 100644 --- a/sky/engine/platform/fonts/FontMetrics.h +++ b/sky/engine/platform/fonts/FontMetrics.h @@ -28,142 +28,134 @@ namespace blink { const unsigned gDefaultUnitsPerEm = 1000; class FontMetrics { -public: - FontMetrics() - : m_unitsPerEm(gDefaultUnitsPerEm) - , m_ascent(0) - , m_descent(0) - , m_lineGap(0) - , m_lineSpacing(0) - , m_xHeight(0) - , m_zeroWidth(0) - , m_underlinethickness(0) - , m_underlinePosition(0) - , m_hasXHeight(false) - , m_hasZeroWidth(false) - { - } - - unsigned unitsPerEm() const { return m_unitsPerEm; } - void setUnitsPerEm(unsigned unitsPerEm) { m_unitsPerEm = unitsPerEm; } - - float floatAscent(FontBaseline baselineType = AlphabeticBaseline) const - { - if (baselineType == AlphabeticBaseline) - return m_ascent; - return floatHeight() / 2; - } - - void setAscent(float ascent) { m_ascent = ascent; } - - float floatDescent(FontBaseline baselineType = AlphabeticBaseline) const - { - if (baselineType == AlphabeticBaseline) - return m_descent; - return floatHeight() / 2; - } - - void setDescent(float descent) { m_descent = descent; } - - float floatHeight(FontBaseline baselineType = AlphabeticBaseline) const - { - return floatAscent(baselineType) + floatDescent(baselineType); - } - - float floatLineGap() const { return m_lineGap; } - void setLineGap(float lineGap) { m_lineGap = lineGap; } - - float floatLineSpacing() const { return m_lineSpacing; } - void setLineSpacing(float lineSpacing) { m_lineSpacing = lineSpacing; } - - float xHeight() const { return m_xHeight; } - void setXHeight(float xHeight) - { - m_xHeight = xHeight; - m_hasXHeight = true; - } - - bool hasXHeight() const { return m_hasXHeight && m_xHeight > 0; } - void setHasXHeight(bool hasXHeight) { m_hasXHeight = hasXHeight; } - - // Integer variants of certain metrics, used for HTML rendering. - int ascent(FontBaseline baselineType = AlphabeticBaseline) const - { - if (baselineType == AlphabeticBaseline) - return lroundf(m_ascent); - return height() - height() / 2; - } - - int descent(FontBaseline baselineType = AlphabeticBaseline) const - { - if (baselineType == AlphabeticBaseline) - return lroundf(m_descent); - return height() / 2; - } - - int height(FontBaseline baselineType = AlphabeticBaseline) const - { - return ascent(baselineType) + descent(baselineType); - } - - int lineGap() const { return lroundf(m_lineGap); } - int lineSpacing() const { return lroundf(m_lineSpacing); } - - bool hasIdenticalAscentDescentAndLineGap(const FontMetrics& other) const - { - return ascent() == other.ascent() && descent() == other.descent() && lineGap() == other.lineGap(); - } - - float zeroWidth() const { return m_zeroWidth; } - void setZeroWidth(float zeroWidth) - { - m_zeroWidth = zeroWidth; - m_hasZeroWidth = true; - } - - bool hasZeroWidth() const { return m_hasZeroWidth; } - void setHasZeroWidth(bool hasZeroWidth) { m_hasZeroWidth = hasZeroWidth; } - - float underlineThickness() const { return m_underlinethickness; } - void setUnderlineThickness(float underlineThickness) { m_underlinethickness = underlineThickness; } - - float underlinePosition() const { return m_underlinePosition; } - void setUnderlinePosition(float underlinePosition) { m_underlinePosition = underlinePosition; } - -private: - friend class SimpleFontData; - - void reset() - { - m_unitsPerEm = gDefaultUnitsPerEm; - m_ascent = 0; - m_descent = 0; - m_lineGap = 0; - m_lineSpacing = 0; - m_xHeight = 0; - m_hasXHeight = false; - m_underlinethickness = 0; - m_underlinePosition = 0; - } - - unsigned m_unitsPerEm; - float m_ascent; - float m_descent; - float m_lineGap; - float m_lineSpacing; - float m_xHeight; - float m_zeroWidth; - float m_underlinethickness; - float m_underlinePosition; - bool m_hasXHeight; - bool m_hasZeroWidth; + public: + FontMetrics() + : m_unitsPerEm(gDefaultUnitsPerEm), + m_ascent(0), + m_descent(0), + m_lineGap(0), + m_lineSpacing(0), + m_xHeight(0), + m_zeroWidth(0), + m_underlinethickness(0), + m_underlinePosition(0), + m_hasXHeight(false), + m_hasZeroWidth(false) {} + + unsigned unitsPerEm() const { return m_unitsPerEm; } + void setUnitsPerEm(unsigned unitsPerEm) { m_unitsPerEm = unitsPerEm; } + + float floatAscent(FontBaseline baselineType = AlphabeticBaseline) const { + if (baselineType == AlphabeticBaseline) + return m_ascent; + return floatHeight() / 2; + } + + void setAscent(float ascent) { m_ascent = ascent; } + + float floatDescent(FontBaseline baselineType = AlphabeticBaseline) const { + if (baselineType == AlphabeticBaseline) + return m_descent; + return floatHeight() / 2; + } + + void setDescent(float descent) { m_descent = descent; } + + float floatHeight(FontBaseline baselineType = AlphabeticBaseline) const { + return floatAscent(baselineType) + floatDescent(baselineType); + } + + float floatLineGap() const { return m_lineGap; } + void setLineGap(float lineGap) { m_lineGap = lineGap; } + + float floatLineSpacing() const { return m_lineSpacing; } + void setLineSpacing(float lineSpacing) { m_lineSpacing = lineSpacing; } + + float xHeight() const { return m_xHeight; } + void setXHeight(float xHeight) { + m_xHeight = xHeight; + m_hasXHeight = true; + } + + bool hasXHeight() const { return m_hasXHeight && m_xHeight > 0; } + void setHasXHeight(bool hasXHeight) { m_hasXHeight = hasXHeight; } + + // Integer variants of certain metrics, used for HTML rendering. + int ascent(FontBaseline baselineType = AlphabeticBaseline) const { + if (baselineType == AlphabeticBaseline) + return lroundf(m_ascent); + return height() - height() / 2; + } + + int descent(FontBaseline baselineType = AlphabeticBaseline) const { + if (baselineType == AlphabeticBaseline) + return lroundf(m_descent); + return height() / 2; + } + + int height(FontBaseline baselineType = AlphabeticBaseline) const { + return ascent(baselineType) + descent(baselineType); + } + + int lineGap() const { return lroundf(m_lineGap); } + int lineSpacing() const { return lroundf(m_lineSpacing); } + + bool hasIdenticalAscentDescentAndLineGap(const FontMetrics& other) const { + return ascent() == other.ascent() && descent() == other.descent() && + lineGap() == other.lineGap(); + } + + float zeroWidth() const { return m_zeroWidth; } + void setZeroWidth(float zeroWidth) { + m_zeroWidth = zeroWidth; + m_hasZeroWidth = true; + } + + bool hasZeroWidth() const { return m_hasZeroWidth; } + void setHasZeroWidth(bool hasZeroWidth) { m_hasZeroWidth = hasZeroWidth; } + + float underlineThickness() const { return m_underlinethickness; } + void setUnderlineThickness(float underlineThickness) { + m_underlinethickness = underlineThickness; + } + + float underlinePosition() const { return m_underlinePosition; } + void setUnderlinePosition(float underlinePosition) { + m_underlinePosition = underlinePosition; + } + + private: + friend class SimpleFontData; + + void reset() { + m_unitsPerEm = gDefaultUnitsPerEm; + m_ascent = 0; + m_descent = 0; + m_lineGap = 0; + m_lineSpacing = 0; + m_xHeight = 0; + m_hasXHeight = false; + m_underlinethickness = 0; + m_underlinePosition = 0; + } + + unsigned m_unitsPerEm; + float m_ascent; + float m_descent; + float m_lineGap; + float m_lineSpacing; + float m_xHeight; + float m_zeroWidth; + float m_underlinethickness; + float m_underlinePosition; + bool m_hasXHeight; + bool m_hasZeroWidth; }; -inline float scaleEmToUnits(float x, unsigned unitsPerEm) -{ - return unitsPerEm ? x / unitsPerEm : x; +inline float scaleEmToUnits(float x, unsigned unitsPerEm) { + return unitsPerEm ? x / unitsPerEm : x; } -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_FONTS_FONTMETRICS_H_ diff --git a/sky/engine/platform/fonts/FontOrientation.h b/sky/engine/platform/fonts/FontOrientation.h index f41dd8bb54fa2..7326de32fd982 100644 --- a/sky/engine/platform/fonts/FontOrientation.h +++ b/sky/engine/platform/fonts/FontOrientation.h @@ -30,6 +30,6 @@ namespace blink { enum FontOrientation { Horizontal, Vertical }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_FONTS_FONTORIENTATION_H_ diff --git a/sky/engine/platform/fonts/FontPlatformData.cpp b/sky/engine/platform/fonts/FontPlatformData.cpp index a843a6bcd33e7..eb9fac8a77fc1 100644 --- a/sky/engine/platform/fonts/FontPlatformData.cpp +++ b/sky/engine/platform/fonts/FontPlatformData.cpp @@ -37,141 +37,132 @@ namespace blink { FontPlatformData::FontPlatformData(WTF::HashTableDeletedValueType) - : m_textSize(0) - , m_syntheticBold(false) - , m_syntheticItalic(false) - , m_orientation(Horizontal) - , m_isHashTableDeletedValue(true) -{ -} + : m_textSize(0), + m_syntheticBold(false), + m_syntheticItalic(false), + m_orientation(Horizontal), + m_isHashTableDeletedValue(true) {} FontPlatformData::FontPlatformData() - : m_textSize(0) - , m_syntheticBold(false) - , m_syntheticItalic(false) - , m_orientation(Horizontal) - , m_isHashTableDeletedValue(false) -{ -} - -FontPlatformData::FontPlatformData(float textSize, bool syntheticBold, bool syntheticItalic) - : m_textSize(textSize) - , m_syntheticBold(syntheticBold) - , m_syntheticItalic(syntheticItalic) - , m_orientation(Horizontal) - , m_isHashTableDeletedValue(false) -{ -} + : m_textSize(0), + m_syntheticBold(false), + m_syntheticItalic(false), + m_orientation(Horizontal), + m_isHashTableDeletedValue(false) {} + +FontPlatformData::FontPlatformData(float textSize, + bool syntheticBold, + bool syntheticItalic) + : m_textSize(textSize), + m_syntheticBold(syntheticBold), + m_syntheticItalic(syntheticItalic), + m_orientation(Horizontal), + m_isHashTableDeletedValue(false) {} FontPlatformData::FontPlatformData(const FontPlatformData& src) - : m_typeface(src.m_typeface) - , m_family(src.m_family) - , m_textSize(src.m_textSize) - , m_syntheticBold(src.m_syntheticBold) - , m_syntheticItalic(src.m_syntheticItalic) - , m_orientation(src.m_orientation) - , m_style(src.m_style) - , m_harfBuzzFace(nullptr) - , m_isHashTableDeletedValue(false) -{ -} - -FontPlatformData::FontPlatformData(sk_sp tf, const char* family, float textSize, bool syntheticBold, bool syntheticItalic, FontOrientation orientation, bool subpixelTextPosition) - : m_typeface(tf) - , m_family(family) - , m_textSize(textSize) - , m_syntheticBold(syntheticBold) - , m_syntheticItalic(syntheticItalic) - , m_orientation(orientation) - , m_isHashTableDeletedValue(false) -{ - querySystemForRenderStyle(subpixelTextPosition); + : m_typeface(src.m_typeface), + m_family(src.m_family), + m_textSize(src.m_textSize), + m_syntheticBold(src.m_syntheticBold), + m_syntheticItalic(src.m_syntheticItalic), + m_orientation(src.m_orientation), + m_style(src.m_style), + m_harfBuzzFace(nullptr), + m_isHashTableDeletedValue(false) {} + +FontPlatformData::FontPlatformData(sk_sp tf, + const char* family, + float textSize, + bool syntheticBold, + bool syntheticItalic, + FontOrientation orientation, + bool subpixelTextPosition) + : m_typeface(tf), + m_family(family), + m_textSize(textSize), + m_syntheticBold(syntheticBold), + m_syntheticItalic(syntheticItalic), + m_orientation(orientation), + m_isHashTableDeletedValue(false) { + querySystemForRenderStyle(subpixelTextPosition); } FontPlatformData::FontPlatformData(const FontPlatformData& src, float textSize) - : m_typeface(src.m_typeface) - , m_family(src.m_family) - , m_textSize(textSize) - , m_syntheticBold(src.m_syntheticBold) - , m_syntheticItalic(src.m_syntheticItalic) - , m_orientation(src.m_orientation) - , m_harfBuzzFace(nullptr) - , m_isHashTableDeletedValue(false) -{ - querySystemForRenderStyle(FontDescription::subpixelPositioning()); -} - -FontPlatformData::~FontPlatformData() -{ + : m_typeface(src.m_typeface), + m_family(src.m_family), + m_textSize(textSize), + m_syntheticBold(src.m_syntheticBold), + m_syntheticItalic(src.m_syntheticItalic), + m_orientation(src.m_orientation), + m_harfBuzzFace(nullptr), + m_isHashTableDeletedValue(false) { + querySystemForRenderStyle(FontDescription::subpixelPositioning()); } -FontPlatformData& FontPlatformData::operator=(const FontPlatformData& src) -{ - m_typeface = src.m_typeface; - m_family = src.m_family; - m_textSize = src.m_textSize; - m_syntheticBold = src.m_syntheticBold; - m_syntheticItalic = src.m_syntheticItalic; - m_harfBuzzFace = nullptr; - m_orientation = src.m_orientation; - m_style = src.m_style; - return *this; +FontPlatformData::~FontPlatformData() {} + +FontPlatformData& FontPlatformData::operator=(const FontPlatformData& src) { + m_typeface = src.m_typeface; + m_family = src.m_family; + m_textSize = src.m_textSize; + m_syntheticBold = src.m_syntheticBold; + m_syntheticItalic = src.m_syntheticItalic; + m_harfBuzzFace = nullptr; + m_orientation = src.m_orientation; + m_style = src.m_style; + return *this; } #ifndef NDEBUG -String FontPlatformData::description() const -{ - return String(); +String FontPlatformData::description() const { + return String(); } #endif -SkFontID FontPlatformData::uniqueID() const -{ - return m_typeface->uniqueID(); +SkFontID FontPlatformData::uniqueID() const { + return m_typeface->uniqueID(); } -String FontPlatformData::fontFamilyName() const -{ - // FIXME(crbug.com/326582): come up with a proper way of handling SVG. - if (!this->typeface()) - return ""; - SkTypeface::LocalizedStrings* fontFamilyIterator = this->typeface()->createFamilyNameIterator(); - SkTypeface::LocalizedString localizedString; - while (fontFamilyIterator->next(&localizedString) && !localizedString.fString.size()) { } - fontFamilyIterator->unref(); - return String(localizedString.fString.c_str()); +String FontPlatformData::fontFamilyName() const { + // FIXME(crbug.com/326582): come up with a proper way of handling SVG. + if (!this->typeface()) + return ""; + SkTypeface::LocalizedStrings* fontFamilyIterator = + this->typeface()->createFamilyNameIterator(); + SkTypeface::LocalizedString localizedString; + while (fontFamilyIterator->next(&localizedString) && + !localizedString.fString.size()) { + } + fontFamilyIterator->unref(); + return String(localizedString.fString.c_str()); } -bool FontPlatformData::operator==(const FontPlatformData& a) const -{ - // If either of the typeface pointers are null then we test for pointer - // equality. Otherwise, we call SkTypeface::Equal on the valid pointers. - bool typefacesEqual; - if (!m_typeface || !a.m_typeface) - typefacesEqual = m_typeface == a.m_typeface; - else - typefacesEqual = SkTypeface::Equal(m_typeface.get(), a.m_typeface.get()); - - return typefacesEqual - && m_textSize == a.m_textSize - && m_syntheticBold == a.m_syntheticBold - && m_syntheticItalic == a.m_syntheticItalic - && m_orientation == a.m_orientation - && m_style == a.m_style - && m_isHashTableDeletedValue == a.m_isHashTableDeletedValue; +bool FontPlatformData::operator==(const FontPlatformData& a) const { + // If either of the typeface pointers are null then we test for pointer + // equality. Otherwise, we call SkTypeface::Equal on the valid pointers. + bool typefacesEqual; + if (!m_typeface || !a.m_typeface) + typefacesEqual = m_typeface == a.m_typeface; + else + typefacesEqual = SkTypeface::Equal(m_typeface.get(), a.m_typeface.get()); + + return typefacesEqual && m_textSize == a.m_textSize && + m_syntheticBold == a.m_syntheticBold && + m_syntheticItalic == a.m_syntheticItalic && + m_orientation == a.m_orientation && m_style == a.m_style && + m_isHashTableDeletedValue == a.m_isHashTableDeletedValue; } -bool FontPlatformData::isFixedPitch() const -{ - return typeface() && typeface()->isFixedPitch(); +bool FontPlatformData::isFixedPitch() const { + return typeface() && typeface()->isFixedPitch(); } -HarfBuzzFace* FontPlatformData::harfBuzzFace() const -{ - if (!m_harfBuzzFace) - m_harfBuzzFace = HarfBuzzFace::create(const_cast(this), uniqueID()); +HarfBuzzFace* FontPlatformData::harfBuzzFace() const { + if (!m_harfBuzzFace) + m_harfBuzzFace = + HarfBuzzFace::create(const_cast(this), uniqueID()); - return m_harfBuzzFace.get(); + return m_harfBuzzFace.get(); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/fonts/FontPlatformData.h b/sky/engine/platform/fonts/FontPlatformData.h index 35ddc969d1fb8..7151fcf8b4522 100644 --- a/sky/engine/platform/fonts/FontPlatformData.h +++ b/sky/engine/platform/fonts/FontPlatformData.h @@ -52,72 +52,83 @@ class GraphicsContext; class HarfBuzzFace; class PLATFORM_EXPORT FontPlatformData { -public: - // 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 0 one (created with the empty constructor), so we can't just - // set everything to 0. - FontPlatformData(WTF::HashTableDeletedValueType); - FontPlatformData(); - FontPlatformData(float textSize, bool syntheticBold, bool syntheticItalic); - FontPlatformData(const FontPlatformData&); - FontPlatformData(sk_sp, const char* name, float textSize, bool syntheticBold, bool syntheticItalic, FontOrientation = Horizontal, bool subpixelTextPosition = defaultUseSubpixelPositioning()); - FontPlatformData(const FontPlatformData& src, float textSize); - ~FontPlatformData(); - - String fontFamilyName() const; - float size() const { return m_textSize; } - bool isFixedPitch() const; - - SkTypeface* typeface() const { return m_typeface.get(); } - HarfBuzzFace* harfBuzzFace() const; - SkFontID uniqueID() const; - unsigned hash() const; - - FontOrientation orientation() const { return m_orientation; } - void setOrientation(FontOrientation orientation) { m_orientation = orientation; } - void setSyntheticBold(bool syntheticBold) { m_syntheticBold = syntheticBold; } - void setSyntheticItalic(bool syntheticItalic) { m_syntheticItalic = syntheticItalic; } - bool operator==(const FontPlatformData&) const; - FontPlatformData& operator=(const FontPlatformData&); - bool isHashTableDeletedValue() const { return m_isHashTableDeletedValue; } - bool fontContainsCharacter(UChar32 character); + public: + // 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 0 one (created with the empty constructor), so we can't just + // set everything to 0. + FontPlatformData(WTF::HashTableDeletedValueType); + FontPlatformData(); + FontPlatformData(float textSize, bool syntheticBold, bool syntheticItalic); + FontPlatformData(const FontPlatformData&); + FontPlatformData(sk_sp, + const char* name, + float textSize, + bool syntheticBold, + bool syntheticItalic, + FontOrientation = Horizontal, + bool subpixelTextPosition = defaultUseSubpixelPositioning()); + FontPlatformData(const FontPlatformData& src, float textSize); + ~FontPlatformData(); + + String fontFamilyName() const; + float size() const { return m_textSize; } + bool isFixedPitch() const; + + SkTypeface* typeface() const { return m_typeface.get(); } + HarfBuzzFace* harfBuzzFace() const; + SkFontID uniqueID() const; + unsigned hash() const; + + FontOrientation orientation() const { return m_orientation; } + void setOrientation(FontOrientation orientation) { + m_orientation = orientation; + } + void setSyntheticBold(bool syntheticBold) { m_syntheticBold = syntheticBold; } + void setSyntheticItalic(bool syntheticItalic) { + m_syntheticItalic = syntheticItalic; + } + bool operator==(const FontPlatformData&) const; + FontPlatformData& operator=(const FontPlatformData&); + bool isHashTableDeletedValue() const { return m_isHashTableDeletedValue; } + bool fontContainsCharacter(UChar32 character); #if ENABLE(OPENTYPE_VERTICAL) - PassRefPtr verticalData() const; - PassRefPtr openTypeTable(uint32_t table) const; + PassRefPtr verticalData() const; + PassRefPtr openTypeTable(uint32_t table) const; #endif #ifndef NDEBUG - String description() const; + String description() const; #endif - // The returned styles are all actual styles without FontRenderStyle::NoPreference. - const FontRenderStyle& fontRenderStyle() const { return m_style; } - void setupPaint(SkPaint*, GraphicsContext* = 0) const; - - static void setHinting(SkPaint::Hinting); - static void setAutoHint(bool); - static void setUseBitmaps(bool); - static void setAntiAlias(bool); - static void setSubpixelRendering(bool); - -private: - bool static defaultUseSubpixelPositioning(); - void querySystemForRenderStyle(bool useSkiaSubpixelPositioning); - - sk_sp m_typeface; - CString m_family; - float m_textSize; - bool m_syntheticBold; - bool m_syntheticItalic; - FontOrientation m_orientation; - FontRenderStyle m_style; - mutable RefPtr m_harfBuzzFace; - bool m_isHashTableDeletedValue; + // The returned styles are all actual styles without + // FontRenderStyle::NoPreference. + const FontRenderStyle& fontRenderStyle() const { return m_style; } + void setupPaint(SkPaint*, GraphicsContext* = 0) const; + + static void setHinting(SkPaint::Hinting); + static void setAutoHint(bool); + static void setUseBitmaps(bool); + static void setAntiAlias(bool); + static void setSubpixelRendering(bool); + + private: + bool static defaultUseSubpixelPositioning(); + void querySystemForRenderStyle(bool useSkiaSubpixelPositioning); + + sk_sp m_typeface; + CString m_family; + float m_textSize; + bool m_syntheticBold; + bool m_syntheticItalic; + FontOrientation m_orientation; + FontRenderStyle m_style; + mutable RefPtr m_harfBuzzFace; + bool m_isHashTableDeletedValue; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_FONTS_FONTPLATFORMDATA_H_ diff --git a/sky/engine/platform/fonts/FontPlatformFeatures.h b/sky/engine/platform/fonts/FontPlatformFeatures.h index 4c8234c0fc750..23ae003ebdc20 100644 --- a/sky/engine/platform/fonts/FontPlatformFeatures.h +++ b/sky/engine/platform/fonts/FontPlatformFeatures.h @@ -36,13 +36,13 @@ namespace blink { class FontPlatformFeatures { -public: - static bool canExpandAroundIdeographsInComplexText(); + public: + static bool canExpandAroundIdeographsInComplexText(); -private: - FontPlatformFeatures(); + private: + FontPlatformFeatures(); }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_FONTS_FONTPLATFORMFEATURES_H_ diff --git a/sky/engine/platform/fonts/FontRenderStyle.h b/sky/engine/platform/fonts/FontRenderStyle.h index bac40e17c4da7..752185ef33c0d 100644 --- a/sky/engine/platform/fonts/FontRenderStyle.h +++ b/sky/engine/platform/fonts/FontRenderStyle.h @@ -35,43 +35,42 @@ namespace blink { // FontRenderStyle describes the user's preferences for rendering a font at a // given size. struct FontRenderStyle { - enum { - NoPreference = 2, - }; + enum { + NoPreference = 2, + }; - FontRenderStyle() - : useBitmaps(0) - , useAutoHint(0) - , useHinting(0) - , hintStyle(0) - , useAntiAlias(0) - , useSubpixelRendering(0) - , useSubpixelPositioning(0) { } + FontRenderStyle() + : useBitmaps(0), + useAutoHint(0), + useHinting(0), + hintStyle(0), + useAntiAlias(0), + useSubpixelRendering(0), + useSubpixelPositioning(0) {} - bool operator==(const FontRenderStyle& a) const - { - return useBitmaps == a.useBitmaps - && useAutoHint == a.useAutoHint - && useHinting == a.useHinting - && hintStyle == a.hintStyle - && useAntiAlias == a.useAntiAlias - && useSubpixelRendering == a.useSubpixelRendering - && useSubpixelPositioning == a.useSubpixelPositioning; - } + bool operator==(const FontRenderStyle& a) const { + return useBitmaps == a.useBitmaps && useAutoHint == a.useAutoHint && + useHinting == a.useHinting && hintStyle == a.hintStyle && + useAntiAlias == a.useAntiAlias && + useSubpixelRendering == a.useSubpixelRendering && + useSubpixelPositioning == a.useSubpixelPositioning; + } - // Each of the use* members below can take one of three values: - // 0: off - // 1: on - // NoPreference: no preference expressed - char useBitmaps; // use embedded bitmap strike if possible - char useAutoHint; // use 'auto' hinting (FreeType specific) - char useHinting; // hint glyphs to the pixel grid - char hintStyle; // level of hinting, 0..3 - char useAntiAlias; // antialias glyph shapes - char useSubpixelRendering; // use subpixel rendering (partially-filled pixels) - char useSubpixelPositioning; // use subpixel positioning (fractional X positions for glyphs) + // Each of the use* members below can take one of three values: + // 0: off + // 1: on + // NoPreference: no preference expressed + char useBitmaps; // use embedded bitmap strike if possible + char useAutoHint; // use 'auto' hinting (FreeType specific) + char useHinting; // hint glyphs to the pixel grid + char hintStyle; // level of hinting, 0..3 + char useAntiAlias; // antialias glyph shapes + char + useSubpixelRendering; // use subpixel rendering (partially-filled pixels) + char useSubpixelPositioning; // use subpixel positioning (fractional X + // positions for glyphs) }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_FONTS_FONTRENDERSTYLE_H_ diff --git a/sky/engine/platform/fonts/FontSelector.h b/sky/engine/platform/fonts/FontSelector.h index b87d4d35d4a55..2c7631454a515 100644 --- a/sky/engine/platform/fonts/FontSelector.h +++ b/sky/engine/platform/fonts/FontSelector.h @@ -37,14 +37,17 @@ class FontData; class FontDescription; class FontSelector : public FontCacheClient { -public: - virtual ~FontSelector() { } - virtual PassRefPtr getFontData(const FontDescription&, const AtomicString& familyName) = 0; - virtual void willUseFontData(const FontDescription&, const AtomicString& familyName, UChar32) = 0; - - virtual unsigned version() const = 0; + public: + virtual ~FontSelector() {} + virtual PassRefPtr getFontData(const FontDescription&, + const AtomicString& familyName) = 0; + virtual void willUseFontData(const FontDescription&, + const AtomicString& familyName, + UChar32) = 0; + + virtual unsigned version() const = 0; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_FONTS_FONTSELECTOR_H_ diff --git a/sky/engine/platform/fonts/FontSmoothingMode.h b/sky/engine/platform/fonts/FontSmoothingMode.h index 1e1121265e054..aa7449f9fdf8a 100644 --- a/sky/engine/platform/fonts/FontSmoothingMode.h +++ b/sky/engine/platform/fonts/FontSmoothingMode.h @@ -28,8 +28,13 @@ namespace blink { -enum FontSmoothingMode { AutoSmoothing, NoSmoothing, Antialiased, SubpixelAntialiased }; +enum FontSmoothingMode { + AutoSmoothing, + NoSmoothing, + Antialiased, + SubpixelAntialiased +}; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_FONTS_FONTSMOOTHINGMODE_H_ diff --git a/sky/engine/platform/fonts/FontTest.cpp b/sky/engine/platform/fonts/FontTest.cpp index 256d7bfd1b206..39a470f5801a6 100644 --- a/sky/engine/platform/fonts/FontTest.cpp +++ b/sky/engine/platform/fonts/FontTest.cpp @@ -25,7 +25,6 @@ // Tests for the Font class. - #include "flutter/sky/engine/platform/fonts/Character.h" #include "flutter/sky/engine/platform/fonts/Font.h" @@ -33,333 +32,332 @@ namespace blink { -static void TestSpecificUCharRange(UChar rangeStart, UChar rangeEnd) -{ - UChar below[1]; - UChar start[1]; - UChar midway[1]; - UChar end[1]; - UChar above[1]; - - below[0] = rangeStart - 1; - start[0] = rangeStart; - midway[0] = ((int)rangeStart + (int)rangeEnd) / 2; - end[0] = rangeEnd; - above[0] = rangeEnd + 1; - - EXPECT_EQ(SimplePath, Character::characterRangeCodePath(below, 1)); - EXPECT_EQ(ComplexPath, Character::characterRangeCodePath(start, 1)); - EXPECT_EQ(ComplexPath, Character::characterRangeCodePath(midway, 1)); - EXPECT_EQ(ComplexPath, Character::characterRangeCodePath(end, 1)); - EXPECT_EQ(SimplePath, Character::characterRangeCodePath(above, 1)); +static void TestSpecificUCharRange(UChar rangeStart, UChar rangeEnd) { + UChar below[1]; + UChar start[1]; + UChar midway[1]; + UChar end[1]; + UChar above[1]; + + below[0] = rangeStart - 1; + start[0] = rangeStart; + midway[0] = ((int)rangeStart + (int)rangeEnd) / 2; + end[0] = rangeEnd; + above[0] = rangeEnd + 1; + + EXPECT_EQ(SimplePath, Character::characterRangeCodePath(below, 1)); + EXPECT_EQ(ComplexPath, Character::characterRangeCodePath(start, 1)); + EXPECT_EQ(ComplexPath, Character::characterRangeCodePath(midway, 1)); + EXPECT_EQ(ComplexPath, Character::characterRangeCodePath(end, 1)); + EXPECT_EQ(SimplePath, Character::characterRangeCodePath(above, 1)); } -TEST(FontTest, TestCharacterRangeCodePath) -{ - static UChar c1[] = { 0x0 }; - EXPECT_EQ(SimplePath, Character::characterRangeCodePath(c1, 1)); - - TestSpecificUCharRange(0x2E5, 0x2E9); - TestSpecificUCharRange(0x300, 0x36F); - TestSpecificUCharRange(0x0591, 0x05BD); - TestSpecificUCharRange(0x05BF, 0x05CF); - TestSpecificUCharRange(0x0600, 0x109F); - TestSpecificUCharRange(0x1100, 0x11FF); - TestSpecificUCharRange(0x135D, 0x135F); - TestSpecificUCharRange(0x1700, 0x18AF); - TestSpecificUCharRange(0x1900, 0x194F); - TestSpecificUCharRange(0x1980, 0x19DF); - TestSpecificUCharRange(0x1A00, 0x1CFF); - - static UChar c2[] = { 0x1DBF }; - EXPECT_EQ(SimplePath, Character::characterRangeCodePath(c2, 1)); - static UChar c3[] = { 0x1DC0 }; - EXPECT_EQ(ComplexPath, Character::characterRangeCodePath(c3, 1)); - static UChar c4[] = { 0x1DD0 }; - EXPECT_EQ(ComplexPath, Character::characterRangeCodePath(c4, 1)); - static UChar c5[] = { 0x1DFF }; - EXPECT_EQ(ComplexPath, Character::characterRangeCodePath(c5, 1)); - static UChar c6[] = { 0x1E00 }; - EXPECT_EQ(SimpleWithGlyphOverflowPath, Character::characterRangeCodePath(c6, 1)); - static UChar c7[] = { 0x2000 }; - EXPECT_EQ(SimpleWithGlyphOverflowPath, Character::characterRangeCodePath(c7, 1)); - static UChar c8[] = { 0x2001 }; - EXPECT_EQ(SimplePath, Character::characterRangeCodePath(c8, 1)); - - TestSpecificUCharRange(0x20D0, 0x20FF); - TestSpecificUCharRange(0x2CEF, 0x2CF1); - TestSpecificUCharRange(0x302A, 0x302F); - - TestSpecificUCharRange(0xA67C, 0xA67D); - TestSpecificUCharRange(0xA6F0, 0xA6F1); - TestSpecificUCharRange(0xA800, 0xABFF); - - TestSpecificUCharRange(0xD7B0, 0xD7FF); - TestSpecificUCharRange(0xFE00, 0xFE0F); - TestSpecificUCharRange(0xFE20, 0xFE2F); +TEST(FontTest, TestCharacterRangeCodePath) { + static UChar c1[] = {0x0}; + EXPECT_EQ(SimplePath, Character::characterRangeCodePath(c1, 1)); + + TestSpecificUCharRange(0x2E5, 0x2E9); + TestSpecificUCharRange(0x300, 0x36F); + TestSpecificUCharRange(0x0591, 0x05BD); + TestSpecificUCharRange(0x05BF, 0x05CF); + TestSpecificUCharRange(0x0600, 0x109F); + TestSpecificUCharRange(0x1100, 0x11FF); + TestSpecificUCharRange(0x135D, 0x135F); + TestSpecificUCharRange(0x1700, 0x18AF); + TestSpecificUCharRange(0x1900, 0x194F); + TestSpecificUCharRange(0x1980, 0x19DF); + TestSpecificUCharRange(0x1A00, 0x1CFF); + + static UChar c2[] = {0x1DBF}; + EXPECT_EQ(SimplePath, Character::characterRangeCodePath(c2, 1)); + static UChar c3[] = {0x1DC0}; + EXPECT_EQ(ComplexPath, Character::characterRangeCodePath(c3, 1)); + static UChar c4[] = {0x1DD0}; + EXPECT_EQ(ComplexPath, Character::characterRangeCodePath(c4, 1)); + static UChar c5[] = {0x1DFF}; + EXPECT_EQ(ComplexPath, Character::characterRangeCodePath(c5, 1)); + static UChar c6[] = {0x1E00}; + EXPECT_EQ(SimpleWithGlyphOverflowPath, + Character::characterRangeCodePath(c6, 1)); + static UChar c7[] = {0x2000}; + EXPECT_EQ(SimpleWithGlyphOverflowPath, + Character::characterRangeCodePath(c7, 1)); + static UChar c8[] = {0x2001}; + EXPECT_EQ(SimplePath, Character::characterRangeCodePath(c8, 1)); + + TestSpecificUCharRange(0x20D0, 0x20FF); + TestSpecificUCharRange(0x2CEF, 0x2CF1); + TestSpecificUCharRange(0x302A, 0x302F); + + TestSpecificUCharRange(0xA67C, 0xA67D); + TestSpecificUCharRange(0xA6F0, 0xA6F1); + TestSpecificUCharRange(0xA800, 0xABFF); + + TestSpecificUCharRange(0xD7B0, 0xD7FF); + TestSpecificUCharRange(0xFE00, 0xFE0F); + TestSpecificUCharRange(0xFE20, 0xFE2F); } -TEST(FontTest, TestCharacterRangeCodePathSurrogate1) -{ - /* To be surrogate ... */ - /* 1st character must be 0xD800 .. 0xDBFF */ - /* 2nd character must be 0xdc00 .. 0xdfff */ - - /* The following 5 should all be Simple because they are not surrogate. */ - static UChar c1[] = { 0xD800, 0xDBFE }; - EXPECT_EQ(SimplePath, Character::characterRangeCodePath(c1, 2)); - static UChar c2[] = { 0xD800, 0xE000 }; - EXPECT_EQ(SimplePath, Character::characterRangeCodePath(c2, 2)); - static UChar c3[] = { 0xDBFF, 0xDBFE }; - EXPECT_EQ(SimplePath, Character::characterRangeCodePath(c3, 2)); - static UChar c4[] = { 0xDBFF, 0xE000 }; - EXPECT_EQ(SimplePath, Character::characterRangeCodePath(c4, 2)); - static UChar c5[] = { 0xDC00, 0xDBFF }; - EXPECT_EQ(SimplePath, Character::characterRangeCodePath(c5, 2)); - - /* To be Complex, the Supplementary Character must be in either */ - /* U+1F1E6 through U+1F1FF or U+E0100 through U+E01EF. */ - /* That is, a lead of 0xD83C with trail 0xDDE6 .. 0xDDFF or */ - /* a lead of 0xDB40 with trail 0xDD00 .. 0xDDEF. */ - static UChar c6[] = { 0xD83C, 0xDDE5 }; - EXPECT_EQ(SimplePath, Character::characterRangeCodePath(c6, 2)); - static UChar c7[] = { 0xD83C, 0xDDE6 }; - EXPECT_EQ(ComplexPath, Character::characterRangeCodePath(c7, 2)); - static UChar c8[] = { 0xD83C, 0xDDF0 }; - EXPECT_EQ(ComplexPath, Character::characterRangeCodePath(c8, 2)); - static UChar c9[] = { 0xD83C, 0xDDFF }; - EXPECT_EQ(ComplexPath, Character::characterRangeCodePath(c9, 2)); - static UChar c10[] = { 0xD83C, 0xDE00 }; - EXPECT_EQ(SimplePath, Character::characterRangeCodePath(c10, 2)); - - static UChar c11[] = { 0xDB40, 0xDCFF }; - EXPECT_EQ(SimplePath, Character::characterRangeCodePath(c11, 2)); - static UChar c12[] = { 0xDB40, 0xDD00 }; - EXPECT_EQ(ComplexPath, Character::characterRangeCodePath(c12, 2)); - static UChar c13[] = { 0xDB40, 0xDDED }; - EXPECT_EQ(ComplexPath, Character::characterRangeCodePath(c13, 2)); - static UChar c14[] = { 0xDB40, 0xDDEF }; - EXPECT_EQ(ComplexPath, Character::characterRangeCodePath(c14, 2)); - static UChar c15[] = { 0xDB40, 0xDDF0 }; - EXPECT_EQ(SimplePath, Character::characterRangeCodePath(c15, 2)); +TEST(FontTest, TestCharacterRangeCodePathSurrogate1) { + /* To be surrogate ... */ + /* 1st character must be 0xD800 .. 0xDBFF */ + /* 2nd character must be 0xdc00 .. 0xdfff */ + + /* The following 5 should all be Simple because they are not surrogate. */ + static UChar c1[] = {0xD800, 0xDBFE}; + EXPECT_EQ(SimplePath, Character::characterRangeCodePath(c1, 2)); + static UChar c2[] = {0xD800, 0xE000}; + EXPECT_EQ(SimplePath, Character::characterRangeCodePath(c2, 2)); + static UChar c3[] = {0xDBFF, 0xDBFE}; + EXPECT_EQ(SimplePath, Character::characterRangeCodePath(c3, 2)); + static UChar c4[] = {0xDBFF, 0xE000}; + EXPECT_EQ(SimplePath, Character::characterRangeCodePath(c4, 2)); + static UChar c5[] = {0xDC00, 0xDBFF}; + EXPECT_EQ(SimplePath, Character::characterRangeCodePath(c5, 2)); + + /* To be Complex, the Supplementary Character must be in either */ + /* U+1F1E6 through U+1F1FF or U+E0100 through U+E01EF. */ + /* That is, a lead of 0xD83C with trail 0xDDE6 .. 0xDDFF or */ + /* a lead of 0xDB40 with trail 0xDD00 .. 0xDDEF. */ + static UChar c6[] = {0xD83C, 0xDDE5}; + EXPECT_EQ(SimplePath, Character::characterRangeCodePath(c6, 2)); + static UChar c7[] = {0xD83C, 0xDDE6}; + EXPECT_EQ(ComplexPath, Character::characterRangeCodePath(c7, 2)); + static UChar c8[] = {0xD83C, 0xDDF0}; + EXPECT_EQ(ComplexPath, Character::characterRangeCodePath(c8, 2)); + static UChar c9[] = {0xD83C, 0xDDFF}; + EXPECT_EQ(ComplexPath, Character::characterRangeCodePath(c9, 2)); + static UChar c10[] = {0xD83C, 0xDE00}; + EXPECT_EQ(SimplePath, Character::characterRangeCodePath(c10, 2)); + + static UChar c11[] = {0xDB40, 0xDCFF}; + EXPECT_EQ(SimplePath, Character::characterRangeCodePath(c11, 2)); + static UChar c12[] = {0xDB40, 0xDD00}; + EXPECT_EQ(ComplexPath, Character::characterRangeCodePath(c12, 2)); + static UChar c13[] = {0xDB40, 0xDDED}; + EXPECT_EQ(ComplexPath, Character::characterRangeCodePath(c13, 2)); + static UChar c14[] = {0xDB40, 0xDDEF}; + EXPECT_EQ(ComplexPath, Character::characterRangeCodePath(c14, 2)); + static UChar c15[] = {0xDB40, 0xDDF0}; + EXPECT_EQ(SimplePath, Character::characterRangeCodePath(c15, 2)); } -TEST(FontTest, TestCharacterRangeCodePathString) -{ - // Simple-Simple is still simple - static UChar c1[] = { 0x2FF, 0x2FF }; - EXPECT_EQ(SimplePath, Character::characterRangeCodePath(c1, 2)); - // Complex-Simple is Complex - static UChar c2[] = { 0x300, 0x2FF }; - EXPECT_EQ(ComplexPath, Character::characterRangeCodePath(c2, 2)); - // Simple-Complex is Complex - static UChar c3[] = { 0x2FF, 0x330 }; - EXPECT_EQ(ComplexPath, Character::characterRangeCodePath(c3, 2)); - // Complex-Complex is Complex - static UChar c4[] = { 0x36F, 0x330 }; - EXPECT_EQ(ComplexPath, Character::characterRangeCodePath(c4, 2)); - // SimpleWithGlyphOverflow-Simple is SimpleWithGlyphOverflow - static UChar c5[] = { 0x1E00, 0x2FF }; - EXPECT_EQ(SimpleWithGlyphOverflowPath, Character::characterRangeCodePath(c5, 2)); - // Simple-SimpleWithGlyphOverflow is SimpleWithGlyphOverflow - static UChar c6[] = { 0x2FF, 0x2000 }; - EXPECT_EQ(SimpleWithGlyphOverflowPath, Character::characterRangeCodePath(c6, 2)); - // SimpleWithGlyphOverflow-Complex is Complex - static UChar c7[] = { 0x1E00, 0x330 }; - EXPECT_EQ(ComplexPath, Character::characterRangeCodePath(c7, 2)); - // Complex-SimpleWithGlyphOverflow is Complex - static UChar c8[] = { 0x330, 0x2000 }; - EXPECT_EQ(ComplexPath, Character::characterRangeCodePath(c8, 2)); - // Surrogate-Complex is Complex - static UChar c9[] = { 0xD83C, 0xDDE5, 0x330 }; - EXPECT_EQ(ComplexPath, Character::characterRangeCodePath(c9, 3)); - // Complex-Surrogate is Complex - static UChar c10[] = { 0x330, 0xD83C, 0xDDE5 }; - EXPECT_EQ(ComplexPath, Character::characterRangeCodePath(c10, 3)); +TEST(FontTest, TestCharacterRangeCodePathString) { + // Simple-Simple is still simple + static UChar c1[] = {0x2FF, 0x2FF}; + EXPECT_EQ(SimplePath, Character::characterRangeCodePath(c1, 2)); + // Complex-Simple is Complex + static UChar c2[] = {0x300, 0x2FF}; + EXPECT_EQ(ComplexPath, Character::characterRangeCodePath(c2, 2)); + // Simple-Complex is Complex + static UChar c3[] = {0x2FF, 0x330}; + EXPECT_EQ(ComplexPath, Character::characterRangeCodePath(c3, 2)); + // Complex-Complex is Complex + static UChar c4[] = {0x36F, 0x330}; + EXPECT_EQ(ComplexPath, Character::characterRangeCodePath(c4, 2)); + // SimpleWithGlyphOverflow-Simple is SimpleWithGlyphOverflow + static UChar c5[] = {0x1E00, 0x2FF}; + EXPECT_EQ(SimpleWithGlyphOverflowPath, + Character::characterRangeCodePath(c5, 2)); + // Simple-SimpleWithGlyphOverflow is SimpleWithGlyphOverflow + static UChar c6[] = {0x2FF, 0x2000}; + EXPECT_EQ(SimpleWithGlyphOverflowPath, + Character::characterRangeCodePath(c6, 2)); + // SimpleWithGlyphOverflow-Complex is Complex + static UChar c7[] = {0x1E00, 0x330}; + EXPECT_EQ(ComplexPath, Character::characterRangeCodePath(c7, 2)); + // Complex-SimpleWithGlyphOverflow is Complex + static UChar c8[] = {0x330, 0x2000}; + EXPECT_EQ(ComplexPath, Character::characterRangeCodePath(c8, 2)); + // Surrogate-Complex is Complex + static UChar c9[] = {0xD83C, 0xDDE5, 0x330}; + EXPECT_EQ(ComplexPath, Character::characterRangeCodePath(c9, 3)); + // Complex-Surrogate is Complex + static UChar c10[] = {0x330, 0xD83C, 0xDDE5}; + EXPECT_EQ(ComplexPath, Character::characterRangeCodePath(c10, 3)); } -static void TestSpecificUChar32RangeIdeograph(UChar32 rangeStart, UChar32 rangeEnd) -{ - EXPECT_FALSE(Character::isCJKIdeograph(rangeStart - 1)); - EXPECT_TRUE(Character::isCJKIdeograph(rangeStart)); - EXPECT_TRUE(Character::isCJKIdeograph((UChar32)((uint64_t)rangeStart + (uint64_t)rangeEnd) / 2)); - EXPECT_TRUE(Character::isCJKIdeograph(rangeEnd)); - EXPECT_FALSE(Character::isCJKIdeograph(rangeEnd + 1)); +static void TestSpecificUChar32RangeIdeograph(UChar32 rangeStart, + UChar32 rangeEnd) { + EXPECT_FALSE(Character::isCJKIdeograph(rangeStart - 1)); + EXPECT_TRUE(Character::isCJKIdeograph(rangeStart)); + EXPECT_TRUE(Character::isCJKIdeograph( + (UChar32)((uint64_t)rangeStart + (uint64_t)rangeEnd) / 2)); + EXPECT_TRUE(Character::isCJKIdeograph(rangeEnd)); + EXPECT_FALSE(Character::isCJKIdeograph(rangeEnd + 1)); } -TEST(FontTest, TestIsCJKIdeograph) -{ - // The basic CJK Unified Ideographs block. - TestSpecificUChar32RangeIdeograph(0x4E00, 0x9FFF); - // CJK Unified Ideographs Extension A. - TestSpecificUChar32RangeIdeograph(0x3400, 0x4DBF); - // CJK Unified Ideographs Extension A and Kangxi Radicals. - TestSpecificUChar32RangeIdeograph(0x2E80, 0x2FDF); - // CJK Strokes. - TestSpecificUChar32RangeIdeograph(0x31C0, 0x31EF); - // CJK Compatibility Ideographs. - TestSpecificUChar32RangeIdeograph(0xF900, 0xFAFF); - // CJK Unified Ideographs Extension B. - TestSpecificUChar32RangeIdeograph(0x20000, 0x2A6DF); - // CJK Unified Ideographs Extension C. - // CJK Unified Ideographs Extension D. - TestSpecificUChar32RangeIdeograph(0x2A700, 0x2B81F); - // CJK Compatibility Ideographs Supplement. - TestSpecificUChar32RangeIdeograph(0x2F800, 0x2FA1F); +TEST(FontTest, TestIsCJKIdeograph) { + // The basic CJK Unified Ideographs block. + TestSpecificUChar32RangeIdeograph(0x4E00, 0x9FFF); + // CJK Unified Ideographs Extension A. + TestSpecificUChar32RangeIdeograph(0x3400, 0x4DBF); + // CJK Unified Ideographs Extension A and Kangxi Radicals. + TestSpecificUChar32RangeIdeograph(0x2E80, 0x2FDF); + // CJK Strokes. + TestSpecificUChar32RangeIdeograph(0x31C0, 0x31EF); + // CJK Compatibility Ideographs. + TestSpecificUChar32RangeIdeograph(0xF900, 0xFAFF); + // CJK Unified Ideographs Extension B. + TestSpecificUChar32RangeIdeograph(0x20000, 0x2A6DF); + // CJK Unified Ideographs Extension C. + // CJK Unified Ideographs Extension D. + TestSpecificUChar32RangeIdeograph(0x2A700, 0x2B81F); + // CJK Compatibility Ideographs Supplement. + TestSpecificUChar32RangeIdeograph(0x2F800, 0x2FA1F); } -static void TestSpecificUChar32RangeIdeographSymbol(UChar32 rangeStart, UChar32 rangeEnd) -{ - EXPECT_FALSE(Character::isCJKIdeographOrSymbol(rangeStart - 1)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(rangeStart)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol((UChar32)((uint64_t)rangeStart + (uint64_t)rangeEnd) / 2)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(rangeEnd)); - EXPECT_FALSE(Character::isCJKIdeographOrSymbol(rangeEnd + 1)); +static void TestSpecificUChar32RangeIdeographSymbol(UChar32 rangeStart, + UChar32 rangeEnd) { + EXPECT_FALSE(Character::isCJKIdeographOrSymbol(rangeStart - 1)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(rangeStart)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol( + (UChar32)((uint64_t)rangeStart + (uint64_t)rangeEnd) / 2)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(rangeEnd)); + EXPECT_FALSE(Character::isCJKIdeographOrSymbol(rangeEnd + 1)); } -TEST(FontTest, TestIsCJKIdeographOrSymbol) -{ - // CJK Compatibility Ideographs Supplement. - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2C7)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2CA)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2CB)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2D9)); - - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2020)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2021)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2030)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x203B)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x203C)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2042)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2047)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2048)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2049)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2051)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x20DD)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x20DE)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2100)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2103)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2105)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2109)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x210A)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2113)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2116)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2121)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x212B)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x213B)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2150)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2151)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2152)); - - TestSpecificUChar32RangeIdeographSymbol(0x2156, 0x215A); - TestSpecificUChar32RangeIdeographSymbol(0x2160, 0x216B); - TestSpecificUChar32RangeIdeographSymbol(0x2170, 0x217B); - - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x217F)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2189)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2307)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2312)); - - EXPECT_FALSE(Character::isCJKIdeographOrSymbol(0x23BD)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x23BE)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x23C4)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x23CC)); - EXPECT_FALSE(Character::isCJKIdeographOrSymbol(0x23CD)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x23CE)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2423)); - - TestSpecificUChar32RangeIdeographSymbol(0x2460, 0x2492); - TestSpecificUChar32RangeIdeographSymbol(0x249C, 0x24FF); - - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x25A0)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x25A1)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x25A2)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x25AA)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x25AB)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x25B1)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x25B2)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x25B3)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x25B6)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x25B7)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x25BC)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x25BD)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x25C0)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x25C1)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x25C6)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x25C7)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x25C9)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x25CB)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x25CC)); - - TestSpecificUChar32RangeIdeographSymbol(0x25CE, 0x25D3); - TestSpecificUChar32RangeIdeographSymbol(0x25E2, 0x25E6); - - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x25EF)); - - TestSpecificUChar32RangeIdeographSymbol(0x2600, 0x2603); - - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2605)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2606)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x260E)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2616)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2617)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2640)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2642)); - - TestSpecificUChar32RangeIdeographSymbol(0x2660, 0x266F); - TestSpecificUChar32RangeIdeographSymbol(0x2672, 0x267D); - - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x26A0)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x26BD)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x26BE)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2713)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x271A)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x273F)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2740)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2756)); - - TestSpecificUChar32RangeIdeographSymbol(0x2776, 0x277F); - - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2B1A)); - - TestSpecificUChar32RangeIdeographSymbol(0x2FF0, 0x302F); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x3031)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x312F)); - EXPECT_FALSE(Character::isCJKIdeographOrSymbol(0x3130)); - - EXPECT_FALSE(Character::isCJKIdeographOrSymbol(0x318F)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x3190)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x319F)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x31BF)); - - EXPECT_FALSE(Character::isCJKIdeographOrSymbol(0x31FF)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x3200)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x3300)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x33FF)); - - TestSpecificUChar32RangeIdeographSymbol(0xF860, 0xF862); - TestSpecificUChar32RangeIdeographSymbol(0xFE30, 0xFE4F); - - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0xFE10)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0xFE11)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0xFE12)); - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0xFE19)); - - EXPECT_FALSE(Character::isCJKIdeographOrSymbol(0xFF0D)); - EXPECT_FALSE(Character::isCJKIdeographOrSymbol(0xFF1B)); - EXPECT_FALSE(Character::isCJKIdeographOrSymbol(0xFF1C)); - EXPECT_FALSE(Character::isCJKIdeographOrSymbol(0xFF1E)); - - TestSpecificUChar32RangeIdeographSymbol(0xFF00, 0xFFEF); - - EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x1F100)); - - TestSpecificUChar32RangeIdeographSymbol(0x1F110, 0x1F129); - TestSpecificUChar32RangeIdeographSymbol(0x1F130, 0x1F149); - TestSpecificUChar32RangeIdeographSymbol(0x1F150, 0x1F169); - TestSpecificUChar32RangeIdeographSymbol(0x1F170, 0x1F189); - TestSpecificUChar32RangeIdeographSymbol(0x1F200, 0x1F6FF); +TEST(FontTest, TestIsCJKIdeographOrSymbol) { + // CJK Compatibility Ideographs Supplement. + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2C7)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2CA)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2CB)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2D9)); + + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2020)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2021)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2030)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x203B)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x203C)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2042)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2047)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2048)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2049)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2051)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x20DD)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x20DE)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2100)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2103)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2105)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2109)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x210A)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2113)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2116)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2121)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x212B)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x213B)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2150)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2151)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2152)); + + TestSpecificUChar32RangeIdeographSymbol(0x2156, 0x215A); + TestSpecificUChar32RangeIdeographSymbol(0x2160, 0x216B); + TestSpecificUChar32RangeIdeographSymbol(0x2170, 0x217B); + + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x217F)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2189)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2307)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2312)); + + EXPECT_FALSE(Character::isCJKIdeographOrSymbol(0x23BD)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x23BE)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x23C4)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x23CC)); + EXPECT_FALSE(Character::isCJKIdeographOrSymbol(0x23CD)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x23CE)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2423)); + + TestSpecificUChar32RangeIdeographSymbol(0x2460, 0x2492); + TestSpecificUChar32RangeIdeographSymbol(0x249C, 0x24FF); + + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x25A0)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x25A1)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x25A2)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x25AA)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x25AB)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x25B1)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x25B2)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x25B3)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x25B6)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x25B7)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x25BC)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x25BD)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x25C0)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x25C1)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x25C6)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x25C7)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x25C9)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x25CB)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x25CC)); + + TestSpecificUChar32RangeIdeographSymbol(0x25CE, 0x25D3); + TestSpecificUChar32RangeIdeographSymbol(0x25E2, 0x25E6); + + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x25EF)); + + TestSpecificUChar32RangeIdeographSymbol(0x2600, 0x2603); + + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2605)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2606)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x260E)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2616)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2617)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2640)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2642)); + + TestSpecificUChar32RangeIdeographSymbol(0x2660, 0x266F); + TestSpecificUChar32RangeIdeographSymbol(0x2672, 0x267D); + + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x26A0)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x26BD)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x26BE)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2713)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x271A)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x273F)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2740)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2756)); + + TestSpecificUChar32RangeIdeographSymbol(0x2776, 0x277F); + + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x2B1A)); + + TestSpecificUChar32RangeIdeographSymbol(0x2FF0, 0x302F); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x3031)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x312F)); + EXPECT_FALSE(Character::isCJKIdeographOrSymbol(0x3130)); + + EXPECT_FALSE(Character::isCJKIdeographOrSymbol(0x318F)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x3190)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x319F)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x31BF)); + + EXPECT_FALSE(Character::isCJKIdeographOrSymbol(0x31FF)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x3200)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x3300)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x33FF)); + + TestSpecificUChar32RangeIdeographSymbol(0xF860, 0xF862); + TestSpecificUChar32RangeIdeographSymbol(0xFE30, 0xFE4F); + + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0xFE10)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0xFE11)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0xFE12)); + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0xFE19)); + + EXPECT_FALSE(Character::isCJKIdeographOrSymbol(0xFF0D)); + EXPECT_FALSE(Character::isCJKIdeographOrSymbol(0xFF1B)); + EXPECT_FALSE(Character::isCJKIdeographOrSymbol(0xFF1C)); + EXPECT_FALSE(Character::isCJKIdeographOrSymbol(0xFF1E)); + + TestSpecificUChar32RangeIdeographSymbol(0xFF00, 0xFFEF); + + EXPECT_TRUE(Character::isCJKIdeographOrSymbol(0x1F100)); + + TestSpecificUChar32RangeIdeographSymbol(0x1F110, 0x1F129); + TestSpecificUChar32RangeIdeographSymbol(0x1F130, 0x1F149); + TestSpecificUChar32RangeIdeographSymbol(0x1F150, 0x1F169); + TestSpecificUChar32RangeIdeographSymbol(0x1F170, 0x1F189); + TestSpecificUChar32RangeIdeographSymbol(0x1F200, 0x1F6FF); } -} // namespace blink - +} // namespace blink diff --git a/sky/engine/platform/fonts/FontTraits.h b/sky/engine/platform/fonts/FontTraits.h index a5feaf9403a44..36a01a7755c40 100644 --- a/sky/engine/platform/fonts/FontTraits.h +++ b/sky/engine/platform/fonts/FontTraits.h @@ -32,78 +32,78 @@ namespace blink { enum FontWeight { - FontWeight100, - FontWeight200, - FontWeight300, - FontWeight400, - FontWeight500, - FontWeight600, - FontWeight700, - FontWeight800, - FontWeight900, - FontWeightNormal = FontWeight400, - FontWeightBold = FontWeight700 + FontWeight100, + FontWeight200, + FontWeight300, + FontWeight400, + FontWeight500, + FontWeight600, + FontWeight700, + FontWeight800, + FontWeight900, + FontWeightNormal = FontWeight400, + FontWeightBold = FontWeight700 }; // Numeric values matching OS/2 & Windows Metrics usWidthClass table. // https://www.microsoft.com/typography/otspec/os2.htm enum FontStretch { - FontStretchUltraCondensed = 1, - FontStretchExtraCondensed = 2, - FontStretchCondensed = 3, - FontStretchSemiCondensed = 4, - FontStretchNormal = 5, - FontStretchSemiExpanded = 6, - FontStretchExpanded = 7, - FontStretchExtraExpanded = 8, - FontStretchUltraExpanded = 9 + FontStretchUltraCondensed = 1, + FontStretchExtraCondensed = 2, + FontStretchCondensed = 3, + FontStretchSemiCondensed = 4, + FontStretchNormal = 5, + FontStretchSemiExpanded = 6, + FontStretchExpanded = 7, + FontStretchExtraExpanded = 8, + FontStretchUltraExpanded = 9 }; -enum FontStyle { - FontStyleNormal = 0, - FontStyleItalic = 1 -}; +enum FontStyle { FontStyleNormal = 0, FontStyleItalic = 1 }; -enum FontVariant { - FontVariantNormal = 0, - FontVariantSmallCaps = 1 -}; +enum FontVariant { FontVariantNormal = 0, FontVariantSmallCaps = 1 }; typedef unsigned FontTraitsBitfield; struct FontTraits { - FontTraits(FontStyle style, FontVariant variant, FontWeight weight, FontStretch stretch) - { - m_traits.m_style = style; - m_traits.m_variant = variant; - m_traits.m_weight = weight; - m_traits.m_stretch = stretch; - m_traits.m_filler = 0; - ASSERT(!(m_bitfield >> 10)); - } - FontTraits(FontTraitsBitfield bitfield) - : m_bitfield(bitfield) - { - ASSERT(!m_traits.m_filler); - ASSERT(!(m_bitfield >> 10)); - } - FontStyle style() const { return static_cast(m_traits.m_style); } - FontVariant variant() const { return static_cast(m_traits.m_variant); } - FontWeight weight() const { return static_cast(m_traits.m_weight); } - FontStretch stretch() const { return static_cast(m_traits.m_stretch); } - FontTraitsBitfield bitfield() const { return m_bitfield; } + FontTraits(FontStyle style, + FontVariant variant, + FontWeight weight, + FontStretch stretch) { + m_traits.m_style = style; + m_traits.m_variant = variant; + m_traits.m_weight = weight; + m_traits.m_stretch = stretch; + m_traits.m_filler = 0; + ASSERT(!(m_bitfield >> 10)); + } + FontTraits(FontTraitsBitfield bitfield) : m_bitfield(bitfield) { + ASSERT(!m_traits.m_filler); + ASSERT(!(m_bitfield >> 10)); + } + FontStyle style() const { return static_cast(m_traits.m_style); } + FontVariant variant() const { + return static_cast(m_traits.m_variant); + } + FontWeight weight() const { + return static_cast(m_traits.m_weight); + } + FontStretch stretch() const { + return static_cast(m_traits.m_stretch); + } + FontTraitsBitfield bitfield() const { return m_bitfield; } - union { - struct { - unsigned m_style : 1; - unsigned m_variant : 1; - unsigned m_weight : 4; - unsigned m_stretch : 4; - unsigned m_filler : 22; - } m_traits; - FontTraitsBitfield m_bitfield; - }; + union { + struct { + unsigned m_style : 1; + unsigned m_variant : 1; + unsigned m_weight : 4; + unsigned m_stretch : 4; + unsigned m_filler : 22; + } m_traits; + FontTraitsBitfield m_bitfield; + }; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_FONTS_FONTTRAITS_H_ diff --git a/sky/engine/platform/fonts/FontWidthVariant.h b/sky/engine/platform/fonts/FontWidthVariant.h index f8b1917e11548..4ce6e1575f570 100644 --- a/sky/engine/platform/fonts/FontWidthVariant.h +++ b/sky/engine/platform/fonts/FontWidthVariant.h @@ -31,17 +31,18 @@ namespace blink { enum FontWidthVariant { - RegularWidth, - HalfWidth, - ThirdWidth, - QuarterWidth, - LastFontWidthVariant = QuarterWidth + RegularWidth, + HalfWidth, + ThirdWidth, + QuarterWidth, + LastFontWidthVariant = QuarterWidth }; const unsigned FontWidthVariantWidth = 2; -COMPILE_ASSERT(LastFontWidthVariant >> FontWidthVariantWidth == 0, FontWidthVariantWidth_is_correct); +COMPILE_ASSERT(LastFontWidthVariant >> FontWidthVariantWidth == 0, + FontWidthVariantWidth_is_correct); -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_FONTS_FONTWIDTHVARIANT_H_ diff --git a/sky/engine/platform/fonts/Glyph.h b/sky/engine/platform/fonts/Glyph.h index f036bb41a7755..3756dd11fd190 100644 --- a/sky/engine/platform/fonts/Glyph.h +++ b/sky/engine/platform/fonts/Glyph.h @@ -34,6 +34,6 @@ namespace blink { typedef unsigned short Glyph; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_FONTS_GLYPH_H_ diff --git a/sky/engine/platform/fonts/GlyphBuffer.h b/sky/engine/platform/fonts/GlyphBuffer.h index fdb39bd034833..90910bff59c61 100644 --- a/sky/engine/platform/fonts/GlyphBuffer.h +++ b/sky/engine/platform/fonts/GlyphBuffer.h @@ -39,79 +39,77 @@ namespace blink { class SimpleFontData; class GlyphBuffer { -public: - GlyphBuffer() { } - - bool isEmpty() const { return m_fontData.isEmpty(); } - bool hasOffsets() const { return !m_offsets.isEmpty(); } - unsigned size() const { return m_fontData.size(); } - - void clear() - { - m_fontData.clear(); - m_glyphs.clear(); - m_advances.clear(); - m_offsets.clear(); - } - - const Glyph* glyphs(unsigned from) const { return m_glyphs.data() + from; } - const float* advances(unsigned from) const { return m_advances.data() + from; } - const FloatSize* offsets(unsigned from) const { return m_offsets.data() + from; } - - const SimpleFontData* fontDataAt(unsigned index) const { return m_fontData[index]; } - - Glyph glyphAt(unsigned index) const - { - return m_glyphs[index]; - } - - float advanceAt(unsigned index) const - { - return m_advances[index]; - } - - void add(Glyph glyph, const SimpleFontData* font, float width) - { - // should not mix offset/advance-only glyphs - ASSERT(!hasOffsets()); - - m_fontData.append(font); - m_glyphs.append(glyph); - m_advances.append(width); - } - - void add(Glyph glyph, const SimpleFontData* font, const FloatSize& offset, float advance) - { - // should not mix offset/advance-only glyphs - ASSERT(size() == m_offsets.size()); - - m_fontData.append(font); - m_glyphs.append(glyph); - m_offsets.append(offset); - m_advances.append(advance); - } - - void reverse() - { - m_fontData.reverse(); - m_glyphs.reverse(); - m_advances.reverse(); - } - - void expandLastAdvance(float width) - { - ASSERT(!isEmpty()); - float& lastAdvance = m_advances.last(); - lastAdvance += width; - } - -private: - Vector m_fontData; - Vector m_glyphs; - Vector m_advances; - Vector m_offsets; + public: + GlyphBuffer() {} + + bool isEmpty() const { return m_fontData.isEmpty(); } + bool hasOffsets() const { return !m_offsets.isEmpty(); } + unsigned size() const { return m_fontData.size(); } + + void clear() { + m_fontData.clear(); + m_glyphs.clear(); + m_advances.clear(); + m_offsets.clear(); + } + + const Glyph* glyphs(unsigned from) const { return m_glyphs.data() + from; } + const float* advances(unsigned from) const { + return m_advances.data() + from; + } + const FloatSize* offsets(unsigned from) const { + return m_offsets.data() + from; + } + + const SimpleFontData* fontDataAt(unsigned index) const { + return m_fontData[index]; + } + + Glyph glyphAt(unsigned index) const { return m_glyphs[index]; } + + float advanceAt(unsigned index) const { return m_advances[index]; } + + void add(Glyph glyph, const SimpleFontData* font, float width) { + // should not mix offset/advance-only glyphs + ASSERT(!hasOffsets()); + + m_fontData.append(font); + m_glyphs.append(glyph); + m_advances.append(width); + } + + void add(Glyph glyph, + const SimpleFontData* font, + const FloatSize& offset, + float advance) { + // should not mix offset/advance-only glyphs + ASSERT(size() == m_offsets.size()); + + m_fontData.append(font); + m_glyphs.append(glyph); + m_offsets.append(offset); + m_advances.append(advance); + } + + void reverse() { + m_fontData.reverse(); + m_glyphs.reverse(); + m_advances.reverse(); + } + + void expandLastAdvance(float width) { + ASSERT(!isEmpty()); + float& lastAdvance = m_advances.last(); + lastAdvance += width; + } + + private: + Vector m_fontData; + Vector m_glyphs; + Vector m_advances; + Vector m_offsets; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_FONTS_GLYPHBUFFER_H_ diff --git a/sky/engine/platform/fonts/GlyphMetricsMap.h b/sky/engine/platform/fonts/GlyphMetricsMap.h index 58704a7c2d892..309f3062e4089 100644 --- a/sky/engine/platform/fonts/GlyphMetricsMap.h +++ b/sky/engine/platform/fonts/GlyphMetricsMap.h @@ -41,91 +41,91 @@ namespace blink { const float cGlyphSizeUnknown = -1; -template class GlyphMetricsMap { - WTF_MAKE_NONCOPYABLE(GlyphMetricsMap); -public: - GlyphMetricsMap() : m_filledPrimaryPage(false) { } - T metricsForGlyph(Glyph glyph) - { - return locatePage(glyph / GlyphMetricsPage::size)->metricsForGlyph(glyph); +template +class GlyphMetricsMap { + WTF_MAKE_NONCOPYABLE(GlyphMetricsMap); + + public: + GlyphMetricsMap() : m_filledPrimaryPage(false) {} + T metricsForGlyph(Glyph glyph) { + return locatePage(glyph / GlyphMetricsPage::size)->metricsForGlyph(glyph); + } + + void setMetricsForGlyph(Glyph glyph, const T& metrics) { + locatePage(glyph / GlyphMetricsPage::size) + ->setMetricsForGlyph(glyph, metrics); + } + + private: + class GlyphMetricsPage { + public: + static const size_t size = 256; // Usually covers Latin-1 in a single page. + + T metricsForGlyph(Glyph glyph) const { return m_metrics[glyph % size]; } + void setMetricsForGlyph(Glyph glyph, const T& metrics) { + setMetricsForIndex(glyph % size, metrics); } - - void setMetricsForGlyph(Glyph glyph, const T& metrics) - { - locatePage(glyph / GlyphMetricsPage::size)->setMetricsForGlyph(glyph, metrics); + void setMetricsForIndex(unsigned index, const T& metrics) { + ASSERT_WITH_SECURITY_IMPLICATION(index < size); + m_metrics[index] = metrics; } -private: - class GlyphMetricsPage { - public: - static const size_t size = 256; // Usually covers Latin-1 in a single page. - - T metricsForGlyph(Glyph glyph) const { return m_metrics[glyph % size]; } - void setMetricsForGlyph(Glyph glyph, const T& metrics) - { - setMetricsForIndex(glyph % size, metrics); - } - void setMetricsForIndex(unsigned index, const T& metrics) - { - ASSERT_WITH_SECURITY_IMPLICATION(index < size); - m_metrics[index] = metrics; - } - - private: - T m_metrics[size]; - }; - - GlyphMetricsPage* locatePage(unsigned pageNumber) - { - if (!pageNumber && m_filledPrimaryPage) - return &m_primaryPage; - return locatePageSlowCase(pageNumber); - } + private: + T m_metrics[size]; + }; + + GlyphMetricsPage* locatePage(unsigned pageNumber) { + if (!pageNumber && m_filledPrimaryPage) + return &m_primaryPage; + return locatePageSlowCase(pageNumber); + } - GlyphMetricsPage* locatePageSlowCase(unsigned pageNumber); + GlyphMetricsPage* locatePageSlowCase(unsigned pageNumber); - static T unknownMetrics(); + static T unknownMetrics(); - bool m_filledPrimaryPage; - GlyphMetricsPage m_primaryPage; // We optimize for the page that contains glyph indices 0-255. - OwnPtr > > m_pages; + bool m_filledPrimaryPage; + GlyphMetricsPage m_primaryPage; // We optimize for the page that contains + // glyph indices 0-255. + OwnPtr>> m_pages; }; -template<> inline float GlyphMetricsMap::unknownMetrics() -{ - return cGlyphSizeUnknown; +template <> +inline float GlyphMetricsMap::unknownMetrics() { + return cGlyphSizeUnknown; } -template<> inline FloatRect GlyphMetricsMap::unknownMetrics() -{ - return FloatRect(0, 0, cGlyphSizeUnknown, cGlyphSizeUnknown); +template <> +inline FloatRect GlyphMetricsMap::unknownMetrics() { + return FloatRect(0, 0, cGlyphSizeUnknown, cGlyphSizeUnknown); } -template typename GlyphMetricsMap::GlyphMetricsPage* GlyphMetricsMap::locatePageSlowCase(unsigned pageNumber) -{ - GlyphMetricsPage* page; - if (!pageNumber) { - ASSERT(!m_filledPrimaryPage); - page = &m_primaryPage; - m_filledPrimaryPage = true; - } else { - if (m_pages) { - page = m_pages->get(pageNumber); - if (page) - return page; - } else - m_pages = adoptPtr(new HashMap >); - page = new GlyphMetricsPage; - m_pages->set(pageNumber, adoptPtr(page)); - } - - // Fill in the whole page with the unknown glyph information. - for (unsigned i = 0; i < GlyphMetricsPage::size; i++) - page->setMetricsForIndex(i, unknownMetrics()); - - return page; +template +typename GlyphMetricsMap::GlyphMetricsPage* +GlyphMetricsMap::locatePageSlowCase(unsigned pageNumber) { + GlyphMetricsPage* page; + if (!pageNumber) { + ASSERT(!m_filledPrimaryPage); + page = &m_primaryPage; + m_filledPrimaryPage = true; + } else { + if (m_pages) { + page = m_pages->get(pageNumber); + if (page) + return page; + } else + m_pages = adoptPtr(new HashMap>); + page = new GlyphMetricsPage; + m_pages->set(pageNumber, adoptPtr(page)); + } + + // Fill in the whole page with the unknown glyph information. + for (unsigned i = 0; i < GlyphMetricsPage::size; i++) + page->setMetricsForIndex(i, unknownMetrics()); + + return page; } -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_FONTS_GLYPHMETRICSMAP_H_ diff --git a/sky/engine/platform/fonts/GlyphPage.h b/sky/engine/platform/fonts/GlyphPage.h index babe32dec1a7c..c5fe77d0b4526 100644 --- a/sky/engine/platform/fonts/GlyphPage.h +++ b/sky/engine/platform/fonts/GlyphPage.h @@ -44,16 +44,12 @@ namespace blink { class SimpleFontData; class GlyphPageTreeNode; -// Holds the glyph index and the corresponding SimpleFontData information for a given -// character. +// Holds the glyph index and the corresponding SimpleFontData information for a +// given character. struct GlyphData { - GlyphData(Glyph g = 0, const SimpleFontData* f = 0) - : glyph(g) - , fontData(f) - { - } - Glyph glyph; - const SimpleFontData* fontData; + GlyphData(Glyph g = 0, const SimpleFontData* f = 0) : glyph(g), fontData(f) {} + Glyph glyph; + const SimpleFontData* fontData; }; // A GlyphPage contains a fixed-size set of GlyphData mappings for a contiguous @@ -65,154 +61,158 @@ struct GlyphData { // although multiple nodes may reference it as their "page" if they are supposed // to be overriding the parent's node, but provide no additional information. class PLATFORM_EXPORT GlyphPage : public RefCounted { -public: - static PassRefPtr createForMixedFontData(GlyphPageTreeNode* owner) - { - void* slot = fastMalloc(sizeof(GlyphPage) + sizeof(SimpleFontData*) * GlyphPage::size); - return adoptRef(new (slot) GlyphPage(owner)); + public: + static PassRefPtr createForMixedFontData( + GlyphPageTreeNode* owner) { + void* slot = fastMalloc(sizeof(GlyphPage) + + sizeof(SimpleFontData*) * GlyphPage::size); + return adoptRef(new (slot) GlyphPage(owner)); + } + + static PassRefPtr createForSingleFontData( + GlyphPageTreeNode* owner, + const SimpleFontData* fontData) { + ASSERT(fontData); + return adoptRef(new GlyphPage(owner, fontData)); + } + + PassRefPtr createCopiedSystemFallbackPage( + GlyphPageTreeNode* owner) const { + RefPtr page = GlyphPage::createForMixedFontData(owner); + memcpy(page->m_glyphs, m_glyphs, sizeof(m_glyphs)); + if (hasPerGlyphFontData()) + memcpy(page->m_perGlyphFontData, m_perGlyphFontData, + sizeof(SimpleFontData*) * GlyphPage::size); + else { + for (size_t i = 0; i < GlyphPage::size; ++i) { + page->m_perGlyphFontData[i] = m_glyphs[i] ? m_fontDataForAllGlyphs : 0; + } } - - static PassRefPtr createForSingleFontData(GlyphPageTreeNode* owner, const SimpleFontData* fontData) - { - ASSERT(fontData); - return adoptRef(new GlyphPage(owner, fontData)); + page->m_customFontToLoad = m_customFontToLoad; + return page.release(); + } + + ~GlyphPage() {} + + static const unsigned char sizeBits = 8; + static const size_t size = + (1 << GlyphPage::sizeBits); // Covers Latin-1 in a single page. + static unsigned indexForCharacter(UChar32 c) { return c & 0xFF; } + + ALWAYS_INLINE GlyphData glyphDataForCharacter(UChar32 c) const { + unsigned index = indexForCharacter(c); + if (const CustomFontData* customData = customFontToLoadAt(index)) + customData->beginLoadIfNeeded(); + return glyphDataForIndex(index); + } + + ALWAYS_INLINE GlyphData glyphDataForIndex(unsigned index) const { + ASSERT_WITH_SECURITY_IMPLICATION(index < size); + Glyph glyph = m_glyphs[index]; + if (hasPerGlyphFontData()) + return GlyphData(glyph, m_perGlyphFontData[index]); + return GlyphData(glyph, glyph ? m_fontDataForAllGlyphs : 0); + } + + ALWAYS_INLINE Glyph glyphForCharacter(UChar32 c) const { + return glyphAt(indexForCharacter(c)); + } + + ALWAYS_INLINE Glyph glyphAt(unsigned index) const { + ASSERT_WITH_SECURITY_IMPLICATION(index < size); + return m_glyphs[index]; + } + + void setGlyphDataForCharacter(UChar32 c, Glyph g, const SimpleFontData* f) { + setGlyphDataForIndex(indexForCharacter(c), g, f); + } + + void setGlyphDataForIndex(unsigned index, + Glyph glyph, + const SimpleFontData* fontData) { + ASSERT_WITH_SECURITY_IMPLICATION(index < size); + m_glyphs[index] = glyph; + setCustomFontToLoad(index, 0); + + if (hasPerGlyphFontData()) { + m_perGlyphFontData[index] = fontData; + } else { + // A single-font GlyphPage already assigned m_fontDataForAllGlyphs in the + // constructor. + ASSERT(!glyph || fontData == m_fontDataForAllGlyphs); } - - PassRefPtr createCopiedSystemFallbackPage(GlyphPageTreeNode* owner) const - { - RefPtr page = GlyphPage::createForMixedFontData(owner); - memcpy(page->m_glyphs, m_glyphs, sizeof(m_glyphs)); - if (hasPerGlyphFontData()) - memcpy(page->m_perGlyphFontData, m_perGlyphFontData, sizeof(SimpleFontData*) * GlyphPage::size); - else { - for (size_t i = 0; i < GlyphPage::size; ++i) { - page->m_perGlyphFontData[i] = m_glyphs[i] ? m_fontDataForAllGlyphs : 0; - } - } - page->m_customFontToLoad = m_customFontToLoad; - return page.release(); + } + + void setGlyphDataForIndex(unsigned index, const GlyphData& glyphData) { + setGlyphDataForIndex(index, glyphData.glyph, glyphData.fontData); + } + + const CustomFontData* customFontToLoadAt(unsigned index) const { + ASSERT_WITH_SECURITY_IMPLICATION(index < size); + return m_customFontToLoad ? m_customFontToLoad->at(index) : 0; + } + + void setCustomFontToLoad(unsigned index, + const CustomFontData* customFontToLoad) { + if (!m_customFontToLoad) { + if (!customFontToLoad) + return; + m_customFontToLoad = CustomDataPage::create(); } - - ~GlyphPage() { } - - static const unsigned char sizeBits = 8; - static const size_t size = (1 << GlyphPage::sizeBits); // Covers Latin-1 in a single page. - static unsigned indexForCharacter(UChar32 c) { return c & 0xFF; } - - ALWAYS_INLINE GlyphData glyphDataForCharacter(UChar32 c) const - { - unsigned index = indexForCharacter(c); - if (const CustomFontData* customData = customFontToLoadAt(index)) - customData->beginLoadIfNeeded(); - return glyphDataForIndex(index); + ASSERT_WITH_SECURITY_IMPLICATION(index < size); + m_customFontToLoad->set(index, customFontToLoad); + } + + void removeFontDataFromSystemFallbackPage(const SimpleFontData* fontData) { + // This method should only be called on the system fallback page, which is + // never single-font. + ASSERT(hasPerGlyphFontData()); + for (size_t i = 0; i < size; ++i) { + if (m_perGlyphFontData[i] == fontData) { + m_glyphs[i] = 0; + m_perGlyphFontData[i] = 0; + } } + } - ALWAYS_INLINE GlyphData glyphDataForIndex(unsigned index) const - { - ASSERT_WITH_SECURITY_IMPLICATION(index < size); - Glyph glyph = m_glyphs[index]; - if (hasPerGlyphFontData()) - return GlyphData(glyph, m_perGlyphFontData[index]); - return GlyphData(glyph, glyph ? m_fontDataForAllGlyphs : 0); - } + GlyphPageTreeNode* owner() const { return m_owner; } - ALWAYS_INLINE Glyph glyphForCharacter(UChar32 c) const - { - return glyphAt(indexForCharacter(c)); - } + private: + explicit GlyphPage(GlyphPageTreeNode* owner, + const SimpleFontData* fontDataForAllGlyphs = 0) + : m_fontDataForAllGlyphs(fontDataForAllGlyphs), m_owner(owner) { + memset(m_glyphs, 0, sizeof(m_glyphs)); + if (hasPerGlyphFontData()) + memset(m_perGlyphFontData, 0, sizeof(SimpleFontData*) * GlyphPage::size); + } - ALWAYS_INLINE Glyph glyphAt(unsigned index) const - { - ASSERT_WITH_SECURITY_IMPLICATION(index < size); - return m_glyphs[index]; - } + bool hasPerGlyphFontData() const { return !m_fontDataForAllGlyphs; } - void setGlyphDataForCharacter(UChar32 c, Glyph g, const SimpleFontData* f) - { - setGlyphDataForIndex(indexForCharacter(c), g, f); + class CustomDataPage : public RefCounted { + public: + static RefPtr create() { + return adoptRef(new CustomDataPage()); } - - void setGlyphDataForIndex(unsigned index, Glyph glyph, const SimpleFontData* fontData) - { - ASSERT_WITH_SECURITY_IMPLICATION(index < size); - m_glyphs[index] = glyph; - setCustomFontToLoad(index, 0); - - if (hasPerGlyphFontData()) { - m_perGlyphFontData[index] = fontData; - } else { - // A single-font GlyphPage already assigned m_fontDataForAllGlyphs in the constructor. - ASSERT(!glyph || fontData == m_fontDataForAllGlyphs); - } + const CustomFontData* at(size_t index) const { return m_customData[index]; } + void set(size_t index, const CustomFontData* data) { + m_customData[index] = data; } - void setGlyphDataForIndex(unsigned index, const GlyphData& glyphData) - { - setGlyphDataForIndex(index, glyphData.glyph, glyphData.fontData); - } - - const CustomFontData* customFontToLoadAt(unsigned index) const - { - ASSERT_WITH_SECURITY_IMPLICATION(index < size); - return m_customFontToLoad ? m_customFontToLoad->at(index) : 0; - } - - void setCustomFontToLoad(unsigned index, const CustomFontData* customFontToLoad) - { - if (!m_customFontToLoad) { - if (!customFontToLoad) - return; - m_customFontToLoad = CustomDataPage::create(); - } - ASSERT_WITH_SECURITY_IMPLICATION(index < size); - m_customFontToLoad->set(index, customFontToLoad); - } + private: + CustomDataPage() { memset(m_customData, 0, sizeof(m_customData)); } + const CustomFontData* m_customData[size]; + }; - void removeFontDataFromSystemFallbackPage(const SimpleFontData* fontData) - { - // This method should only be called on the system fallback page, which is never single-font. - ASSERT(hasPerGlyphFontData()); - for (size_t i = 0; i < size; ++i) { - if (m_perGlyphFontData[i] == fontData) { - m_glyphs[i] = 0; - m_perGlyphFontData[i] = 0; - } - } - } - - GlyphPageTreeNode* owner() const { return m_owner; } - -private: - explicit GlyphPage(GlyphPageTreeNode* owner, const SimpleFontData* fontDataForAllGlyphs = 0) - : m_fontDataForAllGlyphs(fontDataForAllGlyphs) - , m_owner(owner) - { - memset(m_glyphs, 0, sizeof(m_glyphs)); - if (hasPerGlyphFontData()) - memset(m_perGlyphFontData, 0, sizeof(SimpleFontData*) * GlyphPage::size); - } + const SimpleFontData* m_fontDataForAllGlyphs; + GlyphPageTreeNode* m_owner; + RefPtr m_customFontToLoad; + Glyph m_glyphs[size]; - bool hasPerGlyphFontData() const { return !m_fontDataForAllGlyphs; } - - class CustomDataPage : public RefCounted { - public: - static RefPtr create() { return adoptRef(new CustomDataPage()); } - const CustomFontData* at(size_t index) const { return m_customData[index]; } - void set(size_t index, const CustomFontData* data) { m_customData[index] = data; } - private: - CustomDataPage() { memset(m_customData, 0, sizeof(m_customData)); } - const CustomFontData* m_customData[size]; - }; - - const SimpleFontData* m_fontDataForAllGlyphs; - GlyphPageTreeNode* m_owner; - RefPtr m_customFontToLoad; - Glyph m_glyphs[size]; - - // NOTE: This array has (GlyphPage::size) elements if m_fontDataForAllGlyphs is null. - const SimpleFontData* m_perGlyphFontData[0]; + // NOTE: This array has (GlyphPage::size) elements if m_fontDataForAllGlyphs + // is null. + const SimpleFontData* m_perGlyphFontData[0]; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_FONTS_GLYPHPAGE_H_ diff --git a/sky/engine/platform/fonts/GlyphPageTreeNode.cpp b/sky/engine/platform/fonts/GlyphPageTreeNode.cpp index 0930ce1d9fa46..b0b8ac8fe5297 100644 --- a/sky/engine/platform/fonts/GlyphPageTreeNode.cpp +++ b/sky/engine/platform/fonts/GlyphPageTreeNode.cpp @@ -44,371 +44,398 @@ using std::min; HashMap* GlyphPageTreeNode::roots = 0; GlyphPageTreeNode* GlyphPageTreeNode::pageZeroRoot = 0; -GlyphPageTreeNode* GlyphPageTreeNode::getRoot(unsigned pageNumber) -{ - static bool initialized; - if (!initialized) { - initialized = true; - roots = new HashMap; - pageZeroRoot = new GlyphPageTreeNode; - } +GlyphPageTreeNode* GlyphPageTreeNode::getRoot(unsigned pageNumber) { + static bool initialized; + if (!initialized) { + initialized = true; + roots = new HashMap; + pageZeroRoot = new GlyphPageTreeNode; + } - if (!pageNumber) - return pageZeroRoot; + if (!pageNumber) + return pageZeroRoot; - if (GlyphPageTreeNode* foundNode = roots->get(pageNumber)) - return foundNode; + if (GlyphPageTreeNode* foundNode = roots->get(pageNumber)) + return foundNode; - GlyphPageTreeNode* node = new GlyphPageTreeNode; + GlyphPageTreeNode* node = new GlyphPageTreeNode; #if ENABLE(ASSERT) - node->m_pageNumber = pageNumber; + node->m_pageNumber = pageNumber; #endif - roots->set(pageNumber, node); - return node; + roots->set(pageNumber, node); + return node; } -size_t GlyphPageTreeNode::treeGlyphPageCount() -{ - size_t count = 0; - if (roots) { - HashMap::iterator end = roots->end(); - for (HashMap::iterator it = roots->begin(); it != end; ++it) - count += it->value->pageCount(); - } +size_t GlyphPageTreeNode::treeGlyphPageCount() { + size_t count = 0; + if (roots) { + HashMap::iterator end = roots->end(); + for (HashMap::iterator it = roots->begin(); + it != end; ++it) + count += it->value->pageCount(); + } - if (pageZeroRoot) - count += pageZeroRoot->pageCount(); + if (pageZeroRoot) + count += pageZeroRoot->pageCount(); - return count; + return count; } -size_t GlyphPageTreeNode::pageCount() const -{ - size_t count = m_page && m_page->owner() == this ? 1 : 0; - GlyphPageTreeNodeMap::const_iterator end = m_children.end(); - for (GlyphPageTreeNodeMap::const_iterator it = m_children.begin(); it != end; ++it) - count += it->value->pageCount(); +size_t GlyphPageTreeNode::pageCount() const { + size_t count = m_page && m_page->owner() == this ? 1 : 0; + GlyphPageTreeNodeMap::const_iterator end = m_children.end(); + for (GlyphPageTreeNodeMap::const_iterator it = m_children.begin(); it != end; + ++it) + count += it->value->pageCount(); - return count; + return count; } -void GlyphPageTreeNode::pruneTreeCustomFontData(const FontData* fontData) -{ - // Enumerate all the roots and prune any tree that contains our custom font data. - if (roots) { - HashMap::iterator end = roots->end(); - for (HashMap::iterator it = roots->begin(); it != end; ++it) - it->value->pruneCustomFontData(fontData); - } - - if (pageZeroRoot) - pageZeroRoot->pruneCustomFontData(fontData); +void GlyphPageTreeNode::pruneTreeCustomFontData(const FontData* fontData) { + // Enumerate all the roots and prune any tree that contains our custom font + // data. + if (roots) { + HashMap::iterator end = roots->end(); + for (HashMap::iterator it = roots->begin(); + it != end; ++it) + it->value->pruneCustomFontData(fontData); + } + + if (pageZeroRoot) + pageZeroRoot->pruneCustomFontData(fontData); } -void GlyphPageTreeNode::pruneTreeFontData(const SimpleFontData* fontData) -{ - if (roots) { - HashMap::iterator end = roots->end(); - for (HashMap::iterator it = roots->begin(); it != end; ++it) - it->value->pruneFontData(fontData); - } +void GlyphPageTreeNode::pruneTreeFontData(const SimpleFontData* fontData) { + if (roots) { + HashMap::iterator end = roots->end(); + for (HashMap::iterator it = roots->begin(); + it != end; ++it) + it->value->pruneFontData(fontData); + } - if (pageZeroRoot) - pageZeroRoot->pruneFontData(fontData); + if (pageZeroRoot) + pageZeroRoot->pruneFontData(fontData); } -static bool fill(GlyphPage* pageToFill, unsigned offset, unsigned length, UChar* buffer, unsigned bufferLength, const SimpleFontData* fontData) -{ - bool hasGlyphs = fontData->fillGlyphPage(pageToFill, offset, length, buffer, bufferLength); - return hasGlyphs; +static bool fill(GlyphPage* pageToFill, + unsigned offset, + unsigned length, + UChar* buffer, + unsigned bufferLength, + const SimpleFontData* fontData) { + bool hasGlyphs = + fontData->fillGlyphPage(pageToFill, offset, length, buffer, bufferLength); + return hasGlyphs; } -void GlyphPageTreeNode::initializePage(const FontData* fontData, unsigned pageNumber) -{ - ASSERT(!m_page); - - // This function must not be called for the root of the tree, because that - // level does not contain any glyphs. - ASSERT(m_level > 0 && m_parent); - - // The parent's page will be 0 if we are level one or the parent's font data - // did not contain any glyphs for that page. - GlyphPage* parentPage = m_parent->page(); - - // NULL FontData means we're being asked for the system fallback font. - if (fontData) { - if (m_level == 1) { - // Children of the root hold pure pages. These will cover only one - // font data's glyphs, and will have glyph index 0 if the font data does not - // contain the glyph. - unsigned start = pageNumber * GlyphPage::size; - UChar buffer[GlyphPage::size * 2 + 2]; - unsigned bufferLength; - unsigned i; - - // Fill in a buffer with the entire "page" of characters that we want to look up glyphs for. - if (start < 0x10000) { - bufferLength = GlyphPage::size; - for (i = 0; i < GlyphPage::size; i++) - buffer[i] = start + i; - - if (start == 0) { - // Control characters must not render at all. - for (i = 0; i < 0x20; ++i) - buffer[i] = zeroWidthSpace; - for (i = 0x7F; i < 0xA0; i++) - buffer[i] = zeroWidthSpace; - buffer[softHyphen] = zeroWidthSpace; - - // \n, \t, and nonbreaking space must render as a space. - buffer[newlineCharacter] = space; - buffer[characterTabulation] = space; - buffer[noBreakSpace] = space; - } else if (start == (leftToRightMark & ~(GlyphPage::size - 1))) { - // LRM, RLM, LRE, RLE, ZWNJ, ZWJ, and PDF must not render at all. - buffer[leftToRightMark - start] = zeroWidthSpace; - buffer[rightToLeftMark - start] = zeroWidthSpace; - buffer[leftToRightEmbed - start] = zeroWidthSpace; - buffer[rightToLeftEmbed - start] = zeroWidthSpace; - buffer[leftToRightOverride - start] = zeroWidthSpace; - buffer[rightToLeftOverride - start] = zeroWidthSpace; - buffer[zeroWidthNonJoiner - start] = zeroWidthSpace; - buffer[zeroWidthJoiner - start] = zeroWidthSpace; - buffer[popDirectionalFormatting - start] = zeroWidthSpace; - } else if (start == (objectReplacementCharacter & ~(GlyphPage::size - 1))) { - // Object replacement character must not render at all. - buffer[objectReplacementCharacter - start] = zeroWidthSpace; - } else if (start == (zeroWidthNoBreakSpace & ~(GlyphPage::size - 1))) { - // ZWNBS/BOM must not render at all. - buffer[zeroWidthNoBreakSpace - start] = zeroWidthSpace; - } - } else { - bufferLength = GlyphPage::size * 2; - for (i = 0; i < GlyphPage::size; i++) { - int c = i + start; - buffer[i * 2] = U16_LEAD(c); - buffer[i * 2 + 1] = U16_TRAIL(c); - } - } - - // Now that we have a buffer full of characters, we want to get back an array - // of glyph indices. This part involves calling into the platform-specific - // routine of our glyph map for actually filling in the page with the glyphs. - // Success is not guaranteed. For example, Times fails to fill page 260, giving glyph data - // for only 128 out of 256 characters. - bool haveGlyphs; - if (!fontData->isSegmented()) { - m_page = GlyphPage::createForSingleFontData(this, toSimpleFontData(fontData)); - haveGlyphs = fill(m_page.get(), 0, GlyphPage::size, buffer, bufferLength, toSimpleFontData(fontData)); - } else { - m_page = GlyphPage::createForMixedFontData(this); - haveGlyphs = false; - - const SegmentedFontData* segmentedFontData = toSegmentedFontData(fontData); - for (int i = segmentedFontData->numRanges() - 1; i >= 0; i--) { - const FontDataRange& range = segmentedFontData->rangeAt(i); - // all this casting is to ensure all the parameters to min and max have the same type, - // to avoid ambiguous template parameter errors on Windows - int from = max(0, static_cast(range.from()) - static_cast(start)); - int to = 1 + min(static_cast(range.to()) - static_cast(start), static_cast(GlyphPage::size) - 1); - if (from >= static_cast(GlyphPage::size) || to <= 0) - continue; - - // If this is a custom font needs to be loaded, do not fill - // the page so that font fallback is used while loading. - RefPtr customData = range.fontData()->customFontData(); - if (customData && customData->isLoadingFallback()) { - for (int j = from; j < to; j++) { - m_page->setCustomFontToLoad(j, customData.get()); - haveGlyphs = true; - } - continue; - } - - haveGlyphs |= fill(m_page.get(), from, to - from, buffer + from * (start < 0x10000 ? 1 : 2), (to - from) * (start < 0x10000 ? 1 : 2), range.fontData().get()); - } +void GlyphPageTreeNode::initializePage(const FontData* fontData, + unsigned pageNumber) { + ASSERT(!m_page); + + // This function must not be called for the root of the tree, because that + // level does not contain any glyphs. + ASSERT(m_level > 0 && m_parent); + + // The parent's page will be 0 if we are level one or the parent's font data + // did not contain any glyphs for that page. + GlyphPage* parentPage = m_parent->page(); + + // NULL FontData means we're being asked for the system fallback font. + if (fontData) { + if (m_level == 1) { + // Children of the root hold pure pages. These will cover only one + // font data's glyphs, and will have glyph index 0 if the font data does + // not contain the glyph. + unsigned start = pageNumber * GlyphPage::size; + UChar buffer[GlyphPage::size * 2 + 2]; + unsigned bufferLength; + unsigned i; + + // Fill in a buffer with the entire "page" of characters that we want to + // look up glyphs for. + if (start < 0x10000) { + bufferLength = GlyphPage::size; + for (i = 0; i < GlyphPage::size; i++) + buffer[i] = start + i; + + if (start == 0) { + // Control characters must not render at all. + for (i = 0; i < 0x20; ++i) + buffer[i] = zeroWidthSpace; + for (i = 0x7F; i < 0xA0; i++) + buffer[i] = zeroWidthSpace; + buffer[softHyphen] = zeroWidthSpace; + + // \n, \t, and nonbreaking space must render as a space. + buffer[newlineCharacter] = space; + buffer[characterTabulation] = space; + buffer[noBreakSpace] = space; + } else if (start == (leftToRightMark & ~(GlyphPage::size - 1))) { + // LRM, RLM, LRE, RLE, ZWNJ, ZWJ, and PDF must not render at all. + buffer[leftToRightMark - start] = zeroWidthSpace; + buffer[rightToLeftMark - start] = zeroWidthSpace; + buffer[leftToRightEmbed - start] = zeroWidthSpace; + buffer[rightToLeftEmbed - start] = zeroWidthSpace; + buffer[leftToRightOverride - start] = zeroWidthSpace; + buffer[rightToLeftOverride - start] = zeroWidthSpace; + buffer[zeroWidthNonJoiner - start] = zeroWidthSpace; + buffer[zeroWidthJoiner - start] = zeroWidthSpace; + buffer[popDirectionalFormatting - start] = zeroWidthSpace; + } else if (start == + (objectReplacementCharacter & ~(GlyphPage::size - 1))) { + // Object replacement character must not render at all. + buffer[objectReplacementCharacter - start] = zeroWidthSpace; + } else if (start == (zeroWidthNoBreakSpace & ~(GlyphPage::size - 1))) { + // ZWNBS/BOM must not render at all. + buffer[zeroWidthNoBreakSpace - start] = zeroWidthSpace; + } + } else { + bufferLength = GlyphPage::size * 2; + for (i = 0; i < GlyphPage::size; i++) { + int c = i + start; + buffer[i * 2] = U16_LEAD(c); + buffer[i * 2 + 1] = U16_TRAIL(c); + } + } + + // Now that we have a buffer full of characters, we want to get back an + // array of glyph indices. This part involves calling into the + // platform-specific routine of our glyph map for actually filling in the + // page with the glyphs. Success is not guaranteed. For example, Times + // fails to fill page 260, giving glyph data for only 128 out of 256 + // characters. + bool haveGlyphs; + if (!fontData->isSegmented()) { + m_page = GlyphPage::createForSingleFontData(this, + toSimpleFontData(fontData)); + haveGlyphs = fill(m_page.get(), 0, GlyphPage::size, buffer, + bufferLength, toSimpleFontData(fontData)); + } else { + m_page = GlyphPage::createForMixedFontData(this); + haveGlyphs = false; + + const SegmentedFontData* segmentedFontData = + toSegmentedFontData(fontData); + for (int i = segmentedFontData->numRanges() - 1; i >= 0; i--) { + const FontDataRange& range = segmentedFontData->rangeAt(i); + // all this casting is to ensure all the parameters to min and max + // have the same type, to avoid ambiguous template parameter errors on + // Windows + int from = + max(0, static_cast(range.from()) - static_cast(start)); + int to = + 1 + min(static_cast(range.to()) - static_cast(start), + static_cast(GlyphPage::size) - 1); + if (from >= static_cast(GlyphPage::size) || to <= 0) + continue; + + // If this is a custom font needs to be loaded, do not fill + // the page so that font fallback is used while loading. + RefPtr customData = + range.fontData()->customFontData(); + if (customData && customData->isLoadingFallback()) { + for (int j = from; j < to; j++) { + m_page->setCustomFontToLoad(j, customData.get()); + haveGlyphs = true; } + continue; + } - if (!haveGlyphs) - m_page = nullptr; - } else if (parentPage && parentPage->owner() != m_parent) { - // The page we're overriding may not be owned by our parent node. - // This happens when our parent node provides no useful overrides - // and just copies the pointer to an already-existing page (see - // below). - // - // We want our override to be shared by all nodes that reference - // that page to avoid duplication, and so standardize on having the - // page's owner collect all the overrides. Call getChild on the - // page owner with the desired font data (this will populate - // the page) and then reference it. - m_page = parentPage->owner()->getChild(fontData, pageNumber)->page(); - } else { - // Get the pure page for the fallback font (at level 1 with no - // overrides). getRootChild will always create a page if one - // doesn't exist, but the page doesn't necessarily have glyphs - // (this pointer may be 0). - GlyphPage* fallbackPage = getRootChild(fontData, pageNumber)->page(); - if (!parentPage) { - // When the parent has no glyphs for this page, we can easily - // override it just by supplying the glyphs from our font. - m_page = fallbackPage; - } else if (!fallbackPage) { - // When our font has no glyphs for this page, we can just reference the - // parent page. - m_page = parentPage; - } else { - // Combine the parent's glyphs and ours to form a new more complete page. - m_page = GlyphPage::createForMixedFontData(this); - - // Overlay the parent page on the fallback page. Check if the fallback font - // has added anything. - bool newGlyphs = false; - for (unsigned i = 0; i < GlyphPage::size; i++) { - if (parentPage->glyphAt(i)) { - m_page->setGlyphDataForIndex(i, parentPage->glyphDataForIndex(i)); - } else if (fallbackPage->glyphAt(i)) { - m_page->setGlyphDataForIndex(i, fallbackPage->glyphDataForIndex(i)); - newGlyphs = true; - } - - if (parentPage->customFontToLoadAt(i)) { - m_page->setCustomFontToLoad(i, parentPage->customFontToLoadAt(i)); - } else if (fallbackPage->customFontToLoadAt(i) && !parentPage->glyphAt(i)) { - m_page->setCustomFontToLoad(i, fallbackPage->customFontToLoadAt(i)); - newGlyphs = true; - } - } - - if (!newGlyphs) - // We didn't override anything, so our override is just the parent page. - m_page = parentPage; - } + haveGlyphs |= fill(m_page.get(), from, to - from, + buffer + from * (start < 0x10000 ? 1 : 2), + (to - from) * (start < 0x10000 ? 1 : 2), + range.fontData().get()); } + } + + if (!haveGlyphs) + m_page = nullptr; + } else if (parentPage && parentPage->owner() != m_parent) { + // The page we're overriding may not be owned by our parent node. + // This happens when our parent node provides no useful overrides + // and just copies the pointer to an already-existing page (see + // below). + // + // We want our override to be shared by all nodes that reference + // that page to avoid duplication, and so standardize on having the + // page's owner collect all the overrides. Call getChild on the + // page owner with the desired font data (this will populate + // the page) and then reference it. + m_page = parentPage->owner()->getChild(fontData, pageNumber)->page(); } else { - // System fallback. Initialized with the parent's page here, as individual - // entries may use different fonts depending on character. If the Font - // ever finds it needs a glyph out of the system fallback page, it will - // ask the system for the best font to use and fill that glyph in for us. - if (parentPage) - m_page = parentPage->createCopiedSystemFallbackPage(this); - else - m_page = GlyphPage::createForMixedFontData(this); + // Get the pure page for the fallback font (at level 1 with no + // overrides). getRootChild will always create a page if one + // doesn't exist, but the page doesn't necessarily have glyphs + // (this pointer may be 0). + GlyphPage* fallbackPage = getRootChild(fontData, pageNumber)->page(); + if (!parentPage) { + // When the parent has no glyphs for this page, we can easily + // override it just by supplying the glyphs from our font. + m_page = fallbackPage; + } else if (!fallbackPage) { + // When our font has no glyphs for this page, we can just reference the + // parent page. + m_page = parentPage; + } else { + // Combine the parent's glyphs and ours to form a new more complete + // page. + m_page = GlyphPage::createForMixedFontData(this); + + // Overlay the parent page on the fallback page. Check if the fallback + // font has added anything. + bool newGlyphs = false; + for (unsigned i = 0; i < GlyphPage::size; i++) { + if (parentPage->glyphAt(i)) { + m_page->setGlyphDataForIndex(i, parentPage->glyphDataForIndex(i)); + } else if (fallbackPage->glyphAt(i)) { + m_page->setGlyphDataForIndex(i, fallbackPage->glyphDataForIndex(i)); + newGlyphs = true; + } + + if (parentPage->customFontToLoadAt(i)) { + m_page->setCustomFontToLoad(i, parentPage->customFontToLoadAt(i)); + } else if (fallbackPage->customFontToLoadAt(i) && + !parentPage->glyphAt(i)) { + m_page->setCustomFontToLoad(i, fallbackPage->customFontToLoadAt(i)); + newGlyphs = true; + } + } + + if (!newGlyphs) + // We didn't override anything, so our override is just the parent + // page. + m_page = parentPage; + } } + } else { + // System fallback. Initialized with the parent's page here, as individual + // entries may use different fonts depending on character. If the Font + // ever finds it needs a glyph out of the system fallback page, it will + // ask the system for the best font to use and fill that glyph in for us. + if (parentPage) + m_page = parentPage->createCopiedSystemFallbackPage(this); + else + m_page = GlyphPage::createForMixedFontData(this); + } } -GlyphPageTreeNode* GlyphPageTreeNode::getChild(const FontData* fontData, unsigned pageNumber) -{ - ASSERT(fontData || !m_isSystemFallback); - ASSERT(pageNumber == m_pageNumber); +GlyphPageTreeNode* GlyphPageTreeNode::getChild(const FontData* fontData, + unsigned pageNumber) { + ASSERT(fontData || !m_isSystemFallback); + ASSERT(pageNumber == m_pageNumber); - if (GlyphPageTreeNode* foundChild = fontData ? m_children.get(fontData) : m_systemFallbackChild.get()) - return foundChild; + if (GlyphPageTreeNode* foundChild = + fontData ? m_children.get(fontData) : m_systemFallbackChild.get()) + return foundChild; - GlyphPageTreeNode* child = new GlyphPageTreeNode; - child->m_parent = this; - child->m_level = m_level + 1; - if (fontData && fontData->isCustomFont()) { - for (GlyphPageTreeNode* curr = this; curr; curr = curr->m_parent) - curr->m_customFontCount++; - } + GlyphPageTreeNode* child = new GlyphPageTreeNode; + child->m_parent = this; + child->m_level = m_level + 1; + if (fontData && fontData->isCustomFont()) { + for (GlyphPageTreeNode* curr = this; curr; curr = curr->m_parent) + curr->m_customFontCount++; + } #if ENABLE(ASSERT) - child->m_pageNumber = m_pageNumber; + child->m_pageNumber = m_pageNumber; #endif - if (fontData) { - m_children.set(fontData, adoptPtr(child)); - fontData->setMaxGlyphPageTreeLevel(max(fontData->maxGlyphPageTreeLevel(), child->m_level)); - } else { - m_systemFallbackChild = adoptPtr(child); - child->m_isSystemFallback = true; - } - child->initializePage(fontData, pageNumber); - return child; + if (fontData) { + m_children.set(fontData, adoptPtr(child)); + fontData->setMaxGlyphPageTreeLevel( + max(fontData->maxGlyphPageTreeLevel(), child->m_level)); + } else { + m_systemFallbackChild = adoptPtr(child); + child->m_isSystemFallback = true; + } + child->initializePage(fontData, pageNumber); + return child; } -void GlyphPageTreeNode::pruneCustomFontData(const FontData* fontData) -{ - if (!fontData || !m_customFontCount) - return; +void GlyphPageTreeNode::pruneCustomFontData(const FontData* fontData) { + if (!fontData || !m_customFontCount) + return; - // Prune any branch that contains this FontData. - if (OwnPtr node = m_children.take(fontData)) { - if (unsigned customFontCount = node->m_customFontCount + 1) { - for (GlyphPageTreeNode* curr = this; curr; curr = curr->m_parent) - curr->m_customFontCount -= customFontCount; - } + // Prune any branch that contains this FontData. + if (OwnPtr node = m_children.take(fontData)) { + if (unsigned customFontCount = node->m_customFontCount + 1) { + for (GlyphPageTreeNode* curr = this; curr; curr = curr->m_parent) + curr->m_customFontCount -= customFontCount; } + } - // Check any branches that remain that still have custom fonts underneath them. - if (!m_customFontCount) - return; + // Check any branches that remain that still have custom fonts underneath + // them. + if (!m_customFontCount) + return; - GlyphPageTreeNodeMap::iterator end = m_children.end(); - for (GlyphPageTreeNodeMap::iterator it = m_children.begin(); it != end; ++it) - it->value->pruneCustomFontData(fontData); + GlyphPageTreeNodeMap::iterator end = m_children.end(); + for (GlyphPageTreeNodeMap::iterator it = m_children.begin(); it != end; ++it) + it->value->pruneCustomFontData(fontData); } -void GlyphPageTreeNode::pruneFontData(const SimpleFontData* fontData, unsigned level) -{ - ASSERT(fontData); +void GlyphPageTreeNode::pruneFontData(const SimpleFontData* fontData, + unsigned level) { + ASSERT(fontData); - // Prune fall back child (if any) of this font. - if (m_systemFallbackChild && m_systemFallbackChild->m_page) - m_systemFallbackChild->m_page->removeFontDataFromSystemFallbackPage(fontData); + // Prune fall back child (if any) of this font. + if (m_systemFallbackChild && m_systemFallbackChild->m_page) + m_systemFallbackChild->m_page->removeFontDataFromSystemFallbackPage( + fontData); - // Prune any branch that contains this FontData. - if (OwnPtr node = m_children.take(fontData)) { - if (unsigned customFontCount = node->m_customFontCount) { - for (GlyphPageTreeNode* curr = this; curr; curr = curr->m_parent) - curr->m_customFontCount -= customFontCount; - } + // Prune any branch that contains this FontData. + if (OwnPtr node = m_children.take(fontData)) { + if (unsigned customFontCount = node->m_customFontCount) { + for (GlyphPageTreeNode* curr = this; curr; curr = curr->m_parent) + curr->m_customFontCount -= customFontCount; } + } - level++; - if (level > fontData->maxGlyphPageTreeLevel()) - return; + level++; + if (level > fontData->maxGlyphPageTreeLevel()) + return; - GlyphPageTreeNodeMap::iterator end = m_children.end(); - for (GlyphPageTreeNodeMap::iterator it = m_children.begin(); it != end; ++it) - it->value->pruneFontData(fontData, level); + GlyphPageTreeNodeMap::iterator end = m_children.end(); + for (GlyphPageTreeNodeMap::iterator it = m_children.begin(); it != end; ++it) + it->value->pruneFontData(fontData, level); } #ifndef NDEBUG - void GlyphPageTreeNode::showSubtree() - { - Vector indent(level()); - indent.fill('\t', level()); - indent.append(0); - - GlyphPageTreeNodeMap::iterator end = m_children.end(); - for (GlyphPageTreeNodeMap::iterator it = m_children.begin(); it != end; ++it) { - printf("%s\t%p %s\n", indent.data(), it->key, it->key->description().utf8().data()); - it->value->showSubtree(); - } - if (m_systemFallbackChild) { - printf("%s\t* fallback\n", indent.data()); - m_systemFallbackChild->showSubtree(); - } - } +void GlyphPageTreeNode::showSubtree() { + Vector indent(level()); + indent.fill('\t', level()); + indent.append(0); + + GlyphPageTreeNodeMap::iterator end = m_children.end(); + for (GlyphPageTreeNodeMap::iterator it = m_children.begin(); it != end; + ++it) { + printf("%s\t%p %s\n", indent.data(), it->key, + it->key->description().utf8().data()); + it->value->showSubtree(); + } + if (m_systemFallbackChild) { + printf("%s\t* fallback\n", indent.data()); + m_systemFallbackChild->showSubtree(); + } +} #endif -} // namespace blink +} // namespace blink #ifndef NDEBUG -void showGlyphPageTrees() -{ - printf("Page 0:\n"); - showGlyphPageTree(0); - HashMap::iterator end = blink::GlyphPageTreeNode::roots->end(); - for (HashMap::iterator it = blink::GlyphPageTreeNode::roots->begin(); it != end; ++it) { - printf("\nPage %d:\n", it->key); - showGlyphPageTree(it->key); - } +void showGlyphPageTrees() { + printf("Page 0:\n"); + showGlyphPageTree(0); + HashMap::iterator end = + blink::GlyphPageTreeNode::roots->end(); + for (HashMap::iterator it = + blink::GlyphPageTreeNode::roots->begin(); + it != end; ++it) { + printf("\nPage %d:\n", it->key); + showGlyphPageTree(it->key); + } } -void showGlyphPageTree(unsigned pageNumber) -{ - blink::GlyphPageTreeNode::getRoot(pageNumber)->showSubtree(); +void showGlyphPageTree(unsigned pageNumber) { + blink::GlyphPageTreeNode::getRoot(pageNumber)->showSubtree(); } #endif diff --git a/sky/engine/platform/fonts/GlyphPageTreeNode.h b/sky/engine/platform/fonts/GlyphPageTreeNode.h index c67762d316bea..f04f0cdc8645a 100644 --- a/sky/engine/platform/fonts/GlyphPageTreeNode.h +++ b/sky/engine/platform/fonts/GlyphPageTreeNode.h @@ -47,15 +47,16 @@ namespace blink { class FontData; class SimpleFontData; -// The glyph page tree is a data structure that maps (FontData, glyph page number) -// to a GlyphPage. Level 0 (the "root") is special. There is one root +// The glyph page tree is a data structure that maps (FontData, glyph page +// number) to a GlyphPage. Level 0 (the "root") is special. There is one root // GlyphPageTreeNode for each glyph page number. The roots do not have a // GlyphPage associated with them, and their initializePage() function is never // called to fill the glyphs. // // Each root node maps a FontData pointer to another GlyphPageTreeNode at -// level 1 (the "root child") that stores the actual glyphs for a specific font data. -// These nodes will only have a GlyphPage if they have glyphs for that range. +// level 1 (the "root child") that stores the actual glyphs for a specific font +// data. These nodes will only have a GlyphPage if they have glyphs for that +// range. // // Levels greater than one correspond to subsequent levels of the fallback list // for that font. These levels override their parent's page of glyphs by @@ -68,75 +69,80 @@ class SimpleFontData; // but on demand for each glyph, because the system may need to use different // fallback fonts for each. This lazy population is done by the Font. class PLATFORM_EXPORT GlyphPageTreeNode { - WTF_MAKE_FAST_ALLOCATED; WTF_MAKE_NONCOPYABLE(GlyphPageTreeNode); -public: - static GlyphPageTreeNode* getRootChild(const FontData* fontData, unsigned pageNumber) - { - return getRoot(pageNumber)->getChild(fontData, pageNumber); - } + WTF_MAKE_FAST_ALLOCATED; + WTF_MAKE_NONCOPYABLE(GlyphPageTreeNode); - static void pruneTreeCustomFontData(const FontData*); - static void pruneTreeFontData(const SimpleFontData*); + public: + static GlyphPageTreeNode* getRootChild(const FontData* fontData, + unsigned pageNumber) { + return getRoot(pageNumber)->getChild(fontData, pageNumber); + } - void pruneCustomFontData(const FontData*); - void pruneFontData(const SimpleFontData*, unsigned level = 0); + static void pruneTreeCustomFontData(const FontData*); + static void pruneTreeFontData(const SimpleFontData*); - GlyphPageTreeNode* parent() const { return m_parent; } - GlyphPageTreeNode* getChild(const FontData*, unsigned pageNumber); + void pruneCustomFontData(const FontData*); + void pruneFontData(const SimpleFontData*, unsigned level = 0); - // Returns a page of glyphs (or NULL if there are no glyphs in this page's character range). - GlyphPage* page() const { return m_page.get(); } + GlyphPageTreeNode* parent() const { return m_parent; } + GlyphPageTreeNode* getChild(const FontData*, unsigned pageNumber); - // Returns the level of this node. See class-level comment. - unsigned level() const { return m_level; } + // Returns a page of glyphs (or NULL if there are no glyphs in this page's + // character range). + GlyphPage* page() const { return m_page.get(); } - // The system fallback font has special rules (see above). - bool isSystemFallback() const { return m_isSystemFallback; } + // Returns the level of this node. See class-level comment. + unsigned level() const { return m_level; } - static size_t treeGlyphPageCount(); - size_t pageCount() const; + // The system fallback font has special rules (see above). + bool isSystemFallback() const { return m_isSystemFallback; } -private: - GlyphPageTreeNode() - : m_parent(0) - , m_level(0) - , m_isSystemFallback(false) - , m_customFontCount(0) + static size_t treeGlyphPageCount(); + size_t pageCount() const; + + private: + GlyphPageTreeNode() + : m_parent(0), + m_level(0), + m_isSystemFallback(false), + m_customFontCount(0) #if ENABLE(ASSERT) - , m_pageNumber(0) + , + m_pageNumber(0) #endif - { - } + { + } - static GlyphPageTreeNode* getRoot(unsigned pageNumber); - void initializePage(const FontData*, unsigned pageNumber); + static GlyphPageTreeNode* getRoot(unsigned pageNumber); + void initializePage(const FontData*, unsigned pageNumber); #ifndef NDEBUG - void showSubtree(); + void showSubtree(); #endif - static HashMap* roots; - static GlyphPageTreeNode* pageZeroRoot; + static HashMap* roots; + static GlyphPageTreeNode* pageZeroRoot; - typedef HashMap > GlyphPageTreeNodeMap; + typedef HashMap> + GlyphPageTreeNodeMap; - GlyphPageTreeNodeMap m_children; - GlyphPageTreeNode* m_parent; - RefPtr m_page; - unsigned m_level : 31; - bool m_isSystemFallback : 1; - unsigned m_customFontCount; - OwnPtr m_systemFallbackChild; + GlyphPageTreeNodeMap m_children; + GlyphPageTreeNode* m_parent; + RefPtr m_page; + unsigned m_level : 31; + bool m_isSystemFallback : 1; + unsigned m_customFontCount; + OwnPtr m_systemFallbackChild; #if ENABLE(ASSERT) - unsigned m_pageNumber; + unsigned m_pageNumber; #endif #ifndef NDEBUG - friend void ::showGlyphPageTrees(); - friend void ::showGlyphPageTree(unsigned pageNumber); + friend void ::showGlyphPageTrees(); + friend void ::showGlyphPageTree(unsigned pageNumber); #endif }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_FONTS_GLYPHPAGETREENODE_H_ diff --git a/sky/engine/platform/fonts/GlyphPageTreeNodeTest.cpp b/sky/engine/platform/fonts/GlyphPageTreeNodeTest.cpp index a68d6192059bb..cf3f8fea8dee6 100644 --- a/sky/engine/platform/fonts/GlyphPageTreeNodeTest.cpp +++ b/sky/engine/platform/fonts/GlyphPageTreeNodeTest.cpp @@ -11,215 +11,238 @@ namespace blink { class TestCustomFontData : public CustomFontData { -public: - static PassRefPtr create() { return adoptRef(new TestCustomFontData()); } -private: - TestCustomFontData() { } - virtual bool isLoadingFallback() const override { return true; } + public: + static PassRefPtr create() { + return adoptRef(new TestCustomFontData()); + } + + private: + TestCustomFontData() {} + virtual bool isLoadingFallback() const override { return true; } }; class TestSimpleFontData : public SimpleFontData { -public: - static PassRefPtr create(UChar32 from, UChar32 to) - { - return adoptRef(new TestSimpleFontData(nullptr, from, to)); + public: + static PassRefPtr create(UChar32 from, UChar32 to) { + return adoptRef(new TestSimpleFontData(nullptr, from, to)); + } + + static PassRefPtr createUnloaded(UChar32 from, + UChar32 to) { + return adoptRef( + new TestSimpleFontData(TestCustomFontData::create(), from, to)); + } + + private: + TestSimpleFontData(PassRefPtr customData, + UChar32 from, + UChar32 to) + : SimpleFontData(customData, 10, false, false), m_from(from), m_to(to) {} + + bool fillGlyphPage(GlyphPage* pageToFill, + unsigned offset, + unsigned length, + UChar* buffer, + unsigned bufferLength) const override { + const Glyph kGlyph = 1; + String bufferString(buffer, bufferLength); + unsigned bufferIndex = 0; + bool hasGlyphs = false; + for (unsigned i = 0; i < length; i++) { + UChar32 c = bufferString.characterStartingAt(bufferIndex); + bufferIndex += U16_LENGTH(c); + if (m_from <= c && c <= m_to) { + pageToFill->setGlyphDataForIndex(offset + i, kGlyph, this); + hasGlyphs = true; + } } + return hasGlyphs; + } - static PassRefPtr createUnloaded(UChar32 from, UChar32 to) - { - return adoptRef(new TestSimpleFontData(TestCustomFontData::create(), from, to)); - } - -private: - TestSimpleFontData(PassRefPtr customData, UChar32 from, UChar32 to) - : SimpleFontData(customData, 10, false, false) - , m_from(from) - , m_to(to) - { - } - - bool fillGlyphPage(GlyphPage* pageToFill, unsigned offset, unsigned length, UChar* buffer, unsigned bufferLength) const override - { - const Glyph kGlyph = 1; - String bufferString(buffer, bufferLength); - unsigned bufferIndex = 0; - bool hasGlyphs = false; - for (unsigned i = 0; i < length; i++) { - UChar32 c = bufferString.characterStartingAt(bufferIndex); - bufferIndex += U16_LENGTH(c); - if (m_from <= c && c <= m_to) { - pageToFill->setGlyphDataForIndex(offset + i, kGlyph, this); - hasGlyphs = true; - } - } - return hasGlyphs; - } - - UChar32 m_from; - UChar32 m_to; + UChar32 m_from; + UChar32 m_to; }; -TEST(GlyphPageTreeNode, rootChild) -{ - const unsigned kPageNumber = 0; - size_t pageCountBeforeTest = GlyphPageTreeNode::treeGlyphPageCount(); - { - RefPtr data = TestSimpleFontData::create('a', 'z'); - GlyphPageTreeNode* node = GlyphPageTreeNode::getRootChild(data.get(), kPageNumber); - EXPECT_EQ(pageCountBeforeTest + 1, GlyphPageTreeNode::treeGlyphPageCount()); - EXPECT_TRUE(node->page()->glyphAt('a')); - EXPECT_FALSE(node->page()->glyphAt('A')); - EXPECT_FALSE(node->isSystemFallback()); - EXPECT_EQ(1u, node->level()); - EXPECT_EQ(node, node->page()->owner()); - } - EXPECT_EQ(pageCountBeforeTest, GlyphPageTreeNode::treeGlyphPageCount()); +TEST(GlyphPageTreeNode, rootChild) { + const unsigned kPageNumber = 0; + size_t pageCountBeforeTest = GlyphPageTreeNode::treeGlyphPageCount(); + { + RefPtr data = TestSimpleFontData::create('a', 'z'); + GlyphPageTreeNode* node = + GlyphPageTreeNode::getRootChild(data.get(), kPageNumber); + EXPECT_EQ(pageCountBeforeTest + 1, GlyphPageTreeNode::treeGlyphPageCount()); + EXPECT_TRUE(node->page()->glyphAt('a')); + EXPECT_FALSE(node->page()->glyphAt('A')); + EXPECT_FALSE(node->isSystemFallback()); + EXPECT_EQ(1u, node->level()); + EXPECT_EQ(node, node->page()->owner()); + } + EXPECT_EQ(pageCountBeforeTest, GlyphPageTreeNode::treeGlyphPageCount()); } -TEST(GlyphPageTreeNode, level2) -{ - const unsigned kPageNumber = 0; - size_t pageCountBeforeTest = GlyphPageTreeNode::treeGlyphPageCount(); - { - RefPtr dataAtoC = TestSimpleFontData::create('A', 'C'); - RefPtr dataCtoE = TestSimpleFontData::create('C', 'E'); - GlyphPageTreeNode* node1 = GlyphPageTreeNode::getRootChild(dataAtoC.get(), kPageNumber); - GlyphPageTreeNode* node2 = node1->getChild(dataCtoE.get(), kPageNumber); - EXPECT_EQ(pageCountBeforeTest + 3, GlyphPageTreeNode::treeGlyphPageCount()); - - EXPECT_EQ(2u, node2->level()); - EXPECT_EQ(dataAtoC, node2->page()->glyphDataForCharacter('A').fontData); - EXPECT_EQ(dataAtoC, node2->page()->glyphDataForCharacter('C').fontData); - EXPECT_EQ(dataCtoE, node2->page()->glyphDataForCharacter('E').fontData); - } - EXPECT_EQ(pageCountBeforeTest, GlyphPageTreeNode::treeGlyphPageCount()); +TEST(GlyphPageTreeNode, level2) { + const unsigned kPageNumber = 0; + size_t pageCountBeforeTest = GlyphPageTreeNode::treeGlyphPageCount(); + { + RefPtr dataAtoC = TestSimpleFontData::create('A', 'C'); + RefPtr dataCtoE = TestSimpleFontData::create('C', 'E'); + GlyphPageTreeNode* node1 = + GlyphPageTreeNode::getRootChild(dataAtoC.get(), kPageNumber); + GlyphPageTreeNode* node2 = node1->getChild(dataCtoE.get(), kPageNumber); + EXPECT_EQ(pageCountBeforeTest + 3, GlyphPageTreeNode::treeGlyphPageCount()); + + EXPECT_EQ(2u, node2->level()); + EXPECT_EQ(dataAtoC, node2->page()->glyphDataForCharacter('A').fontData); + EXPECT_EQ(dataAtoC, node2->page()->glyphDataForCharacter('C').fontData); + EXPECT_EQ(dataCtoE, node2->page()->glyphDataForCharacter('E').fontData); + } + EXPECT_EQ(pageCountBeforeTest, GlyphPageTreeNode::treeGlyphPageCount()); } -TEST(GlyphPageTreeNode, segmentedData) -{ - const unsigned kPageNumber = 0; - size_t pageCountBeforeTest = GlyphPageTreeNode::treeGlyphPageCount(); - { - RefPtr dataBtoC = TestSimpleFontData::create('B', 'C'); - RefPtr dataCtoE = TestSimpleFontData::create('C', 'E'); - RefPtr segmentedData = SegmentedFontData::create(); - segmentedData->appendRange(FontDataRange('A', 'C', dataBtoC)); - segmentedData->appendRange(FontDataRange('C', 'D', dataCtoE)); - GlyphPageTreeNode* node = GlyphPageTreeNode::getRootChild(segmentedData.get(), kPageNumber); - - EXPECT_EQ(0, node->page()->glyphDataForCharacter('A').fontData); - EXPECT_EQ(dataBtoC, node->page()->glyphDataForCharacter('B').fontData); - EXPECT_EQ(dataBtoC, node->page()->glyphDataForCharacter('C').fontData); - EXPECT_EQ(dataCtoE, node->page()->glyphDataForCharacter('D').fontData); - EXPECT_EQ(0, node->page()->glyphDataForCharacter('E').fontData); - } - EXPECT_EQ(pageCountBeforeTest, GlyphPageTreeNode::treeGlyphPageCount()); +TEST(GlyphPageTreeNode, segmentedData) { + const unsigned kPageNumber = 0; + size_t pageCountBeforeTest = GlyphPageTreeNode::treeGlyphPageCount(); + { + RefPtr dataBtoC = TestSimpleFontData::create('B', 'C'); + RefPtr dataCtoE = TestSimpleFontData::create('C', 'E'); + RefPtr segmentedData = SegmentedFontData::create(); + segmentedData->appendRange(FontDataRange('A', 'C', dataBtoC)); + segmentedData->appendRange(FontDataRange('C', 'D', dataCtoE)); + GlyphPageTreeNode* node = + GlyphPageTreeNode::getRootChild(segmentedData.get(), kPageNumber); + + EXPECT_EQ(0, node->page()->glyphDataForCharacter('A').fontData); + EXPECT_EQ(dataBtoC, node->page()->glyphDataForCharacter('B').fontData); + EXPECT_EQ(dataBtoC, node->page()->glyphDataForCharacter('C').fontData); + EXPECT_EQ(dataCtoE, node->page()->glyphDataForCharacter('D').fontData); + EXPECT_EQ(0, node->page()->glyphDataForCharacter('E').fontData); + } + EXPECT_EQ(pageCountBeforeTest, GlyphPageTreeNode::treeGlyphPageCount()); } -TEST(GlyphPageTreeNode, outsideBMP) -{ - const unsigned kPageNumber = 0x1f300 / GlyphPage::size; - size_t pageCountBeforeTest = GlyphPageTreeNode::treeGlyphPageCount(); - { - RefPtr data = TestSimpleFontData::create(0x1f310, 0x1f320); - GlyphPageTreeNode* node = GlyphPageTreeNode::getRootChild(data.get(), kPageNumber); - EXPECT_EQ(pageCountBeforeTest + 1, GlyphPageTreeNode::treeGlyphPageCount()); - EXPECT_FALSE(node->page()->glyphForCharacter(0x1f30f)); - EXPECT_TRUE(node->page()->glyphForCharacter(0x1f310)); - EXPECT_TRUE(node->page()->glyphForCharacter(0x1f320)); - EXPECT_FALSE(node->page()->glyphForCharacter(0x1f321)); - } - EXPECT_EQ(pageCountBeforeTest, GlyphPageTreeNode::treeGlyphPageCount()); +TEST(GlyphPageTreeNode, outsideBMP) { + const unsigned kPageNumber = 0x1f300 / GlyphPage::size; + size_t pageCountBeforeTest = GlyphPageTreeNode::treeGlyphPageCount(); + { + RefPtr data = + TestSimpleFontData::create(0x1f310, 0x1f320); + GlyphPageTreeNode* node = + GlyphPageTreeNode::getRootChild(data.get(), kPageNumber); + EXPECT_EQ(pageCountBeforeTest + 1, GlyphPageTreeNode::treeGlyphPageCount()); + EXPECT_FALSE(node->page()->glyphForCharacter(0x1f30f)); + EXPECT_TRUE(node->page()->glyphForCharacter(0x1f310)); + EXPECT_TRUE(node->page()->glyphForCharacter(0x1f320)); + EXPECT_FALSE(node->page()->glyphForCharacter(0x1f321)); + } + EXPECT_EQ(pageCountBeforeTest, GlyphPageTreeNode::treeGlyphPageCount()); } -TEST(GlyphPageTreeNode, customData) -{ - const unsigned kPageNumber = 0; - size_t pageCountBeforeTest = GlyphPageTreeNode::treeGlyphPageCount(); - { - RefPtr dataAtoC = TestSimpleFontData::createUnloaded('A', 'C'); - RefPtr dataBtoD = TestSimpleFontData::create('B', 'D'); - RefPtr dataCtoE = TestSimpleFontData::createUnloaded('C', 'E'); - RefPtr segmentedData = SegmentedFontData::create(); - segmentedData->appendRange(FontDataRange('A', 'C', dataAtoC)); - segmentedData->appendRange(FontDataRange('B', 'D', dataBtoD)); - segmentedData->appendRange(FontDataRange('C', 'E', dataCtoE)); - GlyphPageTreeNode* node = GlyphPageTreeNode::getRootChild(segmentedData.get(), kPageNumber); - - EXPECT_EQ(0, node->page()->glyphDataForCharacter('A').fontData); - EXPECT_EQ(dataBtoD, node->page()->glyphDataForCharacter('B').fontData); - EXPECT_EQ(dataBtoD, node->page()->glyphDataForCharacter('C').fontData); - EXPECT_EQ(dataBtoD, node->page()->glyphDataForCharacter('D').fontData); - EXPECT_EQ(0, node->page()->glyphDataForCharacter('E').fontData); - - EXPECT_EQ(dataAtoC->customFontData(), node->page()->customFontToLoadAt('A')); - EXPECT_EQ(dataAtoC->customFontData(), node->page()->customFontToLoadAt('B')); - EXPECT_EQ(dataAtoC->customFontData(), node->page()->customFontToLoadAt('C')); - EXPECT_EQ(0, node->page()->customFontToLoadAt('D')); - EXPECT_EQ(dataCtoE->customFontData(), node->page()->customFontToLoadAt('E')); - } - EXPECT_EQ(pageCountBeforeTest, GlyphPageTreeNode::treeGlyphPageCount()); +TEST(GlyphPageTreeNode, customData) { + const unsigned kPageNumber = 0; + size_t pageCountBeforeTest = GlyphPageTreeNode::treeGlyphPageCount(); + { + RefPtr dataAtoC = + TestSimpleFontData::createUnloaded('A', 'C'); + RefPtr dataBtoD = TestSimpleFontData::create('B', 'D'); + RefPtr dataCtoE = + TestSimpleFontData::createUnloaded('C', 'E'); + RefPtr segmentedData = SegmentedFontData::create(); + segmentedData->appendRange(FontDataRange('A', 'C', dataAtoC)); + segmentedData->appendRange(FontDataRange('B', 'D', dataBtoD)); + segmentedData->appendRange(FontDataRange('C', 'E', dataCtoE)); + GlyphPageTreeNode* node = + GlyphPageTreeNode::getRootChild(segmentedData.get(), kPageNumber); + + EXPECT_EQ(0, node->page()->glyphDataForCharacter('A').fontData); + EXPECT_EQ(dataBtoD, node->page()->glyphDataForCharacter('B').fontData); + EXPECT_EQ(dataBtoD, node->page()->glyphDataForCharacter('C').fontData); + EXPECT_EQ(dataBtoD, node->page()->glyphDataForCharacter('D').fontData); + EXPECT_EQ(0, node->page()->glyphDataForCharacter('E').fontData); + + EXPECT_EQ(dataAtoC->customFontData(), + node->page()->customFontToLoadAt('A')); + EXPECT_EQ(dataAtoC->customFontData(), + node->page()->customFontToLoadAt('B')); + EXPECT_EQ(dataAtoC->customFontData(), + node->page()->customFontToLoadAt('C')); + EXPECT_EQ(0, node->page()->customFontToLoadAt('D')); + EXPECT_EQ(dataCtoE->customFontData(), + node->page()->customFontToLoadAt('E')); + } + EXPECT_EQ(pageCountBeforeTest, GlyphPageTreeNode::treeGlyphPageCount()); } -TEST(GlyphPageTreeNode, customDataWithMultiplePages) -{ - const unsigned kPageNumber = 0; - size_t pageCountBeforeTest = GlyphPageTreeNode::treeGlyphPageCount(); - { - RefPtr dataAtoC = TestSimpleFontData::createUnloaded('A', 'C'); - RefPtr dataBtoD = TestSimpleFontData::create('B', 'D'); - RefPtr dataCtoE = TestSimpleFontData::createUnloaded('C', 'E'); - RefPtr segmentedData1 = SegmentedFontData::create(); - RefPtr segmentedData2 = SegmentedFontData::create(); - RefPtr segmentedData3 = SegmentedFontData::create(); - segmentedData1->appendRange(FontDataRange('A', 'C', dataAtoC)); - segmentedData2->appendRange(FontDataRange('B', 'D', dataBtoD)); - segmentedData3->appendRange(FontDataRange('C', 'E', dataCtoE)); - GlyphPageTreeNode* node1 = GlyphPageTreeNode::getRootChild(segmentedData1.get(), kPageNumber); - GlyphPageTreeNode* node2 = node1->getChild(segmentedData2.get(), kPageNumber); - GlyphPageTreeNode* node3 = node2->getChild(segmentedData3.get(), kPageNumber); - - EXPECT_EQ(0, node3->page()->glyphDataForCharacter('A').fontData); - EXPECT_EQ(dataBtoD, node3->page()->glyphDataForCharacter('B').fontData); - EXPECT_EQ(dataBtoD, node3->page()->glyphDataForCharacter('C').fontData); - EXPECT_EQ(dataBtoD, node3->page()->glyphDataForCharacter('D').fontData); - EXPECT_EQ(0, node3->page()->glyphDataForCharacter('E').fontData); - - EXPECT_EQ(dataAtoC->customFontData(), node3->page()->customFontToLoadAt('A')); - EXPECT_EQ(dataAtoC->customFontData(), node3->page()->customFontToLoadAt('B')); - EXPECT_EQ(dataAtoC->customFontData(), node3->page()->customFontToLoadAt('C')); - EXPECT_EQ(0, node3->page()->customFontToLoadAt('D')); - EXPECT_EQ(dataCtoE->customFontData(), node3->page()->customFontToLoadAt('E')); - } - EXPECT_EQ(pageCountBeforeTest, GlyphPageTreeNode::treeGlyphPageCount()); +TEST(GlyphPageTreeNode, customDataWithMultiplePages) { + const unsigned kPageNumber = 0; + size_t pageCountBeforeTest = GlyphPageTreeNode::treeGlyphPageCount(); + { + RefPtr dataAtoC = + TestSimpleFontData::createUnloaded('A', 'C'); + RefPtr dataBtoD = TestSimpleFontData::create('B', 'D'); + RefPtr dataCtoE = + TestSimpleFontData::createUnloaded('C', 'E'); + RefPtr segmentedData1 = SegmentedFontData::create(); + RefPtr segmentedData2 = SegmentedFontData::create(); + RefPtr segmentedData3 = SegmentedFontData::create(); + segmentedData1->appendRange(FontDataRange('A', 'C', dataAtoC)); + segmentedData2->appendRange(FontDataRange('B', 'D', dataBtoD)); + segmentedData3->appendRange(FontDataRange('C', 'E', dataCtoE)); + GlyphPageTreeNode* node1 = + GlyphPageTreeNode::getRootChild(segmentedData1.get(), kPageNumber); + GlyphPageTreeNode* node2 = + node1->getChild(segmentedData2.get(), kPageNumber); + GlyphPageTreeNode* node3 = + node2->getChild(segmentedData3.get(), kPageNumber); + + EXPECT_EQ(0, node3->page()->glyphDataForCharacter('A').fontData); + EXPECT_EQ(dataBtoD, node3->page()->glyphDataForCharacter('B').fontData); + EXPECT_EQ(dataBtoD, node3->page()->glyphDataForCharacter('C').fontData); + EXPECT_EQ(dataBtoD, node3->page()->glyphDataForCharacter('D').fontData); + EXPECT_EQ(0, node3->page()->glyphDataForCharacter('E').fontData); + + EXPECT_EQ(dataAtoC->customFontData(), + node3->page()->customFontToLoadAt('A')); + EXPECT_EQ(dataAtoC->customFontData(), + node3->page()->customFontToLoadAt('B')); + EXPECT_EQ(dataAtoC->customFontData(), + node3->page()->customFontToLoadAt('C')); + EXPECT_EQ(0, node3->page()->customFontToLoadAt('D')); + EXPECT_EQ(dataCtoE->customFontData(), + node3->page()->customFontToLoadAt('E')); + } + EXPECT_EQ(pageCountBeforeTest, GlyphPageTreeNode::treeGlyphPageCount()); } -TEST(GlyphPageTreeNode, systemFallback) -{ - const unsigned kPageNumber = 0; - size_t pageCountBeforeTest = GlyphPageTreeNode::treeGlyphPageCount(); - { - RefPtr dataAtoC = TestSimpleFontData::createUnloaded('A', 'C'); - RefPtr dataBtoD = TestSimpleFontData::create('B', 'D'); - RefPtr segmentedData = SegmentedFontData::create(); - segmentedData->appendRange(FontDataRange('A', 'C', dataAtoC)); - GlyphPageTreeNode* node1 = GlyphPageTreeNode::getRootChild(segmentedData.get(), kPageNumber); - GlyphPageTreeNode* node2 = node1->getChild(dataBtoD.get(), kPageNumber); - GlyphPageTreeNode* node3 = node2->getChild(0, kPageNumber); - - EXPECT_TRUE(node3->isSystemFallback()); - - EXPECT_EQ(0, node3->page()->glyphDataForCharacter('A').fontData); - EXPECT_EQ(dataBtoD, node3->page()->glyphDataForCharacter('B').fontData); - EXPECT_EQ(dataBtoD, node3->page()->glyphDataForCharacter('C').fontData); - EXPECT_EQ(dataBtoD, node3->page()->glyphDataForCharacter('D').fontData); - - EXPECT_EQ(dataAtoC->customFontData(), node3->page()->customFontToLoadAt('A')); - EXPECT_EQ(dataAtoC->customFontData(), node3->page()->customFontToLoadAt('B')); - EXPECT_EQ(dataAtoC->customFontData(), node3->page()->customFontToLoadAt('C')); - EXPECT_EQ(0, node3->page()->customFontToLoadAt('D')); - } - EXPECT_EQ(pageCountBeforeTest, GlyphPageTreeNode::treeGlyphPageCount()); +TEST(GlyphPageTreeNode, systemFallback) { + const unsigned kPageNumber = 0; + size_t pageCountBeforeTest = GlyphPageTreeNode::treeGlyphPageCount(); + { + RefPtr dataAtoC = + TestSimpleFontData::createUnloaded('A', 'C'); + RefPtr dataBtoD = TestSimpleFontData::create('B', 'D'); + RefPtr segmentedData = SegmentedFontData::create(); + segmentedData->appendRange(FontDataRange('A', 'C', dataAtoC)); + GlyphPageTreeNode* node1 = + GlyphPageTreeNode::getRootChild(segmentedData.get(), kPageNumber); + GlyphPageTreeNode* node2 = node1->getChild(dataBtoD.get(), kPageNumber); + GlyphPageTreeNode* node3 = node2->getChild(0, kPageNumber); + + EXPECT_TRUE(node3->isSystemFallback()); + + EXPECT_EQ(0, node3->page()->glyphDataForCharacter('A').fontData); + EXPECT_EQ(dataBtoD, node3->page()->glyphDataForCharacter('B').fontData); + EXPECT_EQ(dataBtoD, node3->page()->glyphDataForCharacter('C').fontData); + EXPECT_EQ(dataBtoD, node3->page()->glyphDataForCharacter('D').fontData); + + EXPECT_EQ(dataAtoC->customFontData(), + node3->page()->customFontToLoadAt('A')); + EXPECT_EQ(dataAtoC->customFontData(), + node3->page()->customFontToLoadAt('B')); + EXPECT_EQ(dataAtoC->customFontData(), + node3->page()->customFontToLoadAt('C')); + EXPECT_EQ(0, node3->page()->customFontToLoadAt('D')); + } + EXPECT_EQ(pageCountBeforeTest, GlyphPageTreeNode::treeGlyphPageCount()); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/fonts/Latin1TextIterator.h b/sky/engine/platform/fonts/Latin1TextIterator.h index 7b06f0ab9d66c..249658087aad2 100644 --- a/sky/engine/platform/fonts/Latin1TextIterator.h +++ b/sky/engine/platform/fonts/Latin1TextIterator.h @@ -27,41 +27,42 @@ namespace blink { class Latin1TextIterator { -public: - // The passed in LChar pointer starts at 'currentCharacter'. The iterator operates on the range [currentCharacter, lastCharacter]. - // 'endCharacter' denotes the maximum length of the UChar array, which might exceed 'lastCharacter'. - Latin1TextIterator(const LChar* characters, int currentCharacter, int lastCharacter, int /*endCharacter*/) - : m_characters(characters) - , m_currentCharacter(currentCharacter) - , m_lastCharacter(lastCharacter) - { - } + public: + // The passed in LChar pointer starts at 'currentCharacter'. The iterator + // operates on the range [currentCharacter, lastCharacter]. 'endCharacter' + // denotes the maximum length of the UChar array, which might exceed + // 'lastCharacter'. + Latin1TextIterator(const LChar* characters, + int currentCharacter, + int lastCharacter, + int /*endCharacter*/) + : m_characters(characters), + m_currentCharacter(currentCharacter), + m_lastCharacter(lastCharacter) {} - bool consume(UChar32& character, unsigned& clusterLength) - { - if (m_currentCharacter >= m_lastCharacter) - return false; + bool consume(UChar32& character, unsigned& clusterLength) { + if (m_currentCharacter >= m_lastCharacter) + return false; - character = *m_characters; - clusterLength = 1; - return true; - } + character = *m_characters; + clusterLength = 1; + return true; + } - void advance(unsigned advanceLength) - { - m_characters += advanceLength; - m_currentCharacter += advanceLength; - } + void advance(unsigned advanceLength) { + m_characters += advanceLength; + m_currentCharacter += advanceLength; + } - int currentCharacter() const { return m_currentCharacter; } - const LChar* characters() const { return m_characters; } + int currentCharacter() const { return m_currentCharacter; } + const LChar* characters() const { return m_characters; } -private: - const LChar* m_characters; - int m_currentCharacter; - int m_lastCharacter; + private: + const LChar* m_characters; + int m_currentCharacter; + int m_lastCharacter; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_FONTS_LATIN1TEXTITERATOR_H_ diff --git a/sky/engine/platform/fonts/SegmentedFontData.cpp b/sky/engine/platform/fonts/SegmentedFontData.cpp index b8579c1e10833..f92694c4fa265 100644 --- a/sky/engine/platform/fonts/SegmentedFontData.cpp +++ b/sky/engine/platform/fonts/SegmentedFontData.cpp @@ -31,78 +31,74 @@ namespace blink { -SegmentedFontData::~SegmentedFontData() -{ - GlyphPageTreeNode::pruneTreeCustomFontData(this); +SegmentedFontData::~SegmentedFontData() { + GlyphPageTreeNode::pruneTreeCustomFontData(this); } -const SimpleFontData* SegmentedFontData::fontDataForCharacter(UChar32 c) const -{ - Vector::const_iterator end = m_ranges.end(); - for (Vector::const_iterator it = m_ranges.begin(); it != end; ++it) { - if (it->from() <= c && it->to() >= c) - return it->fontData().get(); - } - return m_ranges[0].fontData().get(); +const SimpleFontData* SegmentedFontData::fontDataForCharacter(UChar32 c) const { + Vector::const_iterator end = m_ranges.end(); + for (Vector::const_iterator it = m_ranges.begin(); it != end; + ++it) { + if (it->from() <= c && it->to() >= c) + return it->fontData().get(); + } + return m_ranges[0].fontData().get(); } -bool SegmentedFontData::containsCharacter(UChar32 c) const -{ - Vector::const_iterator end = m_ranges.end(); - for (Vector::const_iterator it = m_ranges.begin(); it != end; ++it) { - if (c >= it->from() && c <= it->to()) - return true; - } - return false; +bool SegmentedFontData::containsCharacter(UChar32 c) const { + Vector::const_iterator end = m_ranges.end(); + for (Vector::const_iterator it = m_ranges.begin(); it != end; + ++it) { + if (c >= it->from() && c <= it->to()) + return true; + } + return false; } -bool SegmentedFontData::isCustomFont() const -{ - // All segmented fonts are custom fonts. - return true; +bool SegmentedFontData::isCustomFont() const { + // All segmented fonts are custom fonts. + return true; } -bool SegmentedFontData::isLoading() const -{ - Vector::const_iterator end = m_ranges.end(); - for (Vector::const_iterator it = m_ranges.begin(); it != end; ++it) { - if (it->fontData()->isLoading()) - return true; - } - return false; +bool SegmentedFontData::isLoading() const { + Vector::const_iterator end = m_ranges.end(); + for (Vector::const_iterator it = m_ranges.begin(); it != end; + ++it) { + if (it->fontData()->isLoading()) + return true; + } + return false; } // Returns true if any of the sub fonts are loadingFallback. -bool SegmentedFontData::isLoadingFallback() const -{ - Vector::const_iterator end = m_ranges.end(); - for (Vector::const_iterator it = m_ranges.begin(); it != end; ++it) { - if (it->fontData()->isLoadingFallback()) - return true; - } - return false; +bool SegmentedFontData::isLoadingFallback() const { + Vector::const_iterator end = m_ranges.end(); + for (Vector::const_iterator it = m_ranges.begin(); it != end; + ++it) { + if (it->fontData()->isLoadingFallback()) + return true; + } + return false; } -bool SegmentedFontData::isSegmented() const -{ - return true; +bool SegmentedFontData::isSegmented() const { + return true; } -bool SegmentedFontData::shouldSkipDrawing() const -{ - Vector::const_iterator end = m_ranges.end(); - for (Vector::const_iterator it = m_ranges.begin(); it != end; ++it) { - if (it->fontData()->shouldSkipDrawing()) - return true; - } - return false; +bool SegmentedFontData::shouldSkipDrawing() const { + Vector::const_iterator end = m_ranges.end(); + for (Vector::const_iterator it = m_ranges.begin(); it != end; + ++it) { + if (it->fontData()->shouldSkipDrawing()) + return true; + } + return false; } #ifndef NDEBUG -String SegmentedFontData::description() const -{ - return "[segmented font]"; +String SegmentedFontData::description() const { + return "[segmented font]"; } #endif -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/fonts/SegmentedFontData.h b/sky/engine/platform/fonts/SegmentedFontData.h index c73ea8d4e3925..899f612daac43 100644 --- a/sky/engine/platform/fonts/SegmentedFontData.h +++ b/sky/engine/platform/fonts/SegmentedFontData.h @@ -35,55 +35,53 @@ namespace blink { class SimpleFontData; struct FontDataRange { - FontDataRange(UChar32 from, UChar32 to, PassRefPtr fontData) - : m_from(from) - , m_to(to) - , m_fontData(fontData) - { - } - - UChar32 from() const { return m_from; } - UChar32 to() const { return m_to; } - bool isEntireRange() const { return !m_from && m_to >= 0x10ffff; } - PassRefPtr fontData() const { return m_fontData; } - -private: - UChar32 m_from; - UChar32 m_to; - RefPtr m_fontData; + FontDataRange(UChar32 from, UChar32 to, PassRefPtr fontData) + : m_from(from), m_to(to), m_fontData(fontData) {} + + UChar32 from() const { return m_from; } + UChar32 to() const { return m_to; } + bool isEntireRange() const { return !m_from && m_to >= 0x10ffff; } + PassRefPtr fontData() const { return m_fontData; } + + private: + UChar32 m_from; + UChar32 m_to; + RefPtr m_fontData; }; class PLATFORM_EXPORT SegmentedFontData : public FontData { -public: - static PassRefPtr create() { return adoptRef(new SegmentedFontData); } + public: + static PassRefPtr create() { + return adoptRef(new SegmentedFontData); + } - virtual ~SegmentedFontData(); + virtual ~SegmentedFontData(); - void appendRange(const FontDataRange& range) { m_ranges.append(range); } - unsigned numRanges() const { return m_ranges.size(); } - const FontDataRange& rangeAt(unsigned i) const { return m_ranges[i]; } - bool containsCharacter(UChar32) const; + void appendRange(const FontDataRange& range) { m_ranges.append(range); } + unsigned numRanges() const { return m_ranges.size(); } + const FontDataRange& rangeAt(unsigned i) const { return m_ranges[i]; } + bool containsCharacter(UChar32) const; #ifndef NDEBUG - virtual String description() const override; + virtual String description() const override; #endif -private: - SegmentedFontData() { } + private: + SegmentedFontData() {} - virtual const SimpleFontData* fontDataForCharacter(UChar32) const override; + virtual const SimpleFontData* fontDataForCharacter(UChar32) const override; - virtual bool isCustomFont() const override; - virtual bool isLoading() const override; - virtual bool isLoadingFallback() const override; - virtual bool isSegmented() const override; - virtual bool shouldSkipDrawing() const override; + virtual bool isCustomFont() const override; + virtual bool isLoading() const override; + virtual bool isLoadingFallback() const override; + virtual bool isSegmented() const override; + virtual bool shouldSkipDrawing() const override; - Vector m_ranges; + Vector m_ranges; }; DEFINE_FONT_DATA_TYPE_CASTS(SegmentedFontData, true); -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_FONTS_SEGMENTEDFONTDATA_H_ diff --git a/sky/engine/platform/fonts/SimpleFontData.cpp b/sky/engine/platform/fonts/SimpleFontData.cpp index 71c838701a56d..bf19a2d067f5a 100644 --- a/sky/engine/platform/fonts/SimpleFontData.cpp +++ b/sky/engine/platform/fonts/SimpleFontData.cpp @@ -36,227 +36,237 @@ namespace blink { const float smallCapsFontSizeMultiplier = 0.7f; const float emphasisMarkFontSizeMultiplier = 0.5f; -SimpleFontData::SimpleFontData(const FontPlatformData& platformData, PassRefPtr customData, bool isTextOrientationFallback) - : m_maxCharWidth(-1) - , m_avgCharWidth(-1) - , m_platformData(platformData) - , m_treatAsFixedPitch(false) - , m_isTextOrientationFallback(isTextOrientationFallback) - , m_isBrokenIdeographFallback(false) +SimpleFontData::SimpleFontData(const FontPlatformData& platformData, + PassRefPtr customData, + bool isTextOrientationFallback) + : m_maxCharWidth(-1), + m_avgCharWidth(-1), + m_platformData(platformData), + m_treatAsFixedPitch(false), + m_isTextOrientationFallback(isTextOrientationFallback), + m_isBrokenIdeographFallback(false) #if ENABLE(OPENTYPE_VERTICAL) - , m_verticalData(nullptr) + , + m_verticalData(nullptr) #endif - , m_hasVerticalGlyphs(false) - , m_customFontData(customData) -{ - platformInit(); - platformGlyphInit(); - platformCharWidthInit(); + , + m_hasVerticalGlyphs(false), + m_customFontData(customData) { + platformInit(); + platformGlyphInit(); + platformCharWidthInit(); #if ENABLE(OPENTYPE_VERTICAL) - if (platformData.orientation() == Vertical && !isTextOrientationFallback) { - m_verticalData = platformData.verticalData(); - m_hasVerticalGlyphs = m_verticalData.get() && m_verticalData->hasVerticalMetrics(); - } + if (platformData.orientation() == Vertical && !isTextOrientationFallback) { + m_verticalData = platformData.verticalData(); + m_hasVerticalGlyphs = + m_verticalData.get() && m_verticalData->hasVerticalMetrics(); + } #endif } -SimpleFontData::SimpleFontData(PassRefPtr customData, float fontSize, bool syntheticBold, bool syntheticItalic) - : m_platformData(FontPlatformData(fontSize, syntheticBold, syntheticItalic)) - , m_treatAsFixedPitch(false) - , m_isTextOrientationFallback(false) - , m_isBrokenIdeographFallback(false) +SimpleFontData::SimpleFontData(PassRefPtr customData, + float fontSize, + bool syntheticBold, + bool syntheticItalic) + : m_platformData( + FontPlatformData(fontSize, syntheticBold, syntheticItalic)), + m_treatAsFixedPitch(false), + m_isTextOrientationFallback(false), + m_isBrokenIdeographFallback(false) #if ENABLE(OPENTYPE_VERTICAL) - , m_verticalData(nullptr) + , + m_verticalData(nullptr) #endif - , m_hasVerticalGlyphs(false) - , m_customFontData(customData) -{ - if (m_customFontData) - m_customFontData->initializeFontData(this, fontSize); + , + m_hasVerticalGlyphs(false), + m_customFontData(customData) { + if (m_customFontData) + m_customFontData->initializeFontData(this, fontSize); } -// Estimates of avgCharWidth and maxCharWidth for platforms that don't support accessing these values from the font. -void SimpleFontData::initCharWidths() -{ - GlyphPage* glyphPageZero = GlyphPageTreeNode::getRootChild(this, 0)->page(); - - // Treat the width of a '0' as the avgCharWidth. - if (m_avgCharWidth <= 0.f && glyphPageZero) { - static const UChar32 digitZeroChar = '0'; - Glyph digitZeroGlyph = glyphPageZero->glyphForCharacter(digitZeroChar); - if (digitZeroGlyph) - m_avgCharWidth = widthForGlyph(digitZeroGlyph); - } - - // If we can't retrieve the width of a '0', fall back to the x height. - if (m_avgCharWidth <= 0.f) - m_avgCharWidth = m_fontMetrics.xHeight(); - - if (m_maxCharWidth <= 0.f) - m_maxCharWidth = std::max(m_avgCharWidth, m_fontMetrics.floatAscent()); +// Estimates of avgCharWidth and maxCharWidth for platforms that don't support +// accessing these values from the font. +void SimpleFontData::initCharWidths() { + GlyphPage* glyphPageZero = GlyphPageTreeNode::getRootChild(this, 0)->page(); + + // Treat the width of a '0' as the avgCharWidth. + if (m_avgCharWidth <= 0.f && glyphPageZero) { + static const UChar32 digitZeroChar = '0'; + Glyph digitZeroGlyph = glyphPageZero->glyphForCharacter(digitZeroChar); + if (digitZeroGlyph) + m_avgCharWidth = widthForGlyph(digitZeroGlyph); + } + + // If we can't retrieve the width of a '0', fall back to the x height. + if (m_avgCharWidth <= 0.f) + m_avgCharWidth = m_fontMetrics.xHeight(); + + if (m_maxCharWidth <= 0.f) + m_maxCharWidth = std::max(m_avgCharWidth, m_fontMetrics.floatAscent()); } -void SimpleFontData::platformGlyphInit() -{ - GlyphPage* glyphPageZero = GlyphPageTreeNode::getRootChild(this, 0)->page(); - if (!glyphPageZero) { - WTF_LOG_ERROR("Failed to get glyph page zero."); - m_spaceGlyph = 0; - m_spaceWidth = 0; - m_zeroGlyph = 0; - determinePitch(); - m_zeroWidthSpaceGlyph = 0; - m_missingGlyphData.fontData = this; - m_missingGlyphData.glyph = 0; - return; - } - - m_zeroWidthSpaceGlyph = glyphPageZero->glyphForCharacter(0); - - // Nasty hack to determine if we should round or ceil space widths. - // If the font is monospace or fake monospace we ceil to ensure that - // every character and the space are the same width. Otherwise we round. - m_spaceGlyph = glyphPageZero->glyphForCharacter(' '); - float width = widthForGlyph(m_spaceGlyph); - m_spaceWidth = width; - m_zeroGlyph = glyphPageZero->glyphForCharacter('0'); - m_fontMetrics.setZeroWidth(widthForGlyph(m_zeroGlyph)); +void SimpleFontData::platformGlyphInit() { + GlyphPage* glyphPageZero = GlyphPageTreeNode::getRootChild(this, 0)->page(); + if (!glyphPageZero) { + WTF_LOG_ERROR("Failed to get glyph page zero."); + m_spaceGlyph = 0; + m_spaceWidth = 0; + m_zeroGlyph = 0; determinePitch(); - - // Force the glyph for ZERO WIDTH SPACE to have zero width, unless it is shared with SPACE. - // Helvetica is an example of a non-zero width ZERO WIDTH SPACE glyph. - // See - // Ask for the glyph for 0 to avoid paging in ZERO WIDTH SPACE. Control characters, including 0, - // are mapped to the ZERO WIDTH SPACE glyph. - if (m_zeroWidthSpaceGlyph == m_spaceGlyph) { - m_zeroWidthSpaceGlyph = 0; - } - + m_zeroWidthSpaceGlyph = 0; m_missingGlyphData.fontData = this; m_missingGlyphData.glyph = 0; + return; + } + + m_zeroWidthSpaceGlyph = glyphPageZero->glyphForCharacter(0); + + // Nasty hack to determine if we should round or ceil space widths. + // If the font is monospace or fake monospace we ceil to ensure that + // every character and the space are the same width. Otherwise we round. + m_spaceGlyph = glyphPageZero->glyphForCharacter(' '); + float width = widthForGlyph(m_spaceGlyph); + m_spaceWidth = width; + m_zeroGlyph = glyphPageZero->glyphForCharacter('0'); + m_fontMetrics.setZeroWidth(widthForGlyph(m_zeroGlyph)); + determinePitch(); + + // Force the glyph for ZERO WIDTH SPACE to have zero width, unless it is + // shared with SPACE. Helvetica is an example of a non-zero width ZERO WIDTH + // SPACE glyph. See Ask for the + // glyph for 0 to avoid paging in ZERO WIDTH SPACE. Control characters, + // including 0, are mapped to the ZERO WIDTH SPACE glyph. + if (m_zeroWidthSpaceGlyph == m_spaceGlyph) { + m_zeroWidthSpaceGlyph = 0; + } + + m_missingGlyphData.fontData = this; + m_missingGlyphData.glyph = 0; } -SimpleFontData::~SimpleFontData() -{ - if (!isSVGFont()) - platformDestroy(); +SimpleFontData::~SimpleFontData() { + if (!isSVGFont()) + platformDestroy(); - if (isCustomFont()) - GlyphPageTreeNode::pruneTreeCustomFontData(this); - else - GlyphPageTreeNode::pruneTreeFontData(this); + if (isCustomFont()) + GlyphPageTreeNode::pruneTreeCustomFontData(this); + else + GlyphPageTreeNode::pruneTreeFontData(this); } -const SimpleFontData* SimpleFontData::fontDataForCharacter(UChar32) const -{ - return this; +const SimpleFontData* SimpleFontData::fontDataForCharacter(UChar32) const { + return this; } -Glyph SimpleFontData::glyphForCharacter(UChar32 character) const -{ - // As GlyphPage::size is power of 2 so shifting is valid - GlyphPageTreeNode* node = GlyphPageTreeNode::getRootChild(this, character >> GlyphPage::sizeBits); - return node->page() ? node->page()->glyphAt(character & 0xFF) : 0; +Glyph SimpleFontData::glyphForCharacter(UChar32 character) const { + // As GlyphPage::size is power of 2 so shifting is valid + GlyphPageTreeNode* node = + GlyphPageTreeNode::getRootChild(this, character >> GlyphPage::sizeBits); + return node->page() ? node->page()->glyphAt(character & 0xFF) : 0; } -bool SimpleFontData::isSegmented() const -{ - return false; +bool SimpleFontData::isSegmented() const { + return false; } -PassRefPtr SimpleFontData::verticalRightOrientationFontData() const -{ - if (!m_derivedFontData) - m_derivedFontData = DerivedFontData::create(isCustomFont()); - if (!m_derivedFontData->verticalRightOrientation) { - FontPlatformData verticalRightPlatformData(m_platformData); - verticalRightPlatformData.setOrientation(Horizontal); - m_derivedFontData->verticalRightOrientation = create(verticalRightPlatformData, isCustomFont() ? CustomFontData::create(): nullptr, true); - } - return m_derivedFontData->verticalRightOrientation; +PassRefPtr SimpleFontData::verticalRightOrientationFontData() + const { + if (!m_derivedFontData) + m_derivedFontData = DerivedFontData::create(isCustomFont()); + if (!m_derivedFontData->verticalRightOrientation) { + FontPlatformData verticalRightPlatformData(m_platformData); + verticalRightPlatformData.setOrientation(Horizontal); + m_derivedFontData->verticalRightOrientation = + create(verticalRightPlatformData, + isCustomFont() ? CustomFontData::create() : nullptr, true); + } + return m_derivedFontData->verticalRightOrientation; } -PassRefPtr SimpleFontData::uprightOrientationFontData() const -{ - if (!m_derivedFontData) - m_derivedFontData = DerivedFontData::create(isCustomFont()); - if (!m_derivedFontData->uprightOrientation) - m_derivedFontData->uprightOrientation = create(m_platformData, isCustomFont() ? CustomFontData::create(): nullptr, true); - return m_derivedFontData->uprightOrientation; +PassRefPtr SimpleFontData::uprightOrientationFontData() const { + if (!m_derivedFontData) + m_derivedFontData = DerivedFontData::create(isCustomFont()); + if (!m_derivedFontData->uprightOrientation) + m_derivedFontData->uprightOrientation = + create(m_platformData, + isCustomFont() ? CustomFontData::create() : nullptr, true); + return m_derivedFontData->uprightOrientation; } -PassRefPtr SimpleFontData::smallCapsFontData(const FontDescription& fontDescription) const -{ - if (!m_derivedFontData) - m_derivedFontData = DerivedFontData::create(isCustomFont()); - if (!m_derivedFontData->smallCaps) - m_derivedFontData->smallCaps = createScaledFontData(fontDescription, smallCapsFontSizeMultiplier); +PassRefPtr SimpleFontData::smallCapsFontData( + const FontDescription& fontDescription) const { + if (!m_derivedFontData) + m_derivedFontData = DerivedFontData::create(isCustomFont()); + if (!m_derivedFontData->smallCaps) + m_derivedFontData->smallCaps = + createScaledFontData(fontDescription, smallCapsFontSizeMultiplier); - return m_derivedFontData->smallCaps; + return m_derivedFontData->smallCaps; } -PassRefPtr SimpleFontData::emphasisMarkFontData(const FontDescription& fontDescription) const -{ - if (!m_derivedFontData) - m_derivedFontData = DerivedFontData::create(isCustomFont()); - if (!m_derivedFontData->emphasisMark) - m_derivedFontData->emphasisMark = createScaledFontData(fontDescription, emphasisMarkFontSizeMultiplier); +PassRefPtr SimpleFontData::emphasisMarkFontData( + const FontDescription& fontDescription) const { + if (!m_derivedFontData) + m_derivedFontData = DerivedFontData::create(isCustomFont()); + if (!m_derivedFontData->emphasisMark) + m_derivedFontData->emphasisMark = + createScaledFontData(fontDescription, emphasisMarkFontSizeMultiplier); - return m_derivedFontData->emphasisMark; + return m_derivedFontData->emphasisMark; } -PassRefPtr SimpleFontData::brokenIdeographFontData() const -{ - if (!m_derivedFontData) - m_derivedFontData = DerivedFontData::create(isCustomFont()); - if (!m_derivedFontData->brokenIdeograph) { - m_derivedFontData->brokenIdeograph = create(m_platformData, isCustomFont() ? CustomFontData::create(): nullptr); - m_derivedFontData->brokenIdeograph->m_isBrokenIdeographFallback = true; - } - return m_derivedFontData->brokenIdeograph; +PassRefPtr SimpleFontData::brokenIdeographFontData() const { + if (!m_derivedFontData) + m_derivedFontData = DerivedFontData::create(isCustomFont()); + if (!m_derivedFontData->brokenIdeograph) { + m_derivedFontData->brokenIdeograph = create( + m_platformData, isCustomFont() ? CustomFontData::create() : nullptr); + m_derivedFontData->brokenIdeograph->m_isBrokenIdeographFallback = true; + } + return m_derivedFontData->brokenIdeograph; } #ifndef NDEBUG -String SimpleFontData::description() const -{ - if (isSVGFont()) - return "[SVG font]"; - if (isCustomFont()) - return "[custom font]"; - - return platformData().description(); +String SimpleFontData::description() const { + if (isSVGFont()) + return "[SVG font]"; + if (isCustomFont()) + return "[custom font]"; + + return platformData().description(); } #endif -PassOwnPtr SimpleFontData::DerivedFontData::create(bool forCustomFont) -{ - return adoptPtr(new DerivedFontData(forCustomFont)); +PassOwnPtr +SimpleFontData::DerivedFontData::create(bool forCustomFont) { + return adoptPtr(new DerivedFontData(forCustomFont)); } -SimpleFontData::DerivedFontData::~DerivedFontData() -{ - if (!forCustomFont) - return; - - if (smallCaps) - GlyphPageTreeNode::pruneTreeCustomFontData(smallCaps.get()); - if (emphasisMark) - GlyphPageTreeNode::pruneTreeCustomFontData(emphasisMark.get()); - if (brokenIdeograph) - GlyphPageTreeNode::pruneTreeCustomFontData(brokenIdeograph.get()); - if (verticalRightOrientation) - GlyphPageTreeNode::pruneTreeCustomFontData(verticalRightOrientation.get()); - if (uprightOrientation) - GlyphPageTreeNode::pruneTreeCustomFontData(uprightOrientation.get()); +SimpleFontData::DerivedFontData::~DerivedFontData() { + if (!forCustomFont) + return; + + if (smallCaps) + GlyphPageTreeNode::pruneTreeCustomFontData(smallCaps.get()); + if (emphasisMark) + GlyphPageTreeNode::pruneTreeCustomFontData(emphasisMark.get()); + if (brokenIdeograph) + GlyphPageTreeNode::pruneTreeCustomFontData(brokenIdeograph.get()); + if (verticalRightOrientation) + GlyphPageTreeNode::pruneTreeCustomFontData(verticalRightOrientation.get()); + if (uprightOrientation) + GlyphPageTreeNode::pruneTreeCustomFontData(uprightOrientation.get()); } -PassRefPtr SimpleFontData::createScaledFontData(const FontDescription& fontDescription, float scaleFactor) const -{ - // FIXME: Support scaled SVG fonts. Given that SVG is scalable in general this should be achievable. - if (isSVGFont()) - return nullptr; +PassRefPtr SimpleFontData::createScaledFontData( + const FontDescription& fontDescription, + float scaleFactor) const { + // FIXME: Support scaled SVG fonts. Given that SVG is scalable in general this + // should be achievable. + if (isSVGFont()) + return nullptr; - return platformCreateScaledFontData(fontDescription, scaleFactor); + return platformCreateScaledFontData(fontDescription, scaleFactor); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/fonts/SimpleFontData.h b/sky/engine/platform/fonts/SimpleFontData.h index 4feb6a200e948..30afc0484f8c2 100644 --- a/sky/engine/platform/fonts/SimpleFontData.h +++ b/sky/engine/platform/fonts/SimpleFontData.h @@ -43,220 +43,263 @@ namespace blink { class FontDescription; -enum FontDataVariant { AutoVariant, NormalVariant, SmallCapsVariant, EmphasisMarkVariant, BrokenIdeographVariant }; +enum FontDataVariant { + AutoVariant, + NormalVariant, + SmallCapsVariant, + EmphasisMarkVariant, + BrokenIdeographVariant +}; enum Pitch { UnknownPitch, FixedPitch, VariablePitch }; class PLATFORM_EXPORT SimpleFontData : public FontData { -public: - // Used to create platform fonts. - static PassRefPtr create(const FontPlatformData& platformData, PassRefPtr customData = nullptr, bool isTextOrientationFallback = false) - { - return adoptRef(new SimpleFontData(platformData, customData, isTextOrientationFallback)); - } - - // Used to create SVG Fonts. - static PassRefPtr create(PassRefPtr customData, float fontSize, bool syntheticBold, bool syntheticItalic) - { - return adoptRef(new SimpleFontData(customData, fontSize, syntheticBold, syntheticItalic)); - } - - virtual ~SimpleFontData(); - - static const SimpleFontData* systemFallback() { return reinterpret_cast(-1); } - - const FontPlatformData& platformData() const { return m_platformData; } + public: + // Used to create platform fonts. + static PassRefPtr create( + const FontPlatformData& platformData, + PassRefPtr customData = nullptr, + bool isTextOrientationFallback = false) { + return adoptRef(new SimpleFontData(platformData, customData, + isTextOrientationFallback)); + } + + // Used to create SVG Fonts. + static PassRefPtr create( + PassRefPtr customData, + float fontSize, + bool syntheticBold, + bool syntheticItalic) { + return adoptRef(new SimpleFontData(customData, fontSize, syntheticBold, + syntheticItalic)); + } + + virtual ~SimpleFontData(); + + static const SimpleFontData* systemFallback() { + return reinterpret_cast(-1); + } + + const FontPlatformData& platformData() const { return m_platformData; } #if ENABLE(OPENTYPE_VERTICAL) - const OpenTypeVerticalData* verticalData() const { return m_verticalData.get(); } + const OpenTypeVerticalData* verticalData() const { + return m_verticalData.get(); + } #endif - PassRefPtr smallCapsFontData(const FontDescription&) const; - PassRefPtr emphasisMarkFontData(const FontDescription&) const; - PassRefPtr brokenIdeographFontData() const; - - PassRefPtr variantFontData(const FontDescription& description, FontDataVariant variant) const - { - switch (variant) { - case SmallCapsVariant: - return smallCapsFontData(description); - case EmphasisMarkVariant: - return emphasisMarkFontData(description); - case BrokenIdeographVariant: - return brokenIdeographFontData(); - case AutoVariant: - case NormalVariant: - break; - } - ASSERT_NOT_REACHED(); - return const_cast(this); + PassRefPtr smallCapsFontData(const FontDescription&) const; + PassRefPtr emphasisMarkFontData(const FontDescription&) const; + PassRefPtr brokenIdeographFontData() const; + + PassRefPtr variantFontData(const FontDescription& description, + FontDataVariant variant) const { + switch (variant) { + case SmallCapsVariant: + return smallCapsFontData(description); + case EmphasisMarkVariant: + return emphasisMarkFontData(description); + case BrokenIdeographVariant: + return brokenIdeographFontData(); + case AutoVariant: + case NormalVariant: + break; } - - PassRefPtr verticalRightOrientationFontData() const; - PassRefPtr uprightOrientationFontData() const; - - bool hasVerticalGlyphs() const { return m_hasVerticalGlyphs; } - bool isTextOrientationFallback() const { return m_isTextOrientationFallback; } - - FontMetrics& fontMetrics() { return m_fontMetrics; } - const FontMetrics& fontMetrics() const { return m_fontMetrics; } - float sizePerUnit() const { return platformData().size() / (fontMetrics().unitsPerEm() ? fontMetrics().unitsPerEm() : 1); } - - float maxCharWidth() const { return m_maxCharWidth; } - void setMaxCharWidth(float maxCharWidth) { m_maxCharWidth = maxCharWidth; } - - float avgCharWidth() const { return m_avgCharWidth; } - void setAvgCharWidth(float avgCharWidth) { m_avgCharWidth = avgCharWidth; } - - FloatRect boundsForGlyph(Glyph) const; - float widthForGlyph(Glyph glyph) const; - FloatRect platformBoundsForGlyph(Glyph) const; - float platformWidthForGlyph(Glyph) const; - - float spaceWidth() const { return m_spaceWidth; } - void setSpaceWidth(float spaceWidth) { m_spaceWidth = spaceWidth; } - - Glyph spaceGlyph() const { return m_spaceGlyph; } - void setSpaceGlyph(Glyph spaceGlyph) { m_spaceGlyph = spaceGlyph; } - Glyph zeroWidthSpaceGlyph() const { return m_zeroWidthSpaceGlyph; } - void setZeroWidthSpaceGlyph(Glyph spaceGlyph) { m_zeroWidthSpaceGlyph = spaceGlyph; } - bool isZeroWidthSpaceGlyph(Glyph glyph) const { return glyph == m_zeroWidthSpaceGlyph && glyph; } - Glyph zeroGlyph() const { return m_zeroGlyph; } - void setZeroGlyph(Glyph zeroGlyph) { m_zeroGlyph = zeroGlyph; } - - virtual const SimpleFontData* fontDataForCharacter(UChar32) const override; - - Glyph glyphForCharacter(UChar32) const; - - void determinePitch(); - Pitch pitch() const { return m_treatAsFixedPitch ? FixedPitch : VariablePitch; } - - bool isSVGFont() const { return m_customFontData && m_customFontData->isSVGFont(); } - virtual bool isCustomFont() const override { return m_customFontData; } - virtual bool isLoading() const override { return m_customFontData ? m_customFontData->isLoading() : false; } - virtual bool isLoadingFallback() const override { return m_customFontData ? m_customFontData->isLoadingFallback() : false; } - virtual bool isSegmented() const override; - virtual bool shouldSkipDrawing() const override { return m_customFontData && m_customFontData->shouldSkipDrawing(); } - - const GlyphData& missingGlyphData() const { return m_missingGlyphData; } - void setMissingGlyphData(const GlyphData& glyphData) { m_missingGlyphData = glyphData; } + ASSERT_NOT_REACHED(); + return const_cast(this); + } + + PassRefPtr verticalRightOrientationFontData() const; + PassRefPtr uprightOrientationFontData() const; + + bool hasVerticalGlyphs() const { return m_hasVerticalGlyphs; } + bool isTextOrientationFallback() const { return m_isTextOrientationFallback; } + + FontMetrics& fontMetrics() { return m_fontMetrics; } + const FontMetrics& fontMetrics() const { return m_fontMetrics; } + float sizePerUnit() const { + return platformData().size() / + (fontMetrics().unitsPerEm() ? fontMetrics().unitsPerEm() : 1); + } + + float maxCharWidth() const { return m_maxCharWidth; } + void setMaxCharWidth(float maxCharWidth) { m_maxCharWidth = maxCharWidth; } + + float avgCharWidth() const { return m_avgCharWidth; } + void setAvgCharWidth(float avgCharWidth) { m_avgCharWidth = avgCharWidth; } + + FloatRect boundsForGlyph(Glyph) const; + float widthForGlyph(Glyph glyph) const; + FloatRect platformBoundsForGlyph(Glyph) const; + float platformWidthForGlyph(Glyph) const; + + float spaceWidth() const { return m_spaceWidth; } + void setSpaceWidth(float spaceWidth) { m_spaceWidth = spaceWidth; } + + Glyph spaceGlyph() const { return m_spaceGlyph; } + void setSpaceGlyph(Glyph spaceGlyph) { m_spaceGlyph = spaceGlyph; } + Glyph zeroWidthSpaceGlyph() const { return m_zeroWidthSpaceGlyph; } + void setZeroWidthSpaceGlyph(Glyph spaceGlyph) { + m_zeroWidthSpaceGlyph = spaceGlyph; + } + bool isZeroWidthSpaceGlyph(Glyph glyph) const { + return glyph == m_zeroWidthSpaceGlyph && glyph; + } + Glyph zeroGlyph() const { return m_zeroGlyph; } + void setZeroGlyph(Glyph zeroGlyph) { m_zeroGlyph = zeroGlyph; } + + virtual const SimpleFontData* fontDataForCharacter(UChar32) const override; + + Glyph glyphForCharacter(UChar32) const; + + void determinePitch(); + Pitch pitch() const { + return m_treatAsFixedPitch ? FixedPitch : VariablePitch; + } + + bool isSVGFont() const { + return m_customFontData && m_customFontData->isSVGFont(); + } + virtual bool isCustomFont() const override { return m_customFontData; } + virtual bool isLoading() const override { + return m_customFontData ? m_customFontData->isLoading() : false; + } + virtual bool isLoadingFallback() const override { + return m_customFontData ? m_customFontData->isLoadingFallback() : false; + } + virtual bool isSegmented() const override; + virtual bool shouldSkipDrawing() const override { + return m_customFontData && m_customFontData->shouldSkipDrawing(); + } + + const GlyphData& missingGlyphData() const { return m_missingGlyphData; } + void setMissingGlyphData(const GlyphData& glyphData) { + m_missingGlyphData = glyphData; + } #ifndef NDEBUG - virtual String description() const override; + virtual String description() const override; #endif - bool canRenderCombiningCharacterSequence(const UChar*, size_t) const; + bool canRenderCombiningCharacterSequence(const UChar*, size_t) const; - PassRefPtr customFontData() const { return m_customFontData; } + PassRefPtr customFontData() const { return m_customFontData; } - // Implemented by the platform. - virtual bool fillGlyphPage(GlyphPage* pageToFill, unsigned offset, unsigned length, UChar* buffer, unsigned bufferLength) const; + // Implemented by the platform. + virtual bool fillGlyphPage(GlyphPage* pageToFill, + unsigned offset, + unsigned length, + UChar* buffer, + unsigned bufferLength) const; -protected: - SimpleFontData(const FontPlatformData&, PassRefPtr customData, bool isTextOrientationFallback = false); + protected: + SimpleFontData(const FontPlatformData&, + PassRefPtr customData, + bool isTextOrientationFallback = false); - SimpleFontData(PassRefPtr customData, float fontSize, bool syntheticBold, bool syntheticItalic); + SimpleFontData(PassRefPtr customData, + float fontSize, + bool syntheticBold, + bool syntheticItalic); -private: - void platformInit(); - void platformGlyphInit(); - void platformCharWidthInit(); - void platformDestroy(); + private: + void platformInit(); + void platformGlyphInit(); + void platformCharWidthInit(); + void platformDestroy(); - void initCharWidths(); + void initCharWidths(); - PassRefPtr createScaledFontData(const FontDescription&, float scaleFactor) const; - PassRefPtr platformCreateScaledFontData(const FontDescription&, float scaleFactor) const; + PassRefPtr createScaledFontData(const FontDescription&, + float scaleFactor) const; + PassRefPtr platformCreateScaledFontData( + const FontDescription&, + float scaleFactor) const; - FontMetrics m_fontMetrics; - float m_maxCharWidth; - float m_avgCharWidth; + FontMetrics m_fontMetrics; + float m_maxCharWidth; + float m_avgCharWidth; - FontPlatformData m_platformData; + FontPlatformData m_platformData; - mutable OwnPtr > m_glyphToBoundsMap; - mutable GlyphMetricsMap m_glyphToWidthMap; + mutable OwnPtr> m_glyphToBoundsMap; + mutable GlyphMetricsMap m_glyphToWidthMap; - bool m_treatAsFixedPitch; + bool m_treatAsFixedPitch; - bool m_isTextOrientationFallback; - bool m_isBrokenIdeographFallback; + bool m_isTextOrientationFallback; + bool m_isBrokenIdeographFallback; #if ENABLE(OPENTYPE_VERTICAL) - RefPtr m_verticalData; + RefPtr m_verticalData; #endif - bool m_hasVerticalGlyphs; + bool m_hasVerticalGlyphs; - Glyph m_spaceGlyph; - float m_spaceWidth; - Glyph m_zeroGlyph; + Glyph m_spaceGlyph; + float m_spaceWidth; + Glyph m_zeroGlyph; - Glyph m_zeroWidthSpaceGlyph; + Glyph m_zeroWidthSpaceGlyph; - GlyphData m_missingGlyphData; + GlyphData m_missingGlyphData; - struct DerivedFontData { - static PassOwnPtr create(bool forCustomFont); - ~DerivedFontData(); + struct DerivedFontData { + static PassOwnPtr create(bool forCustomFont); + ~DerivedFontData(); - bool forCustomFont; - RefPtr smallCaps; - RefPtr emphasisMark; - RefPtr brokenIdeograph; - RefPtr verticalRightOrientation; - RefPtr uprightOrientation; + bool forCustomFont; + RefPtr smallCaps; + RefPtr emphasisMark; + RefPtr brokenIdeograph; + RefPtr verticalRightOrientation; + RefPtr uprightOrientation; - private: - DerivedFontData(bool custom) - : forCustomFont(custom) - { - } - }; + private: + DerivedFontData(bool custom) : forCustomFont(custom) {} + }; - mutable OwnPtr m_derivedFontData; + mutable OwnPtr m_derivedFontData; - RefPtr m_customFontData; + RefPtr m_customFontData; - mutable OwnPtr > m_combiningCharacterSequenceSupport; + mutable OwnPtr> m_combiningCharacterSequenceSupport; }; -ALWAYS_INLINE FloatRect SimpleFontData::boundsForGlyph(Glyph glyph) const -{ - if (isZeroWidthSpaceGlyph(glyph)) - return FloatRect(); - - FloatRect bounds; - if (m_glyphToBoundsMap) { - bounds = m_glyphToBoundsMap->metricsForGlyph(glyph); - if (bounds.width() != cGlyphSizeUnknown) - return bounds; - } - - bounds = platformBoundsForGlyph(glyph); - if (!m_glyphToBoundsMap) - m_glyphToBoundsMap = adoptPtr(new GlyphMetricsMap); - m_glyphToBoundsMap->setMetricsForGlyph(glyph, bounds); - return bounds; +ALWAYS_INLINE FloatRect SimpleFontData::boundsForGlyph(Glyph glyph) const { + if (isZeroWidthSpaceGlyph(glyph)) + return FloatRect(); + + FloatRect bounds; + if (m_glyphToBoundsMap) { + bounds = m_glyphToBoundsMap->metricsForGlyph(glyph); + if (bounds.width() != cGlyphSizeUnknown) + return bounds; + } + + bounds = platformBoundsForGlyph(glyph); + if (!m_glyphToBoundsMap) + m_glyphToBoundsMap = adoptPtr(new GlyphMetricsMap); + m_glyphToBoundsMap->setMetricsForGlyph(glyph, bounds); + return bounds; } -ALWAYS_INLINE float SimpleFontData::widthForGlyph(Glyph glyph) const -{ - if (isZeroWidthSpaceGlyph(glyph)) - return 0; +ALWAYS_INLINE float SimpleFontData::widthForGlyph(Glyph glyph) const { + if (isZeroWidthSpaceGlyph(glyph)) + return 0; - float width = m_glyphToWidthMap.metricsForGlyph(glyph); - if (width != cGlyphSizeUnknown) - return width; + float width = m_glyphToWidthMap.metricsForGlyph(glyph); + if (width != cGlyphSizeUnknown) + return width; #if ENABLE(OPENTYPE_VERTICAL) - if (m_verticalData) - width = m_verticalData->advanceHeight(this, glyph); - else + if (m_verticalData) + width = m_verticalData->advanceHeight(this, glyph); + else #endif - width = platformWidthForGlyph(glyph); + width = platformWidthForGlyph(glyph); - m_glyphToWidthMap.setMetricsForGlyph(glyph, width); - return width; + m_glyphToWidthMap.setMetricsForGlyph(glyph, width); + return width; } DEFINE_FONT_DATA_TYPE_CASTS(SimpleFontData, false); -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_FONTS_SIMPLEFONTDATA_H_ diff --git a/sky/engine/platform/fonts/TextBlob.h b/sky/engine/platform/fonts/TextBlob.h index c3cf244cb5f3c..82c65059676d5 100644 --- a/sky/engine/platform/fonts/TextBlob.h +++ b/sky/engine/platform/fonts/TextBlob.h @@ -14,6 +14,6 @@ namespace blink { // Typedefs are used only to insulate core/ from Skia type names. typedef sk_sp TextBlobPtr; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_FONTS_TEXTBLOB_H_ diff --git a/sky/engine/platform/fonts/TextRenderingMode.h b/sky/engine/platform/fonts/TextRenderingMode.h index 5d4e0d1c8b619..00585153d99a7 100644 --- a/sky/engine/platform/fonts/TextRenderingMode.h +++ b/sky/engine/platform/fonts/TextRenderingMode.h @@ -28,8 +28,13 @@ namespace blink { -enum TextRenderingMode { AutoTextRendering, OptimizeSpeed, OptimizeLegibility, GeometricPrecision }; +enum TextRenderingMode { + AutoTextRendering, + OptimizeSpeed, + OptimizeLegibility, + GeometricPrecision +}; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_FONTS_TEXTRENDERINGMODE_H_ diff --git a/sky/engine/platform/fonts/TypesettingFeatures.h b/sky/engine/platform/fonts/TypesettingFeatures.h index 78a8c951c5df7..a1267fb1710a7 100644 --- a/sky/engine/platform/fonts/TypesettingFeatures.h +++ b/sky/engine/platform/fonts/TypesettingFeatures.h @@ -29,12 +29,12 @@ namespace blink { enum TypesettingFeature { - Kerning = 1 << 0, - Ligatures = 1 << 1, + Kerning = 1 << 0, + Ligatures = 1 << 1, }; typedef unsigned TypesettingFeatures; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_FONTS_TYPESETTINGFEATURES_H_ diff --git a/sky/engine/platform/fonts/VDMXParser.cpp b/sky/engine/platform/fonts/VDMXParser.cpp index 619b61ab26e2b..d881320a6fe1f 100644 --- a/sky/engine/platform/fonts/VDMXParser.cpp +++ b/sky/engine/platform/fonts/VDMXParser.cpp @@ -41,58 +41,46 @@ // out-of-bounds errors. As a family they return false if anything is amiss, // updating the current offset otherwise. class Buffer { -public: - Buffer(const uint8_t* buffer, size_t length) - : m_buffer(buffer) - , m_length(length) - , m_offset(0) { } - - bool skip(size_t numBytes) - { - if (m_offset + numBytes > m_length) - return false; - m_offset += numBytes; - return true; - } - - bool readU8(uint8_t* value) - { - if (m_offset + sizeof(uint8_t) > m_length) - return false; - *value = m_buffer[m_offset]; - m_offset += sizeof(uint8_t); - return true; - } - - bool readU16(uint16_t* value) - { - if (m_offset + sizeof(uint16_t) > m_length) - return false; - memcpy(value, m_buffer + m_offset, sizeof(uint16_t)); - *value = ntohs(*value); - m_offset += sizeof(uint16_t); - return true; - } - - bool readS16(int16_t* value) - { - return readU16(reinterpret_cast(value)); - } - - size_t offset() const - { - return m_offset; - } - - void setOffset(size_t newoffset) - { - m_offset = newoffset; - } - -private: - const uint8_t *const m_buffer; - const size_t m_length; - size_t m_offset; + public: + Buffer(const uint8_t* buffer, size_t length) + : m_buffer(buffer), m_length(length), m_offset(0) {} + + bool skip(size_t numBytes) { + if (m_offset + numBytes > m_length) + return false; + m_offset += numBytes; + return true; + } + + bool readU8(uint8_t* value) { + if (m_offset + sizeof(uint8_t) > m_length) + return false; + *value = m_buffer[m_offset]; + m_offset += sizeof(uint8_t); + return true; + } + + bool readU16(uint16_t* value) { + if (m_offset + sizeof(uint16_t) > m_length) + return false; + memcpy(value, m_buffer + m_offset, sizeof(uint16_t)); + *value = ntohs(*value); + m_offset += sizeof(uint16_t); + return true; + } + + bool readS16(int16_t* value) { + return readU16(reinterpret_cast(value)); + } + + size_t offset() const { return m_offset; } + + void setOffset(size_t newoffset) { m_offset = newoffset; } + + private: + const uint8_t* const m_buffer; + const size_t m_length; + size_t m_offset; }; // VDMX parsing code. @@ -116,87 +104,85 @@ namespace blink { // untouched. size_t must be 32-bits to avoid overflow. // // See http://www.microsoft.com/opentype/otspec/vdmx.htm -bool parseVDMX(int* yMax, int* yMin, - const uint8_t* vdmx, size_t vdmxLength, - unsigned targetPixelSize) -{ - Buffer buf(vdmx, vdmxLength); - - // We ignore the version. Future tables should be backwards compatible with - // this layout. - uint16_t numRatios; - if (!buf.skip(4) || !buf.readU16(&numRatios)) - return false; +bool parseVDMX(int* yMax, + int* yMin, + const uint8_t* vdmx, + size_t vdmxLength, + unsigned targetPixelSize) { + Buffer buf(vdmx, vdmxLength); + + // We ignore the version. Future tables should be backwards compatible with + // this layout. + uint16_t numRatios; + if (!buf.skip(4) || !buf.readU16(&numRatios)) + return false; - // Now we have two tables. Firstly we have @numRatios Ratio records, then a - // matching array of @numRatios offsets. We save the offset of the beginning - // of this second table. - // - // Range 6 <= x <= 262146 - unsigned long offsetTableOffset = - buf.offset() + 4 /* sizeof struct ratio */ * numRatios; - - unsigned desiredRatio = 0xffffffff; - // We read 4 bytes per record, so the offset range is - // 6 <= x <= 524286 - for (unsigned i = 0; i < numRatios; ++i) { - uint8_t xRatio, yRatio1, yRatio2; - - if (!buf.skip(1) - || !buf.readU8(&xRatio) - || !buf.readU8(&yRatio1) - || !buf.readU8(&yRatio2)) - return false; - - // This either covers 1:1, or this is the default entry (0, 0, 0) - if ((xRatio == 1 && yRatio1 <= 1 && yRatio2 >= 1) - || (xRatio == 0 && yRatio1 == 0 && yRatio2 == 0)) { - desiredRatio = i; - break; - } + // Now we have two tables. Firstly we have @numRatios Ratio records, then a + // matching array of @numRatios offsets. We save the offset of the beginning + // of this second table. + // + // Range 6 <= x <= 262146 + unsigned long offsetTableOffset = + buf.offset() + 4 /* sizeof struct ratio */ * numRatios; + + unsigned desiredRatio = 0xffffffff; + // We read 4 bytes per record, so the offset range is + // 6 <= x <= 524286 + for (unsigned i = 0; i < numRatios; ++i) { + uint8_t xRatio, yRatio1, yRatio2; + + if (!buf.skip(1) || !buf.readU8(&xRatio) || !buf.readU8(&yRatio1) || + !buf.readU8(&yRatio2)) + return false; + + // This either covers 1:1, or this is the default entry (0, 0, 0) + if ((xRatio == 1 && yRatio1 <= 1 && yRatio2 >= 1) || + (xRatio == 0 && yRatio1 == 0 && yRatio2 == 0)) { + desiredRatio = i; + break; } + } - if (desiredRatio == 0xffffffff) // no ratio found - return false; + if (desiredRatio == 0xffffffff) // no ratio found + return false; - // Range 10 <= x <= 393216 - buf.setOffset(offsetTableOffset + sizeof(uint16_t) * desiredRatio); + // Range 10 <= x <= 393216 + buf.setOffset(offsetTableOffset + sizeof(uint16_t) * desiredRatio); - // Now we read from the offset table to get the offset of another array - uint16_t groupOffset; - if (!buf.readU16(&groupOffset)) - return false; - // Range 0 <= x <= 65535 - buf.setOffset(groupOffset); + // Now we read from the offset table to get the offset of another array + uint16_t groupOffset; + if (!buf.readU16(&groupOffset)) + return false; + // Range 0 <= x <= 65535 + buf.setOffset(groupOffset); - uint16_t numRecords; - if (!buf.readU16(&numRecords) || !buf.skip(sizeof(uint16_t))) - return false; + uint16_t numRecords; + if (!buf.readU16(&numRecords) || !buf.skip(sizeof(uint16_t))) + return false; - // We read 6 bytes per record, so the offset range is - // 4 <= x <= 458749 - for (unsigned i = 0; i < numRecords; ++i) { - uint16_t pixelSize; - if (!buf.readU16(&pixelSize)) - return false; - // the entries are sorted, so we can abort early if need be - if (pixelSize > targetPixelSize) - return false; - - if (pixelSize == targetPixelSize) { - int16_t tempYMax, tempYMin; - if (!buf.readS16(&tempYMax) - || !buf.readS16(&tempYMin)) - return false; - *yMin = tempYMin; - *yMax = tempYMax; - return true; - } - if (!buf.skip(2 * sizeof(int16_t))) - return false; + // We read 6 bytes per record, so the offset range is + // 4 <= x <= 458749 + for (unsigned i = 0; i < numRecords; ++i) { + uint16_t pixelSize; + if (!buf.readU16(&pixelSize)) + return false; + // the entries are sorted, so we can abort early if need be + if (pixelSize > targetPixelSize) + return false; + + if (pixelSize == targetPixelSize) { + int16_t tempYMax, tempYMin; + if (!buf.readS16(&tempYMax) || !buf.readS16(&tempYMin)) + return false; + *yMin = tempYMin; + *yMax = tempYMax; + return true; } + if (!buf.skip(2 * sizeof(int16_t))) + return false; + } - return false; + return false; } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/fonts/VDMXParser.h b/sky/engine/platform/fonts/VDMXParser.h index e8d4f11f6d9f3..0ca20f8c0cbe2 100644 --- a/sky/engine/platform/fonts/VDMXParser.h +++ b/sky/engine/platform/fonts/VDMXParser.h @@ -31,13 +31,15 @@ #ifndef SKY_ENGINE_PLATFORM_FONTS_VDMXPARSER_H_ #define SKY_ENGINE_PLATFORM_FONTS_VDMXPARSER_H_ -#include #include +#include namespace blink { - bool parseVDMX(int* ymax, int* ymin, - const uint8_t* vdmx, size_t vdmxLength, - unsigned targetPixelSize); +bool parseVDMX(int* ymax, + int* ymin, + const uint8_t* vdmx, + size_t vdmxLength, + unsigned targetPixelSize); } // namespace blink #endif // SKY_ENGINE_PLATFORM_FONTS_VDMXPARSER_H_ diff --git a/sky/engine/platform/fonts/WidthCache.h b/sky/engine/platform/fonts/WidthCache.h index c84a593b2a8fa..cf9fbf011ad98 100644 --- a/sky/engine/platform/fonts/WidthCache.h +++ b/sky/engine/platform/fonts/WidthCache.h @@ -37,177 +37,184 @@ namespace blink { struct WidthCacheEntry { - WidthCacheEntry() - { - width = std::numeric_limits::quiet_NaN(); - } - bool isValid() const { return !std::isnan(width); } - float width; - IntRectExtent glyphBounds; + WidthCacheEntry() { width = std::numeric_limits::quiet_NaN(); } + bool isValid() const { return !std::isnan(width); } + float width; + IntRectExtent glyphBounds; }; class WidthCache { -private: - // Used to optimize small strings as hash table keys. Avoids malloc'ing an out-of-line StringImpl. - class SmallStringKey { - public: - static unsigned capacity() { return s_capacity; } - - SmallStringKey() - : m_length(s_emptyValueLength) - { - } - - SmallStringKey(WTF::HashTableDeletedValueType) - : m_length(s_deletedValueLength) - { - } - - template SmallStringKey(CharacterType* characters, unsigned short length) - : m_length(length) - { - ASSERT(length <= s_capacity); - - StringHasher hasher; - - bool remainder = length & 1; - length >>= 1; - - unsigned i = 0; - while (length--) { - m_characters[i] = characters[i]; - m_characters[i + 1] = characters[i + 1]; - hasher.addCharactersAssumingAligned(characters[i], characters[i + 1]); - i += 2; - } - - if (remainder) { - m_characters[i] = characters[i]; - hasher.addCharacter(characters[i]); - } - - m_hash = hasher.hash(); - } - - const UChar* characters() const { return m_characters; } - unsigned short length() const { return m_length; } - unsigned hash() const { return m_hash; } - - bool isHashTableDeletedValue() const { return m_length == s_deletedValueLength; } - bool isHashTableEmptyValue() const { return m_length == s_emptyValueLength; } - - private: - static const unsigned s_capacity = 15; - static const unsigned s_emptyValueLength = s_capacity + 1; - static const unsigned s_deletedValueLength = s_capacity + 2; - - unsigned m_hash; - unsigned short m_length; - UChar m_characters[s_capacity]; - }; - - struct SmallStringKeyHash { - static unsigned hash(const SmallStringKey& key) { return key.hash(); } - static bool equal(const SmallStringKey& a, const SmallStringKey& b) { return a == b; } - static const bool safeToCompareToEmptyOrDeleted = true; // Empty and deleted values have lengths that are not equal to any valid length. - }; - - struct SmallStringKeyHashTraits : WTF::SimpleClassHashTraits { - static const bool hasIsEmptyValueFunction = true; - static bool isEmptyValue(const SmallStringKey& key) { return key.isHashTableEmptyValue(); } - static const bool needsDestruction = false; - static const unsigned minimumTableSize = 16; - }; - - friend bool operator==(const SmallStringKey&, const SmallStringKey&); - -public: - WidthCache() - : m_interval(s_maxInterval) - , m_countdown(m_interval) - { + private: + // Used to optimize small strings as hash table keys. Avoids malloc'ing an + // out-of-line StringImpl. + class SmallStringKey { + public: + static unsigned capacity() { return s_capacity; } + + SmallStringKey() : m_length(s_emptyValueLength) {} + + SmallStringKey(WTF::HashTableDeletedValueType) + : m_length(s_deletedValueLength) {} + + template + SmallStringKey(CharacterType* characters, unsigned short length) + : m_length(length) { + ASSERT(length <= s_capacity); + + StringHasher hasher; + + bool remainder = length & 1; + length >>= 1; + + unsigned i = 0; + while (length--) { + m_characters[i] = characters[i]; + m_characters[i + 1] = characters[i + 1]; + hasher.addCharactersAssumingAligned(characters[i], characters[i + 1]); + i += 2; + } + + if (remainder) { + m_characters[i] = characters[i]; + hasher.addCharacter(characters[i]); + } + + m_hash = hasher.hash(); + } + + const UChar* characters() const { return m_characters; } + unsigned short length() const { return m_length; } + unsigned hash() const { return m_hash; } + + bool isHashTableDeletedValue() const { + return m_length == s_deletedValueLength; + } + bool isHashTableEmptyValue() const { + return m_length == s_emptyValueLength; } - WidthCacheEntry* add(const TextRun& run, WidthCacheEntry entry) - { - if (static_cast(run.length()) > SmallStringKey::capacity()) - return 0; + private: + static const unsigned s_capacity = 15; + static const unsigned s_emptyValueLength = s_capacity + 1; + static const unsigned s_deletedValueLength = s_capacity + 2; - if (m_countdown > 0) { - --m_countdown; - return 0; - } + unsigned m_hash; + unsigned short m_length; + UChar m_characters[s_capacity]; + }; - return addSlowCase(run, entry); + struct SmallStringKeyHash { + static unsigned hash(const SmallStringKey& key) { return key.hash(); } + static bool equal(const SmallStringKey& a, const SmallStringKey& b) { + return a == b; } + static const bool safeToCompareToEmptyOrDeleted = + true; // Empty and deleted values have lengths that are not equal to + // any valid length. + }; + + struct SmallStringKeyHashTraits : WTF::SimpleClassHashTraits { + static const bool hasIsEmptyValueFunction = true; + static bool isEmptyValue(const SmallStringKey& key) { + return key.isHashTableEmptyValue(); + } + static const bool needsDestruction = false; + static const unsigned minimumTableSize = 16; + }; + + friend bool operator==(const SmallStringKey&, const SmallStringKey&); + + public: + WidthCache() : m_interval(s_maxInterval), m_countdown(m_interval) {} - void clear() - { - m_singleCharMap.clear(); - m_map.clear(); + WidthCacheEntry* add(const TextRun& run, WidthCacheEntry entry) { + if (static_cast(run.length()) > SmallStringKey::capacity()) + return 0; + + if (m_countdown > 0) { + --m_countdown; + return 0; } -private: - WidthCacheEntry* addSlowCase(const TextRun& run, WidthCacheEntry entry) - { - int length = run.length(); - bool isNewEntry; - WidthCacheEntry *value; - if (length == 1) { - SingleCharMap::AddResult addResult = m_singleCharMap.add(run[0], entry); - isNewEntry = addResult.isNewEntry; - value = &addResult.storedValue->value; - } else { - SmallStringKey smallStringKey; - if (run.is8Bit()) - smallStringKey = SmallStringKey(run.characters8(), length); - else - smallStringKey = SmallStringKey(run.characters16(), length); - - Map::AddResult addResult = m_map.add(smallStringKey, entry); - isNewEntry = addResult.isNewEntry; - value = &addResult.storedValue->value; - } - - // Cache hit: ramp up by sampling the next few words. - if (!isNewEntry) { - m_interval = s_minInterval; - return value; - } - - // Cache miss: ramp down by increasing our sampling interval. - if (m_interval < s_maxInterval) - ++m_interval; - m_countdown = m_interval; - - if ((m_singleCharMap.size() + m_map.size()) < s_maxSize) - return value; - - // No need to be fancy: we're just trying to avoid pathological growth. - m_singleCharMap.clear(); - m_map.clear(); - return 0; + return addSlowCase(run, entry); + } + + void clear() { + m_singleCharMap.clear(); + m_map.clear(); + } + + private: + WidthCacheEntry* addSlowCase(const TextRun& run, WidthCacheEntry entry) { + int length = run.length(); + bool isNewEntry; + WidthCacheEntry* value; + if (length == 1) { + SingleCharMap::AddResult addResult = m_singleCharMap.add(run[0], entry); + isNewEntry = addResult.isNewEntry; + value = &addResult.storedValue->value; + } else { + SmallStringKey smallStringKey; + if (run.is8Bit()) + smallStringKey = SmallStringKey(run.characters8(), length); + else + smallStringKey = SmallStringKey(run.characters16(), length); + + Map::AddResult addResult = m_map.add(smallStringKey, entry); + isNewEntry = addResult.isNewEntry; + value = &addResult.storedValue->value; } - typedef HashMap Map; - typedef HashMap::Hash, WTF::UnsignedWithZeroKeyHashTraits > SingleCharMap; - static const int s_minInterval = -3; // A cache hit pays for about 3 cache misses. - static const int s_maxInterval = 20; // Sampling at this interval has almost no overhead. - static const unsigned s_maxSize = 500000; // Just enough to guard against pathological growth. + // Cache hit: ramp up by sampling the next few words. + if (!isNewEntry) { + m_interval = s_minInterval; + return value; + } - int m_interval; - int m_countdown; - SingleCharMap m_singleCharMap; - Map m_map; + // Cache miss: ramp down by increasing our sampling interval. + if (m_interval < s_maxInterval) + ++m_interval; + m_countdown = m_interval; + + if ((m_singleCharMap.size() + m_map.size()) < s_maxSize) + return value; + + // No need to be fancy: we're just trying to avoid pathological growth. + m_singleCharMap.clear(); + m_map.clear(); + return 0; + } + + typedef HashMap + Map; + typedef HashMap::Hash, + WTF::UnsignedWithZeroKeyHashTraits> + SingleCharMap; + static const int s_minInterval = + -3; // A cache hit pays for about 3 cache misses. + static const int s_maxInterval = + 20; // Sampling at this interval has almost no overhead. + static const unsigned s_maxSize = + 500000; // Just enough to guard against pathological growth. + + int m_interval; + int m_countdown; + SingleCharMap m_singleCharMap; + Map m_map; }; -inline bool operator==(const WidthCache::SmallStringKey& a, const WidthCache::SmallStringKey& b) -{ - if (a.length() != b.length()) - return false; - return WTF::equal(a.characters(), b.characters(), a.length()); +inline bool operator==(const WidthCache::SmallStringKey& a, + const WidthCache::SmallStringKey& b) { + if (a.length() != b.length()) + return false; + return WTF::equal(a.characters(), b.characters(), a.length()); } -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_FONTS_WIDTHCACHE_H_ diff --git a/sky/engine/platform/fonts/WidthIterator.cpp b/sky/engine/platform/fonts/WidthIterator.cpp index d1bdeae3b7db1..44a6dfe5bbe70 100644 --- a/sky/engine/platform/fonts/WidthIterator.cpp +++ b/sky/engine/platform/fonts/WidthIterator.cpp @@ -1,5 +1,6 @@ /* - * Copyright (C) 2003, 2006, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2006, 2008, 2009, 2010, 2011 Apple Inc. + * All rights reserved. * Copyright (C) 2008 Holger Hans Peter Freyther * Copyright (C) 2014 Google Inc. All rights reserved. * @@ -36,222 +37,246 @@ using namespace Unicode; namespace blink { -WidthIterator::WidthIterator(const Font* font, const TextRun& run, HashSet* fallbackFonts, bool accountForGlyphBounds, bool forTextEmphasis) - : m_font(font) - , m_run(run) - , m_currentCharacter(0) - , m_runWidthSoFar(0) - , m_isAfterExpansion(!run.allowsLeadingExpansion()) - , m_fallbackFonts(fallbackFonts) - , m_maxGlyphBoundingBoxY(std::numeric_limits::min()) - , m_minGlyphBoundingBoxY(std::numeric_limits::max()) - , m_firstGlyphOverflow(0) - , m_lastGlyphOverflow(0) - , m_accountForGlyphBounds(accountForGlyphBounds) - , m_forTextEmphasis(forTextEmphasis) -{ - // If the padding is non-zero, count the number of spaces in the run - // and divide that by the padding for per space addition. - m_expansion = m_run.expansion(); - if (!m_expansion) - m_expansionPerOpportunity = 0; - else { - bool isAfterExpansion = m_isAfterExpansion; - unsigned expansionOpportunityCount = m_run.is8Bit() ? Character::expansionOpportunityCount(m_run.characters8(), m_run.length(), m_run.direction(), isAfterExpansion) : Character::expansionOpportunityCount(m_run.characters16(), m_run.length(), m_run.direction(), isAfterExpansion); - if (isAfterExpansion && !m_run.allowsTrailingExpansion()) - expansionOpportunityCount--; - - if (!expansionOpportunityCount) - m_expansionPerOpportunity = 0; - else - m_expansionPerOpportunity = m_expansion / expansionOpportunityCount; - } +WidthIterator::WidthIterator(const Font* font, + const TextRun& run, + HashSet* fallbackFonts, + bool accountForGlyphBounds, + bool forTextEmphasis) + : m_font(font), + m_run(run), + m_currentCharacter(0), + m_runWidthSoFar(0), + m_isAfterExpansion(!run.allowsLeadingExpansion()), + m_fallbackFonts(fallbackFonts), + m_maxGlyphBoundingBoxY(std::numeric_limits::min()), + m_minGlyphBoundingBoxY(std::numeric_limits::max()), + m_firstGlyphOverflow(0), + m_lastGlyphOverflow(0), + m_accountForGlyphBounds(accountForGlyphBounds), + m_forTextEmphasis(forTextEmphasis) { + // If the padding is non-zero, count the number of spaces in the run + // and divide that by the padding for per space addition. + m_expansion = m_run.expansion(); + if (!m_expansion) + m_expansionPerOpportunity = 0; + else { + bool isAfterExpansion = m_isAfterExpansion; + unsigned expansionOpportunityCount = + m_run.is8Bit() ? Character::expansionOpportunityCount( + m_run.characters8(), m_run.length(), + m_run.direction(), isAfterExpansion) + : Character::expansionOpportunityCount( + m_run.characters16(), m_run.length(), + m_run.direction(), isAfterExpansion); + if (isAfterExpansion && !m_run.allowsTrailingExpansion()) + expansionOpportunityCount--; + + if (!expansionOpportunityCount) + m_expansionPerOpportunity = 0; + else + m_expansionPerOpportunity = m_expansion / expansionOpportunityCount; + } } -GlyphData WidthIterator::glyphDataForCharacter(CharacterData& charData) -{ - ASSERT(m_font); - return m_font->glyphDataForCharacter(charData.character, m_run.rtl()); +GlyphData WidthIterator::glyphDataForCharacter(CharacterData& charData) { + ASSERT(m_font); + return m_font->glyphDataForCharacter(charData.character, m_run.rtl()); } -float WidthIterator::characterWidth(UChar32 character, const GlyphData& glyphData) const -{ - const SimpleFontData* fontData = glyphData.fontData; - ASSERT(fontData); +float WidthIterator::characterWidth(UChar32 character, + const GlyphData& glyphData) const { + const SimpleFontData* fontData = glyphData.fontData; + ASSERT(fontData); - if (UNLIKELY(character == '\t' && m_run.allowTabs())) - return m_font->tabWidth(*fontData, m_run.tabSize(), m_run.xPos() + m_runWidthSoFar); + if (UNLIKELY(character == '\t' && m_run.allowTabs())) + return m_font->tabWidth(*fontData, m_run.tabSize(), + m_run.xPos() + m_runWidthSoFar); - float width = fontData->widthForGlyph(glyphData.glyph); + float width = fontData->widthForGlyph(glyphData.glyph); - // SVG uses horizontalGlyphStretch(), when textLength is used to stretch/squeeze text. - if (UNLIKELY(m_run.horizontalGlyphStretch() != 1)) - width *= m_run.horizontalGlyphStretch(); + // SVG uses horizontalGlyphStretch(), when textLength is used to + // stretch/squeeze text. + if (UNLIKELY(m_run.horizontalGlyphStretch() != 1)) + width *= m_run.horizontalGlyphStretch(); - return width; + return width; } -void WidthIterator::cacheFallbackFont(UChar32 character, const SimpleFontData* fontData, - const SimpleFontData* primaryFont) -{ - if (fontData == primaryFont) - return; - - // FIXME: This does a little extra work that could be avoided if - // glyphDataForCharacter() returned whether it chose to use a small caps font. - if (m_font->fontDescription().variant() == FontVariantNormal || character == toUpper(character)) { - m_fallbackFonts->add(fontData); - } else { - ASSERT(m_font->fontDescription().variant() == FontVariantSmallCaps); - const GlyphData uppercaseGlyphData = m_font->glyphDataForCharacter(toUpper(character), - m_run.rtl()); - if (uppercaseGlyphData.fontData != primaryFont) - m_fallbackFonts->add(uppercaseGlyphData.fontData); - } +void WidthIterator::cacheFallbackFont(UChar32 character, + const SimpleFontData* fontData, + const SimpleFontData* primaryFont) { + if (fontData == primaryFont) + return; + + // FIXME: This does a little extra work that could be avoided if + // glyphDataForCharacter() returned whether it chose to use a small caps font. + if (m_font->fontDescription().variant() == FontVariantNormal || + character == toUpper(character)) { + m_fallbackFonts->add(fontData); + } else { + ASSERT(m_font->fontDescription().variant() == FontVariantSmallCaps); + const GlyphData uppercaseGlyphData = + m_font->glyphDataForCharacter(toUpper(character), m_run.rtl()); + if (uppercaseGlyphData.fontData != primaryFont) + m_fallbackFonts->add(uppercaseGlyphData.fontData); + } } -float WidthIterator::adjustSpacing(float width, const CharacterData& charData, - const SimpleFontData& fontData, GlyphBuffer* glyphBuffer) -{ - // Account for letter-spacing. - if (width) - width += m_font->fontDescription().letterSpacing(); - - static bool expandAroundIdeographs = FontPlatformFeatures::canExpandAroundIdeographsInComplexText(); - bool treatAsSpace = Character::treatAsSpace(charData.character); - if (treatAsSpace || (expandAroundIdeographs && Character::isCJKIdeographOrSymbol(charData.character))) { - // Distribute the run's total expansion evenly over all expansion opportunities in the run. - if (m_expansion) { - if (!treatAsSpace && !m_isAfterExpansion) { - // Take the expansion opportunity before this ideograph. - m_expansion -= m_expansionPerOpportunity; - float expansionAtThisOpportunity = m_expansionPerOpportunity; - m_runWidthSoFar += expansionAtThisOpportunity; - if (glyphBuffer) { - if (glyphBuffer->isEmpty()) { - if (m_forTextEmphasis) - glyphBuffer->add(fontData.zeroWidthSpaceGlyph(), &fontData, m_expansionPerOpportunity); - else - glyphBuffer->add(fontData.spaceGlyph(), &fontData, expansionAtThisOpportunity); - } else { - glyphBuffer->expandLastAdvance(expansionAtThisOpportunity); - } - } - } - if (m_run.allowsTrailingExpansion() - || (m_run.ltr() && charData.characterOffset + charData.clusterLength < static_cast(m_run.length())) - || (m_run.rtl() && charData.characterOffset)) { - m_expansion -= m_expansionPerOpportunity; - width += m_expansionPerOpportunity; - m_isAfterExpansion = true; - } - } else { - m_isAfterExpansion = false; - } - - // Account for word spacing. - // We apply additional space between "words" by adding width to the space character. - if (treatAsSpace && (charData.character != '\t' || !m_run.allowTabs()) - && (charData.characterOffset || charData.character == noBreakSpace) - && m_font->fontDescription().wordSpacing()) { - width += m_font->fontDescription().wordSpacing(); +float WidthIterator::adjustSpacing(float width, + const CharacterData& charData, + const SimpleFontData& fontData, + GlyphBuffer* glyphBuffer) { + // Account for letter-spacing. + if (width) + width += m_font->fontDescription().letterSpacing(); + + static bool expandAroundIdeographs = + FontPlatformFeatures::canExpandAroundIdeographsInComplexText(); + bool treatAsSpace = Character::treatAsSpace(charData.character); + if (treatAsSpace || (expandAroundIdeographs && + Character::isCJKIdeographOrSymbol(charData.character))) { + // Distribute the run's total expansion evenly over all expansion + // opportunities in the run. + if (m_expansion) { + if (!treatAsSpace && !m_isAfterExpansion) { + // Take the expansion opportunity before this ideograph. + m_expansion -= m_expansionPerOpportunity; + float expansionAtThisOpportunity = m_expansionPerOpportunity; + m_runWidthSoFar += expansionAtThisOpportunity; + if (glyphBuffer) { + if (glyphBuffer->isEmpty()) { + if (m_forTextEmphasis) + glyphBuffer->add(fontData.zeroWidthSpaceGlyph(), &fontData, + m_expansionPerOpportunity); + else + glyphBuffer->add(fontData.spaceGlyph(), &fontData, + expansionAtThisOpportunity); + } else { + glyphBuffer->expandLastAdvance(expansionAtThisOpportunity); + } } + } + if (m_run.allowsTrailingExpansion() || + (m_run.ltr() && charData.characterOffset + charData.clusterLength < + static_cast(m_run.length())) || + (m_run.rtl() && charData.characterOffset)) { + m_expansion -= m_expansionPerOpportunity; + width += m_expansionPerOpportunity; + m_isAfterExpansion = true; + } } else { - m_isAfterExpansion = false; + m_isAfterExpansion = false; } - return width; -} + // Account for word spacing. + // We apply additional space between "words" by adding width to the space + // character. + if (treatAsSpace && (charData.character != '\t' || !m_run.allowTabs()) && + (charData.characterOffset || charData.character == noBreakSpace) && + m_font->fontDescription().wordSpacing()) { + width += m_font->fontDescription().wordSpacing(); + } + } else { + m_isAfterExpansion = false; + } -void WidthIterator::updateGlyphBounds(const GlyphData& glyphData, float width, bool firstCharacter) -{ - ASSERT(glyphData.fontData); - FloatRect bounds = glyphData.fontData->boundsForGlyph(glyphData.glyph); + return width; +} - if (firstCharacter) - m_firstGlyphOverflow = std::max(0, -bounds.x()); - m_lastGlyphOverflow = std::max(0, bounds.maxX() - width); - m_maxGlyphBoundingBoxY = std::max(m_maxGlyphBoundingBoxY, bounds.maxY()); - m_minGlyphBoundingBoxY = std::min(m_minGlyphBoundingBoxY, bounds.y()); +void WidthIterator::updateGlyphBounds(const GlyphData& glyphData, + float width, + bool firstCharacter) { + ASSERT(glyphData.fontData); + FloatRect bounds = glyphData.fontData->boundsForGlyph(glyphData.glyph); + + if (firstCharacter) + m_firstGlyphOverflow = std::max(0, -bounds.x()); + m_lastGlyphOverflow = std::max(0, bounds.maxX() - width); + m_maxGlyphBoundingBoxY = std::max(m_maxGlyphBoundingBoxY, bounds.maxY()); + m_minGlyphBoundingBoxY = std::min(m_minGlyphBoundingBoxY, bounds.y()); } template -unsigned WidthIterator::advanceInternal(TextIterator& textIterator, GlyphBuffer* glyphBuffer) -{ - bool hasExtraSpacing = (m_font->fontDescription().letterSpacing() || m_font->fontDescription().wordSpacing() || m_expansion) - && !m_run.spacingDisabled(); - - const SimpleFontData* primaryFont = m_font->primaryFont(); - const SimpleFontData* lastFontData = primaryFont; - - CharacterData charData; - while (textIterator.consume(charData.character, charData.clusterLength)) { - charData.characterOffset = textIterator.currentCharacter(); - - const GlyphData glyphData = glyphDataForCharacter(charData); - Glyph glyph = glyphData.glyph; - const SimpleFontData* fontData = glyphData.fontData; - ASSERT(fontData); +unsigned WidthIterator::advanceInternal(TextIterator& textIterator, + GlyphBuffer* glyphBuffer) { + bool hasExtraSpacing = + (m_font->fontDescription().letterSpacing() || + m_font->fontDescription().wordSpacing() || m_expansion) && + !m_run.spacingDisabled(); + + const SimpleFontData* primaryFont = m_font->primaryFont(); + const SimpleFontData* lastFontData = primaryFont; + + CharacterData charData; + while (textIterator.consume(charData.character, charData.clusterLength)) { + charData.characterOffset = textIterator.currentCharacter(); + + const GlyphData glyphData = glyphDataForCharacter(charData); + Glyph glyph = glyphData.glyph; + const SimpleFontData* fontData = glyphData.fontData; + ASSERT(fontData); - // Now that we have a glyph and font data, get its width. - float width = characterWidth(charData.character, glyphData); + // Now that we have a glyph and font data, get its width. + float width = characterWidth(charData.character, glyphData); - if (m_fallbackFonts && lastFontData != fontData && width) { - lastFontData = fontData; - cacheFallbackFont(charData.character, fontData, primaryFont); - } + if (m_fallbackFonts && lastFontData != fontData && width) { + lastFontData = fontData; + cacheFallbackFont(charData.character, fontData, primaryFont); + } - if (hasExtraSpacing) - width = adjustSpacing(width, charData, *fontData, glyphBuffer); + if (hasExtraSpacing) + width = adjustSpacing(width, charData, *fontData, glyphBuffer); - if (m_accountForGlyphBounds) - updateGlyphBounds(glyphData, width, !charData.characterOffset); + if (m_accountForGlyphBounds) + updateGlyphBounds(glyphData, width, !charData.characterOffset); - if (m_forTextEmphasis && !Character::canReceiveTextEmphasis(charData.character)) - glyph = 0; + if (m_forTextEmphasis && + !Character::canReceiveTextEmphasis(charData.character)) + glyph = 0; - // Advance past the character we just dealt with. - textIterator.advance(charData.clusterLength); - m_runWidthSoFar += width; + // Advance past the character we just dealt with. + textIterator.advance(charData.clusterLength); + m_runWidthSoFar += width; - if (glyphBuffer) - glyphBuffer->add(glyph, fontData, width); - } + if (glyphBuffer) + glyphBuffer->add(glyph, fontData, width); + } - unsigned consumedCharacters = textIterator.currentCharacter() - m_currentCharacter; - m_currentCharacter = textIterator.currentCharacter(); + unsigned consumedCharacters = + textIterator.currentCharacter() - m_currentCharacter; + m_currentCharacter = textIterator.currentCharacter(); - return consumedCharacters; + return consumedCharacters; } -unsigned WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer) -{ - int length = m_run.length(); +unsigned WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer) { + int length = m_run.length(); - if (offset > length) - offset = length; + if (offset > length) + offset = length; - if (m_currentCharacter >= static_cast(offset)) - return 0; + if (m_currentCharacter >= static_cast(offset)) + return 0; - if (m_run.is8Bit()) { - Latin1TextIterator textIterator(m_run.data8(m_currentCharacter), m_currentCharacter, offset, length); - return advanceInternal(textIterator, glyphBuffer); - } - - SurrogatePairAwareTextIterator textIterator(m_run.data16(m_currentCharacter), m_currentCharacter, offset, length); + if (m_run.is8Bit()) { + Latin1TextIterator textIterator(m_run.data8(m_currentCharacter), + m_currentCharacter, offset, length); return advanceInternal(textIterator, glyphBuffer); + } + + SurrogatePairAwareTextIterator textIterator( + m_run.data16(m_currentCharacter), m_currentCharacter, offset, length); + return advanceInternal(textIterator, glyphBuffer); } -bool WidthIterator::advanceOneCharacter(float& width) -{ - float initialWidth = m_runWidthSoFar; +bool WidthIterator::advanceOneCharacter(float& width) { + float initialWidth = m_runWidthSoFar; - if (!advance(m_currentCharacter + 1)) - return false; + if (!advance(m_currentCharacter + 1)) + return false; - width = m_runWidthSoFar - initialWidth; - return true; + width = m_runWidthSoFar - initialWidth; + return true; } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/fonts/WidthIterator.h b/sky/engine/platform/fonts/WidthIterator.h index b3262185c0e72..6f16e75e96395 100644 --- a/sky/engine/platform/fonts/WidthIterator.h +++ b/sky/engine/platform/fonts/WidthIterator.h @@ -38,57 +38,79 @@ class TextRun; struct GlyphData; struct PLATFORM_EXPORT WidthIterator { - WTF_MAKE_FAST_ALLOCATED; -public: - WidthIterator(const Font*, const TextRun&, HashSet* fallbackFonts = 0, bool accountForGlyphBounds = false, bool forTextEmphasis = false); - - unsigned advance(int to, GlyphBuffer* = 0); - bool advanceOneCharacter(float& width); - - float maxGlyphBoundingBoxY() const { ASSERT(m_accountForGlyphBounds); return m_maxGlyphBoundingBoxY; } - float minGlyphBoundingBoxY() const { ASSERT(m_accountForGlyphBounds); return m_minGlyphBoundingBoxY; } - float firstGlyphOverflow() const { ASSERT(m_accountForGlyphBounds); return m_firstGlyphOverflow; } - float lastGlyphOverflow() const { ASSERT(m_accountForGlyphBounds); return m_lastGlyphOverflow; } - - const TextRun& run() const { return m_run; } - float runWidthSoFar() const { return m_runWidthSoFar; } - - const Font* m_font; - - const TextRun& m_run; - - unsigned m_currentCharacter; - float m_runWidthSoFar; - float m_expansion; - float m_expansionPerOpportunity; - bool m_isAfterExpansion; - -private: - struct CharacterData { - UChar32 character; - unsigned clusterLength; - int characterOffset; - }; - - GlyphData glyphDataForCharacter(CharacterData&); - float characterWidth(UChar32, const GlyphData&) const; - void cacheFallbackFont(UChar32, const SimpleFontData*, const SimpleFontData* primaryFont); - float adjustSpacing(float, const CharacterData&, const SimpleFontData&, GlyphBuffer*); - void updateGlyphBounds(const GlyphData&, float width, bool firstCharacter); - - template - unsigned advanceInternal(TextIterator&, GlyphBuffer*); - - HashSet* m_fallbackFonts; - float m_maxGlyphBoundingBoxY; - float m_minGlyphBoundingBoxY; - float m_firstGlyphOverflow; - float m_lastGlyphOverflow; - - bool m_accountForGlyphBounds : 1; - bool m_forTextEmphasis : 1; + WTF_MAKE_FAST_ALLOCATED; + + public: + WidthIterator(const Font*, + const TextRun&, + HashSet* fallbackFonts = 0, + bool accountForGlyphBounds = false, + bool forTextEmphasis = false); + + unsigned advance(int to, GlyphBuffer* = 0); + bool advanceOneCharacter(float& width); + + float maxGlyphBoundingBoxY() const { + ASSERT(m_accountForGlyphBounds); + return m_maxGlyphBoundingBoxY; + } + float minGlyphBoundingBoxY() const { + ASSERT(m_accountForGlyphBounds); + return m_minGlyphBoundingBoxY; + } + float firstGlyphOverflow() const { + ASSERT(m_accountForGlyphBounds); + return m_firstGlyphOverflow; + } + float lastGlyphOverflow() const { + ASSERT(m_accountForGlyphBounds); + return m_lastGlyphOverflow; + } + + const TextRun& run() const { return m_run; } + float runWidthSoFar() const { return m_runWidthSoFar; } + + const Font* m_font; + + const TextRun& m_run; + + unsigned m_currentCharacter; + float m_runWidthSoFar; + float m_expansion; + float m_expansionPerOpportunity; + bool m_isAfterExpansion; + + private: + struct CharacterData { + UChar32 character; + unsigned clusterLength; + int characterOffset; + }; + + GlyphData glyphDataForCharacter(CharacterData&); + float characterWidth(UChar32, const GlyphData&) const; + void cacheFallbackFont(UChar32, + const SimpleFontData*, + const SimpleFontData* primaryFont); + float adjustSpacing(float, + const CharacterData&, + const SimpleFontData&, + GlyphBuffer*); + void updateGlyphBounds(const GlyphData&, float width, bool firstCharacter); + + template + unsigned advanceInternal(TextIterator&, GlyphBuffer*); + + HashSet* m_fallbackFonts; + float m_maxGlyphBoundingBoxY; + float m_minGlyphBoundingBoxY; + float m_firstGlyphOverflow; + float m_lastGlyphOverflow; + + bool m_accountForGlyphBounds : 1; + bool m_forTextEmphasis : 1; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_FONTS_WIDTHITERATOR_H_ diff --git a/sky/engine/platform/fonts/harfbuzz/FontHarfBuzz.cpp b/sky/engine/platform/fonts/harfbuzz/FontHarfBuzz.cpp index 7dbdb98d94613..a84537282bb3c 100644 --- a/sky/engine/platform/fonts/harfbuzz/FontHarfBuzz.cpp +++ b/sky/engine/platform/fonts/harfbuzz/FontHarfBuzz.cpp @@ -46,281 +46,304 @@ namespace blink { -bool FontPlatformFeatures::canExpandAroundIdeographsInComplexText() -{ - return false; +bool FontPlatformFeatures::canExpandAroundIdeographsInComplexText() { + return false; } -static SkPaint textFillPaint(GraphicsContext* gc, const SimpleFontData* font) -{ - SkPaint paint = gc->fillPaint(); - font->platformData().setupPaint(&paint, gc); - gc->adjustTextRenderMode(&paint); - paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); - return paint; +static SkPaint textFillPaint(GraphicsContext* gc, const SimpleFontData* font) { + SkPaint paint = gc->fillPaint(); + font->platformData().setupPaint(&paint, gc); + gc->adjustTextRenderMode(&paint); + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + return paint; } -static SkPaint textStrokePaint(GraphicsContext* gc, const SimpleFontData* font, bool isFilling) -{ - SkPaint paint = gc->strokePaint(); - font->platformData().setupPaint(&paint, gc); - gc->adjustTextRenderMode(&paint); - paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); - if (isFilling) { - // If there is a shadow and we filled above, there will already be - // a shadow. We don't want to draw it again or it will be too dark - // and it will go on top of the fill. - // - // Note that this isn't strictly correct, since the stroke could be - // very thick and the shadow wouldn't account for this. The "right" - // thing would be to draw to a new layer and then draw that layer - // with a shadow. But this is a lot of extra work for something - // that isn't normally an issue. - paint.setLooper(0); - } - return paint; +static SkPaint textStrokePaint(GraphicsContext* gc, + const SimpleFontData* font, + bool isFilling) { + SkPaint paint = gc->strokePaint(); + font->platformData().setupPaint(&paint, gc); + gc->adjustTextRenderMode(&paint); + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + if (isFilling) { + // If there is a shadow and we filled above, there will already be + // a shadow. We don't want to draw it again or it will be too dark + // and it will go on top of the fill. + // + // Note that this isn't strictly correct, since the stroke could be + // very thick and the shadow wouldn't account for this. The "right" + // thing would be to draw to a new layer and then draw that layer + // with a shadow. But this is a lot of extra work for something + // that isn't normally an issue. + paint.setLooper(0); + } + return paint; } -static void paintGlyphs(GraphicsContext* gc, const SimpleFontData* font, - const Glyph glyphs[], unsigned numGlyphs, - const SkPoint pos[], const FloatRect& textRect) -{ - TextDrawingModeFlags textMode = gc->textDrawingMode(); - - // We draw text up to two times (once for fill, once for stroke). - if (textMode & TextModeFill) { - SkPaint paint = textFillPaint(gc, font); - gc->drawPosText(glyphs, numGlyphs * sizeof(Glyph), pos, textRect, paint); - } - - if ((textMode & TextModeStroke) && gc->hasStroke()) { - SkPaint paint = textStrokePaint(gc, font, textMode & TextModeFill); - gc->drawPosText(glyphs, numGlyphs * sizeof(Glyph), pos, textRect, paint); - } +static void paintGlyphs(GraphicsContext* gc, + const SimpleFontData* font, + const Glyph glyphs[], + unsigned numGlyphs, + const SkPoint pos[], + const FloatRect& textRect) { + TextDrawingModeFlags textMode = gc->textDrawingMode(); + + // We draw text up to two times (once for fill, once for stroke). + if (textMode & TextModeFill) { + SkPaint paint = textFillPaint(gc, font); + gc->drawPosText(glyphs, numGlyphs * sizeof(Glyph), pos, textRect, paint); + } + + if ((textMode & TextModeStroke) && gc->hasStroke()) { + SkPaint paint = textStrokePaint(gc, font, textMode & TextModeFill); + gc->drawPosText(glyphs, numGlyphs * sizeof(Glyph), pos, textRect, paint); + } } -static void paintGlyphsHorizontal(GraphicsContext* gc, const SimpleFontData* font, - const Glyph glyphs[], unsigned numGlyphs, - const SkScalar xpos[], SkScalar constY, const FloatRect& textRect) -{ - TextDrawingModeFlags textMode = gc->textDrawingMode(); - - if (textMode & TextModeFill) { - SkPaint paint = textFillPaint(gc, font); - gc->drawPosTextH(glyphs, numGlyphs * sizeof(Glyph), xpos, constY, textRect, paint); - } - - if ((textMode & TextModeStroke) && gc->hasStroke()) { - SkPaint paint = textStrokePaint(gc, font, textMode & TextModeFill); - gc->drawPosTextH(glyphs, numGlyphs * sizeof(Glyph), xpos, constY, textRect, paint); - } +static void paintGlyphsHorizontal(GraphicsContext* gc, + const SimpleFontData* font, + const Glyph glyphs[], + unsigned numGlyphs, + const SkScalar xpos[], + SkScalar constY, + const FloatRect& textRect) { + TextDrawingModeFlags textMode = gc->textDrawingMode(); + + if (textMode & TextModeFill) { + SkPaint paint = textFillPaint(gc, font); + gc->drawPosTextH(glyphs, numGlyphs * sizeof(Glyph), xpos, constY, textRect, + paint); + } + + if ((textMode & TextModeStroke) && gc->hasStroke()) { + SkPaint paint = textStrokePaint(gc, font, textMode & TextModeFill); + gc->drawPosTextH(glyphs, numGlyphs * sizeof(Glyph), xpos, constY, textRect, + paint); + } } -void Font::drawGlyphs(GraphicsContext* gc, const SimpleFontData* font, - const GlyphBuffer& glyphBuffer, unsigned from, unsigned numGlyphs, - const FloatPoint& point, const FloatRect& textRect) const -{ - SkScalar x = SkFloatToScalar(point.x()); - SkScalar y = SkFloatToScalar(point.y()); +void Font::drawGlyphs(GraphicsContext* gc, + const SimpleFontData* font, + const GlyphBuffer& glyphBuffer, + unsigned from, + unsigned numGlyphs, + const FloatPoint& point, + const FloatRect& textRect) const { + SkScalar x = SkFloatToScalar(point.x()); + SkScalar y = SkFloatToScalar(point.y()); // ENABLE_OPENTYPE_VERTICAL is not enabled on MACOSX #if !OS(MACOSX) - const OpenTypeVerticalData* verticalData = font->verticalData(); - if (font->platformData().orientation() == Vertical && verticalData) { - SkAutoSTMalloc<32, SkPoint> storage(numGlyphs); - SkPoint* pos = storage.get(); - - AffineTransform savedMatrix = gc->getCTM(); - gc->concatCTM(AffineTransform(0, -1, 1, 0, point.x(), point.y())); - gc->concatCTM(AffineTransform(1, 0, 0, 1, -point.x(), -point.y())); - - const unsigned kMaxBufferLength = 256; - Vector translations; - - const FontMetrics& metrics = font->fontMetrics(); - SkScalar verticalOriginX = SkFloatToScalar(point.x() + metrics.floatAscent() - metrics.floatAscent(IdeographicBaseline)); - float horizontalOffset = point.x(); - - unsigned glyphIndex = 0; - while (glyphIndex < numGlyphs) { - unsigned chunkLength = std::min(kMaxBufferLength, numGlyphs - glyphIndex); - - const Glyph* glyphs = glyphBuffer.glyphs(from + glyphIndex); - translations.resize(chunkLength); - verticalData->getVerticalTranslationsForGlyphs(font, &glyphs[0], chunkLength, reinterpret_cast(&translations[0])); - - x = verticalOriginX; - y = SkFloatToScalar(point.y() + horizontalOffset - point.x()); - - float currentWidth = 0; - for (unsigned i = 0; i < chunkLength; ++i, ++glyphIndex) { - pos[i].set( - x + SkIntToScalar(lroundf(translations[i].x())), - y + -SkIntToScalar(-lroundf(currentWidth - translations[i].y()))); - currentWidth += glyphBuffer.advanceAt(from + glyphIndex); - } - horizontalOffset += currentWidth; - paintGlyphs(gc, font, glyphs, chunkLength, pos, textRect); - } - - gc->setCTM(savedMatrix); - return; - } -#endif - - if (!glyphBuffer.hasOffsets()) { - SkAutoSTMalloc<64, SkScalar> storage(numGlyphs); - SkScalar* xpos = storage.get(); - const float* adv = glyphBuffer.advances(from); - for (unsigned i = 0; i < numGlyphs; i++) { - xpos[i] = x; - x += SkFloatToScalar(adv[i]); - } - const Glyph* glyphs = glyphBuffer.glyphs(from); - paintGlyphsHorizontal(gc, font, glyphs, numGlyphs, xpos, SkFloatToScalar(y), textRect); - return; - } - - // FIXME: text rendering speed: - // Android has code in their WebCore fork to special case when the - // GlyphBuffer has no advances other than the defaults. In that case the - // text drawing can proceed faster. However, it's unclear when those - // patches may be upstreamed to WebKit so we always use the slower path - // here. + const OpenTypeVerticalData* verticalData = font->verticalData(); + if (font->platformData().orientation() == Vertical && verticalData) { SkAutoSTMalloc<32, SkPoint> storage(numGlyphs); SkPoint* pos = storage.get(); - const FloatSize* offsets = glyphBuffer.offsets(from); - const float* advances = glyphBuffer.advances(from); - SkScalar advanceSoFar = SkFloatToScalar(0); - for (unsigned i = 0; i < numGlyphs; i++) { + + AffineTransform savedMatrix = gc->getCTM(); + gc->concatCTM(AffineTransform(0, -1, 1, 0, point.x(), point.y())); + gc->concatCTM(AffineTransform(1, 0, 0, 1, -point.x(), -point.y())); + + const unsigned kMaxBufferLength = 256; + Vector translations; + + const FontMetrics& metrics = font->fontMetrics(); + SkScalar verticalOriginX = + SkFloatToScalar(point.x() + metrics.floatAscent() - + metrics.floatAscent(IdeographicBaseline)); + float horizontalOffset = point.x(); + + unsigned glyphIndex = 0; + while (glyphIndex < numGlyphs) { + unsigned chunkLength = std::min(kMaxBufferLength, numGlyphs - glyphIndex); + + const Glyph* glyphs = glyphBuffer.glyphs(from + glyphIndex); + translations.resize(chunkLength); + verticalData->getVerticalTranslationsForGlyphs( + font, &glyphs[0], chunkLength, + reinterpret_cast(&translations[0])); + + x = verticalOriginX; + y = SkFloatToScalar(point.y() + horizontalOffset - point.x()); + + float currentWidth = 0; + for (unsigned i = 0; i < chunkLength; ++i, ++glyphIndex) { pos[i].set( - x + SkFloatToScalar(offsets[i].width()) + advanceSoFar, - y + SkFloatToScalar(offsets[i].height())); - advanceSoFar += SkFloatToScalar(advances[i]); + x + SkIntToScalar(lroundf(translations[i].x())), + y + -SkIntToScalar(-lroundf(currentWidth - translations[i].y()))); + currentWidth += glyphBuffer.advanceAt(from + glyphIndex); + } + horizontalOffset += currentWidth; + paintGlyphs(gc, font, glyphs, chunkLength, pos, textRect); } + gc->setCTM(savedMatrix); + return; + } +#endif + + if (!glyphBuffer.hasOffsets()) { + SkAutoSTMalloc<64, SkScalar> storage(numGlyphs); + SkScalar* xpos = storage.get(); + const float* adv = glyphBuffer.advances(from); + for (unsigned i = 0; i < numGlyphs; i++) { + xpos[i] = x; + x += SkFloatToScalar(adv[i]); + } const Glyph* glyphs = glyphBuffer.glyphs(from); - paintGlyphs(gc, font, glyphs, numGlyphs, pos, textRect); + paintGlyphsHorizontal(gc, font, glyphs, numGlyphs, xpos, SkFloatToScalar(y), + textRect); + return; + } + + // FIXME: text rendering speed: + // Android has code in their WebCore fork to special case when the + // GlyphBuffer has no advances other than the defaults. In that case the + // text drawing can proceed faster. However, it's unclear when those + // patches may be upstreamed to WebKit so we always use the slower path + // here. + SkAutoSTMalloc<32, SkPoint> storage(numGlyphs); + SkPoint* pos = storage.get(); + const FloatSize* offsets = glyphBuffer.offsets(from); + const float* advances = glyphBuffer.advances(from); + SkScalar advanceSoFar = SkFloatToScalar(0); + for (unsigned i = 0; i < numGlyphs; i++) { + pos[i].set(x + SkFloatToScalar(offsets[i].width()) + advanceSoFar, + y + SkFloatToScalar(offsets[i].height())); + advanceSoFar += SkFloatToScalar(advances[i]); + } + + const Glyph* glyphs = glyphBuffer.glyphs(from); + paintGlyphs(gc, font, glyphs, numGlyphs, pos, textRect); } -void Font::drawTextBlob(GraphicsContext* gc, const SkTextBlob* blob, const SkPoint& origin) const -{ - // FIXME: It would be good to move this to Font.cpp, if we're sure that none - // of the things in FontMac's setupPaint need to apply here. - // See also paintGlyphs. - TextDrawingModeFlags textMode = gc->textDrawingMode(); +void Font::drawTextBlob(GraphicsContext* gc, + const SkTextBlob* blob, + const SkPoint& origin) const { + // FIXME: It would be good to move this to Font.cpp, if we're sure that none + // of the things in FontMac's setupPaint need to apply here. + // See also paintGlyphs. + TextDrawingModeFlags textMode = gc->textDrawingMode(); - if (textMode & TextModeFill) - gc->drawTextBlob(blob, origin, gc->fillPaint()); + if (textMode & TextModeFill) + gc->drawTextBlob(blob, origin, gc->fillPaint()); - if ((textMode & TextModeStroke) && gc->hasStroke()) { - SkPaint paint = gc->strokePaint(); - if (textMode & TextModeFill) - paint.setLooper(0); - gc->drawTextBlob(blob, origin, paint); - } + if ((textMode & TextModeStroke) && gc->hasStroke()) { + SkPaint paint = gc->strokePaint(); + if (textMode & TextModeFill) + paint.setLooper(0); + gc->drawTextBlob(blob, origin, paint); + } } -float Font::floatWidthForComplexText(const TextRun& run, HashSet* fallbackFonts, IntRectExtent* glyphBounds) const -{ - HarfBuzzShaper shaper(this, run, HarfBuzzShaper::NotForTextEmphasis, fallbackFonts); - if (!shaper.shape()) - return 0; - - glyphBounds->setTop(floorf(-shaper.glyphBoundingBox().top())); - glyphBounds->setBottom(ceilf(shaper.glyphBoundingBox().bottom())); - glyphBounds->setLeft(std::max(0, floorf(-shaper.glyphBoundingBox().left()))); - glyphBounds->setRight(std::max(0, ceilf(shaper.glyphBoundingBox().right() - shaper.totalWidth()))); - - return shaper.totalWidth(); +float Font::floatWidthForComplexText( + const TextRun& run, + HashSet* fallbackFonts, + IntRectExtent* glyphBounds) const { + HarfBuzzShaper shaper(this, run, HarfBuzzShaper::NotForTextEmphasis, + fallbackFonts); + if (!shaper.shape()) + return 0; + + glyphBounds->setTop(floorf(-shaper.glyphBoundingBox().top())); + glyphBounds->setBottom(ceilf(shaper.glyphBoundingBox().bottom())); + glyphBounds->setLeft( + std::max(0, floorf(-shaper.glyphBoundingBox().left()))); + glyphBounds->setRight(std::max( + 0, ceilf(shaper.glyphBoundingBox().right() - shaper.totalWidth()))); + + return shaper.totalWidth(); } // Return the code point index for the given |x| offset into the text run. -int Font::offsetForPositionForComplexText(const TextRun& run, float xFloat, - bool includePartialGlyphs) const -{ - HarfBuzzShaper shaper(this, run); - if (!shaper.shape()) - return 0; - return shaper.offsetForPosition(xFloat); +int Font::offsetForPositionForComplexText(const TextRun& run, + float xFloat, + bool includePartialGlyphs) const { + HarfBuzzShaper shaper(this, run); + if (!shaper.shape()) + return 0; + return shaper.offsetForPosition(xFloat); } -// Return the rectangle for selecting the given range of code-points in the TextRun. +// Return the rectangle for selecting the given range of code-points in the +// TextRun. FloatRect Font::selectionRectForComplexText(const TextRun& run, - const FloatPoint& point, int height, - int from, int to) const -{ - HarfBuzzShaper shaper(this, run); - if (!shaper.shape()) - return FloatRect(); - return shaper.selectionRect(point, height, from, to); + const FloatPoint& point, + int height, + int from, + int to) const { + HarfBuzzShaper shaper(this, run); + if (!shaper.shape()) + return FloatRect(); + return shaper.selectionRect(point, height, from, to); } namespace { template -bool buildTextBlobInternal(const GlyphBuffer& glyphBuffer, SkScalar initialAdvance, SkTextBlobBuilder& builder) -{ - SkScalar x = initialAdvance; - unsigned i = 0; - while (i < glyphBuffer.size()) { - const SimpleFontData* fontData = glyphBuffer.fontDataAt(i); - - // FIXME: Handle vertical text. - if (fontData->platformData().orientation() == Vertical) - return false; - - // FIXME: Handle SVG fonts. - if (fontData->isSVGFont()) - return false; - - // FIXME: FontPlatformData makes some decisions on the device scale - // factor, which is found via the GraphicsContext. This should be fixed - // to avoid correctness problems here. - SkPaint paint; - fontData->platformData().setupPaint(&paint); - paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); - - unsigned start = i++; - while (i < glyphBuffer.size() && glyphBuffer.fontDataAt(i) == fontData) - i++; - unsigned count = i - start; - - const SkTextBlobBuilder::RunBuffer& buffer = hasOffsets ? - builder.allocRunPos(paint, count) : - builder.allocRunPosH(paint, count, 0); - - const uint16_t* glyphs = glyphBuffer.glyphs(start); - std::copy(glyphs, glyphs + count, buffer.glyphs); - - const float* advances = glyphBuffer.advances(start); - const FloatSize* offsets = glyphBuffer.offsets(start); - for (unsigned j = 0; j < count; j++) { - if (hasOffsets) { - const FloatSize& offset = offsets[j]; - buffer.pos[2 * j] = x + offset.width(); - buffer.pos[2 * j + 1] = offset.height(); - } else { - buffer.pos[j] = x; - } - x += SkFloatToScalar(advances[j]); - } +bool buildTextBlobInternal(const GlyphBuffer& glyphBuffer, + SkScalar initialAdvance, + SkTextBlobBuilder& builder) { + SkScalar x = initialAdvance; + unsigned i = 0; + while (i < glyphBuffer.size()) { + const SimpleFontData* fontData = glyphBuffer.fontDataAt(i); + + // FIXME: Handle vertical text. + if (fontData->platformData().orientation() == Vertical) + return false; + + // FIXME: Handle SVG fonts. + if (fontData->isSVGFont()) + return false; + + // FIXME: FontPlatformData makes some decisions on the device scale + // factor, which is found via the GraphicsContext. This should be fixed + // to avoid correctness problems here. + SkPaint paint; + fontData->platformData().setupPaint(&paint); + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + + unsigned start = i++; + while (i < glyphBuffer.size() && glyphBuffer.fontDataAt(i) == fontData) + i++; + unsigned count = i - start; + + const SkTextBlobBuilder::RunBuffer& buffer = + hasOffsets ? builder.allocRunPos(paint, count) + : builder.allocRunPosH(paint, count, 0); + + const uint16_t* glyphs = glyphBuffer.glyphs(start); + std::copy(glyphs, glyphs + count, buffer.glyphs); + + const float* advances = glyphBuffer.advances(start); + const FloatSize* offsets = glyphBuffer.offsets(start); + for (unsigned j = 0; j < count; j++) { + if (hasOffsets) { + const FloatSize& offset = offsets[j]; + buffer.pos[2 * j] = x + offset.width(); + buffer.pos[2 * j + 1] = offset.height(); + } else { + buffer.pos[j] = x; + } + x += SkFloatToScalar(advances[j]); } - return true; + } + return true; } -} // namespace +} // namespace -TextBlobPtr Font::buildTextBlob(const GlyphBuffer& glyphBuffer, float initialAdvance, const FloatRect& bounds) const -{ - SkTextBlobBuilder builder; - SkScalar advance = SkFloatToScalar(initialAdvance); +TextBlobPtr Font::buildTextBlob(const GlyphBuffer& glyphBuffer, + float initialAdvance, + const FloatRect& bounds) const { + SkTextBlobBuilder builder; + SkScalar advance = SkFloatToScalar(initialAdvance); - bool success = glyphBuffer.hasOffsets() ? - buildTextBlobInternal(glyphBuffer, advance, builder) : - buildTextBlobInternal(glyphBuffer, advance, builder); - return success ? TextBlobPtr(builder.make()) : nullptr; + bool success = + glyphBuffer.hasOffsets() + ? buildTextBlobInternal(glyphBuffer, advance, builder) + : buildTextBlobInternal(glyphBuffer, advance, builder); + return success ? TextBlobPtr(builder.make()) : nullptr; } - -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/fonts/harfbuzz/HarfBuzzFace.cpp b/sky/engine/platform/fonts/harfbuzz/HarfBuzzFace.cpp index 5a0eceaaa8686..75ac940bb54f8 100644 --- a/sky/engine/platform/fonts/harfbuzz/HarfBuzzFace.cpp +++ b/sky/engine/platform/fonts/harfbuzz/HarfBuzzFace.cpp @@ -30,9 +30,9 @@ #include "flutter/sky/engine/platform/fonts/harfbuzz/HarfBuzzFace.h" +#include "flutter/sky/engine/platform/fonts/FontPlatformData.h" #include "hb-ot.h" #include "hb.h" -#include "flutter/sky/engine/platform/fonts/FontPlatformData.h" namespace blink { @@ -45,86 +45,87 @@ const hb_tag_t HarfBuzzFace::vrt2Tag = HB_TAG('v', 'r', 't', '2'); // underling font data (e.g. CTFontRef, FTFace). class FaceCacheEntry : public RefCounted { -public: - static PassRefPtr create(hb_face_t* face) - { - ASSERT(face); - return adoptRef(new FaceCacheEntry(face)); - } - ~FaceCacheEntry() - { - hb_face_destroy(m_face); - } + public: + static PassRefPtr create(hb_face_t* face) { + ASSERT(face); + return adoptRef(new FaceCacheEntry(face)); + } + ~FaceCacheEntry() { hb_face_destroy(m_face); } - hb_face_t* face() { return m_face; } - HashMap* glyphCache() { return &m_glyphCache; } + hb_face_t* face() { return m_face; } + HashMap* glyphCache() { return &m_glyphCache; } -private: - explicit FaceCacheEntry(hb_face_t* face) - : m_face(face) - { } + private: + explicit FaceCacheEntry(hb_face_t* face) : m_face(face) {} - hb_face_t* m_face; - HashMap m_glyphCache; + hb_face_t* m_face; + HashMap m_glyphCache; }; -typedef HashMap, WTF::IntHash, WTF::UnsignedWithZeroKeyHashTraits > HarfBuzzFaceCache; +typedef HashMap, + WTF::IntHash, + WTF::UnsignedWithZeroKeyHashTraits> + HarfBuzzFaceCache; -static HarfBuzzFaceCache* harfBuzzFaceCache() -{ - DEFINE_STATIC_LOCAL(HarfBuzzFaceCache, s_harfBuzzFaceCache, ()); - return &s_harfBuzzFaceCache; +static HarfBuzzFaceCache* harfBuzzFaceCache() { + DEFINE_STATIC_LOCAL(HarfBuzzFaceCache, s_harfBuzzFaceCache, ()); + return &s_harfBuzzFaceCache; } HarfBuzzFace::HarfBuzzFace(FontPlatformData* platformData, uint64_t uniqueID) - : m_platformData(platformData) - , m_uniqueID(uniqueID) - , m_scriptForVerticalText(HB_SCRIPT_INVALID) -{ - HarfBuzzFaceCache::AddResult result = harfBuzzFaceCache()->add(m_uniqueID, nullptr); - if (result.isNewEntry) - result.storedValue->value = FaceCacheEntry::create(createFace()); - result.storedValue->value->ref(); - m_face = result.storedValue->value->face(); - m_glyphCacheForFaceCacheEntry = result.storedValue->value->glyphCache(); + : m_platformData(platformData), + m_uniqueID(uniqueID), + m_scriptForVerticalText(HB_SCRIPT_INVALID) { + HarfBuzzFaceCache::AddResult result = + harfBuzzFaceCache()->add(m_uniqueID, nullptr); + if (result.isNewEntry) + result.storedValue->value = FaceCacheEntry::create(createFace()); + result.storedValue->value->ref(); + m_face = result.storedValue->value->face(); + m_glyphCacheForFaceCacheEntry = result.storedValue->value->glyphCache(); } -HarfBuzzFace::~HarfBuzzFace() -{ - HarfBuzzFaceCache::iterator result = harfBuzzFaceCache()->find(m_uniqueID); - ASSERT_WITH_SECURITY_IMPLICATION(result != harfBuzzFaceCache()->end()); - ASSERT(result.get()->value->refCount() > 1); - result.get()->value->deref(); - if (result.get()->value->refCount() == 1) - harfBuzzFaceCache()->remove(m_uniqueID); +HarfBuzzFace::~HarfBuzzFace() { + HarfBuzzFaceCache::iterator result = harfBuzzFaceCache()->find(m_uniqueID); + ASSERT_WITH_SECURITY_IMPLICATION(result != harfBuzzFaceCache()->end()); + ASSERT(result.get()->value->refCount() > 1); + result.get()->value->deref(); + if (result.get()->value->refCount() == 1) + harfBuzzFaceCache()->remove(m_uniqueID); } -static hb_script_t findScriptForVerticalGlyphSubstitution(hb_face_t* face) -{ - static const unsigned maxCount = 32; - - unsigned scriptCount = maxCount; - hb_tag_t scriptTags[maxCount]; - hb_ot_layout_table_get_script_tags(face, HB_OT_TAG_GSUB, 0, &scriptCount, scriptTags); - for (unsigned scriptIndex = 0; scriptIndex < scriptCount; ++scriptIndex) { - unsigned languageCount = maxCount; - hb_tag_t languageTags[maxCount]; - hb_ot_layout_script_get_language_tags(face, HB_OT_TAG_GSUB, scriptIndex, 0, &languageCount, languageTags); - for (unsigned languageIndex = 0; languageIndex < languageCount; ++languageIndex) { - unsigned featureIndex; - if (hb_ot_layout_language_find_feature(face, HB_OT_TAG_GSUB, scriptIndex, languageIndex, HarfBuzzFace::vertTag, &featureIndex) - || hb_ot_layout_language_find_feature(face, HB_OT_TAG_GSUB, scriptIndex, languageIndex, HarfBuzzFace::vrt2Tag, &featureIndex)) - return hb_ot_tag_to_script(scriptTags[scriptIndex]); - } +static hb_script_t findScriptForVerticalGlyphSubstitution(hb_face_t* face) { + static const unsigned maxCount = 32; + + unsigned scriptCount = maxCount; + hb_tag_t scriptTags[maxCount]; + hb_ot_layout_table_get_script_tags(face, HB_OT_TAG_GSUB, 0, &scriptCount, + scriptTags); + for (unsigned scriptIndex = 0; scriptIndex < scriptCount; ++scriptIndex) { + unsigned languageCount = maxCount; + hb_tag_t languageTags[maxCount]; + hb_ot_layout_script_get_language_tags(face, HB_OT_TAG_GSUB, scriptIndex, 0, + &languageCount, languageTags); + for (unsigned languageIndex = 0; languageIndex < languageCount; + ++languageIndex) { + unsigned featureIndex; + if (hb_ot_layout_language_find_feature( + face, HB_OT_TAG_GSUB, scriptIndex, languageIndex, + HarfBuzzFace::vertTag, &featureIndex) || + hb_ot_layout_language_find_feature( + face, HB_OT_TAG_GSUB, scriptIndex, languageIndex, + HarfBuzzFace::vrt2Tag, &featureIndex)) + return hb_ot_tag_to_script(scriptTags[scriptIndex]); } - return HB_SCRIPT_INVALID; + } + return HB_SCRIPT_INVALID; } -void HarfBuzzFace::setScriptForVerticalGlyphSubstitution(hb_buffer_t* buffer) -{ - if (m_scriptForVerticalText == HB_SCRIPT_INVALID) - m_scriptForVerticalText = findScriptForVerticalGlyphSubstitution(m_face); - hb_buffer_set_script(buffer, m_scriptForVerticalText); +void HarfBuzzFace::setScriptForVerticalGlyphSubstitution(hb_buffer_t* buffer) { + if (m_scriptForVerticalText == HB_SCRIPT_INVALID) + m_scriptForVerticalText = findScriptForVerticalGlyphSubstitution(m_face); + hb_buffer_set_script(buffer, m_scriptForVerticalText); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/fonts/harfbuzz/HarfBuzzFace.h b/sky/engine/platform/fonts/harfbuzz/HarfBuzzFace.h index 0cbf88a8e6674..1d638b5b78012 100644 --- a/sky/engine/platform/fonts/harfbuzz/HarfBuzzFace.h +++ b/sky/engine/platform/fonts/harfbuzz/HarfBuzzFace.h @@ -43,33 +43,33 @@ namespace blink { class FontPlatformData; class HarfBuzzFace : public RefCounted { -public: - static const hb_tag_t vertTag; - static const hb_tag_t vrt2Tag; + public: + static const hb_tag_t vertTag; + static const hb_tag_t vrt2Tag; - static PassRefPtr create(FontPlatformData* platformData, uint64_t uniqueID) - { - return adoptRef(new HarfBuzzFace(platformData, uniqueID)); - } - ~HarfBuzzFace(); + static PassRefPtr create(FontPlatformData* platformData, + uint64_t uniqueID) { + return adoptRef(new HarfBuzzFace(platformData, uniqueID)); + } + ~HarfBuzzFace(); - hb_font_t* createFont(); + hb_font_t* createFont(); - void setScriptForVerticalGlyphSubstitution(hb_buffer_t*); + void setScriptForVerticalGlyphSubstitution(hb_buffer_t*); -private: - HarfBuzzFace(FontPlatformData*, uint64_t); + private: + HarfBuzzFace(FontPlatformData*, uint64_t); - hb_face_t* createFace(); + hb_face_t* createFace(); - FontPlatformData* m_platformData; - uint64_t m_uniqueID; - hb_face_t* m_face; - WTF::HashMap* m_glyphCacheForFaceCacheEntry; + FontPlatformData* m_platformData; + uint64_t m_uniqueID; + hb_face_t* m_face; + WTF::HashMap* m_glyphCacheForFaceCacheEntry; - hb_script_t m_scriptForVerticalText; + hb_script_t m_scriptForVerticalText; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_FONTS_HARFBUZZ_HARFBUZZFACE_H_ diff --git a/sky/engine/platform/fonts/harfbuzz/HarfBuzzFaceSkia.cpp b/sky/engine/platform/fonts/harfbuzz/HarfBuzzFaceSkia.cpp index f1dc1fc301def..9150b18900a88 100644 --- a/sky/engine/platform/fonts/harfbuzz/HarfBuzzFaceSkia.cpp +++ b/sky/engine/platform/fonts/harfbuzz/HarfBuzzFaceSkia.cpp @@ -41,8 +41,8 @@ #include "third_party/skia/include/core/SkTypeface.h" #include "third_party/skia/include/private/SkFixed.h" -#include "hb.h" #include "flutter/sky/engine/wtf/HashMap.h" +#include "hb.h" namespace blink { @@ -50,191 +50,222 @@ namespace blink { // calls. See the HarfBuzz source for references about what these callbacks do. struct HarfBuzzFontData { - HarfBuzzFontData(WTF::HashMap* glyphCacheForFaceCacheEntry) - : m_glyphCacheForFaceCacheEntry(glyphCacheForFaceCacheEntry) - { } - SkPaint m_paint; - WTF::HashMap* m_glyphCacheForFaceCacheEntry; + HarfBuzzFontData( + WTF::HashMap* glyphCacheForFaceCacheEntry) + : m_glyphCacheForFaceCacheEntry(glyphCacheForFaceCacheEntry) {} + SkPaint m_paint; + WTF::HashMap* m_glyphCacheForFaceCacheEntry; }; -static hb_position_t SkiaScalarToHarfBuzzPosition(SkScalar value) -{ - return SkScalarToFixed(value); +static hb_position_t SkiaScalarToHarfBuzzPosition(SkScalar value) { + return SkScalarToFixed(value); } -static void SkiaGetGlyphWidthAndExtents(SkPaint* paint, hb_codepoint_t codepoint, hb_position_t* width, hb_glyph_extents_t* extents) -{ - ASSERT(codepoint <= 0xFFFF); - paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding); - - SkScalar skWidth; - SkRect skBounds; - uint16_t glyph = codepoint; - - paint->getTextWidths(&glyph, sizeof(glyph), &skWidth, &skBounds); - if (width) - *width = SkiaScalarToHarfBuzzPosition(skWidth); - if (extents) { - // Invert y-axis because Skia is y-grows-down but we set up HarfBuzz to be y-grows-up. - extents->x_bearing = SkiaScalarToHarfBuzzPosition(skBounds.fLeft); - extents->y_bearing = SkiaScalarToHarfBuzzPosition(-skBounds.fTop); - extents->width = SkiaScalarToHarfBuzzPosition(skBounds.width()); - extents->height = SkiaScalarToHarfBuzzPosition(-skBounds.height()); - } +static void SkiaGetGlyphWidthAndExtents(SkPaint* paint, + hb_codepoint_t codepoint, + hb_position_t* width, + hb_glyph_extents_t* extents) { + ASSERT(codepoint <= 0xFFFF); + paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding); + + SkScalar skWidth; + SkRect skBounds; + uint16_t glyph = codepoint; + + paint->getTextWidths(&glyph, sizeof(glyph), &skWidth, &skBounds); + if (width) + *width = SkiaScalarToHarfBuzzPosition(skWidth); + if (extents) { + // Invert y-axis because Skia is y-grows-down but we set up HarfBuzz to be + // y-grows-up. + extents->x_bearing = SkiaScalarToHarfBuzzPosition(skBounds.fLeft); + extents->y_bearing = SkiaScalarToHarfBuzzPosition(-skBounds.fTop); + extents->width = SkiaScalarToHarfBuzzPosition(skBounds.width()); + extents->height = SkiaScalarToHarfBuzzPosition(-skBounds.height()); + } } -static hb_bool_t harfBuzzGetGlyph(hb_font_t* hbFont, void* fontData, hb_codepoint_t unicode, hb_codepoint_t variationSelector, hb_codepoint_t* glyph, void* userData) -{ - // Variation selectors not supported. - if (variationSelector) - return false; - - HarfBuzzFontData* hbFontData = reinterpret_cast(fontData); - - WTF::HashMap::AddResult result = hbFontData->m_glyphCacheForFaceCacheEntry->add(unicode, 0); - if (result.isNewEntry) { - SkPaint* paint = &hbFontData->m_paint; - paint->setTextEncoding(SkPaint::kUTF32_TextEncoding); - uint16_t glyph16; - paint->textToGlyphs(&unicode, sizeof(hb_codepoint_t), &glyph16); - result.storedValue->value = glyph16; - *glyph = glyph16; - } - *glyph = result.storedValue->value; - return !!*glyph; +static hb_bool_t harfBuzzGetGlyph(hb_font_t* hbFont, + void* fontData, + hb_codepoint_t unicode, + hb_codepoint_t variationSelector, + hb_codepoint_t* glyph, + void* userData) { + // Variation selectors not supported. + if (variationSelector) + return false; + + HarfBuzzFontData* hbFontData = reinterpret_cast(fontData); + + WTF::HashMap::AddResult result = + hbFontData->m_glyphCacheForFaceCacheEntry->add(unicode, 0); + if (result.isNewEntry) { + SkPaint* paint = &hbFontData->m_paint; + paint->setTextEncoding(SkPaint::kUTF32_TextEncoding); + uint16_t glyph16; + paint->textToGlyphs(&unicode, sizeof(hb_codepoint_t), &glyph16); + result.storedValue->value = glyph16; + *glyph = glyph16; + } + *glyph = result.storedValue->value; + return !!*glyph; } -static hb_position_t harfBuzzGetGlyphHorizontalAdvance(hb_font_t* hbFont, void* fontData, hb_codepoint_t glyph, void* userData) -{ - HarfBuzzFontData* hbFontData = reinterpret_cast(fontData); - hb_position_t advance = 0; +static hb_position_t harfBuzzGetGlyphHorizontalAdvance(hb_font_t* hbFont, + void* fontData, + hb_codepoint_t glyph, + void* userData) { + HarfBuzzFontData* hbFontData = reinterpret_cast(fontData); + hb_position_t advance = 0; - SkiaGetGlyphWidthAndExtents(&hbFontData->m_paint, glyph, &advance, 0); - return advance; + SkiaGetGlyphWidthAndExtents(&hbFontData->m_paint, glyph, &advance, 0); + return advance; } -static hb_bool_t harfBuzzGetGlyphHorizontalOrigin(hb_font_t* hbFont, void* fontData, hb_codepoint_t glyph, hb_position_t* x, hb_position_t* y, void* userData) -{ - // Just return true, following the way that HarfBuzz-FreeType - // implementation does. - return true; +static hb_bool_t harfBuzzGetGlyphHorizontalOrigin(hb_font_t* hbFont, + void* fontData, + hb_codepoint_t glyph, + hb_position_t* x, + hb_position_t* y, + void* userData) { + // Just return true, following the way that HarfBuzz-FreeType + // implementation does. + return true; } -static hb_position_t harfBuzzGetGlyphHorizontalKerning(hb_font_t*, void* fontData, hb_codepoint_t leftGlyph, hb_codepoint_t rightGlyph, void*) -{ - HarfBuzzFontData* hbFontData = reinterpret_cast(fontData); - if (hbFontData->m_paint.isVerticalText()) { - // We don't support cross-stream kerning - return 0; - } +static hb_position_t harfBuzzGetGlyphHorizontalKerning( + hb_font_t*, + void* fontData, + hb_codepoint_t leftGlyph, + hb_codepoint_t rightGlyph, + void*) { + HarfBuzzFontData* hbFontData = reinterpret_cast(fontData); + if (hbFontData->m_paint.isVerticalText()) { + // We don't support cross-stream kerning + return 0; + } - SkTypeface* typeface = hbFontData->m_paint.getTypeface(); + SkTypeface* typeface = hbFontData->m_paint.getTypeface(); - const uint16_t glyphs[2] = { static_cast(leftGlyph), static_cast(rightGlyph) }; - int32_t kerningAdjustments[1] = { 0 }; + const uint16_t glyphs[2] = {static_cast(leftGlyph), + static_cast(rightGlyph)}; + int32_t kerningAdjustments[1] = {0}; - if (typeface->getKerningPairAdjustments(glyphs, 2, kerningAdjustments)) { - SkScalar upm = SkIntToScalar(typeface->getUnitsPerEm()); - SkScalar size = hbFontData->m_paint.getTextSize(); - return SkiaScalarToHarfBuzzPosition(kerningAdjustments[0] * size / upm); - } + if (typeface->getKerningPairAdjustments(glyphs, 2, kerningAdjustments)) { + SkScalar upm = SkIntToScalar(typeface->getUnitsPerEm()); + SkScalar size = hbFontData->m_paint.getTextSize(); + return SkiaScalarToHarfBuzzPosition(kerningAdjustments[0] * size / upm); + } - return 0; + return 0; } -static hb_position_t harfBuzzGetGlyphVerticalKerning(hb_font_t*, void* fontData, hb_codepoint_t topGlyph, hb_codepoint_t bottomGlyph, void*) -{ - HarfBuzzFontData* hbFontData = reinterpret_cast(fontData); - if (!hbFontData->m_paint.isVerticalText()) { - // We don't support cross-stream kerning - return 0; - } +static hb_position_t harfBuzzGetGlyphVerticalKerning(hb_font_t*, + void* fontData, + hb_codepoint_t topGlyph, + hb_codepoint_t bottomGlyph, + void*) { + HarfBuzzFontData* hbFontData = reinterpret_cast(fontData); + if (!hbFontData->m_paint.isVerticalText()) { + // We don't support cross-stream kerning + return 0; + } - SkTypeface* typeface = hbFontData->m_paint.getTypeface(); + SkTypeface* typeface = hbFontData->m_paint.getTypeface(); - const uint16_t glyphs[2] = { static_cast(topGlyph), static_cast(bottomGlyph) }; - int32_t kerningAdjustments[1] = { 0 }; + const uint16_t glyphs[2] = {static_cast(topGlyph), + static_cast(bottomGlyph)}; + int32_t kerningAdjustments[1] = {0}; - if (typeface->getKerningPairAdjustments(glyphs, 2, kerningAdjustments)) { - SkScalar upm = SkIntToScalar(typeface->getUnitsPerEm()); - SkScalar size = hbFontData->m_paint.getTextSize(); - return SkiaScalarToHarfBuzzPosition(kerningAdjustments[0] * size / upm); - } + if (typeface->getKerningPairAdjustments(glyphs, 2, kerningAdjustments)) { + SkScalar upm = SkIntToScalar(typeface->getUnitsPerEm()); + SkScalar size = hbFontData->m_paint.getTextSize(); + return SkiaScalarToHarfBuzzPosition(kerningAdjustments[0] * size / upm); + } - return 0; + return 0; } -static hb_bool_t harfBuzzGetGlyphExtents(hb_font_t* hbFont, void* fontData, hb_codepoint_t glyph, hb_glyph_extents_t* extents, void* userData) -{ - HarfBuzzFontData* hbFontData = reinterpret_cast(fontData); +static hb_bool_t harfBuzzGetGlyphExtents(hb_font_t* hbFont, + void* fontData, + hb_codepoint_t glyph, + hb_glyph_extents_t* extents, + void* userData) { + HarfBuzzFontData* hbFontData = reinterpret_cast(fontData); - SkiaGetGlyphWidthAndExtents(&hbFontData->m_paint, glyph, 0, extents); - return true; + SkiaGetGlyphWidthAndExtents(&hbFontData->m_paint, glyph, 0, extents); + return true; } -static hb_font_funcs_t* harfBuzzSkiaGetFontFuncs() -{ - static hb_font_funcs_t* harfBuzzSkiaFontFuncs = 0; - - // We don't set callback functions which we can't support. - // HarfBuzz will use the fallback implementation if they aren't set. - if (!harfBuzzSkiaFontFuncs) { - harfBuzzSkiaFontFuncs = hb_font_funcs_create(); - hb_font_funcs_set_glyph_func(harfBuzzSkiaFontFuncs, harfBuzzGetGlyph, 0, 0); - hb_font_funcs_set_glyph_h_advance_func(harfBuzzSkiaFontFuncs, harfBuzzGetGlyphHorizontalAdvance, 0, 0); - hb_font_funcs_set_glyph_h_kerning_func(harfBuzzSkiaFontFuncs, harfBuzzGetGlyphHorizontalKerning, 0, 0); - hb_font_funcs_set_glyph_h_origin_func(harfBuzzSkiaFontFuncs, harfBuzzGetGlyphHorizontalOrigin, 0, 0); - hb_font_funcs_set_glyph_v_kerning_func(harfBuzzSkiaFontFuncs, harfBuzzGetGlyphVerticalKerning, 0, 0); - hb_font_funcs_set_glyph_extents_func(harfBuzzSkiaFontFuncs, harfBuzzGetGlyphExtents, 0, 0); - hb_font_funcs_make_immutable(harfBuzzSkiaFontFuncs); - } - return harfBuzzSkiaFontFuncs; +static hb_font_funcs_t* harfBuzzSkiaGetFontFuncs() { + static hb_font_funcs_t* harfBuzzSkiaFontFuncs = 0; + + // We don't set callback functions which we can't support. + // HarfBuzz will use the fallback implementation if they aren't set. + if (!harfBuzzSkiaFontFuncs) { + harfBuzzSkiaFontFuncs = hb_font_funcs_create(); + hb_font_funcs_set_glyph_func(harfBuzzSkiaFontFuncs, harfBuzzGetGlyph, 0, 0); + hb_font_funcs_set_glyph_h_advance_func( + harfBuzzSkiaFontFuncs, harfBuzzGetGlyphHorizontalAdvance, 0, 0); + hb_font_funcs_set_glyph_h_kerning_func( + harfBuzzSkiaFontFuncs, harfBuzzGetGlyphHorizontalKerning, 0, 0); + hb_font_funcs_set_glyph_h_origin_func( + harfBuzzSkiaFontFuncs, harfBuzzGetGlyphHorizontalOrigin, 0, 0); + hb_font_funcs_set_glyph_v_kerning_func( + harfBuzzSkiaFontFuncs, harfBuzzGetGlyphVerticalKerning, 0, 0); + hb_font_funcs_set_glyph_extents_func(harfBuzzSkiaFontFuncs, + harfBuzzGetGlyphExtents, 0, 0); + hb_font_funcs_make_immutable(harfBuzzSkiaFontFuncs); + } + return harfBuzzSkiaFontFuncs; } -static hb_blob_t* harfBuzzSkiaGetTable(hb_face_t* face, hb_tag_t tag, void* userData) -{ - SkTypeface* typeface = reinterpret_cast(userData); +static hb_blob_t* harfBuzzSkiaGetTable(hb_face_t* face, + hb_tag_t tag, + void* userData) { + SkTypeface* typeface = reinterpret_cast(userData); - const size_t tableSize = typeface->getTableSize(tag); - if (!tableSize) - return 0; + const size_t tableSize = typeface->getTableSize(tag); + if (!tableSize) + return 0; - char* buffer = reinterpret_cast(fastMalloc(tableSize)); - if (!buffer) - return 0; - size_t actualSize = typeface->getTableData(tag, 0, tableSize, buffer); - if (tableSize != actualSize) { - fastFree(buffer); - return 0; - } + char* buffer = reinterpret_cast(fastMalloc(tableSize)); + if (!buffer) + return 0; + size_t actualSize = typeface->getTableData(tag, 0, tableSize, buffer); + if (tableSize != actualSize) { + fastFree(buffer); + return 0; + } - return hb_blob_create(const_cast(buffer), tableSize, HB_MEMORY_MODE_WRITABLE, buffer, fastFree); + return hb_blob_create(const_cast(buffer), tableSize, + HB_MEMORY_MODE_WRITABLE, buffer, fastFree); } -static void destroyHarfBuzzFontData(void* userData) -{ - HarfBuzzFontData* hbFontData = reinterpret_cast(userData); - delete hbFontData; +static void destroyHarfBuzzFontData(void* userData) { + HarfBuzzFontData* hbFontData = reinterpret_cast(userData); + delete hbFontData; } -hb_face_t* HarfBuzzFace::createFace() -{ - hb_face_t* face = hb_face_create_for_tables(harfBuzzSkiaGetTable, m_platformData->typeface(), 0); - ASSERT(face); - return face; +hb_face_t* HarfBuzzFace::createFace() { + hb_face_t* face = hb_face_create_for_tables(harfBuzzSkiaGetTable, + m_platformData->typeface(), 0); + ASSERT(face); + return face; } -hb_font_t* HarfBuzzFace::createFont() -{ - HarfBuzzFontData* hbFontData = new HarfBuzzFontData(m_glyphCacheForFaceCacheEntry); - m_platformData->setupPaint(&hbFontData->m_paint); - hb_font_t* font = hb_font_create(m_face); - hb_font_set_funcs(font, harfBuzzSkiaGetFontFuncs(), hbFontData, destroyHarfBuzzFontData); - float size = m_platformData->size(); - int scale = SkiaScalarToHarfBuzzPosition(size); - hb_font_set_scale(font, scale, scale); - hb_font_make_immutable(font); - return font; +hb_font_t* HarfBuzzFace::createFont() { + HarfBuzzFontData* hbFontData = + new HarfBuzzFontData(m_glyphCacheForFaceCacheEntry); + m_platformData->setupPaint(&hbFontData->m_paint); + hb_font_t* font = hb_font_create(m_face); + hb_font_set_funcs(font, harfBuzzSkiaGetFontFuncs(), hbFontData, + destroyHarfBuzzFontData); + float size = m_platformData->size(); + int scale = SkiaScalarToHarfBuzzPosition(size); + hb_font_set_scale(font, scale, scale); + hb_font_make_immutable(font); + return font; } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/fonts/harfbuzz/HarfBuzzShaper.cpp b/sky/engine/platform/fonts/harfbuzz/HarfBuzzShaper.cpp index cb13b0ceefe6e..588ce21c089da 100644 --- a/sky/engine/platform/fonts/harfbuzz/HarfBuzzShaper.cpp +++ b/sky/engine/platform/fonts/harfbuzz/HarfBuzzShaper.cpp @@ -34,7 +34,6 @@ #include #include #include -#include "hb.h" #include "flutter/sky/engine/platform/LayoutUnit.h" #include "flutter/sky/engine/platform/fonts/Character.h" #include "flutter/sky/engine/platform/fonts/Font.h" @@ -45,6 +44,7 @@ #include "flutter/sky/engine/wtf/Compiler.h" #include "flutter/sky/engine/wtf/MathExtras.h" #include "flutter/sky/engine/wtf/unicode/Unicode.h" +#include "hb.h" #include #include @@ -52,31 +52,28 @@ namespace blink { -template +template class HarfBuzzScopedPtr { -public: - typedef void (*DestroyFunction)(T*); - - HarfBuzzScopedPtr(T* ptr, DestroyFunction destroy) - : m_ptr(ptr) - , m_destroy(destroy) - { - ASSERT(m_destroy); - } - ~HarfBuzzScopedPtr() - { - if (m_ptr) - (*m_destroy)(m_ptr); - } - - T* get() { return m_ptr; } - void set(T* ptr) { m_ptr = ptr; } -private: - T* m_ptr; - DestroyFunction m_destroy; + public: + typedef void (*DestroyFunction)(T*); + + HarfBuzzScopedPtr(T* ptr, DestroyFunction destroy) + : m_ptr(ptr), m_destroy(destroy) { + ASSERT(m_destroy); + } + ~HarfBuzzScopedPtr() { + if (m_ptr) + (*m_destroy)(m_ptr); + } + + T* get() { return m_ptr; } + void set(T* ptr) { m_ptr = ptr; } + + private: + T* m_ptr; + DestroyFunction m_destroy; }; - static const unsigned cHarfBuzzCacheMaxSize = 256; struct CachedShapingResultsLRUNode; @@ -85,1061 +82,1145 @@ typedef std::map CachedShapingResultsMap; typedef std::list CachedShapingResultsLRU; struct CachedShapingResults { - CachedShapingResults(hb_buffer_t* harfBuzzBuffer, const Font* runFont, hb_direction_t runDir, const String& newLocale); - ~CachedShapingResults(); - - hb_buffer_t* buffer; - Font font; - hb_direction_t dir; - String locale; - CachedShapingResultsLRU::iterator lru; + CachedShapingResults(hb_buffer_t* harfBuzzBuffer, + const Font* runFont, + hb_direction_t runDir, + const String& newLocale); + ~CachedShapingResults(); + + hb_buffer_t* buffer; + Font font; + hb_direction_t dir; + String locale; + CachedShapingResultsLRU::iterator lru; }; struct CachedShapingResultsLRUNode { - CachedShapingResultsLRUNode(const CachedShapingResultsMap::iterator& cacheEntry); - ~CachedShapingResultsLRUNode(); + CachedShapingResultsLRUNode( + const CachedShapingResultsMap::iterator& cacheEntry); + ~CachedShapingResultsLRUNode(); - CachedShapingResultsMap::iterator entry; + CachedShapingResultsMap::iterator entry; }; -CachedShapingResults::CachedShapingResults(hb_buffer_t* harfBuzzBuffer, const Font* fontData, hb_direction_t dirData, const String& newLocale) - : buffer(harfBuzzBuffer) - , font(*fontData) - , dir(dirData) - , locale(newLocale) -{ +CachedShapingResults::CachedShapingResults(hb_buffer_t* harfBuzzBuffer, + const Font* fontData, + hb_direction_t dirData, + const String& newLocale) + : buffer(harfBuzzBuffer), + font(*fontData), + dir(dirData), + locale(newLocale) {} + +CachedShapingResults::~CachedShapingResults() { + hb_buffer_destroy(buffer); } -CachedShapingResults::~CachedShapingResults() -{ - hb_buffer_destroy(buffer); -} +CachedShapingResultsLRUNode::CachedShapingResultsLRUNode( + const CachedShapingResultsMap::iterator& cacheEntry) + : entry(cacheEntry) {} -CachedShapingResultsLRUNode::CachedShapingResultsLRUNode(const CachedShapingResultsMap::iterator& cacheEntry) - : entry(cacheEntry) -{ -} - -CachedShapingResultsLRUNode::~CachedShapingResultsLRUNode() -{ -} +CachedShapingResultsLRUNode::~CachedShapingResultsLRUNode() {} class HarfBuzzRunCache { -public: - HarfBuzzRunCache(); - ~HarfBuzzRunCache(); - - CachedShapingResults* find(const std::wstring& key) const; - void remove(CachedShapingResults* node); - void moveToBack(CachedShapingResults* node); - bool insert(const std::wstring& key, CachedShapingResults* run); - -private: - CachedShapingResultsMap m_harfBuzzRunMap; - CachedShapingResultsLRU m_harfBuzzRunLRU; + public: + HarfBuzzRunCache(); + ~HarfBuzzRunCache(); + + CachedShapingResults* find(const std::wstring& key) const; + void remove(CachedShapingResults* node); + void moveToBack(CachedShapingResults* node); + bool insert(const std::wstring& key, CachedShapingResults* run); + + private: + CachedShapingResultsMap m_harfBuzzRunMap; + CachedShapingResultsLRU m_harfBuzzRunLRU; }; +HarfBuzzRunCache::HarfBuzzRunCache() {} -HarfBuzzRunCache::HarfBuzzRunCache() -{ +HarfBuzzRunCache::~HarfBuzzRunCache() { + for (CachedShapingResultsMap::iterator it = m_harfBuzzRunMap.begin(); + it != m_harfBuzzRunMap.end(); ++it) + delete it->second; + for (CachedShapingResultsLRU::iterator it = m_harfBuzzRunLRU.begin(); + it != m_harfBuzzRunLRU.end(); ++it) + delete *it; } -HarfBuzzRunCache::~HarfBuzzRunCache() -{ - for (CachedShapingResultsMap::iterator it = m_harfBuzzRunMap.begin(); it != m_harfBuzzRunMap.end(); ++it) - delete it->second; - for (CachedShapingResultsLRU::iterator it = m_harfBuzzRunLRU.begin(); it != m_harfBuzzRunLRU.end(); ++it) - delete *it; -} - -bool HarfBuzzRunCache::insert(const std::wstring& key, CachedShapingResults* data) -{ - std::pair results = - m_harfBuzzRunMap.insert(CachedShapingResultsMap::value_type(key, data)); +bool HarfBuzzRunCache::insert(const std::wstring& key, + CachedShapingResults* data) { + std::pair results = + m_harfBuzzRunMap.insert(CachedShapingResultsMap::value_type(key, data)); - if (!results.second) - return false; + if (!results.second) + return false; - CachedShapingResultsLRUNode* node = new CachedShapingResultsLRUNode(results.first); + CachedShapingResultsLRUNode* node = + new CachedShapingResultsLRUNode(results.first); - m_harfBuzzRunLRU.push_back(node); - data->lru = --m_harfBuzzRunLRU.end(); + m_harfBuzzRunLRU.push_back(node); + data->lru = --m_harfBuzzRunLRU.end(); - if (m_harfBuzzRunMap.size() > cHarfBuzzCacheMaxSize) { - CachedShapingResultsLRUNode* lru = m_harfBuzzRunLRU.front(); - CachedShapingResults* foo = lru->entry->second; - m_harfBuzzRunMap.erase(lru->entry); - m_harfBuzzRunLRU.pop_front(); - delete foo; - delete lru; - } + if (m_harfBuzzRunMap.size() > cHarfBuzzCacheMaxSize) { + CachedShapingResultsLRUNode* lru = m_harfBuzzRunLRU.front(); + CachedShapingResults* foo = lru->entry->second; + m_harfBuzzRunMap.erase(lru->entry); + m_harfBuzzRunLRU.pop_front(); + delete foo; + delete lru; + } - return true; + return true; } -inline CachedShapingResults* HarfBuzzRunCache::find(const std::wstring& key) const -{ - CachedShapingResultsMap::const_iterator it = m_harfBuzzRunMap.find(key); +inline CachedShapingResults* HarfBuzzRunCache::find( + const std::wstring& key) const { + CachedShapingResultsMap::const_iterator it = m_harfBuzzRunMap.find(key); - return it != m_harfBuzzRunMap.end() ? it->second : 0; + return it != m_harfBuzzRunMap.end() ? it->second : 0; } -inline void HarfBuzzRunCache::remove(CachedShapingResults* node) -{ - CachedShapingResultsLRUNode* lruNode = *node->lru; +inline void HarfBuzzRunCache::remove(CachedShapingResults* node) { + CachedShapingResultsLRUNode* lruNode = *node->lru; - m_harfBuzzRunLRU.erase(node->lru); - m_harfBuzzRunMap.erase(lruNode->entry); - delete lruNode; - delete node; + m_harfBuzzRunLRU.erase(node->lru); + m_harfBuzzRunMap.erase(lruNode->entry); + delete lruNode; + delete node; } -inline void HarfBuzzRunCache::moveToBack(CachedShapingResults* node) -{ - CachedShapingResultsLRUNode* lruNode = *node->lru; - m_harfBuzzRunLRU.erase(node->lru); - m_harfBuzzRunLRU.push_back(lruNode); - node->lru = --m_harfBuzzRunLRU.end(); +inline void HarfBuzzRunCache::moveToBack(CachedShapingResults* node) { + CachedShapingResultsLRUNode* lruNode = *node->lru; + m_harfBuzzRunLRU.erase(node->lru); + m_harfBuzzRunLRU.push_back(lruNode); + node->lru = --m_harfBuzzRunLRU.end(); } -HarfBuzzRunCache& harfBuzzRunCache() -{ - DEFINE_STATIC_LOCAL(HarfBuzzRunCache, globalHarfBuzzRunCache, ()); - return globalHarfBuzzRunCache; +HarfBuzzRunCache& harfBuzzRunCache() { + DEFINE_STATIC_LOCAL(HarfBuzzRunCache, globalHarfBuzzRunCache, ()); + return globalHarfBuzzRunCache; } -static inline float harfBuzzPositionToFloat(hb_position_t value) -{ - return static_cast(value) / (1 << 16); +static inline float harfBuzzPositionToFloat(hb_position_t value) { + return static_cast(value) / (1 << 16); } -static inline unsigned countGraphemesInCluster(const UChar* normalizedBuffer, unsigned normalizedBufferLength, uint16_t startIndex, uint16_t endIndex) -{ - if (startIndex > endIndex) { - uint16_t tempIndex = startIndex; - startIndex = endIndex; - endIndex = tempIndex; - } - uint16_t length = endIndex - startIndex; - ASSERT(static_cast(startIndex + length) <= normalizedBufferLength); - TextBreakIterator* cursorPosIterator = cursorMovementIterator(&normalizedBuffer[startIndex], length); - - int cursorPos = cursorPosIterator->current(); - int numGraphemes = -1; - while (0 <= cursorPos) { - cursorPos = cursorPosIterator->next(); - numGraphemes++; - } - return numGraphemes < 0 ? 0 : numGraphemes; +static inline unsigned countGraphemesInCluster(const UChar* normalizedBuffer, + unsigned normalizedBufferLength, + uint16_t startIndex, + uint16_t endIndex) { + if (startIndex > endIndex) { + uint16_t tempIndex = startIndex; + startIndex = endIndex; + endIndex = tempIndex; + } + uint16_t length = endIndex - startIndex; + ASSERT(static_cast(startIndex + length) <= normalizedBufferLength); + TextBreakIterator* cursorPosIterator = + cursorMovementIterator(&normalizedBuffer[startIndex], length); + + int cursorPos = cursorPosIterator->current(); + int numGraphemes = -1; + while (0 <= cursorPos) { + cursorPos = cursorPosIterator->next(); + numGraphemes++; + } + return numGraphemes < 0 ? 0 : numGraphemes; } -inline HarfBuzzShaper::HarfBuzzRun::HarfBuzzRun(const SimpleFontData* fontData, unsigned startIndex, unsigned numCharacters, hb_direction_t direction, hb_script_t script) - : m_fontData(fontData) - , m_startIndex(startIndex) - , m_numCharacters(numCharacters) - , m_numGlyphs(0) - , m_direction(direction) - , m_script(script) - , m_width(0) -{ -} +inline HarfBuzzShaper::HarfBuzzRun::HarfBuzzRun(const SimpleFontData* fontData, + unsigned startIndex, + unsigned numCharacters, + hb_direction_t direction, + hb_script_t script) + : m_fontData(fontData), + m_startIndex(startIndex), + m_numCharacters(numCharacters), + m_numGlyphs(0), + m_direction(direction), + m_script(script), + m_width(0) {} inline HarfBuzzShaper::HarfBuzzRun::HarfBuzzRun(const HarfBuzzRun& rhs) - : m_fontData(rhs.m_fontData) - , m_startIndex(rhs.m_startIndex) - , m_numCharacters(rhs.m_numCharacters) - , m_numGlyphs(rhs.m_numGlyphs) - , m_direction(rhs.m_direction) - , m_script(rhs.m_script) - , m_glyphs(rhs.m_glyphs) - , m_advances(rhs.m_advances) - , m_glyphToCharacterIndexes(rhs.m_glyphToCharacterIndexes) - , m_offsets(rhs.m_offsets) - , m_width(rhs.m_width) -{ + : m_fontData(rhs.m_fontData), + m_startIndex(rhs.m_startIndex), + m_numCharacters(rhs.m_numCharacters), + m_numGlyphs(rhs.m_numGlyphs), + m_direction(rhs.m_direction), + m_script(rhs.m_script), + m_glyphs(rhs.m_glyphs), + m_advances(rhs.m_advances), + m_glyphToCharacterIndexes(rhs.m_glyphToCharacterIndexes), + m_offsets(rhs.m_offsets), + m_width(rhs.m_width) {} + +HarfBuzzShaper::HarfBuzzRun::~HarfBuzzRun() {} + +inline void HarfBuzzShaper::HarfBuzzRun::applyShapeResult( + hb_buffer_t* harfBuzzBuffer) { + m_numGlyphs = hb_buffer_get_length(harfBuzzBuffer); + m_glyphs.resize(m_numGlyphs); + m_advances.resize(m_numGlyphs); + m_glyphToCharacterIndexes.resize(m_numGlyphs); + m_offsets.resize(m_numGlyphs); } -HarfBuzzShaper::HarfBuzzRun::~HarfBuzzRun() -{ +inline void HarfBuzzShaper::HarfBuzzRun::setGlyphAndPositions(unsigned index, + uint16_t glyphId, + float advance, + float offsetX, + float offsetY) { + m_glyphs[index] = glyphId; + m_advances[index] = advance; + m_offsets[index] = FloatSize(offsetX, offsetY); } -inline void HarfBuzzShaper::HarfBuzzRun::applyShapeResult(hb_buffer_t* harfBuzzBuffer) -{ - m_numGlyphs = hb_buffer_get_length(harfBuzzBuffer); - m_glyphs.resize(m_numGlyphs); - m_advances.resize(m_numGlyphs); - m_glyphToCharacterIndexes.resize(m_numGlyphs); - m_offsets.resize(m_numGlyphs); -} - -inline void HarfBuzzShaper::HarfBuzzRun::setGlyphAndPositions(unsigned index, uint16_t glyphId, float advance, float offsetX, float offsetY) -{ - m_glyphs[index] = glyphId; - m_advances[index] = advance; - m_offsets[index] = FloatSize(offsetX, offsetY); -} - -int HarfBuzzShaper::HarfBuzzRun::characterIndexForXPosition(float targetX) -{ - ASSERT(targetX <= m_width); - float currentX = 0; - float currentAdvance = m_advances[0]; - unsigned glyphIndex = 0; - - // Sum up advances that belong to a character. - while (glyphIndex < m_numGlyphs - 1 && m_glyphToCharacterIndexes[glyphIndex] == m_glyphToCharacterIndexes[glyphIndex + 1]) - currentAdvance += m_advances[++glyphIndex]; +int HarfBuzzShaper::HarfBuzzRun::characterIndexForXPosition(float targetX) { + ASSERT(targetX <= m_width); + float currentX = 0; + float currentAdvance = m_advances[0]; + unsigned glyphIndex = 0; + + // Sum up advances that belong to a character. + while (glyphIndex < m_numGlyphs - 1 && + m_glyphToCharacterIndexes[glyphIndex] == + m_glyphToCharacterIndexes[glyphIndex + 1]) + currentAdvance += m_advances[++glyphIndex]; + currentAdvance = currentAdvance / 2.0; + if (targetX <= currentAdvance) + return rtl() ? m_numCharacters : 0; + + currentX = currentAdvance; + ++glyphIndex; + while (glyphIndex < m_numGlyphs) { + unsigned prevCharacterIndex = m_glyphToCharacterIndexes[glyphIndex - 1]; + float prevAdvance = currentAdvance; + currentAdvance = m_advances[glyphIndex]; + while (glyphIndex < m_numGlyphs - 1 && + m_glyphToCharacterIndexes[glyphIndex] == + m_glyphToCharacterIndexes[glyphIndex + 1]) + currentAdvance += m_advances[++glyphIndex]; currentAdvance = currentAdvance / 2.0; - if (targetX <= currentAdvance) - return rtl() ? m_numCharacters : 0; - - currentX = currentAdvance; + float nextX = currentX + prevAdvance + currentAdvance; + if (currentX <= targetX && targetX <= nextX) + return rtl() ? prevCharacterIndex : m_glyphToCharacterIndexes[glyphIndex]; + currentX = nextX; ++glyphIndex; - while (glyphIndex < m_numGlyphs) { - unsigned prevCharacterIndex = m_glyphToCharacterIndexes[glyphIndex - 1]; - float prevAdvance = currentAdvance; - currentAdvance = m_advances[glyphIndex]; - while (glyphIndex < m_numGlyphs - 1 && m_glyphToCharacterIndexes[glyphIndex] == m_glyphToCharacterIndexes[glyphIndex + 1]) - currentAdvance += m_advances[++glyphIndex]; - currentAdvance = currentAdvance / 2.0; - float nextX = currentX + prevAdvance + currentAdvance; - if (currentX <= targetX && targetX <= nextX) - return rtl() ? prevCharacterIndex : m_glyphToCharacterIndexes[glyphIndex]; - currentX = nextX; - ++glyphIndex; - } + } - return rtl() ? 0 : m_numCharacters; + return rtl() ? 0 : m_numCharacters; } -float HarfBuzzShaper::HarfBuzzRun::xPositionForOffset(unsigned offset) -{ - ASSERT(offset < m_numCharacters); - unsigned glyphIndex = 0; - float position = 0; - if (rtl()) { - while (glyphIndex < m_numGlyphs && m_glyphToCharacterIndexes[glyphIndex] > offset) { - position += m_advances[glyphIndex]; - ++glyphIndex; - } - // For RTL, we need to return the right side boundary of the character. - // Add advance of glyphs which are part of the character. - while (glyphIndex < m_numGlyphs - 1 && m_glyphToCharacterIndexes[glyphIndex] == m_glyphToCharacterIndexes[glyphIndex + 1]) { - position += m_advances[glyphIndex]; - ++glyphIndex; - } - position += m_advances[glyphIndex]; - } else { - while (glyphIndex < m_numGlyphs && m_glyphToCharacterIndexes[glyphIndex] < offset) { - position += m_advances[glyphIndex]; - ++glyphIndex; - } +float HarfBuzzShaper::HarfBuzzRun::xPositionForOffset(unsigned offset) { + ASSERT(offset < m_numCharacters); + unsigned glyphIndex = 0; + float position = 0; + if (rtl()) { + while (glyphIndex < m_numGlyphs && + m_glyphToCharacterIndexes[glyphIndex] > offset) { + position += m_advances[glyphIndex]; + ++glyphIndex; + } + // For RTL, we need to return the right side boundary of the character. + // Add advance of glyphs which are part of the character. + while (glyphIndex < m_numGlyphs - 1 && + m_glyphToCharacterIndexes[glyphIndex] == + m_glyphToCharacterIndexes[glyphIndex + 1]) { + position += m_advances[glyphIndex]; + ++glyphIndex; + } + position += m_advances[glyphIndex]; + } else { + while (glyphIndex < m_numGlyphs && + m_glyphToCharacterIndexes[glyphIndex] < offset) { + position += m_advances[glyphIndex]; + ++glyphIndex; } - return position; + } + return position; } -static void normalizeCharacters(const TextRun& run, unsigned length, UChar* destination, unsigned* destinationLength) -{ - unsigned position = 0; - bool error = false; - const UChar* source; - String stringFor8BitRun; - if (run.is8Bit()) { - stringFor8BitRun = String::make16BitFrom8BitSource(run.characters8(), run.length()); - source = stringFor8BitRun.characters16(); - } else - source = run.characters16(); - - *destinationLength = 0; - while (position < length) { - UChar32 character; - U16_NEXT(source, position, length, character); - // Don't normalize tabs as they are not treated as spaces for word-end. - if (Character::treatAsSpace(character) && character != characterTabulation) - character = space; - else if (Character::treatAsZeroWidthSpaceInComplexScript(character)) - character = zeroWidthSpace; - U16_APPEND(destination, *destinationLength, length, character, error); - ASSERT_UNUSED(error, !error); - } +static void normalizeCharacters(const TextRun& run, + unsigned length, + UChar* destination, + unsigned* destinationLength) { + unsigned position = 0; + bool error = false; + const UChar* source; + String stringFor8BitRun; + if (run.is8Bit()) { + stringFor8BitRun = + String::make16BitFrom8BitSource(run.characters8(), run.length()); + source = stringFor8BitRun.characters16(); + } else + source = run.characters16(); + + *destinationLength = 0; + while (position < length) { + UChar32 character; + U16_NEXT(source, position, length, character); + // Don't normalize tabs as they are not treated as spaces for word-end. + if (Character::treatAsSpace(character) && character != characterTabulation) + character = space; + else if (Character::treatAsZeroWidthSpaceInComplexScript(character)) + character = zeroWidthSpace; + U16_APPEND(destination, *destinationLength, length, character, error); + ASSERT_UNUSED(error, !error); + } } -HarfBuzzShaper::HarfBuzzShaper(const Font* font, const TextRun& run, ForTextEmphasisOrNot forTextEmphasis, HashSet* fallbackFonts) - : m_font(font) - , m_normalizedBufferLength(0) - , m_run(run) - , m_wordSpacingAdjustment(font->fontDescription().wordSpacing()) - , m_padding(0) - , m_padPerWordBreak(0) - , m_padError(0) - , m_letterSpacing(font->fontDescription().letterSpacing()) - , m_fromIndex(0) - , m_toIndex(m_run.length()) - , m_forTextEmphasis(forTextEmphasis) - , m_glyphBoundingBox(std::numeric_limits::max(), std::numeric_limits::min(), std::numeric_limits::min(), std::numeric_limits::max()) - , m_fallbackFonts(fallbackFonts) -{ - m_normalizedBuffer = adoptArrayPtr(new UChar[m_run.length() + 1]); - normalizeCharacters(m_run, m_run.length(), m_normalizedBuffer.get(), &m_normalizedBufferLength); - setPadding(m_run.expansion()); - setFontFeatures(); +HarfBuzzShaper::HarfBuzzShaper(const Font* font, + const TextRun& run, + ForTextEmphasisOrNot forTextEmphasis, + HashSet* fallbackFonts) + : m_font(font), + m_normalizedBufferLength(0), + m_run(run), + m_wordSpacingAdjustment(font->fontDescription().wordSpacing()), + m_padding(0), + m_padPerWordBreak(0), + m_padError(0), + m_letterSpacing(font->fontDescription().letterSpacing()), + m_fromIndex(0), + m_toIndex(m_run.length()), + m_forTextEmphasis(forTextEmphasis), + m_glyphBoundingBox(std::numeric_limits::max(), + std::numeric_limits::min(), + std::numeric_limits::min(), + std::numeric_limits::max()), + m_fallbackFonts(fallbackFonts) { + m_normalizedBuffer = adoptArrayPtr(new UChar[m_run.length() + 1]); + normalizeCharacters(m_run, m_run.length(), m_normalizedBuffer.get(), + &m_normalizedBufferLength); + setPadding(m_run.expansion()); + setFontFeatures(); } -// In complex text word-spacing affects each line-break, space (U+0020) and non-breaking space (U+00A0). -static inline bool isCodepointSpace(UChar c) -{ - return c == space || c == noBreakSpace || c == newlineCharacter; +// In complex text word-spacing affects each line-break, space (U+0020) and +// non-breaking space (U+00A0). +static inline bool isCodepointSpace(UChar c) { + return c == space || c == noBreakSpace || c == newlineCharacter; } -static inline bool isWordEnd(const UChar* normalizedBuffer, unsigned index) -{ - // This could refer a high-surrogate, but should work. - return index && isCodepointSpace(normalizedBuffer[index]); +static inline bool isWordEnd(const UChar* normalizedBuffer, unsigned index) { + // This could refer a high-surrogate, but should work. + return index && isCodepointSpace(normalizedBuffer[index]); } -int HarfBuzzShaper::determineWordBreakSpacing() -{ - int wordBreakSpacing = m_wordSpacingAdjustment; +int HarfBuzzShaper::determineWordBreakSpacing() { + int wordBreakSpacing = m_wordSpacingAdjustment; - if (m_padding > 0) { - int toPad = roundf(m_padPerWordBreak + m_padError); - m_padError += m_padPerWordBreak - toPad; + if (m_padding > 0) { + int toPad = roundf(m_padPerWordBreak + m_padError); + m_padError += m_padPerWordBreak - toPad; - if (m_padding < toPad) - toPad = m_padding; - m_padding -= toPad; - wordBreakSpacing += toPad; - } - return wordBreakSpacing; + if (m_padding < toPad) + toPad = m_padding; + m_padding -= toPad; + wordBreakSpacing += toPad; + } + return wordBreakSpacing; } // setPadding sets a number of pixels to be distributed across the TextRun. // WebKit uses this to justify text. -void HarfBuzzShaper::setPadding(int padding) -{ - m_padding = padding; - m_padError = 0; - if (!m_padding) - return; - - // If we have padding to distribute, then we try to give an equal - // amount to each space. The last space gets the smaller amount, if - // any. - unsigned numWordEnds = 0; - - for (unsigned i = 0; i < m_normalizedBufferLength; i++) { - if (isWordEnd(m_normalizedBuffer.get(), i)) - numWordEnds++; - } - - if (numWordEnds) - m_padPerWordBreak = m_padding / numWordEnds; - else - m_padPerWordBreak = 0; +void HarfBuzzShaper::setPadding(int padding) { + m_padding = padding; + m_padError = 0; + if (!m_padding) + return; + + // If we have padding to distribute, then we try to give an equal + // amount to each space. The last space gets the smaller amount, if + // any. + unsigned numWordEnds = 0; + + for (unsigned i = 0; i < m_normalizedBufferLength; i++) { + if (isWordEnd(m_normalizedBuffer.get(), i)) + numWordEnds++; + } + + if (numWordEnds) + m_padPerWordBreak = m_padding / numWordEnds; + else + m_padPerWordBreak = 0; } - -void HarfBuzzShaper::setDrawRange(int from, int to) -{ - ASSERT_WITH_SECURITY_IMPLICATION(from >= 0); - ASSERT_WITH_SECURITY_IMPLICATION(to <= m_run.length()); - m_fromIndex = from; - m_toIndex = to; +void HarfBuzzShaper::setDrawRange(int from, int to) { + ASSERT_WITH_SECURITY_IMPLICATION(from >= 0); + ASSERT_WITH_SECURITY_IMPLICATION(to <= m_run.length()); + m_fromIndex = from; + m_toIndex = to; } -void HarfBuzzShaper::setFontFeatures() -{ - const FontDescription& description = m_font->fontDescription(); - if (description.orientation() == Vertical) { - static hb_feature_t vert = { HarfBuzzFace::vertTag, 1, 0, static_cast(-1) }; - static hb_feature_t vrt2 = { HarfBuzzFace::vrt2Tag, 1, 0, static_cast(-1) }; - m_features.append(vert); - m_features.append(vrt2); - } - - static hb_feature_t noKern = { HB_TAG('k', 'e', 'r', 'n'), 0, 0, static_cast(-1) }; - static hb_feature_t noVkrn = { HB_TAG('v', 'k', 'r', 'n'), 0, 0, static_cast(-1) }; - switch (description.kerning()) { +void HarfBuzzShaper::setFontFeatures() { + const FontDescription& description = m_font->fontDescription(); + if (description.orientation() == Vertical) { + static hb_feature_t vert = {HarfBuzzFace::vertTag, 1, 0, + static_cast(-1)}; + static hb_feature_t vrt2 = {HarfBuzzFace::vrt2Tag, 1, 0, + static_cast(-1)}; + m_features.append(vert); + m_features.append(vrt2); + } + + static hb_feature_t noKern = {HB_TAG('k', 'e', 'r', 'n'), 0, 0, + static_cast(-1)}; + static hb_feature_t noVkrn = {HB_TAG('v', 'k', 'r', 'n'), 0, 0, + static_cast(-1)}; + switch (description.kerning()) { case FontDescription::NormalKerning: - // kern/vkrn are enabled by default - break; + // kern/vkrn are enabled by default + break; case FontDescription::NoneKerning: - m_features.append(description.orientation() == Vertical ? noVkrn : noKern); - break; + m_features.append(description.orientation() == Vertical ? noVkrn + : noKern); + break; case FontDescription::AutoKerning: - break; - } - - static hb_feature_t noClig = { HB_TAG('c', 'l', 'i', 'g'), 0, 0, static_cast(-1) }; - static hb_feature_t noLiga = { HB_TAG('l', 'i', 'g', 'a'), 0, 0, static_cast(-1) }; - switch (description.commonLigaturesState()) { + break; + } + + static hb_feature_t noClig = {HB_TAG('c', 'l', 'i', 'g'), 0, 0, + static_cast(-1)}; + static hb_feature_t noLiga = {HB_TAG('l', 'i', 'g', 'a'), 0, 0, + static_cast(-1)}; + switch (description.commonLigaturesState()) { case FontDescription::DisabledLigaturesState: - m_features.append(noLiga); - m_features.append(noClig); - break; + m_features.append(noLiga); + m_features.append(noClig); + break; case FontDescription::EnabledLigaturesState: - // liga and clig are on by default - break; + // liga and clig are on by default + break; case FontDescription::NormalLigaturesState: - break; - } - static hb_feature_t dlig = { HB_TAG('d', 'l', 'i', 'g'), 1, 0, static_cast(-1) }; - switch (description.discretionaryLigaturesState()) { + break; + } + static hb_feature_t dlig = {HB_TAG('d', 'l', 'i', 'g'), 1, 0, + static_cast(-1)}; + switch (description.discretionaryLigaturesState()) { case FontDescription::DisabledLigaturesState: - // dlig is off by default - break; + // dlig is off by default + break; case FontDescription::EnabledLigaturesState: - m_features.append(dlig); - break; + m_features.append(dlig); + break; case FontDescription::NormalLigaturesState: - break; - } - static hb_feature_t hlig = { HB_TAG('h', 'l', 'i', 'g'), 1, 0, static_cast(-1) }; - switch (description.historicalLigaturesState()) { + break; + } + static hb_feature_t hlig = {HB_TAG('h', 'l', 'i', 'g'), 1, 0, + static_cast(-1)}; + switch (description.historicalLigaturesState()) { case FontDescription::DisabledLigaturesState: - // hlig is off by default - break; + // hlig is off by default + break; case FontDescription::EnabledLigaturesState: - m_features.append(hlig); - break; + m_features.append(hlig); + break; case FontDescription::NormalLigaturesState: - break; - } - static hb_feature_t noCalt = { HB_TAG('c', 'a', 'l', 't'), 0, 0, static_cast(-1) }; - switch (description.contextualLigaturesState()) { + break; + } + static hb_feature_t noCalt = {HB_TAG('c', 'a', 'l', 't'), 0, 0, + static_cast(-1)}; + switch (description.contextualLigaturesState()) { case FontDescription::DisabledLigaturesState: - m_features.append(noCalt); - break; + m_features.append(noCalt); + break; case FontDescription::EnabledLigaturesState: - // calt is on by default - break; + // calt is on by default + break; case FontDescription::NormalLigaturesState: - break; - } - - static hb_feature_t hwid = { HB_TAG('h', 'w', 'i', 'd'), 1, 0, static_cast(-1) }; - static hb_feature_t twid = { HB_TAG('t', 'w', 'i', 'd'), 1, 0, static_cast(-1) }; - static hb_feature_t qwid = { HB_TAG('d', 'w', 'i', 'd'), 1, 0, static_cast(-1) }; - switch (description.widthVariant()) { + break; + } + + static hb_feature_t hwid = {HB_TAG('h', 'w', 'i', 'd'), 1, 0, + static_cast(-1)}; + static hb_feature_t twid = {HB_TAG('t', 'w', 'i', 'd'), 1, 0, + static_cast(-1)}; + static hb_feature_t qwid = {HB_TAG('d', 'w', 'i', 'd'), 1, 0, + static_cast(-1)}; + switch (description.widthVariant()) { case HalfWidth: - m_features.append(hwid); - break; + m_features.append(hwid); + break; case ThirdWidth: - m_features.append(twid); - break; + m_features.append(twid); + break; case QuarterWidth: - m_features.append(qwid); - break; + m_features.append(qwid); + break; case RegularWidth: - break; - } - - FontFeatureSettings* settings = description.featureSettings(); - if (!settings) - return; - - unsigned numFeatures = settings->size(); - for (unsigned i = 0; i < numFeatures; ++i) { - hb_feature_t feature; - const AtomicString& tag = settings->at(i).tag(); - feature.tag = HB_TAG(tag[0], tag[1], tag[2], tag[3]); - feature.value = settings->at(i).value(); - feature.start = 0; - feature.end = static_cast(-1); - m_features.append(feature); - } + break; + } + + FontFeatureSettings* settings = description.featureSettings(); + if (!settings) + return; + + unsigned numFeatures = settings->size(); + for (unsigned i = 0; i < numFeatures; ++i) { + hb_feature_t feature; + const AtomicString& tag = settings->at(i).tag(); + feature.tag = HB_TAG(tag[0], tag[1], tag[2], tag[3]); + feature.value = settings->at(i).value(); + feature.start = 0; + feature.end = static_cast(-1); + m_features.append(feature); + } } -bool HarfBuzzShaper::shape(GlyphBuffer* glyphBuffer) -{ - if (!createHarfBuzzRuns()) - return false; +bool HarfBuzzShaper::shape(GlyphBuffer* glyphBuffer) { + if (!createHarfBuzzRuns()) + return false; - m_totalWidth = 0; - if (!shapeHarfBuzzRuns()) - return false; + m_totalWidth = 0; + if (!shapeHarfBuzzRuns()) + return false; - if (m_harfBuzzRuns.last()->hasGlyphToCharacterIndexes() - && glyphBuffer && !fillGlyphBuffer(glyphBuffer)) - return false; + if (m_harfBuzzRuns.last()->hasGlyphToCharacterIndexes() && glyphBuffer && + !fillGlyphBuffer(glyphBuffer)) + return false; - return true; + return true; } -static inline int handleMultipleUChar( - UChar32 character, - unsigned clusterLength, - const SimpleFontData* currentFontData, - const UChar* currentCharacterPosition, - const UChar* markCharactersEnd, - const UChar* normalizedBufferEnd) -{ - if (U_GET_GC_MASK(character) & U_GC_M_MASK) { - int markLength = clusterLength; - while (markCharactersEnd < normalizedBufferEnd) { - UChar32 nextCharacter; - int nextCharacterLength = 0; - U16_NEXT(markCharactersEnd, nextCharacterLength, normalizedBufferEnd - markCharactersEnd, nextCharacter); - if (!(U_GET_GC_MASK(nextCharacter) & U_GC_M_MASK)) - break; - markLength += nextCharacterLength; - markCharactersEnd += nextCharacterLength; - } - - if (currentFontData->canRenderCombiningCharacterSequence(currentCharacterPosition, markCharactersEnd - currentCharacterPosition)) { - return markLength; - } +static inline int handleMultipleUChar(UChar32 character, + unsigned clusterLength, + const SimpleFontData* currentFontData, + const UChar* currentCharacterPosition, + const UChar* markCharactersEnd, + const UChar* normalizedBufferEnd) { + if (U_GET_GC_MASK(character) & U_GC_M_MASK) { + int markLength = clusterLength; + while (markCharactersEnd < normalizedBufferEnd) { + UChar32 nextCharacter; + int nextCharacterLength = 0; + U16_NEXT(markCharactersEnd, nextCharacterLength, + normalizedBufferEnd - markCharactersEnd, nextCharacter); + if (!(U_GET_GC_MASK(nextCharacter) & U_GC_M_MASK)) + break; + markLength += nextCharacterLength; + markCharactersEnd += nextCharacterLength; + } + + if (currentFontData->canRenderCombiningCharacterSequence( + currentCharacterPosition, + markCharactersEnd - currentCharacterPosition)) { + return markLength; } - return 0; + } + return 0; } struct CandidateRun { - UChar32 character; - unsigned start; - unsigned end; - const SimpleFontData* fontData; - UScriptCode script; + UChar32 character; + unsigned start; + unsigned end; + const SimpleFontData* fontData; + UScriptCode script; }; static inline bool collectCandidateRuns(const UChar* normalizedBuffer, - size_t bufferLength, const Font* font, Vector* runs) -{ - const UChar* normalizedBufferEnd = normalizedBuffer + bufferLength; - SurrogatePairAwareTextIterator iterator(normalizedBuffer, 0, bufferLength, bufferLength); - UChar32 character; - unsigned clusterLength = 0; - unsigned startIndexOfCurrentRun = 0; - if (!iterator.consume(character, clusterLength)) - return false; + size_t bufferLength, + const Font* font, + Vector* runs) { + const UChar* normalizedBufferEnd = normalizedBuffer + bufferLength; + SurrogatePairAwareTextIterator iterator(normalizedBuffer, 0, bufferLength, + bufferLength); + UChar32 character; + unsigned clusterLength = 0; + unsigned startIndexOfCurrentRun = 0; + if (!iterator.consume(character, clusterLength)) + return false; - const SimpleFontData* nextFontData = font->glyphDataForCharacter(character, false).fontData; - UErrorCode errorCode = U_ZERO_ERROR; - UScriptCode nextScript = uscript_getScript(character, &errorCode); - if (U_FAILURE(errorCode)) + const SimpleFontData* nextFontData = + font->glyphDataForCharacter(character, false).fontData; + UErrorCode errorCode = U_ZERO_ERROR; + UScriptCode nextScript = uscript_getScript(character, &errorCode); + if (U_FAILURE(errorCode)) + return false; + + do { + const UChar* currentCharacterPosition = iterator.characters(); + const SimpleFontData* currentFontData = nextFontData; + UScriptCode currentScript = nextScript; + + UChar32 lastCharacter = character; + for (iterator.advance(clusterLength); + iterator.consume(character, clusterLength); + iterator.advance(clusterLength)) { + if (Character::treatAsZeroWidthSpace(character)) + continue; + + int length = handleMultipleUChar( + character, clusterLength, currentFontData, currentCharacterPosition, + iterator.characters() + clusterLength, normalizedBufferEnd); + if (length) { + clusterLength = length; + continue; + } + + nextFontData = font->glyphDataForCharacter(character, false).fontData; + nextScript = uscript_getScript(character, &errorCode); + if (U_FAILURE(errorCode)) return false; + if (lastCharacter == zeroWidthJoiner) + currentFontData = nextFontData; + if ((nextFontData != currentFontData) || + ((currentScript != nextScript) && (nextScript != USCRIPT_INHERITED) && + (!uscript_hasScript(character, currentScript)))) + break; + currentCharacterPosition = iterator.characters(); + lastCharacter = character; + } - do { - const UChar* currentCharacterPosition = iterator.characters(); - const SimpleFontData* currentFontData = nextFontData; - UScriptCode currentScript = nextScript; - - UChar32 lastCharacter = character; - for (iterator.advance(clusterLength); iterator.consume(character, clusterLength); iterator.advance(clusterLength)) { - if (Character::treatAsZeroWidthSpace(character)) - continue; - - int length = handleMultipleUChar(character, clusterLength, currentFontData, currentCharacterPosition, iterator.characters() + clusterLength, normalizedBufferEnd); - if (length) { - clusterLength = length; - continue; - } - - nextFontData = font->glyphDataForCharacter(character, false).fontData; - nextScript = uscript_getScript(character, &errorCode); - if (U_FAILURE(errorCode)) - return false; - if (lastCharacter == zeroWidthJoiner) - currentFontData = nextFontData; - if ((nextFontData != currentFontData) || ((currentScript != nextScript) && (nextScript != USCRIPT_INHERITED) && (!uscript_hasScript(character, currentScript)))) - break; - currentCharacterPosition = iterator.characters(); - lastCharacter = character; - } - - CandidateRun run = { character, startIndexOfCurrentRun, static_cast(iterator.currentCharacter()), currentFontData, currentScript }; - runs->append(run); - - startIndexOfCurrentRun = iterator.currentCharacter(); - } while (iterator.consume(character, clusterLength)); - - return true; -} + CandidateRun run = {character, startIndexOfCurrentRun, + static_cast(iterator.currentCharacter()), + currentFontData, currentScript}; + runs->append(run); -static inline bool matchesAdjacentRun(UScriptCode* scriptExtensions, int length, - CandidateRun& adjacentRun) -{ - for (int i = 0; i < length; i++) { - if (scriptExtensions[i] == adjacentRun.script) - return true; - } - return false; + startIndexOfCurrentRun = iterator.currentCharacter(); + } while (iterator.consume(character, clusterLength)); + + return true; } -static inline void resolveRunBasedOnScriptExtensions(Vector& runs, - CandidateRun& run, size_t i, size_t length, UScriptCode* scriptExtensions, - int extensionsLength, size_t& nextResolvedRun) -{ - // If uscript_getScriptExtensions returns 1 it only contains the script value, - // we only care about ScriptExtensions which is indicated by a value >= 2. - if (extensionsLength <= 1) - return; - - if (i > 0 && matchesAdjacentRun(scriptExtensions, extensionsLength, runs[i - 1])) { - run.script = runs[i - 1].script; - return; - } +static inline bool matchesAdjacentRun(UScriptCode* scriptExtensions, + int length, + CandidateRun& adjacentRun) { + for (int i = 0; i < length; i++) { + if (scriptExtensions[i] == adjacentRun.script) + return true; + } + return false; +} - for (size_t j = i + 1; j < length; j++) { - if (runs[j].script != USCRIPT_COMMON - && runs[j].script != USCRIPT_INHERITED - && matchesAdjacentRun(scriptExtensions, extensionsLength, runs[j])) { - nextResolvedRun = j; - break; - } +static inline void resolveRunBasedOnScriptExtensions( + Vector& runs, + CandidateRun& run, + size_t i, + size_t length, + UScriptCode* scriptExtensions, + int extensionsLength, + size_t& nextResolvedRun) { + // If uscript_getScriptExtensions returns 1 it only contains the script value, + // we only care about ScriptExtensions which is indicated by a value >= 2. + if (extensionsLength <= 1) + return; + + if (i > 0 && + matchesAdjacentRun(scriptExtensions, extensionsLength, runs[i - 1])) { + run.script = runs[i - 1].script; + return; + } + + for (size_t j = i + 1; j < length; j++) { + if (runs[j].script != USCRIPT_COMMON && + runs[j].script != USCRIPT_INHERITED && + matchesAdjacentRun(scriptExtensions, extensionsLength, runs[j])) { + nextResolvedRun = j; + break; } + } } static inline void resolveRunBasedOnScriptValue(Vector& runs, - CandidateRun& run, size_t i, size_t length, size_t& nextResolvedRun) -{ - if (run.script != USCRIPT_COMMON) - return; - - if (i > 0 && runs[i - 1].script != USCRIPT_COMMON) { - run.script = runs[i - 1].script; - return; - } - - for (size_t j = i + 1; j < length; j++) { - if (runs[j].script != USCRIPT_COMMON - && runs[j].script != USCRIPT_INHERITED) { - nextResolvedRun = j; - break; - } + CandidateRun& run, + size_t i, + size_t length, + size_t& nextResolvedRun) { + if (run.script != USCRIPT_COMMON) + return; + + if (i > 0 && runs[i - 1].script != USCRIPT_COMMON) { + run.script = runs[i - 1].script; + return; + } + + for (size_t j = i + 1; j < length; j++) { + if (runs[j].script != USCRIPT_COMMON && + runs[j].script != USCRIPT_INHERITED) { + nextResolvedRun = j; + break; } + } } -static inline bool resolveCandidateRuns(Vector& runs) -{ - UScriptCode scriptExtensions[8]; - UErrorCode errorCode = U_ZERO_ERROR; - size_t length = runs.size(); - size_t nextResolvedRun = 0; - for (size_t i = 0; i < length; i++) { - CandidateRun& run = runs[i]; - nextResolvedRun = 0; - - if (run.script == USCRIPT_INHERITED) - run.script = i > 0 ? runs[i - 1].script : USCRIPT_COMMON; - - int extensionsLength = uscript_getScriptExtensions(run.character, - scriptExtensions, sizeof(scriptExtensions), &errorCode); - if (U_FAILURE(errorCode)) - return false; - - resolveRunBasedOnScriptExtensions(runs, run, i, length, - scriptExtensions, extensionsLength, nextResolvedRun); - resolveRunBasedOnScriptValue(runs, run, i, length, - nextResolvedRun); - for (size_t j = i; j < nextResolvedRun; j++) - runs[j].script = runs[nextResolvedRun].script; - - i = std::max(i, nextResolvedRun); - } - return true; +static inline bool resolveCandidateRuns(Vector& runs) { + UScriptCode scriptExtensions[8]; + UErrorCode errorCode = U_ZERO_ERROR; + size_t length = runs.size(); + size_t nextResolvedRun = 0; + for (size_t i = 0; i < length; i++) { + CandidateRun& run = runs[i]; + nextResolvedRun = 0; + + if (run.script == USCRIPT_INHERITED) + run.script = i > 0 ? runs[i - 1].script : USCRIPT_COMMON; + + int extensionsLength = uscript_getScriptExtensions( + run.character, scriptExtensions, sizeof(scriptExtensions), &errorCode); + if (U_FAILURE(errorCode)) + return false; + + resolveRunBasedOnScriptExtensions(runs, run, i, length, scriptExtensions, + extensionsLength, nextResolvedRun); + resolveRunBasedOnScriptValue(runs, run, i, length, nextResolvedRun); + for (size_t j = i; j < nextResolvedRun; j++) + runs[j].script = runs[nextResolvedRun].script; + + i = std::max(i, nextResolvedRun); + } + return true; } -bool HarfBuzzShaper::createHarfBuzzRuns() -{ - Vector candidateRuns; - if (!collectCandidateRuns(m_normalizedBuffer.get(), - m_normalizedBufferLength, m_font, &candidateRuns)) - return false; +bool HarfBuzzShaper::createHarfBuzzRuns() { + Vector candidateRuns; + if (!collectCandidateRuns(m_normalizedBuffer.get(), m_normalizedBufferLength, + m_font, &candidateRuns)) + return false; - if (!resolveCandidateRuns(candidateRuns)) - return false; + if (!resolveCandidateRuns(candidateRuns)) + return false; - size_t length = candidateRuns.size(); - for (size_t i = 0; i < length; ) { - CandidateRun& run = candidateRuns[i]; - CandidateRun lastMatchingRun = run; - for (i++; i < length; i++) { - if (candidateRuns[i].script != run.script - || candidateRuns[i].fontData != run.fontData) - break; - lastMatchingRun = candidateRuns[i]; - } - addHarfBuzzRun(run.start, lastMatchingRun.end, run.fontData, run.script); + size_t length = candidateRuns.size(); + for (size_t i = 0; i < length;) { + CandidateRun& run = candidateRuns[i]; + CandidateRun lastMatchingRun = run; + for (i++; i < length; i++) { + if (candidateRuns[i].script != run.script || + candidateRuns[i].fontData != run.fontData) + break; + lastMatchingRun = candidateRuns[i]; } - return !m_harfBuzzRuns.isEmpty(); + addHarfBuzzRun(run.start, lastMatchingRun.end, run.fontData, run.script); + } + return !m_harfBuzzRuns.isEmpty(); } // A port of hb_icu_script_to_script because harfbuzz on CrOS is built // without hb-icu. See http://crbug.com/356929 -static inline hb_script_t ICUScriptToHBScript(UScriptCode script) -{ - if (UNLIKELY(script == USCRIPT_INVALID_CODE)) - return HB_SCRIPT_INVALID; +static inline hb_script_t ICUScriptToHBScript(UScriptCode script) { + if (UNLIKELY(script == USCRIPT_INVALID_CODE)) + return HB_SCRIPT_INVALID; - return hb_script_from_string(uscript_getShortName(script), -1); + return hb_script_from_string(uscript_getShortName(script), -1); } -static inline hb_direction_t TextDirectionToHBDirection(TextDirection dir) -{ - return dir == RTL ? HB_DIRECTION_RTL : HB_DIRECTION_LTR; +static inline hb_direction_t TextDirectionToHBDirection(TextDirection dir) { + return dir == RTL ? HB_DIRECTION_RTL : HB_DIRECTION_LTR; } - void HarfBuzzShaper::addHarfBuzzRun(unsigned startCharacter, - unsigned endCharacter, const SimpleFontData* fontData, - UScriptCode script) -{ - ASSERT(endCharacter > startCharacter); - ASSERT(script != USCRIPT_INVALID_CODE); - if (m_fallbackFonts) - m_fallbackFonts->add(fontData); - return m_harfBuzzRuns.append(HarfBuzzRun::create(fontData, - startCharacter, endCharacter - startCharacter, - TextDirectionToHBDirection(m_run.direction()), - ICUScriptToHBScript(script))); + unsigned endCharacter, + const SimpleFontData* fontData, + UScriptCode script) { + ASSERT(endCharacter > startCharacter); + ASSERT(script != USCRIPT_INVALID_CODE); + if (m_fallbackFonts) + m_fallbackFonts->add(fontData); + return m_harfBuzzRuns.append(HarfBuzzRun::create( + fontData, startCharacter, endCharacter - startCharacter, + TextDirectionToHBDirection(m_run.direction()), + ICUScriptToHBScript(script))); } -static const uint16_t* toUint16(const UChar* src) -{ - // FIXME: This relies on undefined behavior however it works on the - // current versions of all compilers we care about and avoids making - // a copy of the string. - COMPILE_ASSERT(sizeof(UChar) == sizeof(uint16_t), UChar_is_the_same_size_as_uint16_t); - return reinterpret_cast(src); +static const uint16_t* toUint16(const UChar* src) { + // FIXME: This relies on undefined behavior however it works on the + // current versions of all compilers we care about and avoids making + // a copy of the string. + COMPILE_ASSERT(sizeof(UChar) == sizeof(uint16_t), + UChar_is_the_same_size_as_uint16_t); + return reinterpret_cast(src); } -bool HarfBuzzShaper::shapeHarfBuzzRuns() -{ - HarfBuzzScopedPtr harfBuzzBuffer(hb_buffer_create(), hb_buffer_destroy); - - HarfBuzzRunCache& runCache = harfBuzzRunCache(); - const FontDescription& fontDescription = m_font->fontDescription(); - const String& localeString = fontDescription.locale(); - CString locale = localeString.latin1(); - - for (unsigned i = 0; i < m_harfBuzzRuns.size(); ++i) { - unsigned runIndex = m_run.rtl() ? m_harfBuzzRuns.size() - i - 1 : i; - HarfBuzzRun* currentRun = m_harfBuzzRuns[runIndex].get(); - const SimpleFontData* currentFontData = currentRun->fontData(); - if (currentFontData->isSVGFont()) - return false; - - FontPlatformData* platformData = const_cast(¤tFontData->platformData()); - HarfBuzzFace* face = platformData->harfBuzzFace(); - if (!face) - return false; - - hb_buffer_set_language(harfBuzzBuffer.get(), hb_language_from_string(locale.data(), locale.length())); - hb_buffer_set_script(harfBuzzBuffer.get(), currentRun->script()); - hb_buffer_set_direction(harfBuzzBuffer.get(), currentRun->direction()); - - const UChar* src = m_normalizedBuffer.get() + currentRun->startIndex(); - std::wstring key(src, src + currentRun->numCharacters()); +bool HarfBuzzShaper::shapeHarfBuzzRuns() { + HarfBuzzScopedPtr harfBuzzBuffer(hb_buffer_create(), + hb_buffer_destroy); - CachedShapingResults* cachedResults = runCache.find(key); - if (cachedResults) { - if (cachedResults->dir == currentRun->direction() && cachedResults->font == *m_font && cachedResults->locale == localeString) { - currentRun->applyShapeResult(cachedResults->buffer); - setGlyphPositionsForHarfBuzzRun(currentRun, cachedResults->buffer); + HarfBuzzRunCache& runCache = harfBuzzRunCache(); + const FontDescription& fontDescription = m_font->fontDescription(); + const String& localeString = fontDescription.locale(); + CString locale = localeString.latin1(); - hb_buffer_clear_contents(harfBuzzBuffer.get()); - - runCache.moveToBack(cachedResults); - - continue; - } + for (unsigned i = 0; i < m_harfBuzzRuns.size(); ++i) { + unsigned runIndex = m_run.rtl() ? m_harfBuzzRuns.size() - i - 1 : i; + HarfBuzzRun* currentRun = m_harfBuzzRuns[runIndex].get(); + const SimpleFontData* currentFontData = currentRun->fontData(); + if (currentFontData->isSVGFont()) + return false; - runCache.remove(cachedResults); - } + FontPlatformData* platformData = + const_cast(¤tFontData->platformData()); + HarfBuzzFace* face = platformData->harfBuzzFace(); + if (!face) + return false; - // Add a space as pre-context to the buffer. This prevents showing dotted-circle - // for combining marks at the beginning of runs. - static const uint16_t preContext = ' '; - hb_buffer_add_utf16(harfBuzzBuffer.get(), &preContext, 1, 1, 0); + hb_buffer_set_language( + harfBuzzBuffer.get(), + hb_language_from_string(locale.data(), locale.length())); + hb_buffer_set_script(harfBuzzBuffer.get(), currentRun->script()); + hb_buffer_set_direction(harfBuzzBuffer.get(), currentRun->direction()); - if (fontDescription.variant() == FontVariantSmallCaps && u_islower(m_normalizedBuffer[currentRun->startIndex()])) { - String upperText = String(m_normalizedBuffer.get() + currentRun->startIndex(), currentRun->numCharacters()).upper(); - ASSERT(!upperText.is8Bit()); // m_normalizedBuffer is 16 bit, therefore upperText is 16 bit, even after we call makeUpper(). - hb_buffer_add_utf16(harfBuzzBuffer.get(), toUint16(upperText.characters16()), currentRun->numCharacters(), 0, currentRun->numCharacters()); - } else { - hb_buffer_add_utf16(harfBuzzBuffer.get(), toUint16(m_normalizedBuffer.get() + currentRun->startIndex()), currentRun->numCharacters(), 0, currentRun->numCharacters()); - } + const UChar* src = m_normalizedBuffer.get() + currentRun->startIndex(); + std::wstring key(src, src + currentRun->numCharacters()); - if (fontDescription.orientation() == Vertical) - face->setScriptForVerticalGlyphSubstitution(harfBuzzBuffer.get()); + CachedShapingResults* cachedResults = runCache.find(key); + if (cachedResults) { + if (cachedResults->dir == currentRun->direction() && + cachedResults->font == *m_font && + cachedResults->locale == localeString) { + currentRun->applyShapeResult(cachedResults->buffer); + setGlyphPositionsForHarfBuzzRun(currentRun, cachedResults->buffer); - HarfBuzzScopedPtr harfBuzzFont(face->createFont(), hb_font_destroy); + hb_buffer_clear_contents(harfBuzzBuffer.get()); - hb_shape(harfBuzzFont.get(), harfBuzzBuffer.get(), m_features.isEmpty() ? 0 : m_features.data(), m_features.size()); - currentRun->applyShapeResult(harfBuzzBuffer.get()); - setGlyphPositionsForHarfBuzzRun(currentRun, harfBuzzBuffer.get()); + runCache.moveToBack(cachedResults); - runCache.insert(key, new CachedShapingResults(harfBuzzBuffer.get(), m_font, currentRun->direction(), localeString)); + continue; + } - harfBuzzBuffer.set(hb_buffer_create()); + runCache.remove(cachedResults); } - return true; -} - -void HarfBuzzShaper::setGlyphPositionsForHarfBuzzRun(HarfBuzzRun* currentRun, hb_buffer_t* harfBuzzBuffer) -{ - const SimpleFontData* currentFontData = currentRun->fontData(); - hb_glyph_info_t* glyphInfos = hb_buffer_get_glyph_infos(harfBuzzBuffer, 0); - hb_glyph_position_t* glyphPositions = hb_buffer_get_glyph_positions(harfBuzzBuffer, 0); - - if (!currentRun->hasGlyphToCharacterIndexes()) { - // FIXME: https://crbug.com/337886 - ASSERT_NOT_REACHED(); - return; + // Add a space as pre-context to the buffer. This prevents showing + // dotted-circle for combining marks at the beginning of runs. + static const uint16_t preContext = ' '; + hb_buffer_add_utf16(harfBuzzBuffer.get(), &preContext, 1, 1, 0); + + if (fontDescription.variant() == FontVariantSmallCaps && + u_islower(m_normalizedBuffer[currentRun->startIndex()])) { + String upperText = + String(m_normalizedBuffer.get() + currentRun->startIndex(), + currentRun->numCharacters()) + .upper(); + ASSERT(!upperText.is8Bit()); // m_normalizedBuffer is 16 bit, therefore + // upperText is 16 bit, even after we call + // makeUpper(). + hb_buffer_add_utf16( + harfBuzzBuffer.get(), toUint16(upperText.characters16()), + currentRun->numCharacters(), 0, currentRun->numCharacters()); + } else { + hb_buffer_add_utf16( + harfBuzzBuffer.get(), + toUint16(m_normalizedBuffer.get() + currentRun->startIndex()), + currentRun->numCharacters(), 0, currentRun->numCharacters()); } - unsigned numGlyphs = currentRun->numGlyphs(); - uint16_t* glyphToCharacterIndexes = currentRun->glyphToCharacterIndexes(); - float totalAdvance = 0; - FloatPoint glyphOrigin; - - // HarfBuzz returns the shaping result in visual order. We need not to flip for RTL. - for (size_t i = 0; i < numGlyphs; ++i) { - bool runEnd = i + 1 == numGlyphs; - uint16_t glyph = glyphInfos[i].codepoint; - float offsetX = harfBuzzPositionToFloat(glyphPositions[i].x_offset); - float offsetY = -harfBuzzPositionToFloat(glyphPositions[i].y_offset); - float advance = harfBuzzPositionToFloat(glyphPositions[i].x_advance); + if (fontDescription.orientation() == Vertical) + face->setScriptForVerticalGlyphSubstitution(harfBuzzBuffer.get()); - unsigned currentCharacterIndex = currentRun->startIndex() + glyphInfos[i].cluster; - bool isClusterEnd = runEnd || glyphInfos[i].cluster != glyphInfos[i + 1].cluster; - float spacing = 0; + HarfBuzzScopedPtr harfBuzzFont(face->createFont(), + hb_font_destroy); - glyphToCharacterIndexes[i] = glyphInfos[i].cluster; + hb_shape(harfBuzzFont.get(), harfBuzzBuffer.get(), + m_features.isEmpty() ? 0 : m_features.data(), m_features.size()); + currentRun->applyShapeResult(harfBuzzBuffer.get()); + setGlyphPositionsForHarfBuzzRun(currentRun, harfBuzzBuffer.get()); - if (isClusterEnd && !Character::treatAsZeroWidthSpace(m_normalizedBuffer[currentCharacterIndex])) - spacing += m_letterSpacing; + runCache.insert( + key, new CachedShapingResults(harfBuzzBuffer.get(), m_font, + currentRun->direction(), localeString)); - if (isClusterEnd && isWordEnd(m_normalizedBuffer.get(), currentCharacterIndex)) - spacing += determineWordBreakSpacing(); + harfBuzzBuffer.set(hb_buffer_create()); + } - if (currentFontData->isZeroWidthSpaceGlyph(glyph)) { - currentRun->setGlyphAndPositions(i, glyph, 0, 0, 0); - continue; - } - - advance += spacing; - if (m_run.rtl()) { - // In RTL, spacing should be added to left side of glyphs. - offsetX += spacing; - if (!isClusterEnd) - offsetX += m_letterSpacing; - } - - currentRun->setGlyphAndPositions(i, glyph, advance, offsetX, offsetY); - - FloatRect glyphBounds = currentFontData->boundsForGlyph(glyph); - glyphBounds.move(glyphOrigin.x(), glyphOrigin.y()); - m_glyphBoundingBox.unite(glyphBounds); - glyphOrigin += FloatSize(advance + offsetX, offsetY); + return true; +} - totalAdvance += advance; +void HarfBuzzShaper::setGlyphPositionsForHarfBuzzRun( + HarfBuzzRun* currentRun, + hb_buffer_t* harfBuzzBuffer) { + const SimpleFontData* currentFontData = currentRun->fontData(); + hb_glyph_info_t* glyphInfos = hb_buffer_get_glyph_infos(harfBuzzBuffer, 0); + hb_glyph_position_t* glyphPositions = + hb_buffer_get_glyph_positions(harfBuzzBuffer, 0); + + if (!currentRun->hasGlyphToCharacterIndexes()) { + // FIXME: https://crbug.com/337886 + ASSERT_NOT_REACHED(); + return; + } + + unsigned numGlyphs = currentRun->numGlyphs(); + uint16_t* glyphToCharacterIndexes = currentRun->glyphToCharacterIndexes(); + float totalAdvance = 0; + FloatPoint glyphOrigin; + + // HarfBuzz returns the shaping result in visual order. We need not to flip + // for RTL. + for (size_t i = 0; i < numGlyphs; ++i) { + bool runEnd = i + 1 == numGlyphs; + uint16_t glyph = glyphInfos[i].codepoint; + float offsetX = harfBuzzPositionToFloat(glyphPositions[i].x_offset); + float offsetY = -harfBuzzPositionToFloat(glyphPositions[i].y_offset); + float advance = harfBuzzPositionToFloat(glyphPositions[i].x_advance); + + unsigned currentCharacterIndex = + currentRun->startIndex() + glyphInfos[i].cluster; + bool isClusterEnd = + runEnd || glyphInfos[i].cluster != glyphInfos[i + 1].cluster; + float spacing = 0; + + glyphToCharacterIndexes[i] = glyphInfos[i].cluster; + + if (isClusterEnd && !Character::treatAsZeroWidthSpace( + m_normalizedBuffer[currentCharacterIndex])) + spacing += m_letterSpacing; + + if (isClusterEnd && + isWordEnd(m_normalizedBuffer.get(), currentCharacterIndex)) + spacing += determineWordBreakSpacing(); + + if (currentFontData->isZeroWidthSpaceGlyph(glyph)) { + currentRun->setGlyphAndPositions(i, glyph, 0, 0, 0); + continue; } - currentRun->setWidth(totalAdvance > 0.0 ? totalAdvance : 0.0); - m_totalWidth += currentRun->width(); -} -void HarfBuzzShaper::fillGlyphBufferFromHarfBuzzRun(GlyphBuffer* glyphBuffer, HarfBuzzRun* currentRun, float& carryAdvance) -{ - FloatSize* offsets = currentRun->offsets(); - uint16_t* glyphs = currentRun->glyphs(); - float* advances = currentRun->advances(); - unsigned numGlyphs = currentRun->numGlyphs(); - uint16_t* glyphToCharacterIndexes = currentRun->glyphToCharacterIndexes(); - FloatSize runStartOffset = FloatSize(); + advance += spacing; if (m_run.rtl()) { - for (unsigned i = 0; i < numGlyphs; ++i) { - uint16_t currentCharacterIndex = currentRun->startIndex() + glyphToCharacterIndexes[i]; - if (currentCharacterIndex >= m_toIndex) { - carryAdvance += advances[i]; - } else if (currentCharacterIndex >= m_fromIndex) { - runStartOffset = HB_DIRECTION_IS_HORIZONTAL(currentRun->direction()) ? FloatSize(carryAdvance, 0) : FloatSize(0, carryAdvance); - glyphBuffer->add(glyphs[i], currentRun->fontData(), runStartOffset + offsets[i], carryAdvance + advances[i]); - carryAdvance = 0; - } - } - } else { - for (unsigned i = 0; i < numGlyphs; ++i) { - uint16_t currentCharacterIndex = currentRun->startIndex() + glyphToCharacterIndexes[i]; - if (currentCharacterIndex < m_fromIndex) { - carryAdvance += advances[i]; - } else if (currentCharacterIndex < m_toIndex) { - runStartOffset = HB_DIRECTION_IS_HORIZONTAL(currentRun->direction()) ? FloatSize(carryAdvance, 0) : FloatSize(0, carryAdvance); - glyphBuffer->add(glyphs[i], currentRun->fontData(), runStartOffset + offsets[i], carryAdvance + advances[i]); - carryAdvance = 0; - } - } + // In RTL, spacing should be added to left side of glyphs. + offsetX += spacing; + if (!isClusterEnd) + offsetX += m_letterSpacing; } -} -void HarfBuzzShaper::fillGlyphBufferForTextEmphasis(GlyphBuffer* glyphBuffer, HarfBuzzRun* currentRun) -{ - // FIXME: Instead of generating a synthetic GlyphBuffer here which is then used by the - // drawEmphasisMarks method of FontFastPath, we should roll our own emphasis mark drawing function. - - float* advances = currentRun->advances(); - unsigned numGlyphs = currentRun->numGlyphs(); - uint16_t* glyphToCharacterIndexes = currentRun->glyphToCharacterIndexes(); - unsigned graphemesInCluster = 1; - float clusterAdvance = 0; - uint16_t clusterStart; - - // A "cluster" in this context means a cluster as it is used by HarfBuzz: - // The minimal group of characters and corresponding glyphs, that cannot be broken - // down further from a text shaping point of view. - // A cluster can contain multiple glyphs and grapheme clusters, with mutually - // overlapping boundaries. Below we count grapheme clusters per HarfBuzz clusters, - // then linearly split the sum of corresponding glyph advances by the number of - // grapheme clusters in order to find positions for emphasis mark drawing. + currentRun->setGlyphAndPositions(i, glyph, advance, offsetX, offsetY); - if (m_run.rtl()) - clusterStart = currentRun->startIndex() + currentRun->numCharacters(); - else - clusterStart = currentRun->startIndex() + glyphToCharacterIndexes[0]; + FloatRect glyphBounds = currentFontData->boundsForGlyph(glyph); + glyphBounds.move(glyphOrigin.x(), glyphOrigin.y()); + m_glyphBoundingBox.unite(glyphBounds); + glyphOrigin += FloatSize(advance + offsetX, offsetY); + + totalAdvance += advance; + } + currentRun->setWidth(totalAdvance > 0.0 ? totalAdvance : 0.0); + m_totalWidth += currentRun->width(); +} +void HarfBuzzShaper::fillGlyphBufferFromHarfBuzzRun(GlyphBuffer* glyphBuffer, + HarfBuzzRun* currentRun, + float& carryAdvance) { + FloatSize* offsets = currentRun->offsets(); + uint16_t* glyphs = currentRun->glyphs(); + float* advances = currentRun->advances(); + unsigned numGlyphs = currentRun->numGlyphs(); + uint16_t* glyphToCharacterIndexes = currentRun->glyphToCharacterIndexes(); + FloatSize runStartOffset = FloatSize(); + if (m_run.rtl()) { + for (unsigned i = 0; i < numGlyphs; ++i) { + uint16_t currentCharacterIndex = + currentRun->startIndex() + glyphToCharacterIndexes[i]; + if (currentCharacterIndex >= m_toIndex) { + carryAdvance += advances[i]; + } else if (currentCharacterIndex >= m_fromIndex) { + runStartOffset = HB_DIRECTION_IS_HORIZONTAL(currentRun->direction()) + ? FloatSize(carryAdvance, 0) + : FloatSize(0, carryAdvance); + glyphBuffer->add(glyphs[i], currentRun->fontData(), + runStartOffset + offsets[i], + carryAdvance + advances[i]); + carryAdvance = 0; + } + } + } else { for (unsigned i = 0; i < numGlyphs; ++i) { - uint16_t currentCharacterIndex = currentRun->startIndex() + glyphToCharacterIndexes[i]; - bool isRunEnd = (i + 1 == numGlyphs); - bool isClusterEnd = isRunEnd || (currentRun->startIndex() + glyphToCharacterIndexes[i + 1] != currentCharacterIndex); - clusterAdvance += advances[i]; - - if (isClusterEnd) { - uint16_t clusterEnd; - if (m_run.rtl()) - clusterEnd = currentCharacterIndex; - else - clusterEnd = isRunEnd ? currentRun->startIndex() + currentRun->numCharacters() : currentRun->startIndex() + glyphToCharacterIndexes[i + 1]; - - graphemesInCluster = countGraphemesInCluster(m_normalizedBuffer.get(), m_normalizedBufferLength, clusterStart, clusterEnd); - if (!graphemesInCluster || !clusterAdvance) - continue; - - float glyphAdvanceX = clusterAdvance / graphemesInCluster; - for (unsigned j = 0; j < graphemesInCluster; ++j) { - // Do not put emphasis marks on space, separator, and control characters. - Glyph glyphToAdd = Character::canReceiveTextEmphasis(m_run[currentCharacterIndex]) ? 1 : 0; - glyphBuffer->add(glyphToAdd, currentRun->fontData(), glyphAdvanceX); - } - clusterStart = clusterEnd; - clusterAdvance = 0; - } + uint16_t currentCharacterIndex = + currentRun->startIndex() + glyphToCharacterIndexes[i]; + if (currentCharacterIndex < m_fromIndex) { + carryAdvance += advances[i]; + } else if (currentCharacterIndex < m_toIndex) { + runStartOffset = HB_DIRECTION_IS_HORIZONTAL(currentRun->direction()) + ? FloatSize(carryAdvance, 0) + : FloatSize(0, carryAdvance); + glyphBuffer->add(glyphs[i], currentRun->fontData(), + runStartOffset + offsets[i], + carryAdvance + advances[i]); + carryAdvance = 0; + } } + } } -bool HarfBuzzShaper::fillGlyphBuffer(GlyphBuffer* glyphBuffer) -{ - unsigned numRuns = m_harfBuzzRuns.size(); - float carryAdvance = 0; - if (m_run.rtl()) { - for (int runIndex = numRuns - 1; runIndex >= 0; --runIndex) { - HarfBuzzRun* currentRun = m_harfBuzzRuns[runIndex].get(); - if (!currentRun->hasGlyphToCharacterIndexes()) { - // FIXME: bug 337886, 359664 - continue; - } - if (m_forTextEmphasis == ForTextEmphasis) - fillGlyphBufferForTextEmphasis(glyphBuffer, currentRun); - else - fillGlyphBufferFromHarfBuzzRun(glyphBuffer, currentRun, carryAdvance); - } - } else { - for (unsigned runIndex = 0; runIndex < numRuns; ++runIndex) { - HarfBuzzRun* currentRun = m_harfBuzzRuns[runIndex].get(); - if (!currentRun->hasGlyphToCharacterIndexes()) { - // FIXME: bug 337886, 359664 - continue; - } - if (m_forTextEmphasis == ForTextEmphasis) - fillGlyphBufferForTextEmphasis(glyphBuffer, currentRun); - else - fillGlyphBufferFromHarfBuzzRun(glyphBuffer, currentRun, carryAdvance); - } +void HarfBuzzShaper::fillGlyphBufferForTextEmphasis(GlyphBuffer* glyphBuffer, + HarfBuzzRun* currentRun) { + // FIXME: Instead of generating a synthetic GlyphBuffer here which is then + // used by the drawEmphasisMarks method of FontFastPath, we should roll our + // own emphasis mark drawing function. + + float* advances = currentRun->advances(); + unsigned numGlyphs = currentRun->numGlyphs(); + uint16_t* glyphToCharacterIndexes = currentRun->glyphToCharacterIndexes(); + unsigned graphemesInCluster = 1; + float clusterAdvance = 0; + uint16_t clusterStart; + + // A "cluster" in this context means a cluster as it is used by HarfBuzz: + // The minimal group of characters and corresponding glyphs, that cannot be + // broken down further from a text shaping point of view. A cluster can + // contain multiple glyphs and grapheme clusters, with mutually overlapping + // boundaries. Below we count grapheme clusters per HarfBuzz clusters, then + // linearly split the sum of corresponding glyph advances by the number of + // grapheme clusters in order to find positions for emphasis mark drawing. + + if (m_run.rtl()) + clusterStart = currentRun->startIndex() + currentRun->numCharacters(); + else + clusterStart = currentRun->startIndex() + glyphToCharacterIndexes[0]; + + for (unsigned i = 0; i < numGlyphs; ++i) { + uint16_t currentCharacterIndex = + currentRun->startIndex() + glyphToCharacterIndexes[i]; + bool isRunEnd = (i + 1 == numGlyphs); + bool isClusterEnd = isRunEnd || (currentRun->startIndex() + + glyphToCharacterIndexes[i + 1] != + currentCharacterIndex); + clusterAdvance += advances[i]; + + if (isClusterEnd) { + uint16_t clusterEnd; + if (m_run.rtl()) + clusterEnd = currentCharacterIndex; + else + clusterEnd = + isRunEnd + ? currentRun->startIndex() + currentRun->numCharacters() + : currentRun->startIndex() + glyphToCharacterIndexes[i + 1]; + + graphemesInCluster = countGraphemesInCluster(m_normalizedBuffer.get(), + m_normalizedBufferLength, + clusterStart, clusterEnd); + if (!graphemesInCluster || !clusterAdvance) + continue; + + float glyphAdvanceX = clusterAdvance / graphemesInCluster; + for (unsigned j = 0; j < graphemesInCluster; ++j) { + // Do not put emphasis marks on space, separator, and control + // characters. + Glyph glyphToAdd = + Character::canReceiveTextEmphasis(m_run[currentCharacterIndex]) ? 1 + : 0; + glyphBuffer->add(glyphToAdd, currentRun->fontData(), glyphAdvanceX); + } + clusterStart = clusterEnd; + clusterAdvance = 0; } - return glyphBuffer->size(); + } } -int HarfBuzzShaper::offsetForPosition(float targetX) -{ - int charactersSoFar = 0; - float currentX = 0; - - if (m_run.rtl()) { - charactersSoFar = m_normalizedBufferLength; - for (int i = m_harfBuzzRuns.size() - 1; i >= 0; --i) { - charactersSoFar -= m_harfBuzzRuns[i]->numCharacters(); - float nextX = currentX + m_harfBuzzRuns[i]->width(); - float offsetForRun = targetX - currentX; - if (offsetForRun >= 0 && offsetForRun <= m_harfBuzzRuns[i]->width()) { - // The x value in question is within this script run. - const unsigned index = m_harfBuzzRuns[i]->characterIndexForXPosition(offsetForRun); - return charactersSoFar + index; - } - currentX = nextX; - } - } else { - for (unsigned i = 0; i < m_harfBuzzRuns.size(); ++i) { - float nextX = currentX + m_harfBuzzRuns[i]->width(); - float offsetForRun = targetX - currentX; - if (offsetForRun >= 0 && offsetForRun <= m_harfBuzzRuns[i]->width()) { - const unsigned index = m_harfBuzzRuns[i]->characterIndexForXPosition(offsetForRun); - return charactersSoFar + index; - } - charactersSoFar += m_harfBuzzRuns[i]->numCharacters(); - currentX = nextX; - } +bool HarfBuzzShaper::fillGlyphBuffer(GlyphBuffer* glyphBuffer) { + unsigned numRuns = m_harfBuzzRuns.size(); + float carryAdvance = 0; + if (m_run.rtl()) { + for (int runIndex = numRuns - 1; runIndex >= 0; --runIndex) { + HarfBuzzRun* currentRun = m_harfBuzzRuns[runIndex].get(); + if (!currentRun->hasGlyphToCharacterIndexes()) { + // FIXME: bug 337886, 359664 + continue; + } + if (m_forTextEmphasis == ForTextEmphasis) + fillGlyphBufferForTextEmphasis(glyphBuffer, currentRun); + else + fillGlyphBufferFromHarfBuzzRun(glyphBuffer, currentRun, carryAdvance); } - - return charactersSoFar; + } else { + for (unsigned runIndex = 0; runIndex < numRuns; ++runIndex) { + HarfBuzzRun* currentRun = m_harfBuzzRuns[runIndex].get(); + if (!currentRun->hasGlyphToCharacterIndexes()) { + // FIXME: bug 337886, 359664 + continue; + } + if (m_forTextEmphasis == ForTextEmphasis) + fillGlyphBufferForTextEmphasis(glyphBuffer, currentRun); + else + fillGlyphBufferFromHarfBuzzRun(glyphBuffer, currentRun, carryAdvance); + } + } + return glyphBuffer->size(); } -FloatRect HarfBuzzShaper::selectionRect(const FloatPoint& point, int height, int from, int to) -{ - float currentX = 0; - float fromX = 0; - float toX = 0; - bool foundFromX = false; - bool foundToX = false; - - if (m_run.rtl()) - currentX = m_totalWidth; +int HarfBuzzShaper::offsetForPosition(float targetX) { + int charactersSoFar = 0; + float currentX = 0; + + if (m_run.rtl()) { + charactersSoFar = m_normalizedBufferLength; + for (int i = m_harfBuzzRuns.size() - 1; i >= 0; --i) { + charactersSoFar -= m_harfBuzzRuns[i]->numCharacters(); + float nextX = currentX + m_harfBuzzRuns[i]->width(); + float offsetForRun = targetX - currentX; + if (offsetForRun >= 0 && offsetForRun <= m_harfBuzzRuns[i]->width()) { + // The x value in question is within this script run. + const unsigned index = + m_harfBuzzRuns[i]->characterIndexForXPosition(offsetForRun); + return charactersSoFar + index; + } + currentX = nextX; + } + } else { for (unsigned i = 0; i < m_harfBuzzRuns.size(); ++i) { - if (m_run.rtl()) - currentX -= m_harfBuzzRuns[i]->width(); - int numCharacters = m_harfBuzzRuns[i]->numCharacters(); - if (!foundFromX && from >= 0 && from < numCharacters) { - fromX = m_harfBuzzRuns[i]->xPositionForOffset(from) + currentX; - foundFromX = true; - } else - from -= numCharacters; - - if (!foundToX && to >= 0 && to < numCharacters) { - toX = m_harfBuzzRuns[i]->xPositionForOffset(to) + currentX; - foundToX = true; - } else - to -= numCharacters; - - if (foundFromX && foundToX) - break; - if (!m_run.rtl()) - currentX += m_harfBuzzRuns[i]->width(); + float nextX = currentX + m_harfBuzzRuns[i]->width(); + float offsetForRun = targetX - currentX; + if (offsetForRun >= 0 && offsetForRun <= m_harfBuzzRuns[i]->width()) { + const unsigned index = + m_harfBuzzRuns[i]->characterIndexForXPosition(offsetForRun); + return charactersSoFar + index; + } + charactersSoFar += m_harfBuzzRuns[i]->numCharacters(); + currentX = nextX; } + } - // The position in question might be just after the text. - if (!foundFromX) - fromX = 0; - if (!foundToX) - toX = m_run.rtl() ? 0 : m_totalWidth; + return charactersSoFar; +} - if (fromX < toX) { - return Font::pixelSnappedSelectionRect( - point.x() + fromX, point.x() + toX, - point.y(), height); - } +FloatRect HarfBuzzShaper::selectionRect(const FloatPoint& point, + int height, + int from, + int to) { + float currentX = 0; + float fromX = 0; + float toX = 0; + bool foundFromX = false; + bool foundToX = false; + + if (m_run.rtl()) + currentX = m_totalWidth; + for (unsigned i = 0; i < m_harfBuzzRuns.size(); ++i) { + if (m_run.rtl()) + currentX -= m_harfBuzzRuns[i]->width(); + int numCharacters = m_harfBuzzRuns[i]->numCharacters(); + if (!foundFromX && from >= 0 && from < numCharacters) { + fromX = m_harfBuzzRuns[i]->xPositionForOffset(from) + currentX; + foundFromX = true; + } else + from -= numCharacters; - return Font::pixelSnappedSelectionRect( - point.x() + toX, point.x() + fromX, - point.y(), height); + if (!foundToX && to >= 0 && to < numCharacters) { + toX = m_harfBuzzRuns[i]->xPositionForOffset(to) + currentX; + foundToX = true; + } else + to -= numCharacters; + + if (foundFromX && foundToX) + break; + if (!m_run.rtl()) + currentX += m_harfBuzzRuns[i]->width(); + } + + // The position in question might be just after the text. + if (!foundFromX) + fromX = 0; + if (!foundToX) + toX = m_run.rtl() ? 0 : m_totalWidth; + + if (fromX < toX) { + return Font::pixelSnappedSelectionRect(point.x() + fromX, point.x() + toX, + point.y(), height); + } + + return Font::pixelSnappedSelectionRect(point.x() + toX, point.x() + fromX, + point.y(), height); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/fonts/harfbuzz/HarfBuzzShaper.h b/sky/engine/platform/fonts/harfbuzz/HarfBuzzShaper.h index 1fe964e53eff6..6464f36463fad 100644 --- a/sky/engine/platform/fonts/harfbuzz/HarfBuzzShaper.h +++ b/sky/engine/platform/fonts/harfbuzz/HarfBuzzShaper.h @@ -31,7 +31,6 @@ #ifndef SKY_ENGINE_PLATFORM_FONTS_HARFBUZZ_HARFBUZZSHAPER_H_ #define SKY_ENGINE_PLATFORM_FONTS_HARFBUZZ_HARFBUZZSHAPER_H_ -#include "hb.h" #include "flutter/sky/engine/platform/geometry/FloatBoxExtent.h" #include "flutter/sky/engine/platform/geometry/FloatPoint.h" #include "flutter/sky/engine/platform/text/TextRun.h" @@ -40,6 +39,7 @@ #include "flutter/sky/engine/wtf/PassOwnPtr.h" #include "flutter/sky/engine/wtf/Vector.h" #include "flutter/sky/engine/wtf/unicode/CharacterNames.h" +#include "hb.h" #include @@ -50,116 +50,135 @@ class GlyphBuffer; class SimpleFontData; class HarfBuzzShaper final { -public: - enum ForTextEmphasisOrNot { - NotForTextEmphasis, - ForTextEmphasis - }; - - HarfBuzzShaper(const Font*, const TextRun&, ForTextEmphasisOrNot = NotForTextEmphasis, HashSet* fallbackFonts = 0); - - void setDrawRange(int from, int to); - bool shape(GlyphBuffer* = 0); - float totalWidth() { return m_totalWidth; } - int offsetForPosition(float targetX); - FloatRect selectionRect(const FloatPoint&, int height, int from, int to); - FloatBoxExtent glyphBoundingBox() const { return m_glyphBoundingBox; } - -private: - class HarfBuzzRun { - public: - HarfBuzzRun(const HarfBuzzRun&); - ~HarfBuzzRun(); - - static PassOwnPtr create(const SimpleFontData* fontData, unsigned startIndex, unsigned numCharacters, hb_direction_t direction, hb_script_t script) - { - return adoptPtr(new HarfBuzzRun(fontData, startIndex, numCharacters, direction, script)); - } - - void applyShapeResult(hb_buffer_t*); - void setGlyphAndPositions(unsigned index, uint16_t glyphId, float advance, float offsetX, float offsetY); - void setWidth(float width) { m_width = width; } - - int characterIndexForXPosition(float targetX); - float xPositionForOffset(unsigned offset); - - const SimpleFontData* fontData() { return m_fontData; } - unsigned startIndex() const { return m_startIndex; } - unsigned numCharacters() const { return m_numCharacters; } - unsigned numGlyphs() const { return m_numGlyphs; } - uint16_t* glyphs() { return &m_glyphs[0]; } - float* advances() { return &m_advances[0]; } - FloatSize* offsets() { return &m_offsets[0]; } - bool hasGlyphToCharacterIndexes() const - { - return m_glyphToCharacterIndexes.size() > 0; - } - uint16_t* glyphToCharacterIndexes() - { - return &m_glyphToCharacterIndexes[0]; - } - float width() { return m_width; } - hb_direction_t direction() { return m_direction; } - bool rtl() { return m_direction == HB_DIRECTION_RTL; } - hb_script_t script() { return m_script; } - - private: - HarfBuzzRun(const SimpleFontData*, unsigned startIndex, unsigned numCharacters, hb_direction_t, hb_script_t); - - const SimpleFontData* m_fontData; - unsigned m_startIndex; - size_t m_numCharacters; - unsigned m_numGlyphs; - hb_direction_t m_direction; - hb_script_t m_script; - Vector m_glyphs; - Vector m_advances; - Vector m_glyphToCharacterIndexes; - Vector m_offsets; - float m_width; - }; - - int determineWordBreakSpacing(); - // setPadding sets a number of pixels to be distributed across the TextRun. - // WebKit uses this to justify text. - void setPadding(int); - - void setFontFeatures(); - - bool createHarfBuzzRuns(); - bool shapeHarfBuzzRuns(); - bool fillGlyphBuffer(GlyphBuffer*); - void fillGlyphBufferFromHarfBuzzRun(GlyphBuffer*, HarfBuzzRun*, float& carryAdvance); - void fillGlyphBufferForTextEmphasis(GlyphBuffer*, HarfBuzzRun* currentRun); - void setGlyphPositionsForHarfBuzzRun(HarfBuzzRun*, hb_buffer_t*); - void addHarfBuzzRun(unsigned startCharacter, unsigned endCharacter, const SimpleFontData*, UScriptCode); - - const Font* m_font; - OwnPtr m_normalizedBuffer; - unsigned m_normalizedBufferLength; - const TextRun& m_run; - - float m_wordSpacingAdjustment; // Delta adjustment (pixels) for each word break. - float m_padding; // Pixels to be distributed over the line at word breaks. - float m_padPerWordBreak; // Pixels to be added to each word break. - float m_padError; // m_padPerWordBreak might have a fractional component. Since we only add a whole number of padding pixels at each word break we accumulate error. This is the number of pixels that we are behind so far. - float m_letterSpacing; // Pixels to be added after each glyph. - - Vector m_features; - Vector, 16> m_harfBuzzRuns; - - int m_fromIndex; - int m_toIndex; - - ForTextEmphasisOrNot m_forTextEmphasis; - - float m_totalWidth; - FloatBoxExtent m_glyphBoundingBox; - HashSet* m_fallbackFonts; - - friend struct CachedShapingResults; + public: + enum ForTextEmphasisOrNot { NotForTextEmphasis, ForTextEmphasis }; + + HarfBuzzShaper(const Font*, + const TextRun&, + ForTextEmphasisOrNot = NotForTextEmphasis, + HashSet* fallbackFonts = 0); + + void setDrawRange(int from, int to); + bool shape(GlyphBuffer* = 0); + float totalWidth() { return m_totalWidth; } + int offsetForPosition(float targetX); + FloatRect selectionRect(const FloatPoint&, int height, int from, int to); + FloatBoxExtent glyphBoundingBox() const { return m_glyphBoundingBox; } + + private: + class HarfBuzzRun { + public: + HarfBuzzRun(const HarfBuzzRun&); + ~HarfBuzzRun(); + + static PassOwnPtr create(const SimpleFontData* fontData, + unsigned startIndex, + unsigned numCharacters, + hb_direction_t direction, + hb_script_t script) { + return adoptPtr(new HarfBuzzRun(fontData, startIndex, numCharacters, + direction, script)); + } + + void applyShapeResult(hb_buffer_t*); + void setGlyphAndPositions(unsigned index, + uint16_t glyphId, + float advance, + float offsetX, + float offsetY); + void setWidth(float width) { m_width = width; } + + int characterIndexForXPosition(float targetX); + float xPositionForOffset(unsigned offset); + + const SimpleFontData* fontData() { return m_fontData; } + unsigned startIndex() const { return m_startIndex; } + unsigned numCharacters() const { return m_numCharacters; } + unsigned numGlyphs() const { return m_numGlyphs; } + uint16_t* glyphs() { return &m_glyphs[0]; } + float* advances() { return &m_advances[0]; } + FloatSize* offsets() { return &m_offsets[0]; } + bool hasGlyphToCharacterIndexes() const { + return m_glyphToCharacterIndexes.size() > 0; + } + uint16_t* glyphToCharacterIndexes() { + return &m_glyphToCharacterIndexes[0]; + } + float width() { return m_width; } + hb_direction_t direction() { return m_direction; } + bool rtl() { return m_direction == HB_DIRECTION_RTL; } + hb_script_t script() { return m_script; } + + private: + HarfBuzzRun(const SimpleFontData*, + unsigned startIndex, + unsigned numCharacters, + hb_direction_t, + hb_script_t); + + const SimpleFontData* m_fontData; + unsigned m_startIndex; + size_t m_numCharacters; + unsigned m_numGlyphs; + hb_direction_t m_direction; + hb_script_t m_script; + Vector m_glyphs; + Vector m_advances; + Vector m_glyphToCharacterIndexes; + Vector m_offsets; + float m_width; + }; + + int determineWordBreakSpacing(); + // setPadding sets a number of pixels to be distributed across the TextRun. + // WebKit uses this to justify text. + void setPadding(int); + + void setFontFeatures(); + + bool createHarfBuzzRuns(); + bool shapeHarfBuzzRuns(); + bool fillGlyphBuffer(GlyphBuffer*); + void fillGlyphBufferFromHarfBuzzRun(GlyphBuffer*, + HarfBuzzRun*, + float& carryAdvance); + void fillGlyphBufferForTextEmphasis(GlyphBuffer*, HarfBuzzRun* currentRun); + void setGlyphPositionsForHarfBuzzRun(HarfBuzzRun*, hb_buffer_t*); + void addHarfBuzzRun(unsigned startCharacter, + unsigned endCharacter, + const SimpleFontData*, + UScriptCode); + + const Font* m_font; + OwnPtr m_normalizedBuffer; + unsigned m_normalizedBufferLength; + const TextRun& m_run; + + float m_wordSpacingAdjustment; // Delta adjustment (pixels) for each word + // break. + float m_padding; // Pixels to be distributed over the line at word breaks. + float m_padPerWordBreak; // Pixels to be added to each word break. + float m_padError; // m_padPerWordBreak might have a fractional component. + // Since we only add a whole number of padding pixels at + // each word break we accumulate error. This is the number + // of pixels that we are behind so far. + float m_letterSpacing; // Pixels to be added after each glyph. + + Vector m_features; + Vector, 16> m_harfBuzzRuns; + + int m_fromIndex; + int m_toIndex; + + ForTextEmphasisOrNot m_forTextEmphasis; + + float m_totalWidth; + FloatBoxExtent m_glyphBoundingBox; + HashSet* m_fallbackFonts; + + friend struct CachedShapingResults; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_FONTS_HARFBUZZ_HARFBUZZSHAPER_H_ diff --git a/sky/engine/platform/fonts/linux/FontCacheLinux.cpp b/sky/engine/platform/fonts/linux/FontCacheLinux.cpp index efb4c8d5eb94b..46ea32ce6c819 100644 --- a/sky/engine/platform/fonts/linux/FontCacheLinux.cpp +++ b/sky/engine/platform/fonts/linux/FontCacheLinux.cpp @@ -37,10 +37,10 @@ #include "flutter/sky/engine/wtf/Noncopyable.h" #include "flutter/sky/engine/wtf/OwnPtr.h" #include "flutter/sky/engine/wtf/PassOwnPtr.h" +#include "flutter/sky/engine/wtf/Vector.h" #include "flutter/sky/engine/wtf/text/AtomicString.h" #include "flutter/sky/engine/wtf/text/AtomicStringHash.h" #include "flutter/sky/engine/wtf/text/CString.h" -#include "flutter/sky/engine/wtf/Vector.h" namespace blink { diff --git a/sky/engine/platform/fonts/mobile/FontCacheMobile.cpp b/sky/engine/platform/fonts/mobile/FontCacheMobile.cpp index 053488fee2332..09456ed1f48b9 100644 --- a/sky/engine/platform/fonts/mobile/FontCacheMobile.cpp +++ b/sky/engine/platform/fonts/mobile/FontCacheMobile.cpp @@ -42,47 +42,52 @@ namespace blink { // SkFontMgr requires script-based locale names, like "zh-Hant" and "zh-Hans", // instead of "zh-CN" and "zh-TW". -static CString toSkFontMgrLocale(const String& locale) -{ - if (!locale.startsWith("zh", TextCaseInsensitive)) - return locale.ascii(); - switch (localeToScriptCodeForFontSelection(locale)) { +static CString toSkFontMgrLocale(const String& locale) { + if (!locale.startsWith("zh", TextCaseInsensitive)) + return locale.ascii(); + switch (localeToScriptCodeForFontSelection(locale)) { case USCRIPT_SIMPLIFIED_HAN: - return "zh-Hans"; + return "zh-Hans"; case USCRIPT_TRADITIONAL_HAN: - return "zh-Hant"; + return "zh-Hant"; default: - return locale.ascii(); - } + return locale.ascii(); + } } -static AtomicString getFamilyNameForCharacter(UChar32 c, const FontDescription& fontDescription) -{ - sk_sp fm(SkFontMgr::RefDefault()); - const char* bcp47Locales[2]; - int localeCount = 0; - CString defaultLocale = toSkFontMgrLocale(defaultLanguage()); - bcp47Locales[localeCount++] = defaultLocale.data(); - CString fontLocale; - if (!fontDescription.locale().isEmpty()) { - fontLocale = toSkFontMgrLocale(fontDescription.locale()); - bcp47Locales[localeCount++] = fontLocale.data(); - } - sk_sp typeface(fm->matchFamilyStyleCharacter(0, SkFontStyle(), bcp47Locales, localeCount, c)); - if (!typeface) - return emptyAtom; +static AtomicString getFamilyNameForCharacter( + UChar32 c, + const FontDescription& fontDescription) { + sk_sp fm(SkFontMgr::RefDefault()); + const char* bcp47Locales[2]; + int localeCount = 0; + CString defaultLocale = toSkFontMgrLocale(defaultLanguage()); + bcp47Locales[localeCount++] = defaultLocale.data(); + CString fontLocale; + if (!fontDescription.locale().isEmpty()) { + fontLocale = toSkFontMgrLocale(fontDescription.locale()); + bcp47Locales[localeCount++] = fontLocale.data(); + } + sk_sp typeface(fm->matchFamilyStyleCharacter( + 0, SkFontStyle(), bcp47Locales, localeCount, c)); + if (!typeface) + return emptyAtom; - SkString skiaFamilyName; - typeface->getFamilyName(&skiaFamilyName); - return skiaFamilyName.c_str(); + SkString skiaFamilyName; + typeface->getFamilyName(&skiaFamilyName); + return skiaFamilyName.c_str(); } -PassRefPtr FontCache::fallbackFontForCharacter(const FontDescription& fontDescription, UChar32 c, const SimpleFontData*) -{ - AtomicString familyName = getFamilyNameForCharacter(c, fontDescription); - if (familyName.isEmpty()) - return getLastResortFallbackFont(fontDescription, DoNotRetain); - return fontDataFromFontPlatformData(getFontPlatformData(fontDescription, FontFaceCreationParams(familyName)), DoNotRetain); +PassRefPtr FontCache::fallbackFontForCharacter( + const FontDescription& fontDescription, + UChar32 c, + const SimpleFontData*) { + AtomicString familyName = getFamilyNameForCharacter(c, fontDescription); + if (familyName.isEmpty()) + return getLastResortFallbackFont(fontDescription, DoNotRetain); + return fontDataFromFontPlatformData( + getFontPlatformData(fontDescription, FontFaceCreationParams(familyName)), + DoNotRetain); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/fonts/mobile/FontCacheMobileTest.cpp b/sky/engine/platform/fonts/mobile/FontCacheMobileTest.cpp index 33c9a469c5bc0..3e4b91166e424 100644 --- a/sky/engine/platform/fonts/mobile/FontCacheMobileTest.cpp +++ b/sky/engine/platform/fonts/mobile/FontCacheMobileTest.cpp @@ -9,20 +9,20 @@ namespace blink { -TEST(FontCacheMobile, fallbackFontForCharacter) -{ - // A Latin character in the common locale system font, but not in the - // Chinese locale-preferred font. - const UChar32 testChar = 228; +TEST(FontCacheMobile, fallbackFontForCharacter) { + // A Latin character in the common locale system font, but not in the + // Chinese locale-preferred font. + const UChar32 testChar = 228; - FontDescription fontDescription; - fontDescription.setScript(USCRIPT_SIMPLIFIED_HAN); - fontDescription.setGenericFamily(FontDescription::StandardFamily); + FontDescription fontDescription; + fontDescription.setScript(USCRIPT_SIMPLIFIED_HAN); + fontDescription.setGenericFamily(FontDescription::StandardFamily); - FontCache* fontCache = FontCache::fontCache(); - ASSERT_TRUE(fontCache); - RefPtr fontData = fontCache->fallbackFontForCharacter(fontDescription, testChar, 0); - EXPECT_TRUE(fontData); + FontCache* fontCache = FontCache::fontCache(); + ASSERT_TRUE(fontCache); + RefPtr fontData = + fontCache->fallbackFontForCharacter(fontDescription, testChar, 0); + EXPECT_TRUE(fontData); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/fonts/opentype/OpenTypeTypes.h b/sky/engine/platform/fonts/opentype/OpenTypeTypes.h index e88a4c2d44721..af38f9806c58b 100644 --- a/sky/engine/platform/fonts/opentype/OpenTypeTypes.h +++ b/sky/engine/platform/fonts/opentype/OpenTypeTypes.h @@ -10,16 +10,16 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef SKY_ENGINE_PLATFORM_FONTS_OPENTYPE_OPENTYPETYPES_H_ @@ -32,27 +32,27 @@ namespace blink { namespace OpenType { struct Int16 { - Int16(int16_t u) : v(htons(static_cast(u))) { } - operator int16_t() const { return static_cast(ntohs(v)); } - uint16_t v; // in BigEndian + Int16(int16_t u) : v(htons(static_cast(u))) {} + operator int16_t() const { return static_cast(ntohs(v)); } + uint16_t v; // in BigEndian }; struct UInt16 { - UInt16(uint16_t u) : v(htons(u)) { } - operator uint16_t() const { return ntohs(v); } - uint16_t v; // in BigEndian + UInt16(uint16_t u) : v(htons(u)) {} + operator uint16_t() const { return ntohs(v); } + uint16_t v; // in BigEndian }; struct Int32 { - Int32(int32_t u) : v(htonl(static_cast(u))) { } - operator int32_t() const { return static_cast(ntohl(v)); } - uint32_t v; // in BigEndian + Int32(int32_t u) : v(htonl(static_cast(u))) {} + operator int32_t() const { return static_cast(ntohl(v)); } + uint32_t v; // in BigEndian }; struct UInt32 { - UInt32(uint32_t u) : v(htonl(u)) { } - operator uint32_t() const { return ntohl(v); } - uint32_t v; // in BigEndian + UInt32(uint32_t u) : v(htonl(u)) {} + operator uint32_t() const { return ntohl(v); } + uint32_t v; // in BigEndian }; typedef UInt32 Fixed; @@ -63,40 +63,44 @@ typedef UInt16 GlyphID; // do endian conversion here but make sure constants are in big-endian order. // Note that multi-character literal is implementation-defined in C++0x. typedef uint32_t Tag; -#define OT_MAKE_TAG(ch1, ch2, ch3, ch4) ((((uint32_t)(ch4)) << 24) | (((uint32_t)(ch3)) << 16) | (((uint32_t)(ch2)) << 8) | ((uint32_t)(ch1))) +#define OT_MAKE_TAG(ch1, ch2, ch3, ch4) \ + ((((uint32_t)(ch4)) << 24) | (((uint32_t)(ch3)) << 16) | \ + (((uint32_t)(ch2)) << 8) | ((uint32_t)(ch1))) -template static const T* validateTable(const RefPtr& buffer, size_t count = 1) -{ - if (!buffer || buffer->size() < sizeof(T) * count) - return 0; - return reinterpret_cast(buffer->data()); +template +static const T* validateTable(const RefPtr& buffer, + size_t count = 1) { + if (!buffer || buffer->size() < sizeof(T) * count) + return 0; + return reinterpret_cast(buffer->data()); } struct TableBase { -protected: - static bool isValidEnd(const SharedBuffer& buffer, const void* position) - { - if (position < buffer.data()) - return false; - size_t offset = reinterpret_cast(position) - buffer.data(); - return offset <= buffer.size(); // "<=" because end is included as valid - } + protected: + static bool isValidEnd(const SharedBuffer& buffer, const void* position) { + if (position < buffer.data()) + return false; + size_t offset = reinterpret_cast(position) - buffer.data(); + return offset <= buffer.size(); // "<=" because end is included as valid + } - template static const T* validatePtr(const SharedBuffer& buffer, const void* position) - { - const T* casted = reinterpret_cast(position); - if (!isValidEnd(buffer, &casted[1])) - return 0; - return casted; - } + template + static const T* validatePtr(const SharedBuffer& buffer, + const void* position) { + const T* casted = reinterpret_cast(position); + if (!isValidEnd(buffer, &casted[1])) + return 0; + return casted; + } - template const T* validateOffset(const SharedBuffer& buffer, uint16_t offset) const - { - return validatePtr(buffer, reinterpret_cast(this) + offset); - } + template + const T* validateOffset(const SharedBuffer& buffer, uint16_t offset) const { + return validatePtr(buffer, + reinterpret_cast(this) + offset); + } }; -} // namespace OpenType -} // namespace blink +} // namespace OpenType +} // namespace blink -#endif // SKY_ENGINE_PLATFORM_FONTS_OPENTYPE_OPENTYPETYPES_H_ +#endif // SKY_ENGINE_PLATFORM_FONTS_OPENTYPE_OPENTYPETYPES_H_ diff --git a/sky/engine/platform/fonts/opentype/OpenTypeVerticalData.cpp b/sky/engine/platform/fonts/opentype/OpenTypeVerticalData.cpp index 634efcdc33896..7addf33a1a274 100644 --- a/sky/engine/platform/fonts/opentype/OpenTypeVerticalData.cpp +++ b/sky/engine/platform/fonts/opentype/OpenTypeVerticalData.cpp @@ -10,23 +10,23 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "flutter/sky/engine/platform/fonts/opentype/OpenTypeVerticalData.h" #include "flutter/sky/engine/platform/SharedBuffer.h" -#include "flutter/sky/engine/platform/fonts/SimpleFontData.h" #include "flutter/sky/engine/platform/fonts/GlyphPage.h" +#include "flutter/sky/engine/platform/fonts/SimpleFontData.h" #include "flutter/sky/engine/platform/fonts/opentype/OpenTypeTypes.h" #include "flutter/sky/engine/platform/geometry/FloatRect.h" #include "flutter/sky/engine/wtf/RefPtr.h" @@ -43,217 +43,239 @@ const uint32_t VORGTag = OT_MAKE_TAG('V', 'O', 'R', 'G'); #pragma pack(1) struct HheaTable { - OpenType::Fixed version; - OpenType::Int16 ascender; - OpenType::Int16 descender; - OpenType::Int16 lineGap; - OpenType::Int16 advanceWidthMax; - OpenType::Int16 minLeftSideBearing; - OpenType::Int16 minRightSideBearing; - OpenType::Int16 xMaxExtent; - OpenType::Int16 caretSlopeRise; - OpenType::Int16 caretSlopeRun; - OpenType::Int16 caretOffset; - OpenType::Int16 reserved[4]; - OpenType::Int16 metricDataFormat; - OpenType::UInt16 numberOfHMetrics; + OpenType::Fixed version; + OpenType::Int16 ascender; + OpenType::Int16 descender; + OpenType::Int16 lineGap; + OpenType::Int16 advanceWidthMax; + OpenType::Int16 minLeftSideBearing; + OpenType::Int16 minRightSideBearing; + OpenType::Int16 xMaxExtent; + OpenType::Int16 caretSlopeRise; + OpenType::Int16 caretSlopeRun; + OpenType::Int16 caretOffset; + OpenType::Int16 reserved[4]; + OpenType::Int16 metricDataFormat; + OpenType::UInt16 numberOfHMetrics; }; struct VheaTable { - OpenType::Fixed version; - OpenType::Int16 ascent; - OpenType::Int16 descent; - OpenType::Int16 lineGap; - OpenType::Int16 advanceHeightMax; - OpenType::Int16 minTopSideBearing; - OpenType::Int16 minBottomSideBearing; - OpenType::Int16 yMaxExtent; - OpenType::Int16 caretSlopeRise; - OpenType::Int16 caretSlopeRun; - OpenType::Int16 caretOffset; - OpenType::Int16 reserved[4]; - OpenType::Int16 metricDataFormat; - OpenType::UInt16 numOfLongVerMetrics; + OpenType::Fixed version; + OpenType::Int16 ascent; + OpenType::Int16 descent; + OpenType::Int16 lineGap; + OpenType::Int16 advanceHeightMax; + OpenType::Int16 minTopSideBearing; + OpenType::Int16 minBottomSideBearing; + OpenType::Int16 yMaxExtent; + OpenType::Int16 caretSlopeRise; + OpenType::Int16 caretSlopeRun; + OpenType::Int16 caretOffset; + OpenType::Int16 reserved[4]; + OpenType::Int16 metricDataFormat; + OpenType::UInt16 numOfLongVerMetrics; }; struct HmtxTable { - struct Entry { - OpenType::UInt16 advanceWidth; - OpenType::Int16 lsb; - } entries[1]; + struct Entry { + OpenType::UInt16 advanceWidth; + OpenType::Int16 lsb; + } entries[1]; }; struct VmtxTable { - struct Entry { - OpenType::UInt16 advanceHeight; - OpenType::Int16 topSideBearing; - } entries[1]; + struct Entry { + OpenType::UInt16 advanceHeight; + OpenType::Int16 topSideBearing; + } entries[1]; }; struct VORGTable { - OpenType::UInt16 majorVersion; - OpenType::UInt16 minorVersion; - OpenType::Int16 defaultVertOriginY; - OpenType::UInt16 numVertOriginYMetrics; - struct VertOriginYMetrics { - OpenType::UInt16 glyphIndex; - OpenType::Int16 vertOriginY; - } vertOriginYMetrics[1]; + OpenType::UInt16 majorVersion; + OpenType::UInt16 minorVersion; + OpenType::Int16 defaultVertOriginY; + OpenType::UInt16 numVertOriginYMetrics; + struct VertOriginYMetrics { + OpenType::UInt16 glyphIndex; + OpenType::Int16 vertOriginY; + } vertOriginYMetrics[1]; - size_t requiredSize() const { return sizeof(*this) + sizeof(VertOriginYMetrics) * (numVertOriginYMetrics - 1); } + size_t requiredSize() const { + return sizeof(*this) + + sizeof(VertOriginYMetrics) * (numVertOriginYMetrics - 1); + } }; #pragma pack() -} // namespace OpenType +} // namespace OpenType OpenTypeVerticalData::OpenTypeVerticalData(const FontPlatformData& platformData) - : m_defaultVertOriginY(0) -{ - loadMetrics(platformData); + : m_defaultVertOriginY(0) { + loadMetrics(platformData); } -void OpenTypeVerticalData::loadMetrics(const FontPlatformData& platformData) -{ - // Load hhea and hmtx to get x-component of vertical origins. - // If these tables are missing, it's not an OpenType font. - RefPtr buffer = platformData.openTypeTable(OpenType::HheaTag); - const OpenType::HheaTable* hhea = OpenType::validateTable(buffer); - if (!hhea) - return; - uint16_t countHmtxEntries = hhea->numberOfHMetrics; - if (!countHmtxEntries) { - WTF_LOG_ERROR("Invalid numberOfHMetrics"); - return; - } +void OpenTypeVerticalData::loadMetrics(const FontPlatformData& platformData) { + // Load hhea and hmtx to get x-component of vertical origins. + // If these tables are missing, it's not an OpenType font. + RefPtr buffer = platformData.openTypeTable(OpenType::HheaTag); + const OpenType::HheaTable* hhea = + OpenType::validateTable(buffer); + if (!hhea) + return; + uint16_t countHmtxEntries = hhea->numberOfHMetrics; + if (!countHmtxEntries) { + WTF_LOG_ERROR("Invalid numberOfHMetrics"); + return; + } - buffer = platformData.openTypeTable(OpenType::HmtxTag); - const OpenType::HmtxTable* hmtx = OpenType::validateTable(buffer, countHmtxEntries); - if (!hmtx) { - WTF_LOG_ERROR("hhea exists but hmtx does not (or broken)"); - return; - } - m_advanceWidths.resize(countHmtxEntries); - for (uint16_t i = 0; i < countHmtxEntries; ++i) - m_advanceWidths[i] = hmtx->entries[i].advanceWidth; + buffer = platformData.openTypeTable(OpenType::HmtxTag); + const OpenType::HmtxTable* hmtx = + OpenType::validateTable(buffer, countHmtxEntries); + if (!hmtx) { + WTF_LOG_ERROR("hhea exists but hmtx does not (or broken)"); + return; + } + m_advanceWidths.resize(countHmtxEntries); + for (uint16_t i = 0; i < countHmtxEntries; ++i) + m_advanceWidths[i] = hmtx->entries[i].advanceWidth; - // Load vhea first. This table is required for fonts that support vertical flow. - buffer = platformData.openTypeTable(OpenType::VheaTag); - const OpenType::VheaTable* vhea = OpenType::validateTable(buffer); - if (!vhea) - return; - uint16_t countVmtxEntries = vhea->numOfLongVerMetrics; - if (!countVmtxEntries) { - WTF_LOG_ERROR("Invalid numOfLongVerMetrics"); - return; - } + // Load vhea first. This table is required for fonts that support vertical + // flow. + buffer = platformData.openTypeTable(OpenType::VheaTag); + const OpenType::VheaTable* vhea = + OpenType::validateTable(buffer); + if (!vhea) + return; + uint16_t countVmtxEntries = vhea->numOfLongVerMetrics; + if (!countVmtxEntries) { + WTF_LOG_ERROR("Invalid numOfLongVerMetrics"); + return; + } - // Load VORG. This table is optional. - buffer = platformData.openTypeTable(OpenType::VORGTag); - const OpenType::VORGTable* vorg = OpenType::validateTable(buffer); - if (vorg && buffer->size() >= vorg->requiredSize()) { - m_defaultVertOriginY = vorg->defaultVertOriginY; - uint16_t countVertOriginYMetrics = vorg->numVertOriginYMetrics; - if (!countVertOriginYMetrics) { - // Add one entry so that hasVORG() becomes true - m_vertOriginY.set(0, m_defaultVertOriginY); - } else { - for (uint16_t i = 0; i < countVertOriginYMetrics; ++i) { - const OpenType::VORGTable::VertOriginYMetrics& metrics = vorg->vertOriginYMetrics[i]; - m_vertOriginY.set(metrics.glyphIndex, metrics.vertOriginY); - } - } + // Load VORG. This table is optional. + buffer = platformData.openTypeTable(OpenType::VORGTag); + const OpenType::VORGTable* vorg = + OpenType::validateTable(buffer); + if (vorg && buffer->size() >= vorg->requiredSize()) { + m_defaultVertOriginY = vorg->defaultVertOriginY; + uint16_t countVertOriginYMetrics = vorg->numVertOriginYMetrics; + if (!countVertOriginYMetrics) { + // Add one entry so that hasVORG() becomes true + m_vertOriginY.set(0, m_defaultVertOriginY); + } else { + for (uint16_t i = 0; i < countVertOriginYMetrics; ++i) { + const OpenType::VORGTable::VertOriginYMetrics& metrics = + vorg->vertOriginYMetrics[i]; + m_vertOriginY.set(metrics.glyphIndex, metrics.vertOriginY); + } } + } - // Load vmtx then. This table is required for fonts that support vertical flow. - buffer = platformData.openTypeTable(OpenType::VmtxTag); - const OpenType::VmtxTable* vmtx = OpenType::validateTable(buffer, countVmtxEntries); - if (!vmtx) { - WTF_LOG_ERROR("vhea exists but vmtx does not (or broken)"); - return; - } - m_advanceHeights.resize(countVmtxEntries); - for (uint16_t i = 0; i < countVmtxEntries; ++i) - m_advanceHeights[i] = vmtx->entries[i].advanceHeight; + // Load vmtx then. This table is required for fonts that support vertical + // flow. + buffer = platformData.openTypeTable(OpenType::VmtxTag); + const OpenType::VmtxTable* vmtx = + OpenType::validateTable(buffer, countVmtxEntries); + if (!vmtx) { + WTF_LOG_ERROR("vhea exists but vmtx does not (or broken)"); + return; + } + m_advanceHeights.resize(countVmtxEntries); + for (uint16_t i = 0; i < countVmtxEntries; ++i) + m_advanceHeights[i] = vmtx->entries[i].advanceHeight; - // VORG is preferred way to calculate vertical origin than vmtx, - // so load topSideBearing from vmtx only if VORG is missing. - if (hasVORG()) - return; + // VORG is preferred way to calculate vertical origin than vmtx, + // so load topSideBearing from vmtx only if VORG is missing. + if (hasVORG()) + return; - size_t sizeExtra = buffer->size() - sizeof(OpenType::VmtxTable::Entry) * countVmtxEntries; - if (sizeExtra % sizeof(OpenType::Int16)) { - WTF_LOG_ERROR("vmtx has incorrect tsb count"); - return; - } - size_t countTopSideBearings = countVmtxEntries + sizeExtra / sizeof(OpenType::Int16); - m_topSideBearings.resize(countTopSideBearings); - size_t i; - for (i = 0; i < countVmtxEntries; ++i) - m_topSideBearings[i] = vmtx->entries[i].topSideBearing; - if (i < countTopSideBearings) { - const OpenType::Int16* pTopSideBearingsExtra = reinterpret_cast(&vmtx->entries[countVmtxEntries]); - for (; i < countTopSideBearings; ++i, ++pTopSideBearingsExtra) - m_topSideBearings[i] = *pTopSideBearingsExtra; - } + size_t sizeExtra = + buffer->size() - sizeof(OpenType::VmtxTable::Entry) * countVmtxEntries; + if (sizeExtra % sizeof(OpenType::Int16)) { + WTF_LOG_ERROR("vmtx has incorrect tsb count"); + return; + } + size_t countTopSideBearings = + countVmtxEntries + sizeExtra / sizeof(OpenType::Int16); + m_topSideBearings.resize(countTopSideBearings); + size_t i; + for (i = 0; i < countVmtxEntries; ++i) + m_topSideBearings[i] = vmtx->entries[i].topSideBearing; + if (i < countTopSideBearings) { + const OpenType::Int16* pTopSideBearingsExtra = + reinterpret_cast( + &vmtx->entries[countVmtxEntries]); + for (; i < countTopSideBearings; ++i, ++pTopSideBearingsExtra) + m_topSideBearings[i] = *pTopSideBearingsExtra; + } } -float OpenTypeVerticalData::advanceHeight(const SimpleFontData* font, Glyph glyph) const -{ - size_t countHeights = m_advanceHeights.size(); - if (countHeights) { - uint16_t advanceFUnit = m_advanceHeights[glyph < countHeights ? glyph : countHeights - 1]; - float advance = advanceFUnit * font->sizePerUnit(); - return advance; - } +float OpenTypeVerticalData::advanceHeight(const SimpleFontData* font, + Glyph glyph) const { + size_t countHeights = m_advanceHeights.size(); + if (countHeights) { + uint16_t advanceFUnit = + m_advanceHeights[glyph < countHeights ? glyph : countHeights - 1]; + float advance = advanceFUnit * font->sizePerUnit(); + return advance; + } - // No vertical info in the font file; use height as advance. - return font->fontMetrics().height(); + // No vertical info in the font file; use height as advance. + return font->fontMetrics().height(); } -void OpenTypeVerticalData::getVerticalTranslationsForGlyphs(const SimpleFontData* font, const Glyph* glyphs, size_t count, float* outXYArray) const -{ - size_t countWidths = m_advanceWidths.size(); - ASSERT(countWidths > 0); - const FontMetrics& metrics = font->fontMetrics(); - float sizePerUnit = font->sizePerUnit(); - float ascent = metrics.ascent(); - bool useVORG = hasVORG(); - size_t countTopSideBearings = m_topSideBearings.size(); - float defaultVertOriginY = std::numeric_limits::quiet_NaN(); - for (float* end = &(outXYArray[count * 2]); outXYArray != end; ++glyphs, outXYArray += 2) { - Glyph glyph = *glyphs; - uint16_t widthFUnit = m_advanceWidths[glyph < countWidths ? glyph : countWidths - 1]; - float width = widthFUnit * sizePerUnit; - outXYArray[0] = -width / 2; +void OpenTypeVerticalData::getVerticalTranslationsForGlyphs( + const SimpleFontData* font, + const Glyph* glyphs, + size_t count, + float* outXYArray) const { + size_t countWidths = m_advanceWidths.size(); + ASSERT(countWidths > 0); + const FontMetrics& metrics = font->fontMetrics(); + float sizePerUnit = font->sizePerUnit(); + float ascent = metrics.ascent(); + bool useVORG = hasVORG(); + size_t countTopSideBearings = m_topSideBearings.size(); + float defaultVertOriginY = std::numeric_limits::quiet_NaN(); + for (float *end = &(outXYArray[count * 2]); outXYArray != end; + ++glyphs, outXYArray += 2) { + Glyph glyph = *glyphs; + uint16_t widthFUnit = + m_advanceWidths[glyph < countWidths ? glyph : countWidths - 1]; + float width = widthFUnit * sizePerUnit; + outXYArray[0] = -width / 2; - // For Y, try VORG first. - if (useVORG) { - if (glyph) { - int16_t vertOriginYFUnit = m_vertOriginY.get(glyph); - if (vertOriginYFUnit) { - outXYArray[1] = -vertOriginYFUnit * sizePerUnit; - continue; - } - } - if (std::isnan(defaultVertOriginY)) - defaultVertOriginY = -m_defaultVertOriginY * sizePerUnit; - outXYArray[1] = defaultVertOriginY; - continue; - } - - // If no VORG, try vmtx next. - if (countTopSideBearings) { - int16_t topSideBearingFUnit = m_topSideBearings[glyph < countTopSideBearings ? glyph : countTopSideBearings - 1]; - float topSideBearing = topSideBearingFUnit * sizePerUnit; - FloatRect bounds = font->boundsForGlyph(glyph); - outXYArray[1] = bounds.y() - topSideBearing; - continue; + // For Y, try VORG first. + if (useVORG) { + if (glyph) { + int16_t vertOriginYFUnit = m_vertOriginY.get(glyph); + if (vertOriginYFUnit) { + outXYArray[1] = -vertOriginYFUnit * sizePerUnit; + continue; } + } + if (std::isnan(defaultVertOriginY)) + defaultVertOriginY = -m_defaultVertOriginY * sizePerUnit; + outXYArray[1] = defaultVertOriginY; + continue; + } - // No vertical info in the font file; use ascent as vertical origin. - outXYArray[1] = -ascent; + // If no VORG, try vmtx next. + if (countTopSideBearings) { + int16_t topSideBearingFUnit = + m_topSideBearings[glyph < countTopSideBearings + ? glyph + : countTopSideBearings - 1]; + float topSideBearing = topSideBearingFUnit * sizePerUnit; + FloatRect bounds = font->boundsForGlyph(glyph); + outXYArray[1] = bounds.y() - topSideBearing; + continue; } + + // No vertical info in the font file; use ascent as vertical origin. + outXYArray[1] = -ascent; + } } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/fonts/opentype/OpenTypeVerticalData.h b/sky/engine/platform/fonts/opentype/OpenTypeVerticalData.h index 1be5de8dab80a..d42b917607574 100644 --- a/sky/engine/platform/fonts/opentype/OpenTypeVerticalData.h +++ b/sky/engine/platform/fonts/opentype/OpenTypeVerticalData.h @@ -10,16 +10,16 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef SKY_ENGINE_PLATFORM_FONTS_OPENTYPE_OPENTYPEVERTICALDATA_H_ @@ -37,39 +37,42 @@ namespace blink { class FontPlatformData; class SimpleFontData; -class PLATFORM_EXPORT OpenTypeVerticalData : public RefCounted { -public: - static PassRefPtr create(const FontPlatformData& platformData) - { - return adoptRef(new OpenTypeVerticalData(platformData)); - } +class PLATFORM_EXPORT OpenTypeVerticalData + : public RefCounted { + public: + static PassRefPtr create( + const FontPlatformData& platformData) { + return adoptRef(new OpenTypeVerticalData(platformData)); + } - bool isOpenType() const { return !m_advanceWidths.isEmpty(); } - bool hasVerticalMetrics() const { return !m_advanceHeights.isEmpty(); } - float advanceHeight(const SimpleFontData*, Glyph) const; + bool isOpenType() const { return !m_advanceWidths.isEmpty(); } + bool hasVerticalMetrics() const { return !m_advanceHeights.isEmpty(); } + float advanceHeight(const SimpleFontData*, Glyph) const; - bool inFontCache() const { return m_inFontCache; } - void setInFontCache(bool inFontCache) { m_inFontCache = inFontCache; } + bool inFontCache() const { return m_inFontCache; } + void setInFontCache(bool inFontCache) { m_inFontCache = inFontCache; } - void getVerticalTranslationsForGlyphs(const SimpleFontData*, const Glyph*, size_t, float* outXYArray) const; + void getVerticalTranslationsForGlyphs(const SimpleFontData*, + const Glyph*, + size_t, + float* outXYArray) const; -private: + private: + explicit OpenTypeVerticalData(const FontPlatformData&); - explicit OpenTypeVerticalData(const FontPlatformData&); + void loadMetrics(const FontPlatformData&); + bool hasVORG() const { return !m_vertOriginY.isEmpty(); } - void loadMetrics(const FontPlatformData&); - bool hasVORG() const { return !m_vertOriginY.isEmpty(); } + HashMap m_verticalGlyphMap; + Vector m_advanceWidths; + Vector m_advanceHeights; + Vector m_topSideBearings; + int16_t m_defaultVertOriginY; + HashMap m_vertOriginY; - HashMap m_verticalGlyphMap; - Vector m_advanceWidths; - Vector m_advanceHeights; - Vector m_topSideBearings; - int16_t m_defaultVertOriginY; - HashMap m_vertOriginY; - - bool m_inFontCache; // for mark & sweep in FontCache::purgeInactiveFontData() + bool m_inFontCache; // for mark & sweep in FontCache::purgeInactiveFontData() }; -} // namespace blink +} // namespace blink -#endif // SKY_ENGINE_PLATFORM_FONTS_OPENTYPE_OPENTYPEVERTICALDATA_H_ +#endif // SKY_ENGINE_PLATFORM_FONTS_OPENTYPE_OPENTYPEVERTICALDATA_H_ diff --git a/sky/engine/platform/fonts/skia/FontCacheSkia.cpp b/sky/engine/platform/fonts/skia/FontCacheSkia.cpp index 7d67999966acd..10b73d324063c 100644 --- a/sky/engine/platform/fonts/skia/FontCacheSkia.cpp +++ b/sky/engine/platform/fonts/skia/FontCacheSkia.cpp @@ -28,7 +28,6 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #include #include "flutter/sky/engine/platform/NotImplemented.h" #include "flutter/sky/engine/platform/fonts/AlternateFontFamily.h" @@ -52,198 +51,212 @@ namespace blink { #if !OS(FUCHSIA) -static int toSkiaWeight(FontWeight weight) -{ - switch (weight) { +static int toSkiaWeight(FontWeight weight) { + switch (weight) { case FontWeight100: - return SkFontStyle::kThin_Weight; + return SkFontStyle::kThin_Weight; case FontWeight200: - return SkFontStyle::kExtraLight_Weight; + return SkFontStyle::kExtraLight_Weight; case FontWeight300: - return SkFontStyle::kLight_Weight; + return SkFontStyle::kLight_Weight; case FontWeight400: - return SkFontStyle::kNormal_Weight; + return SkFontStyle::kNormal_Weight; case FontWeight500: - return SkFontStyle::kMedium_Weight; + return SkFontStyle::kMedium_Weight; case FontWeight600: - return SkFontStyle::kSemiBold_Weight; + return SkFontStyle::kSemiBold_Weight; case FontWeight700: - return SkFontStyle::kBold_Weight; + return SkFontStyle::kBold_Weight; case FontWeight800: - return SkFontStyle::kExtraBold_Weight; + return SkFontStyle::kExtraBold_Weight; case FontWeight900: - return SkFontStyle::kBlack_Weight; - } - ASSERT_NOT_REACHED(); - return SkFontStyle::kNormal_Weight; + return SkFontStyle::kBlack_Weight; + } + ASSERT_NOT_REACHED(); + return SkFontStyle::kNormal_Weight; } -static SkFontStyle::Slant toSkiaSlant(FontStyle style) -{ - switch (style) { +static SkFontStyle::Slant toSkiaSlant(FontStyle style) { + switch (style) { case FontStyleNormal: - return SkFontStyle::kUpright_Slant; + return SkFontStyle::kUpright_Slant; case FontStyleItalic: - return SkFontStyle::kItalic_Slant; - } - ASSERT_NOT_REACHED(); - return SkFontStyle::kUpright_Slant; + return SkFontStyle::kItalic_Slant; + } + ASSERT_NOT_REACHED(); + return SkFontStyle::kUpright_Slant; } -static int toSkiaWidth(FontStretch stretch) -{ - // Numeric values matching OS/2 & Windows Metrics usWidthClass table. - // https://www.microsoft.com/typography/otspec/os2.htm - return static_cast(stretch); +static int toSkiaWidth(FontStretch stretch) { + // Numeric values matching OS/2 & Windows Metrics usWidthClass table. + // https://www.microsoft.com/typography/otspec/os2.htm + return static_cast(stretch); } -static SkFontStyle toSkiaFontStyle(const FontDescription& fontDescription) -{ - return SkFontStyle(toSkiaWeight(fontDescription.weight()), - toSkiaWidth(fontDescription.stretch()), - toSkiaSlant(fontDescription.style())); +static SkFontStyle toSkiaFontStyle(const FontDescription& fontDescription) { + return SkFontStyle(toSkiaWeight(fontDescription.weight()), + toSkiaWidth(fontDescription.stretch()), + toSkiaSlant(fontDescription.style())); } #endif // !OS(FUCHSIA) -void FontCache::platformInit() -{ -} +void FontCache::platformInit() {} PassRefPtr FontCache::fallbackOnStandardFontStyle( - const FontDescription& fontDescription, UChar32 character) -{ - FontDescription substituteDescription(fontDescription); - substituteDescription.setStyle(FontStyleNormal); - substituteDescription.setWeight(FontWeightNormal); - - FontFaceCreationParams creationParams(substituteDescription.family().family()); - FontPlatformData* substitutePlatformData = getFontPlatformData(substituteDescription, creationParams); - if (substitutePlatformData && substitutePlatformData->fontContainsCharacter(character)) { - FontPlatformData platformData = FontPlatformData(*substitutePlatformData); - platformData.setSyntheticBold(fontDescription.weight() >= FontWeight600); - platformData.setSyntheticItalic(fontDescription.style() == FontStyleItalic); - return fontDataFromFontPlatformData(&platformData, DoNotRetain); - } + const FontDescription& fontDescription, + UChar32 character) { + FontDescription substituteDescription(fontDescription); + substituteDescription.setStyle(FontStyleNormal); + substituteDescription.setWeight(FontWeightNormal); - return nullptr; + FontFaceCreationParams creationParams( + substituteDescription.family().family()); + FontPlatformData* substitutePlatformData = + getFontPlatformData(substituteDescription, creationParams); + if (substitutePlatformData && + substitutePlatformData->fontContainsCharacter(character)) { + FontPlatformData platformData = FontPlatformData(*substitutePlatformData); + platformData.setSyntheticBold(fontDescription.weight() >= FontWeight600); + platformData.setSyntheticItalic(fontDescription.style() == FontStyleItalic); + return fontDataFromFontPlatformData(&platformData, DoNotRetain); + } + + return nullptr; } #if !OS(WIN) && !OS(ANDROID) && !OS(IOS) -PassRefPtr FontCache::fallbackFontForCharacter(const FontDescription& fontDescription, UChar32 c, const SimpleFontData*) -{ - // First try the specified font with standard style & weight. - if (fontDescription.style() == FontStyleItalic - || fontDescription.weight() >= FontWeight600) { - RefPtr fontData = fallbackOnStandardFontStyle( - fontDescription, c); - if (fontData) - return fontData; - } - - FontCache::PlatformFallbackFont fallbackFont; - FontCache::getFontForCharacter(c, "", &fallbackFont); - if (fallbackFont.name.isEmpty()) - return nullptr; - - FontFaceCreationParams creationParams; - creationParams = FontFaceCreationParams(fallbackFont.filename, fallbackFont.fontconfigInterfaceId, fallbackFont.ttcIndex); - - // Changes weight and/or italic of given FontDescription depends on - // the result of fontconfig so that keeping the correct font mapping - // of the given character. See http://crbug.com/32109 for details. - bool shouldSetSyntheticBold = false; - bool shouldSetSyntheticItalic = false; - FontDescription description(fontDescription); - if (fallbackFont.isBold && description.weight() < FontWeightBold) - description.setWeight(FontWeightBold); - if (!fallbackFont.isBold && description.weight() >= FontWeightBold) { - shouldSetSyntheticBold = true; - description.setWeight(FontWeightNormal); - } - if (fallbackFont.isItalic && description.style() == FontStyleNormal) - description.setStyle(FontStyleItalic); - if (!fallbackFont.isItalic && description.style() == FontStyleItalic) { - shouldSetSyntheticItalic = true; - description.setStyle(FontStyleNormal); - } - - FontPlatformData* substitutePlatformData = getFontPlatformData(description, creationParams); - if (!substitutePlatformData) - return nullptr; - FontPlatformData platformData = FontPlatformData(*substitutePlatformData); - platformData.setSyntheticBold(shouldSetSyntheticBold); - platformData.setSyntheticItalic(shouldSetSyntheticItalic); - return fontDataFromFontPlatformData(&platformData, DoNotRetain); +PassRefPtr FontCache::fallbackFontForCharacter( + const FontDescription& fontDescription, + UChar32 c, + const SimpleFontData*) { + // First try the specified font with standard style & weight. + if (fontDescription.style() == FontStyleItalic || + fontDescription.weight() >= FontWeight600) { + RefPtr fontData = + fallbackOnStandardFontStyle(fontDescription, c); + if (fontData) + return fontData; + } + + FontCache::PlatformFallbackFont fallbackFont; + FontCache::getFontForCharacter(c, "", &fallbackFont); + if (fallbackFont.name.isEmpty()) + return nullptr; + + FontFaceCreationParams creationParams; + creationParams = FontFaceCreationParams(fallbackFont.filename, + fallbackFont.fontconfigInterfaceId, + fallbackFont.ttcIndex); + + // Changes weight and/or italic of given FontDescription depends on + // the result of fontconfig so that keeping the correct font mapping + // of the given character. See http://crbug.com/32109 for details. + bool shouldSetSyntheticBold = false; + bool shouldSetSyntheticItalic = false; + FontDescription description(fontDescription); + if (fallbackFont.isBold && description.weight() < FontWeightBold) + description.setWeight(FontWeightBold); + if (!fallbackFont.isBold && description.weight() >= FontWeightBold) { + shouldSetSyntheticBold = true; + description.setWeight(FontWeightNormal); + } + if (fallbackFont.isItalic && description.style() == FontStyleNormal) + description.setStyle(FontStyleItalic); + if (!fallbackFont.isItalic && description.style() == FontStyleItalic) { + shouldSetSyntheticItalic = true; + description.setStyle(FontStyleNormal); + } + + FontPlatformData* substitutePlatformData = + getFontPlatformData(description, creationParams); + if (!substitutePlatformData) + return nullptr; + FontPlatformData platformData = FontPlatformData(*substitutePlatformData); + platformData.setSyntheticBold(shouldSetSyntheticBold); + platformData.setSyntheticItalic(shouldSetSyntheticItalic); + return fontDataFromFontPlatformData(&platformData, DoNotRetain); } -#endif // !OS(WIN) && !OS(ANDROID) - -PassRefPtr FontCache::getLastResortFallbackFont(const FontDescription& description, ShouldRetain shouldRetain) -{ - const FontFaceCreationParams fallbackCreationParams(getFallbackFontFamily(description)); - const FontPlatformData* fontPlatformData = getFontPlatformData(description, fallbackCreationParams); - - // We should at least have Sans or Arial which is the last resort fallback of SkFontHost ports. - if (!fontPlatformData) { - DEFINE_STATIC_LOCAL(const FontFaceCreationParams, sansCreationParams, (AtomicString("Sans", AtomicString::ConstructFromLiteral))); - fontPlatformData = getFontPlatformData(description, sansCreationParams); - } - if (!fontPlatformData) { - DEFINE_STATIC_LOCAL(const FontFaceCreationParams, arialCreationParams, (AtomicString("Arial", AtomicString::ConstructFromLiteral))); - fontPlatformData = getFontPlatformData(description, arialCreationParams); - } - - ASSERT(fontPlatformData); - return fontDataFromFontPlatformData(fontPlatformData, shouldRetain); +#endif // !OS(WIN) && !OS(ANDROID) + +PassRefPtr FontCache::getLastResortFallbackFont( + const FontDescription& description, + ShouldRetain shouldRetain) { + const FontFaceCreationParams fallbackCreationParams( + getFallbackFontFamily(description)); + const FontPlatformData* fontPlatformData = + getFontPlatformData(description, fallbackCreationParams); + + // We should at least have Sans or Arial which is the last resort fallback of + // SkFontHost ports. + if (!fontPlatformData) { + DEFINE_STATIC_LOCAL( + const FontFaceCreationParams, sansCreationParams, + (AtomicString("Sans", AtomicString::ConstructFromLiteral))); + fontPlatformData = getFontPlatformData(description, sansCreationParams); + } + if (!fontPlatformData) { + DEFINE_STATIC_LOCAL( + const FontFaceCreationParams, arialCreationParams, + (AtomicString("Arial", AtomicString::ConstructFromLiteral))); + fontPlatformData = getFontPlatformData(description, arialCreationParams); + } + + ASSERT(fontPlatformData); + return fontDataFromFontPlatformData(fontPlatformData, shouldRetain); } #if !OS(FUCHSIA) -sk_sp FontCache::createTypeface(const FontDescription& fontDescription, const FontFaceCreationParams& creationParams, CString& name) -{ +sk_sp FontCache::createTypeface( + const FontDescription& fontDescription, + const FontFaceCreationParams& creationParams, + CString& name) { #if !OS(WIN) && !OS(ANDROID) - if (creationParams.creationType() == CreateFontByFciIdAndTtcIndex) { - return SkTypeface::MakeFromFile(creationParams.filename().data()); - } + if (creationParams.creationType() == CreateFontByFciIdAndTtcIndex) { + return SkTypeface::MakeFromFile(creationParams.filename().data()); + } #endif - AtomicString family = creationParams.family(); - // If we're creating a fallback font (e.g. "-webkit-monospace"), convert the name into - // the fallback name (like "monospace") that fontconfig understands. - if (!family.length() || family.startsWith("-webkit-")) { - name = getFallbackFontFamily(fontDescription).string().utf8(); - } else { - // convert the name to utf8 - name = family.utf8(); - } - - SkFontStyle style = toSkiaFontStyle(fontDescription); - sk_sp fm(SkFontMgr::RefDefault()); - sk_sp typeface(fm->matchFamilyStyle(name.data(), style)); - if (typeface) - return typeface; - - return SkTypeface::MakeFromName(name.data(), style); + AtomicString family = creationParams.family(); + // If we're creating a fallback font (e.g. "-webkit-monospace"), convert the + // name into the fallback name (like "monospace") that fontconfig understands. + if (!family.length() || family.startsWith("-webkit-")) { + name = getFallbackFontFamily(fontDescription).string().utf8(); + } else { + // convert the name to utf8 + name = family.utf8(); + } + + SkFontStyle style = toSkiaFontStyle(fontDescription); + sk_sp fm(SkFontMgr::RefDefault()); + sk_sp typeface(fm->matchFamilyStyle(name.data(), style)); + if (typeface) + return typeface; + + return SkTypeface::MakeFromName(name.data(), style); } #endif #if !OS(WIN) -FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontDescription, const FontFaceCreationParams& creationParams, float fontSize) -{ - CString name; - sk_sp tf = createTypeface(fontDescription, creationParams, name); - if (!tf) - return 0; - - FontPlatformData* result = new FontPlatformData(tf, - name.data(), - fontSize, - (fontDescription.weight() >= FontWeight600 && !tf->isBold()) || fontDescription.isSyntheticBold(), - (fontDescription.style() && !tf->isItalic()) || fontDescription.isSyntheticItalic(), - fontDescription.orientation(), - fontDescription.useSubpixelPositioning()); - return result; +FontPlatformData* FontCache::createFontPlatformData( + const FontDescription& fontDescription, + const FontFaceCreationParams& creationParams, + float fontSize) { + CString name; + sk_sp tf = createTypeface(fontDescription, creationParams, name); + if (!tf) + return 0; + + FontPlatformData* result = new FontPlatformData( + tf, name.data(), fontSize, + (fontDescription.weight() >= FontWeight600 && !tf->isBold()) || + fontDescription.isSyntheticBold(), + (fontDescription.style() && !tf->isItalic()) || + fontDescription.isSyntheticItalic(), + fontDescription.orientation(), fontDescription.useSubpixelPositioning()); + return result; } -#endif // !OS(WIN) +#endif // !OS(WIN) -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/fonts/skia/FontCustomPlatformDataSkia.cpp b/sky/engine/platform/fonts/skia/FontCustomPlatformDataSkia.cpp index 3cb4d662c369e..f3f42e5bb4bda 100644 --- a/sky/engine/platform/fonts/skia/FontCustomPlatformDataSkia.cpp +++ b/sky/engine/platform/fonts/skia/FontCustomPlatformDataSkia.cpp @@ -42,35 +42,36 @@ namespace blink { FontCustomPlatformData::FontCustomPlatformData(sk_sp typeface) - : m_typeface(typeface) -{ -} + : m_typeface(typeface) {} -FontCustomPlatformData::~FontCustomPlatformData() -{ -} +FontCustomPlatformData::~FontCustomPlatformData() {} -FontPlatformData FontCustomPlatformData::fontPlatformData(float size, bool bold, bool italic, FontOrientation orientation, FontWidthVariant) -{ - ASSERT(m_typeface); - return FontPlatformData(m_typeface, "", size, bold && !m_typeface->isBold(), italic && !m_typeface->isItalic(), orientation); +FontPlatformData FontCustomPlatformData::fontPlatformData( + float size, + bool bold, + bool italic, + FontOrientation orientation, + FontWidthVariant) { + ASSERT(m_typeface); + return FontPlatformData(m_typeface, "", size, bold && !m_typeface->isBold(), + italic && !m_typeface->isItalic(), orientation); } -PassOwnPtr FontCustomPlatformData::create(SharedBuffer* buffer) -{ - ASSERT_ARG(buffer, buffer); +PassOwnPtr FontCustomPlatformData::create( + SharedBuffer* buffer) { + ASSERT_ARG(buffer, buffer); - SkMemoryStream* stream = new SkMemoryStream(buffer->getAsSkData()); - sk_sp typeface = SkTypeface::MakeFromStream(stream); - if (!typeface) - return nullptr; + SkMemoryStream* stream = new SkMemoryStream(buffer->getAsSkData()); + sk_sp typeface = SkTypeface::MakeFromStream(stream); + if (!typeface) + return nullptr; - return adoptPtr(new FontCustomPlatformData(typeface)); + return adoptPtr(new FontCustomPlatformData(typeface)); } -bool FontCustomPlatformData::supportsFormat(const String& format) -{ - return equalIgnoringCase(format, "truetype") || equalIgnoringCase(format, "opentype"); +bool FontCustomPlatformData::supportsFormat(const String& format) { + return equalIgnoringCase(format, "truetype") || + equalIgnoringCase(format, "opentype"); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/fonts/skia/FontPlatformDataSkia.cpp b/sky/engine/platform/fonts/skia/FontPlatformDataSkia.cpp index aff887004e425..8e46405b7049f 100644 --- a/sky/engine/platform/fonts/skia/FontPlatformDataSkia.cpp +++ b/sky/engine/platform/fonts/skia/FontPlatformDataSkia.cpp @@ -31,56 +31,54 @@ #include "flutter/sky/engine/platform/fonts/FontPlatformData.h" #include "flutter/sky/engine/platform/fonts/FontCache.h" -#include "third_party/skia/src/core/SkEndian.h" #include "third_party/skia/include/core/SkTypeface.h" +#include "third_party/skia/src/core/SkEndian.h" namespace blink { -unsigned FontPlatformData::hash() const -{ - unsigned h = SkTypeface::UniqueID(m_typeface.get()); - h ^= 0x01010101 * ((static_cast(m_orientation) << 2) | (static_cast(m_syntheticBold) << 1) | static_cast(m_syntheticItalic)); +unsigned FontPlatformData::hash() const { + unsigned h = SkTypeface::UniqueID(m_typeface.get()); + h ^= 0x01010101 * ((static_cast(m_orientation) << 2) | + (static_cast(m_syntheticBold) << 1) | + static_cast(m_syntheticItalic)); - // This memcpy is to avoid a reinterpret_cast that breaks strict-aliasing - // rules. Memcpy is generally optimized enough so that performance doesn't - // matter here. - uint32_t textSizeBytes; - memcpy(&textSizeBytes, &m_textSize, sizeof(uint32_t)); - h ^= textSizeBytes; + // This memcpy is to avoid a reinterpret_cast that breaks strict-aliasing + // rules. Memcpy is generally optimized enough so that performance doesn't + // matter here. + uint32_t textSizeBytes; + memcpy(&textSizeBytes, &m_textSize, sizeof(uint32_t)); + h ^= textSizeBytes; - return h; + return h; } -bool FontPlatformData::fontContainsCharacter(UChar32 character) -{ - SkPaint paint; - setupPaint(&paint); - paint.setTextEncoding(SkPaint::kUTF32_TextEncoding); +bool FontPlatformData::fontContainsCharacter(UChar32 character) { + SkPaint paint; + setupPaint(&paint); + paint.setTextEncoding(SkPaint::kUTF32_TextEncoding); - uint16_t glyph; - paint.textToGlyphs(&character, sizeof(character), &glyph); - return glyph; + uint16_t glyph; + paint.textToGlyphs(&character, sizeof(character), &glyph); + return glyph; } #if ENABLE(OPENTYPE_VERTICAL) -PassRefPtr FontPlatformData::verticalData() const -{ - return FontCache::fontCache()->getVerticalData(typeface()->uniqueID(), *this); +PassRefPtr FontPlatformData::verticalData() const { + return FontCache::fontCache()->getVerticalData(typeface()->uniqueID(), *this); } -PassRefPtr FontPlatformData::openTypeTable(uint32_t table) const -{ - RefPtr buffer; +PassRefPtr FontPlatformData::openTypeTable(uint32_t table) const { + RefPtr buffer; - SkFontTableTag tag = SkEndianSwap32(table); - const size_t tableSize = m_typeface->getTableSize(tag); - if (tableSize) { - Vector tableBuffer(tableSize); - m_typeface->getTableData(tag, 0, tableSize, &tableBuffer[0]); - buffer = SharedBuffer::adoptVector(tableBuffer); - } - return buffer.release(); + SkFontTableTag tag = SkEndianSwap32(table); + const size_t tableSize = m_typeface->getTableSize(tag); + if (tableSize) { + Vector tableBuffer(tableSize); + m_typeface->getTableData(tag, 0, tableSize, &tableBuffer[0]); + buffer = SharedBuffer::adoptVector(tableBuffer); + } + return buffer.release(); } #endif -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/fonts/skia/SimpleFontDataSkia.cpp b/sky/engine/platform/fonts/skia/SimpleFontDataSkia.cpp index cf688c74e4731..54569a10ed28e 100644 --- a/sky/engine/platform/fonts/skia/SimpleFontDataSkia.cpp +++ b/sky/engine/platform/fonts/skia/SimpleFontDataSkia.cpp @@ -50,255 +50,266 @@ static const size_t maxVDMXTableSize = 1024 * 1024; // 1 MB #endif // OS(LINUX) || OS(ANDROID) -void SimpleFontData::platformInit() -{ - if (!m_platformData.size()) { - m_fontMetrics.reset(); - m_avgCharWidth = 0; - m_maxCharWidth = 0; - return; - } +void SimpleFontData::platformInit() { + if (!m_platformData.size()) { + m_fontMetrics.reset(); + m_avgCharWidth = 0; + m_maxCharWidth = 0; + return; + } - SkPaint paint; - SkPaint::FontMetrics metrics; + SkPaint paint; + SkPaint::FontMetrics metrics; - m_platformData.setupPaint(&paint); - paint.getFontMetrics(&metrics); - SkTypeface* face = paint.getTypeface(); - ASSERT(face); + m_platformData.setupPaint(&paint); + paint.getFontMetrics(&metrics); + SkTypeface* face = paint.getTypeface(); + ASSERT(face); - int vdmxAscent = 0, vdmxDescent = 0; - bool isVDMXValid = false; + int vdmxAscent = 0, vdmxDescent = 0; + bool isVDMXValid = false; #if OS(LINUX) || OS(ANDROID) - // Manually digging up VDMX metrics is only applicable when bytecode hinting using FreeType. - // With GDI, the metrics will already have taken this into account (as needed). - // With DirectWrite or CoreText, no bytecode hinting is ever done. - // This code should be pushed into FreeType (hinted font metrics). - static const uint32_t vdmxTag = SkSetFourByteTag('V', 'D', 'M', 'X'); - int pixelSize = m_platformData.size() + 0.5; - if (!paint.isAutohinted() - && (paint.getHinting() == SkPaint::kFull_Hinting - || paint.getHinting() == SkPaint::kNormal_Hinting)) - { - size_t vdmxSize = face->getTableSize(vdmxTag); - if (vdmxSize && vdmxSize < maxVDMXTableSize) { - uint8_t* vdmxTable = (uint8_t*) fastMalloc(vdmxSize); - if (vdmxTable - && face->getTableData(vdmxTag, 0, vdmxSize, vdmxTable) == vdmxSize - && parseVDMX(&vdmxAscent, &vdmxDescent, vdmxTable, vdmxSize, pixelSize)) - isVDMXValid = true; - fastFree(vdmxTable); - } + // Manually digging up VDMX metrics is only applicable when bytecode hinting + // using FreeType. With GDI, the metrics will already have taken this into + // account (as needed). With DirectWrite or CoreText, no bytecode hinting is + // ever done. This code should be pushed into FreeType (hinted font metrics). + static const uint32_t vdmxTag = SkSetFourByteTag('V', 'D', 'M', 'X'); + int pixelSize = m_platformData.size() + 0.5; + if (!paint.isAutohinted() && + (paint.getHinting() == SkPaint::kFull_Hinting || + paint.getHinting() == SkPaint::kNormal_Hinting)) { + size_t vdmxSize = face->getTableSize(vdmxTag); + if (vdmxSize && vdmxSize < maxVDMXTableSize) { + uint8_t* vdmxTable = (uint8_t*)fastMalloc(vdmxSize); + if (vdmxTable && + face->getTableData(vdmxTag, 0, vdmxSize, vdmxTable) == vdmxSize && + parseVDMX(&vdmxAscent, &vdmxDescent, vdmxTable, vdmxSize, pixelSize)) + isVDMXValid = true; + fastFree(vdmxTable); } + } #endif - float ascent; - float descent; - - // Beware those who step here: This code is designed to match Win32 font - // metrics *exactly* (except the adjustment of ascent/descent on Linux/Android). - if (isVDMXValid) { - ascent = vdmxAscent; - descent = -vdmxDescent; - } else { - ascent = SkScalarRoundToInt(-metrics.fAscent); - descent = SkScalarRoundToInt(metrics.fDescent); + float ascent; + float descent; + + // Beware those who step here: This code is designed to match Win32 font + // metrics *exactly* (except the adjustment of ascent/descent on + // Linux/Android). + if (isVDMXValid) { + ascent = vdmxAscent; + descent = -vdmxDescent; + } else { + ascent = SkScalarRoundToInt(-metrics.fAscent); + descent = SkScalarRoundToInt(metrics.fDescent); #if OS(LINUX) || OS(ANDROID) - // When subpixel positioning is enabled, if the descent is rounded down, the descent part - // of the glyph may be truncated when displayed in a 'overflow: hidden' container. - // To avoid that, borrow 1 unit from the ascent when possible. - // FIXME: This can be removed if sub-pixel ascent/descent is supported. - if (platformData().fontRenderStyle().useSubpixelPositioning && descent < SkScalarToFloat(metrics.fDescent) && ascent >= 1) { - ++descent; - --ascent; - } -#endif - } - - m_fontMetrics.setAscent(ascent); - m_fontMetrics.setDescent(descent); - - float xHeight; - if (metrics.fXHeight) { - xHeight = metrics.fXHeight; - m_fontMetrics.setXHeight(xHeight); - } else { - xHeight = ascent * 0.56; // Best guess from Windows font metrics. - m_fontMetrics.setXHeight(xHeight); - m_fontMetrics.setHasXHeight(false); - } - - float lineGap = SkScalarToFloat(metrics.fLeading); - m_fontMetrics.setLineGap(lineGap); - m_fontMetrics.setLineSpacing(lroundf(ascent) + lroundf(descent) + lroundf(lineGap)); - - SkScalar underlineThickness, underlinePosition; - if (metrics.hasUnderlineThickness(&underlineThickness) - && metrics.hasUnderlinePosition(&underlinePosition)) { - m_fontMetrics.setUnderlineThickness(SkScalarToFloat(underlineThickness)); - m_fontMetrics.setUnderlinePosition(SkScalarToFloat(-underlinePosition)); - } - - if (platformData().orientation() == Vertical && !isTextOrientationFallback()) { - static const uint32_t vheaTag = SkSetFourByteTag('v', 'h', 'e', 'a'); - static const uint32_t vorgTag = SkSetFourByteTag('V', 'O', 'R', 'G'); - size_t vheaSize = face->getTableSize(vheaTag); - size_t vorgSize = face->getTableSize(vorgTag); - if ((vheaSize > 0) || (vorgSize > 0)) - m_hasVerticalGlyphs = true; + // When subpixel positioning is enabled, if the descent is rounded down, the + // descent part of the glyph may be truncated when displayed in a 'overflow: + // hidden' container. To avoid that, borrow 1 unit from the ascent when + // possible. + // FIXME: This can be removed if sub-pixel ascent/descent is supported. + if (platformData().fontRenderStyle().useSubpixelPositioning && + descent < SkScalarToFloat(metrics.fDescent) && ascent >= 1) { + ++descent; + --ascent; } - - // In WebKit/WebCore/platform/graphics/SimpleFontData.cpp, m_spaceWidth is - // calculated for us, but we need to calculate m_maxCharWidth and - // m_avgCharWidth in order for text entry widgets to be sized correctly. - // FIXME: This seems incorrect and should probably use fMaxCharWidth as - // the code path above. - SkScalar xRange = metrics.fXMax - metrics.fXMin; - m_maxCharWidth = SkScalarRoundToInt(xRange * SkScalarRoundToInt(m_platformData.size())); - - if (metrics.fAvgCharWidth) - m_avgCharWidth = SkScalarRoundToInt(metrics.fAvgCharWidth); - else { - m_avgCharWidth = xHeight; - - GlyphPage* glyphPageZero = GlyphPageTreeNode::getRootChild(this, 0)->page(); - - if (glyphPageZero) { - static const UChar32 xChar = 'x'; - const Glyph xGlyph = glyphPageZero->glyphForCharacter(xChar); - - if (xGlyph) { - // In widthForGlyph(), xGlyph will be compared with - // m_zeroWidthSpaceGlyph, which isn't initialized yet here. - // Initialize it with zero to make sure widthForGlyph() returns - // the right width. - m_zeroWidthSpaceGlyph = 0; - m_avgCharWidth = widthForGlyph(xGlyph); - } - } +#endif + } + + m_fontMetrics.setAscent(ascent); + m_fontMetrics.setDescent(descent); + + float xHeight; + if (metrics.fXHeight) { + xHeight = metrics.fXHeight; + m_fontMetrics.setXHeight(xHeight); + } else { + xHeight = ascent * 0.56; // Best guess from Windows font metrics. + m_fontMetrics.setXHeight(xHeight); + m_fontMetrics.setHasXHeight(false); + } + + float lineGap = SkScalarToFloat(metrics.fLeading); + m_fontMetrics.setLineGap(lineGap); + m_fontMetrics.setLineSpacing(lroundf(ascent) + lroundf(descent) + + lroundf(lineGap)); + + SkScalar underlineThickness, underlinePosition; + if (metrics.hasUnderlineThickness(&underlineThickness) && + metrics.hasUnderlinePosition(&underlinePosition)) { + m_fontMetrics.setUnderlineThickness(SkScalarToFloat(underlineThickness)); + m_fontMetrics.setUnderlinePosition(SkScalarToFloat(-underlinePosition)); + } + + if (platformData().orientation() == Vertical && + !isTextOrientationFallback()) { + static const uint32_t vheaTag = SkSetFourByteTag('v', 'h', 'e', 'a'); + static const uint32_t vorgTag = SkSetFourByteTag('V', 'O', 'R', 'G'); + size_t vheaSize = face->getTableSize(vheaTag); + size_t vorgSize = face->getTableSize(vorgTag); + if ((vheaSize > 0) || (vorgSize > 0)) + m_hasVerticalGlyphs = true; + } + + // In WebKit/WebCore/platform/graphics/SimpleFontData.cpp, m_spaceWidth is + // calculated for us, but we need to calculate m_maxCharWidth and + // m_avgCharWidth in order for text entry widgets to be sized correctly. + // FIXME: This seems incorrect and should probably use fMaxCharWidth as + // the code path above. + SkScalar xRange = metrics.fXMax - metrics.fXMin; + m_maxCharWidth = + SkScalarRoundToInt(xRange * SkScalarRoundToInt(m_platformData.size())); + + if (metrics.fAvgCharWidth) + m_avgCharWidth = SkScalarRoundToInt(metrics.fAvgCharWidth); + else { + m_avgCharWidth = xHeight; + + GlyphPage* glyphPageZero = GlyphPageTreeNode::getRootChild(this, 0)->page(); + + if (glyphPageZero) { + static const UChar32 xChar = 'x'; + const Glyph xGlyph = glyphPageZero->glyphForCharacter(xChar); + + if (xGlyph) { + // In widthForGlyph(), xGlyph will be compared with + // m_zeroWidthSpaceGlyph, which isn't initialized yet here. + // Initialize it with zero to make sure widthForGlyph() returns + // the right width. + m_zeroWidthSpaceGlyph = 0; + m_avgCharWidth = widthForGlyph(xGlyph); + } } + } - if (int unitsPerEm = face->getUnitsPerEm()) - m_fontMetrics.setUnitsPerEm(unitsPerEm); + if (int unitsPerEm = face->getUnitsPerEm()) + m_fontMetrics.setUnitsPerEm(unitsPerEm); } -void SimpleFontData::platformCharWidthInit() -{ - // charwidths are set in platformInit. +void SimpleFontData::platformCharWidthInit() { + // charwidths are set in platformInit. } -void SimpleFontData::platformDestroy() -{ -} +void SimpleFontData::platformDestroy() {} -PassRefPtr SimpleFontData::platformCreateScaledFontData(const FontDescription& fontDescription, float scaleFactor) const -{ - const float scaledSize = lroundf(fontDescription.computedSize() * scaleFactor); - return SimpleFontData::create(FontPlatformData(m_platformData, scaledSize), isCustomFont() ? CustomFontData::create() : nullptr); +PassRefPtr SimpleFontData::platformCreateScaledFontData( + const FontDescription& fontDescription, + float scaleFactor) const { + const float scaledSize = + lroundf(fontDescription.computedSize() * scaleFactor); + return SimpleFontData::create( + FontPlatformData(m_platformData, scaledSize), + isCustomFont() ? CustomFontData::create() : nullptr); } -void SimpleFontData::determinePitch() -{ - m_treatAsFixedPitch = platformData().isFixedPitch(); +void SimpleFontData::determinePitch() { + m_treatAsFixedPitch = platformData().isFixedPitch(); } -static inline void getSkiaBoundsForGlyph(SkPaint& paint, Glyph glyph, SkRect& bounds) -{ - paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); +static inline void getSkiaBoundsForGlyph(SkPaint& paint, + Glyph glyph, + SkRect& bounds) { + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); - SkPath path; - paint.getTextPath(&glyph, sizeof(glyph), 0, 0, &path); - bounds = path.getBounds(); + SkPath path; + paint.getTextPath(&glyph, sizeof(glyph), 0, 0, &path); + bounds = path.getBounds(); - if (!paint.isSubpixelText()) { - SkIRect ir; - bounds.round(&ir); - bounds.set(ir); - } + if (!paint.isSubpixelText()) { + SkIRect ir; + bounds.round(&ir); + bounds.set(ir); + } } -FloatRect SimpleFontData::platformBoundsForGlyph(Glyph glyph) const -{ - if (!m_platformData.size()) - return FloatRect(); +FloatRect SimpleFontData::platformBoundsForGlyph(Glyph glyph) const { + if (!m_platformData.size()) + return FloatRect(); - SkASSERT(sizeof(glyph) == 2); // compile-time assert + SkASSERT(sizeof(glyph) == 2); // compile-time assert - SkPaint paint; - m_platformData.setupPaint(&paint); + SkPaint paint; + m_platformData.setupPaint(&paint); - SkRect bounds; - getSkiaBoundsForGlyph(paint, glyph, bounds); - return FloatRect(bounds); + SkRect bounds; + getSkiaBoundsForGlyph(paint, glyph, bounds); + return FloatRect(bounds); } -float SimpleFontData::platformWidthForGlyph(Glyph glyph) const -{ - if (!m_platformData.size()) - return 0; +float SimpleFontData::platformWidthForGlyph(Glyph glyph) const { + if (!m_platformData.size()) + return 0; - SkASSERT(sizeof(glyph) == 2); // compile-time assert + SkASSERT(sizeof(glyph) == 2); // compile-time assert - SkPaint paint; + SkPaint paint; - m_platformData.setupPaint(&paint); + m_platformData.setupPaint(&paint); - paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); - SkScalar width = paint.measureText(&glyph, 2); - if (!paint.isSubpixelText()) - width = SkScalarRoundToInt(width); - return SkScalarToFloat(width); + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + SkScalar width = paint.measureText(&glyph, 2); + if (!paint.isSubpixelText()) + width = SkScalarRoundToInt(width); + return SkScalarToFloat(width); } -bool SimpleFontData::canRenderCombiningCharacterSequence(const UChar* characters, size_t length) const -{ - if (!m_combiningCharacterSequenceSupport) - m_combiningCharacterSequenceSupport = adoptPtr(new HashMap); - - WTF::HashMap::AddResult addResult = m_combiningCharacterSequenceSupport->add(String(characters, length), false); - if (!addResult.isNewEntry) - return addResult.storedValue->value; - - UErrorCode error = U_ZERO_ERROR; - Vector normalizedCharacters(length); - int32_t normalizedLength = unorm_normalize(characters, length, UNORM_NFC, UNORM_UNICODE_3_2, &normalizedCharacters[0], length, &error); - // Can't render if we have an error or no composition occurred. - if (U_FAILURE(error) || (static_cast(normalizedLength) == length)) - return false; - - SkPaint paint; - m_platformData.setupPaint(&paint); - paint.setTextEncoding(SkPaint::kUTF16_TextEncoding); - if (paint.textToGlyphs(&normalizedCharacters[0], normalizedLength * 2, 0)) { - addResult.storedValue->value = true; - return true; - } +bool SimpleFontData::canRenderCombiningCharacterSequence( + const UChar* characters, + size_t length) const { + if (!m_combiningCharacterSequenceSupport) + m_combiningCharacterSequenceSupport = adoptPtr(new HashMap); + + WTF::HashMap::AddResult addResult = + m_combiningCharacterSequenceSupport->add(String(characters, length), + false); + if (!addResult.isNewEntry) + return addResult.storedValue->value; + + UErrorCode error = U_ZERO_ERROR; + Vector normalizedCharacters(length); + int32_t normalizedLength = + unorm_normalize(characters, length, UNORM_NFC, UNORM_UNICODE_3_2, + &normalizedCharacters[0], length, &error); + // Can't render if we have an error or no composition occurred. + if (U_FAILURE(error) || (static_cast(normalizedLength) == length)) return false; + + SkPaint paint; + m_platformData.setupPaint(&paint); + paint.setTextEncoding(SkPaint::kUTF16_TextEncoding); + if (paint.textToGlyphs(&normalizedCharacters[0], normalizedLength * 2, 0)) { + addResult.storedValue->value = true; + return true; + } + return false; } -bool SimpleFontData::fillGlyphPage(GlyphPage* pageToFill, unsigned offset, unsigned length, UChar* buffer, unsigned bufferLength) const -{ - if (U16_IS_LEAD(buffer[bufferLength-1])) { - SkDebugf("%s last char is high-surrogate", __FUNCTION__); - return false; - } +bool SimpleFontData::fillGlyphPage(GlyphPage* pageToFill, + unsigned offset, + unsigned length, + UChar* buffer, + unsigned bufferLength) const { + if (U16_IS_LEAD(buffer[bufferLength - 1])) { + SkDebugf("%s last char is high-surrogate", __FUNCTION__); + return false; + } - SkAutoSTMalloc glyphStorage(length); + SkAutoSTMalloc glyphStorage(length); - uint16_t* glyphs = glyphStorage.get(); - SkTypeface* typeface = platformData().typeface(); - typeface->charsToGlyphs(buffer, SkTypeface::kUTF16_Encoding, glyphs, length); + uint16_t* glyphs = glyphStorage.get(); + SkTypeface* typeface = platformData().typeface(); + typeface->charsToGlyphs(buffer, SkTypeface::kUTF16_Encoding, glyphs, length); - bool haveGlyphs = false; - for (unsigned i = 0; i < length; i++) { - if (glyphs[i]) { - pageToFill->setGlyphDataForIndex(offset + i, glyphs[i], this); - haveGlyphs = true; - } + bool haveGlyphs = false; + for (unsigned i = 0; i < length; i++) { + if (glyphs[i]) { + pageToFill->setGlyphDataForIndex(offset + i, glyphs[i], this); + haveGlyphs = true; } + } - return haveGlyphs; + return haveGlyphs; } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/geometry/FloatBox.h b/sky/engine/platform/geometry/FloatBox.h index 8df298fbc00de..59a8a268f591e 100644 --- a/sky/engine/platform/geometry/FloatBox.h +++ b/sky/engine/platform/geometry/FloatBox.h @@ -36,145 +36,127 @@ namespace blink { class FloatBox { -public: - FloatBox() - : m_x(0) - , m_y(0) - , m_z(0) - , m_width(0) - , m_height(0) - , m_depth(0) - { + public: + FloatBox() : m_x(0), m_y(0), m_z(0), m_width(0), m_height(0), m_depth(0) {} + + FloatBox(float x, float y, float z, float width, float height, float depth) + : m_x(x), + m_y(y), + m_z(z), + m_width(width), + m_height(height), + m_depth(depth) {} + + FloatBox(const FloatBox& box) + : m_x(box.x()), + m_y(box.y()), + m_z(box.z()), + m_width(box.width()), + m_height(box.height()), + m_depth(box.depth()) {} + + void setOrigin(const FloatPoint3D& origin) { + m_x = origin.x(); + m_y = origin.y(); + m_z = origin.z(); + } + + void setSize(const FloatPoint3D& origin) { + ASSERT(origin.x() >= 0); + ASSERT(origin.y() >= 0); + ASSERT(origin.z() >= 0); + + m_width = origin.x(); + m_height = origin.y(); + m_depth = origin.z(); + } + + void move(const FloatPoint3D& location) { + m_x += location.x(); + m_y += location.y(); + m_z += location.z(); + } + + void flatten() { + m_z = 0; + m_depth = 0; + } + + void expandTo(const FloatPoint3D& low, const FloatPoint3D& high) { + ASSERT(low.x() <= high.x()); + ASSERT(low.y() <= high.y()); + ASSERT(low.z() <= high.z()); + + float minX = std::min(m_x, low.x()); + float minY = std::min(m_y, low.y()); + float minZ = std::min(m_z, low.z()); + + float maxX = std::max(right(), high.x()); + float maxY = std::max(bottom(), high.y()); + float maxZ = std::max(front(), high.z()); + + m_x = minX; + m_y = minY; + m_z = minZ; + + m_width = maxX - minX; + m_height = maxY - minY; + m_depth = maxZ - minZ; + } + + void expandTo(const FloatPoint3D& point) { expandTo(point, point); } + + void expandTo(const FloatBox& box) { + expandTo(FloatPoint3D(box.x(), box.y(), box.z()), + FloatPoint3D(box.right(), box.bottom(), box.front())); + } + + void unionBounds(const FloatBox& box) { + if (box.isEmpty()) + return; + + if (isEmpty()) { + *this = box; + return; } - FloatBox(float x, float y, float z, float width, float height, float depth) - : m_x(x) - , m_y(y) - , m_z(z) - , m_width(width) - , m_height(height) - , m_depth(depth) - { - } - - FloatBox(const FloatBox& box) - : m_x(box.x()) - , m_y(box.y()) - , m_z(box.z()) - , m_width(box.width()) - , m_height(box.height()) - , m_depth(box.depth()) - { - } - - void setOrigin(const FloatPoint3D& origin) - { - m_x = origin.x(); - m_y = origin.y(); - m_z = origin.z(); - } - - void setSize(const FloatPoint3D& origin) - { - ASSERT(origin.x() >= 0); - ASSERT(origin.y() >= 0); - ASSERT(origin.z() >= 0); - - m_width = origin.x(); - m_height = origin.y(); - m_depth = origin.z(); - } - - void move(const FloatPoint3D& location) - { - m_x += location.x(); - m_y += location.y(); - m_z += location.z(); - } - - void flatten() - { - m_z = 0; - m_depth = 0; - } - - void expandTo(const FloatPoint3D& low, const FloatPoint3D& high) - { - ASSERT(low.x() <= high.x()); - ASSERT(low.y() <= high.y()); - ASSERT(low.z() <= high.z()); - - float minX = std::min(m_x, low.x()); - float minY = std::min(m_y, low.y()); - float minZ = std::min(m_z, low.z()); - - float maxX = std::max(right(), high.x()); - float maxY = std::max(bottom(), high.y()); - float maxZ = std::max(front(), high.z()); - - m_x = minX; - m_y = minY; - m_z = minZ; - - m_width = maxX - minX; - m_height = maxY - minY; - m_depth = maxZ - minZ; - } - - void expandTo(const FloatPoint3D& point) - { - expandTo(point, point); - } - - void expandTo(const FloatBox& box) - { - expandTo(FloatPoint3D(box.x(), box.y(), box.z()), FloatPoint3D(box.right(), box.bottom(), box.front())); - } - - void unionBounds(const FloatBox& box) - { - if (box.isEmpty()) - return; - - if (isEmpty()) { - *this = box; - return; - } - - expandTo(box); - } - - bool isEmpty() const { return (m_width <= 0 && m_height <= 0) || (m_width <= 0 && m_depth <= 0) || (m_height <= 0 && m_depth <= 0); } - - float right() const { return m_x + m_width; } - float bottom() const { return m_y + m_height; } - float front() const { return m_z + m_depth; } - float x() const { return m_x; } - float y() const { return m_y; } - float z() const { return m_z; } - float width() const { return m_width; } - float height() const { return m_height; } - float depth() const { return m_depth; } -private: - float m_x; - float m_y; - float m_z; - float m_width; - float m_height; - float m_depth; + expandTo(box); + } + + bool isEmpty() const { + return (m_width <= 0 && m_height <= 0) || (m_width <= 0 && m_depth <= 0) || + (m_height <= 0 && m_depth <= 0); + } + + float right() const { return m_x + m_width; } + float bottom() const { return m_y + m_height; } + float front() const { return m_z + m_depth; } + float x() const { return m_x; } + float y() const { return m_y; } + float z() const { return m_z; } + float width() const { return m_width; } + float height() const { return m_height; } + float depth() const { return m_depth; } + + private: + float m_x; + float m_y; + float m_z; + float m_width; + float m_height; + float m_depth; }; -inline bool operator==(const FloatBox& a, const FloatBox& b) -{ - return a.x() == b.x() && a.y() == b.y() && a.z() == b.z() && a.width() == b.width() - && a.height() == b.height() && a.depth() == b.depth(); +inline bool operator==(const FloatBox& a, const FloatBox& b) { + return a.x() == b.x() && a.y() == b.y() && a.z() == b.z() && + a.width() == b.width() && a.height() == b.height() && + a.depth() == b.depth(); } -inline bool operator!=(const FloatBox& a, const FloatBox& b) -{ - return !(a == b); +inline bool operator!=(const FloatBox& a, const FloatBox& b) { + return !(a == b); } -} // namespace WebKit +} // namespace blink #endif // SKY_ENGINE_PLATFORM_GEOMETRY_FLOATBOX_H_ diff --git a/sky/engine/platform/geometry/FloatBoxExtent.h b/sky/engine/platform/geometry/FloatBoxExtent.h index b15d451184ce0..f3cb9760dd8a7 100644 --- a/sky/engine/platform/geometry/FloatBoxExtent.h +++ b/sky/engine/platform/geometry/FloatBoxExtent.h @@ -35,91 +35,71 @@ namespace blink { class FloatBoxExtent { -public: - FloatBoxExtent() - : m_top(0) - , m_right(0) - , m_bottom(0) - , m_left(0) - { - } - - FloatBoxExtent(float top, float right, float bottom, float left) - : m_top(top) - , m_right(right) - , m_bottom(bottom) - , m_left(left) - { - } - - float top() const { return m_top; } - void setTop(float top) { m_top = top; } - - float right() const { return m_right; } - void setRight(float right) { m_right = right; } - - float bottom() const { return m_bottom; } - void setBottom(float bottom) { m_bottom = bottom; } - - float left() const { return m_left; } - void setLeft(float left) { m_left = left; } - - bool isZero() const { return !left() && !right() && !top() && !bottom(); } - - void expandRect(FloatRect& rect) const - { - if (isZero()) - return; - - rect.move(-left(), -top()); - rect.expand(left() + right(), top() + bottom()); - } - - void unite(const FloatBoxExtent& other) - { - m_top = std::min(m_top, other.top()); - m_right = std::max(m_right, other.right()); - m_bottom = std::max(m_bottom, other.bottom()); - m_left = std::min(m_left, other.left()); - } - - void unite(const FloatRect& rect) - { - m_top = std::min(m_top, rect.y()); - m_right = std::max(m_right, rect.maxX()); - m_bottom = std::max(m_bottom, rect.maxY()); - m_left = std::min(m_left, rect.x()); - } - -private: - float m_top; - float m_right; - float m_bottom; - float m_left; + public: + FloatBoxExtent() : m_top(0), m_right(0), m_bottom(0), m_left(0) {} + + FloatBoxExtent(float top, float right, float bottom, float left) + : m_top(top), m_right(right), m_bottom(bottom), m_left(left) {} + + float top() const { return m_top; } + void setTop(float top) { m_top = top; } + + float right() const { return m_right; } + void setRight(float right) { m_right = right; } + + float bottom() const { return m_bottom; } + void setBottom(float bottom) { m_bottom = bottom; } + + float left() const { return m_left; } + void setLeft(float left) { m_left = left; } + + bool isZero() const { return !left() && !right() && !top() && !bottom(); } + + void expandRect(FloatRect& rect) const { + if (isZero()) + return; + + rect.move(-left(), -top()); + rect.expand(left() + right(), top() + bottom()); + } + + void unite(const FloatBoxExtent& other) { + m_top = std::min(m_top, other.top()); + m_right = std::max(m_right, other.right()); + m_bottom = std::max(m_bottom, other.bottom()); + m_left = std::min(m_left, other.left()); + } + + void unite(const FloatRect& rect) { + m_top = std::min(m_top, rect.y()); + m_right = std::max(m_right, rect.maxX()); + m_bottom = std::max(m_bottom, rect.maxY()); + m_left = std::min(m_left, rect.x()); + } + + private: + float m_top; + float m_right; + float m_bottom; + float m_left; }; -inline bool operator==(const FloatBoxExtent& a, const FloatBoxExtent& b) -{ - return a.top() == b.top() - && a.right() == b.right() - && a.bottom() == b.bottom() - && a.left() == b.left(); +inline bool operator==(const FloatBoxExtent& a, const FloatBoxExtent& b) { + return a.top() == b.top() && a.right() == b.right() && + a.bottom() == b.bottom() && a.left() == b.left(); } -inline bool operator!=(const FloatBoxExtent& a, const FloatBoxExtent& b) -{ - return !(a == b); +inline bool operator!=(const FloatBoxExtent& a, const FloatBoxExtent& b) { + return !(a == b); } -inline void operator+=(FloatBoxExtent& a, const FloatBoxExtent& b) -{ - a.setTop(a.top() + b.top()); - a.setRight(a.right() + b.right()); - a.setBottom(a.bottom() + b.bottom()); - a.setLeft(a.left() + b.left()); +inline void operator+=(FloatBoxExtent& a, const FloatBoxExtent& b) { + a.setTop(a.top() + b.top()); + a.setRight(a.right() + b.right()); + a.setBottom(a.bottom() + b.bottom()); + a.setLeft(a.left() + b.left()); } -} // namespace blink - +} // namespace blink #endif // SKY_ENGINE_PLATFORM_GEOMETRY_FLOATBOXEXTENT_H_ diff --git a/sky/engine/platform/geometry/FloatBoxTest.cpp b/sky/engine/platform/geometry/FloatBoxTest.cpp index dd4976dd0efa7..45fdf44bac00a 100644 --- a/sky/engine/platform/geometry/FloatBoxTest.cpp +++ b/sky/engine/platform/geometry/FloatBoxTest.cpp @@ -10,16 +10,16 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES,:tabnew INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES,:tabnew INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "flutter/sky/engine/platform/geometry/FloatBox.h" @@ -32,89 +32,91 @@ using namespace blink; namespace { -TEST(FloatBoxTest, SimpleCreationTest) -{ - FloatBox box(1, 2, 3, 4, 5, 6); - EXPECT_EQ(1, box.x()); - EXPECT_EQ(2, box.y()); - EXPECT_EQ(3, box.z()); - EXPECT_EQ(4, box.width()); - EXPECT_EQ(5, box.height()); - EXPECT_EQ(6, box.depth()); - EXPECT_EQ(5, box.right()); - EXPECT_EQ(7, box.bottom()); - EXPECT_EQ(9, box.front()); +TEST(FloatBoxTest, SimpleCreationTest) { + FloatBox box(1, 2, 3, 4, 5, 6); + EXPECT_EQ(1, box.x()); + EXPECT_EQ(2, box.y()); + EXPECT_EQ(3, box.z()); + EXPECT_EQ(4, box.width()); + EXPECT_EQ(5, box.height()); + EXPECT_EQ(6, box.depth()); + EXPECT_EQ(5, box.right()); + EXPECT_EQ(7, box.bottom()); + EXPECT_EQ(9, box.front()); } -TEST(FloatBoxTest, PositionTest) -{ - FloatBox box(0, 0, 0, 4, 4, 4); - box.move(FloatPoint3D(1, 2, 3)); - EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(1, 2, 3, 4, 4, 4), box); - box.setOrigin(FloatPoint3D(-1, -2, -3)); - box.move(FloatPoint3D(-1, -2, -3)); - EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(-2, -4, -6, 4, 4, 4), box); +TEST(FloatBoxTest, PositionTest) { + FloatBox box(0, 0, 0, 4, 4, 4); + box.move(FloatPoint3D(1, 2, 3)); + EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, + FloatBox(1, 2, 3, 4, 4, 4), box); + box.setOrigin(FloatPoint3D(-1, -2, -3)); + box.move(FloatPoint3D(-1, -2, -3)); + EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, + FloatBox(-2, -4, -6, 4, 4, 4), box); } -TEST(FloatBoxTest, CopyTest) -{ - FloatBox box(1, 2, 3, 4, 4, 4); - FloatBox box2(box); - EXPECT_EQ(box, box2); - box.setSize(FloatPoint3D(3, 3, 3)); - EXPECT_NE(box, box2); - EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(1, 2, 3, 3, 3, 3), box); +TEST(FloatBoxTest, CopyTest) { + FloatBox box(1, 2, 3, 4, 4, 4); + FloatBox box2(box); + EXPECT_EQ(box, box2); + box.setSize(FloatPoint3D(3, 3, 3)); + EXPECT_NE(box, box2); + EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, + FloatBox(1, 2, 3, 3, 3, 3), box); } -TEST(FloatBoxTest, FlattenTest) -{ - FloatBox box(1, 2, 3, 4, 4, 4); - box.flatten(); - EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(1, 2, 0, 4, 4, 0), box); +TEST(FloatBoxTest, FlattenTest) { + FloatBox box(1, 2, 3, 4, 4, 4); + box.flatten(); + EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, + FloatBox(1, 2, 0, 4, 4, 0), box); } -TEST(FloatBoxTest, ExpandTests) -{ - FloatBox box; - box.expandTo(FloatPoint3D(10, -3, 2)); - EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(0, -3, 0, 10, 3, 2), box); - - box.expandTo(FloatPoint3D(-15, 6, 8)); - EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(-15, -3, 0, 25, 9, 8), box); - - box = FloatBox(); - box.expandTo(FloatPoint3D(-3, 6, 9), FloatPoint3D(-2, 10, 11)); - EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(-3, 0, 0, 3, 10, 11), box); - - box = FloatBox(); - box.expandTo(FloatBox(-10, -10, -10, 3, 30, 40)); - box.expandTo(FloatBox(-11, 3, 50, 10, 15, 1)); - EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(-11, -10, -10, 11, 30, 61), box); +TEST(FloatBoxTest, ExpandTests) { + FloatBox box; + box.expandTo(FloatPoint3D(10, -3, 2)); + EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, + FloatBox(0, -3, 0, 10, 3, 2), box); + + box.expandTo(FloatPoint3D(-15, 6, 8)); + EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, + FloatBox(-15, -3, 0, 25, 9, 8), box); + + box = FloatBox(); + box.expandTo(FloatPoint3D(-3, 6, 9), FloatPoint3D(-2, 10, 11)); + EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, + FloatBox(-3, 0, 0, 3, 10, 11), box); + + box = FloatBox(); + box.expandTo(FloatBox(-10, -10, -10, 3, 30, 40)); + box.expandTo(FloatBox(-11, 3, 50, 10, 15, 1)); + EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, + FloatBox(-11, -10, -10, 11, 30, 61), box); } -TEST(FloatBoxTest, UnionTest) -{ - FloatBox box; - EXPECT_TRUE(box.isEmpty()); - FloatBox unionedBox(3, 5, 6, 5, 3, 9); - box.unionBounds(unionedBox); - EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, unionedBox, box); +TEST(FloatBoxTest, UnionTest) { + FloatBox box; + EXPECT_TRUE(box.isEmpty()); + FloatBox unionedBox(3, 5, 6, 5, 3, 9); + box.unionBounds(unionedBox); + EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, unionedBox, box); - box.unionBounds(FloatBox()); - EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, unionedBox, box); + box.unionBounds(FloatBox()); + EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, unionedBox, box); - box.unionBounds(FloatBox(0, 0, 0, 1, 1, 1)); - EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(0, 0, 0, 8, 8, 15), box); + box.unionBounds(FloatBox(0, 0, 0, 1, 1, 1)); + EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, + FloatBox(0, 0, 0, 8, 8, 15), box); } -TEST(FloatBoxTest, EmptyBoxTest) -{ - FloatBox box; - EXPECT_TRUE(box.isEmpty()); - box.expandTo(FloatPoint3D(1, 0, 0)); - EXPECT_TRUE(box.isEmpty()); - box.expandTo(FloatPoint3D(0, 1, 0)); - EXPECT_FALSE(box.isEmpty()); +TEST(FloatBoxTest, EmptyBoxTest) { + FloatBox box; + EXPECT_TRUE(box.isEmpty()); + box.expandTo(FloatPoint3D(1, 0, 0)); + EXPECT_TRUE(box.isEmpty()); + box.expandTo(FloatPoint3D(0, 1, 0)); + EXPECT_FALSE(box.isEmpty()); } -} +} // namespace diff --git a/sky/engine/platform/geometry/FloatBoxTestHelpers.cpp b/sky/engine/platform/geometry/FloatBoxTestHelpers.cpp index 45035e4cf844a..a8b5ac8ccb8d0 100644 --- a/sky/engine/platform/geometry/FloatBoxTestHelpers.cpp +++ b/sky/engine/platform/geometry/FloatBoxTestHelpers.cpp @@ -10,16 +10,16 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "flutter/sky/engine/platform/geometry/FloatBoxTestHelpers.h" @@ -27,66 +27,64 @@ #include "flutter/sky/engine/platform/geometry/FloatBox.h" const static float kTestEpsilon = 1e-6; -void blink::PrintTo(const FloatBox& box, ::std::ostream* os) -{ - *os << "FloatBox(" - << box.x() << ", " - << box.y() << ", " - << box.z() << ", " - << box.width() << ", " - << box.height() << ", " - << box.depth() << ")"; +void blink::PrintTo(const FloatBox& box, ::std::ostream* os) { + *os << "FloatBox(" << box.x() << ", " << box.y() << ", " << box.z() << ", " + << box.width() << ", " << box.height() << ", " << box.depth() << ")"; } -bool blink::FloatBoxTest::ApproximatelyEqual(const float& a, const float& b) -{ - float absA = ::fabs(a); - float absB = ::fabs(b); - float absErr = ::fabs(a - b); - if (a == b) - return true; +bool blink::FloatBoxTest::ApproximatelyEqual(const float& a, const float& b) { + float absA = ::fabs(a); + float absB = ::fabs(b); + float absErr = ::fabs(a - b); + if (a == b) + return true; - if (a == 0 || b == 0 || absErr < std::numeric_limits::min()) - return absErr < (kTestEpsilon * std::numeric_limits::min()); + if (a == 0 || b == 0 || absErr < std::numeric_limits::min()) + return absErr < (kTestEpsilon * std::numeric_limits::min()); - return ((absErr / (absA + absB)) < kTestEpsilon); + return ((absErr / (absA + absB)) < kTestEpsilon); } -bool blink::FloatBoxTest::ApproximatelyEqual(const FloatBox& a, const FloatBox& b) -{ - if (!ApproximatelyEqual(a.x(), b.x()) - || !ApproximatelyEqual(a.y(), b.y()) - || !ApproximatelyEqual(a.z(), b.z()) - || !ApproximatelyEqual(a.width(), b.width()) - || !ApproximatelyEqual(a.height(), b.height()) - || !ApproximatelyEqual(a.depth(), b.depth())) { - return false; - } - return true; +bool blink::FloatBoxTest::ApproximatelyEqual(const FloatBox& a, + const FloatBox& b) { + if (!ApproximatelyEqual(a.x(), b.x()) || !ApproximatelyEqual(a.y(), b.y()) || + !ApproximatelyEqual(a.z(), b.z()) || + !ApproximatelyEqual(a.width(), b.width()) || + !ApproximatelyEqual(a.height(), b.height()) || + !ApproximatelyEqual(a.depth(), b.depth())) { + return false; + } + return true; } -::testing::AssertionResult blink::FloatBoxTest::AssertAlmostEqual(const char* m_expr, const char* n_expr, const FloatBox& m, const FloatBox& n) -{ - if (!ApproximatelyEqual(m, n)) { - return ::testing::AssertionFailure() << " Value of:" << n_expr << std::endl - << " Actual:" << testing::PrintToString(n) << std::endl - << "Expected Approx:" << m_expr << std::endl - << " Which is:" << ::testing::PrintToString(m); - } - return ::testing::AssertionSuccess(); +::testing::AssertionResult blink::FloatBoxTest::AssertAlmostEqual( + const char* m_expr, + const char* n_expr, + const FloatBox& m, + const FloatBox& n) { + if (!ApproximatelyEqual(m, n)) { + return ::testing::AssertionFailure() + << " Value of:" << n_expr << std::endl + << " Actual:" << testing::PrintToString(n) << std::endl + << "Expected Approx:" << m_expr << std::endl + << " Which is:" << ::testing::PrintToString(m); + } + return ::testing::AssertionSuccess(); } -::testing::AssertionResult blink::FloatBoxTest::AssertContains(const char* m_expr, const char* n_expr, const FloatBox& m, const FloatBox& n) -{ - FloatBox newM = m; - newM.expandTo(n); - if (!ApproximatelyEqual(m, newM)) { - return ::testing::AssertionFailure() << " Value of:" << n_expr << std::endl - << " Actual:" << testing::PrintToString(n) << std::endl - << "Not Contained in:" << m_expr << std::endl - << " Which is:" << ::testing::PrintToString(m); - } - return ::testing::AssertionSuccess(); +::testing::AssertionResult blink::FloatBoxTest::AssertContains( + const char* m_expr, + const char* n_expr, + const FloatBox& m, + const FloatBox& n) { + FloatBox newM = m; + newM.expandTo(n); + if (!ApproximatelyEqual(m, newM)) { + return ::testing::AssertionFailure() + << " Value of:" << n_expr << std::endl + << " Actual:" << testing::PrintToString(n) << std::endl + << "Not Contained in:" << m_expr << std::endl + << " Which is:" << ::testing::PrintToString(m); + } + return ::testing::AssertionSuccess(); } - - diff --git a/sky/engine/platform/geometry/FloatBoxTestHelpers.h b/sky/engine/platform/geometry/FloatBoxTestHelpers.h index a7644cd126607..8025ab28829ca 100644 --- a/sky/engine/platform/geometry/FloatBoxTestHelpers.h +++ b/sky/engine/platform/geometry/FloatBoxTestHelpers.h @@ -10,16 +10,16 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef SKY_ENGINE_PLATFORM_GEOMETRY_FLOATBOXTESTHELPERS_H_ #define SKY_ENGINE_PLATFORM_GEOMETRY_FLOATBOXTESTHELPERS_H_ @@ -34,9 +34,15 @@ bool ApproximatelyEqual(const float&, const float&); namespace FloatBoxTest { bool ApproximatelyEqual(const float&, const float&); bool ApproximatelyEqual(const FloatBox&, const FloatBox&); -::testing::AssertionResult AssertAlmostEqual(const char*, const char*, const FloatBox&, const FloatBox&); -::testing::AssertionResult AssertContains(const char*, const char*, const FloatBox&, const FloatBox&); -} // namespace FloatBoxTest -} // namespace blink +::testing::AssertionResult AssertAlmostEqual(const char*, + const char*, + const FloatBox&, + const FloatBox&); +::testing::AssertionResult AssertContains(const char*, + const char*, + const FloatBox&, + const FloatBox&); +} // namespace FloatBoxTest +} // namespace blink #endif // SKY_ENGINE_PLATFORM_GEOMETRY_FLOATBOXTESTHELPERS_H_ diff --git a/sky/engine/platform/geometry/FloatPoint.cpp b/sky/engine/platform/geometry/FloatPoint.cpp index 5bdbd2075eb7c..b9d6aa3c22c9d 100644 --- a/sky/engine/platform/geometry/FloatPoint.cpp +++ b/sky/engine/platform/geometry/FloatPoint.cpp @@ -36,92 +36,82 @@ namespace blink { // Skia has problems when passed infinite, etc floats, filter them to 0. -static inline SkScalar WebCoreFloatToSkScalar(float f) -{ - return SkFloatToScalar(std::isfinite(f) ? f : 0); +static inline SkScalar WebCoreFloatToSkScalar(float f) { + return SkFloatToScalar(std::isfinite(f) ? f : 0); } -FloatPoint::FloatPoint(const IntPoint& p) : m_x(p.x()), m_y(p.y()) -{ -} +FloatPoint::FloatPoint(const IntPoint& p) : m_x(p.x()), m_y(p.y()) {} FloatPoint::FloatPoint(const LayoutPoint& p) - : m_x(p.x().toFloat()) - , m_y(p.y().toFloat()) -{ -} + : m_x(p.x().toFloat()), m_y(p.y().toFloat()) {} -void FloatPoint::normalize() -{ - float tempLength = length(); +void FloatPoint::normalize() { + float tempLength = length(); - if (tempLength) { - m_x /= tempLength; - m_y /= tempLength; - } + if (tempLength) { + m_x /= tempLength; + m_y /= tempLength; + } } -float FloatPoint::slopeAngleRadians() const -{ - return atan2f(m_y, m_x); +float FloatPoint::slopeAngleRadians() const { + return atan2f(m_y, m_x); } -float FloatPoint::length() const -{ - return sqrtf(lengthSquared()); +float FloatPoint::length() const { + return sqrtf(lengthSquared()); } -void FloatPoint::move(const LayoutSize& size) -{ - m_x += size.width(); - m_y += size.height(); +void FloatPoint::move(const LayoutSize& size) { + m_x += size.width(); + m_y += size.height(); } -void FloatPoint::moveBy(const LayoutPoint& point) -{ - m_x += point.x(); - m_y += point.y(); +void FloatPoint::moveBy(const LayoutPoint& point) { + m_x += point.x(); + m_y += point.y(); } -SkPoint FloatPoint::data() const -{ - SkPoint p = { WebCoreFloatToSkScalar(m_x), WebCoreFloatToSkScalar(m_y) }; - return p; +SkPoint FloatPoint::data() const { + SkPoint p = {WebCoreFloatToSkScalar(m_x), WebCoreFloatToSkScalar(m_y)}; + return p; } -FloatPoint FloatPoint::narrowPrecision(double x, double y) -{ - return FloatPoint(narrowPrecisionToFloat(x), narrowPrecisionToFloat(y)); +FloatPoint FloatPoint::narrowPrecision(double x, double y) { + return FloatPoint(narrowPrecisionToFloat(x), narrowPrecisionToFloat(y)); } -float findSlope(const FloatPoint& p1, const FloatPoint& p2, float& c) -{ - if (p2.x() == p1.x()) - return std::numeric_limits::infinity(); +float findSlope(const FloatPoint& p1, const FloatPoint& p2, float& c) { + if (p2.x() == p1.x()) + return std::numeric_limits::infinity(); - // y = mx + c - float slope = (p2.y() - p1.y()) / (p2.x() - p1.x()); - c = p1.y() - slope * p1.x(); - return slope; + // y = mx + c + float slope = (p2.y() - p1.y()) / (p2.x() - p1.x()); + c = p1.y() - slope * p1.x(); + return slope; } -bool findIntersection(const FloatPoint& p1, const FloatPoint& p2, const FloatPoint& d1, const FloatPoint& d2, FloatPoint& intersection) -{ - float pxLength = p2.x() - p1.x(); - float pyLength = p2.y() - p1.y(); +bool findIntersection(const FloatPoint& p1, + const FloatPoint& p2, + const FloatPoint& d1, + const FloatPoint& d2, + FloatPoint& intersection) { + float pxLength = p2.x() - p1.x(); + float pyLength = p2.y() - p1.y(); - float dxLength = d2.x() - d1.x(); - float dyLength = d2.y() - d1.y(); + float dxLength = d2.x() - d1.x(); + float dyLength = d2.y() - d1.y(); - float denom = pxLength * dyLength - pyLength * dxLength; - if (!denom) - return false; + float denom = pxLength * dyLength - pyLength * dxLength; + if (!denom) + return false; - float param = ((d1.x() - p1.x()) * dyLength - (d1.y() - p1.y()) * dxLength) / denom; + float param = + ((d1.x() - p1.x()) * dyLength - (d1.y() - p1.y()) * dxLength) / denom; - intersection.setX(p1.x() + param * pxLength); - intersection.setY(p1.y() + param * pyLength); - return true; + intersection.setX(p1.x() + param * pxLength); + intersection.setY(p1.y() + param * pyLength); + return true; } -} +} // namespace blink diff --git a/sky/engine/platform/geometry/FloatPoint.h b/sky/engine/platform/geometry/FloatPoint.h index 1cff3e1ba0597..0484a015d3edc 100644 --- a/sky/engine/platform/geometry/FloatPoint.h +++ b/sky/engine/platform/geometry/FloatPoint.h @@ -42,190 +42,162 @@ class LayoutPoint; class LayoutSize; class PLATFORM_EXPORT FloatPoint { -public: - FloatPoint() : m_x(0), m_y(0) { } - FloatPoint(float x, float y) : m_x(x), m_y(y) { } - FloatPoint(const IntPoint&); - FloatPoint(const LayoutPoint&); - explicit FloatPoint(const FloatSize& size) : m_x(size.width()), m_y(size.height()) { } - - static FloatPoint zero() { return FloatPoint(); } - - static FloatPoint narrowPrecision(double x, double y); - - float x() const { return m_x; } - float y() const { return m_y; } - - void setX(float x) { m_x = x; } - void setY(float y) { m_y = y; } - void set(float x, float y) - { - m_x = x; - m_y = y; - } - void move(float dx, float dy) - { - m_x += dx; - m_y += dy; - } - void move(const IntSize& a) - { - m_x += a.width(); - m_y += a.height(); - } - void move(const LayoutSize&); - void move(const FloatSize& a) - { - m_x += a.width(); - m_y += a.height(); - } - void moveBy(const IntPoint& a) - { - m_x += a.x(); - m_y += a.y(); - } - void moveBy(const LayoutPoint&); - void moveBy(const FloatPoint& a) - { - m_x += a.x(); - m_y += a.y(); - } - void scale(float sx, float sy) - { - m_x *= sx; - m_y *= sy; - } - - void normalize(); - - float dot(const FloatPoint& a) const - { - return m_x * a.x() + m_y * a.y(); - } - - float slopeAngleRadians() const; - float length() const; - float lengthSquared() const - { - return m_x * m_x + m_y * m_y; - } - - FloatPoint expandedTo(const FloatPoint& other) const - { - return FloatPoint(std::max(m_x, other.m_x), std::max(m_y, other.m_y)); - } - - FloatPoint shrunkTo(const FloatPoint& other) const - { - return FloatPoint(std::min(m_x, other.m_x), std::min(m_y, other.m_y)); - } - - FloatPoint transposedPoint() const - { - return FloatPoint(m_y, m_x); - } - - FloatPoint scaledBy(float scale) const - { - return FloatPoint(m_x * scale, m_y * scale); - } - - SkPoint data() const; - -private: - float m_x, m_y; + public: + FloatPoint() : m_x(0), m_y(0) {} + FloatPoint(float x, float y) : m_x(x), m_y(y) {} + FloatPoint(const IntPoint&); + FloatPoint(const LayoutPoint&); + explicit FloatPoint(const FloatSize& size) + : m_x(size.width()), m_y(size.height()) {} + + static FloatPoint zero() { return FloatPoint(); } + + static FloatPoint narrowPrecision(double x, double y); + + float x() const { return m_x; } + float y() const { return m_y; } + + void setX(float x) { m_x = x; } + void setY(float y) { m_y = y; } + void set(float x, float y) { + m_x = x; + m_y = y; + } + void move(float dx, float dy) { + m_x += dx; + m_y += dy; + } + void move(const IntSize& a) { + m_x += a.width(); + m_y += a.height(); + } + void move(const LayoutSize&); + void move(const FloatSize& a) { + m_x += a.width(); + m_y += a.height(); + } + void moveBy(const IntPoint& a) { + m_x += a.x(); + m_y += a.y(); + } + void moveBy(const LayoutPoint&); + void moveBy(const FloatPoint& a) { + m_x += a.x(); + m_y += a.y(); + } + void scale(float sx, float sy) { + m_x *= sx; + m_y *= sy; + } + + void normalize(); + + float dot(const FloatPoint& a) const { return m_x * a.x() + m_y * a.y(); } + + float slopeAngleRadians() const; + float length() const; + float lengthSquared() const { return m_x * m_x + m_y * m_y; } + + FloatPoint expandedTo(const FloatPoint& other) const { + return FloatPoint(std::max(m_x, other.m_x), std::max(m_y, other.m_y)); + } + + FloatPoint shrunkTo(const FloatPoint& other) const { + return FloatPoint(std::min(m_x, other.m_x), std::min(m_y, other.m_y)); + } + + FloatPoint transposedPoint() const { return FloatPoint(m_y, m_x); } + + FloatPoint scaledBy(float scale) const { + return FloatPoint(m_x * scale, m_y * scale); + } + + SkPoint data() const; + + private: + float m_x, m_y; }; - -inline FloatPoint& operator+=(FloatPoint& a, const FloatSize& b) -{ - a.move(b.width(), b.height()); - return a; +inline FloatPoint& operator+=(FloatPoint& a, const FloatSize& b) { + a.move(b.width(), b.height()); + return a; } -inline FloatPoint& operator+=(FloatPoint& a, const FloatPoint& b) -{ - a.move(b.x(), b.y()); - return a; +inline FloatPoint& operator+=(FloatPoint& a, const FloatPoint& b) { + a.move(b.x(), b.y()); + return a; } -inline FloatPoint& operator-=(FloatPoint& a, const FloatSize& b) -{ - a.move(-b.width(), -b.height()); - return a; +inline FloatPoint& operator-=(FloatPoint& a, const FloatSize& b) { + a.move(-b.width(), -b.height()); + return a; } -inline FloatPoint operator+(const FloatPoint& a, const FloatSize& b) -{ - return FloatPoint(a.x() + b.width(), a.y() + b.height()); +inline FloatPoint operator+(const FloatPoint& a, const FloatSize& b) { + return FloatPoint(a.x() + b.width(), a.y() + b.height()); } -inline FloatPoint operator+(const FloatPoint& a, const FloatPoint& b) -{ - return FloatPoint(a.x() + b.x(), a.y() + b.y()); +inline FloatPoint operator+(const FloatPoint& a, const FloatPoint& b) { + return FloatPoint(a.x() + b.x(), a.y() + b.y()); } -inline FloatSize operator-(const FloatPoint& a, const FloatPoint& b) -{ - return FloatSize(a.x() - b.x(), a.y() - b.y()); +inline FloatSize operator-(const FloatPoint& a, const FloatPoint& b) { + return FloatSize(a.x() - b.x(), a.y() - b.y()); } -inline FloatPoint operator-(const FloatPoint& a, const FloatSize& b) -{ - return FloatPoint(a.x() - b.width(), a.y() - b.height()); +inline FloatPoint operator-(const FloatPoint& a, const FloatSize& b) { + return FloatPoint(a.x() - b.width(), a.y() - b.height()); } -inline FloatPoint operator-(const FloatPoint& a) -{ - return FloatPoint(-a.x(), -a.y()); +inline FloatPoint operator-(const FloatPoint& a) { + return FloatPoint(-a.x(), -a.y()); } -inline bool operator==(const FloatPoint& a, const FloatPoint& b) -{ - return a.x() == b.x() && a.y() == b.y(); +inline bool operator==(const FloatPoint& a, const FloatPoint& b) { + return a.x() == b.x() && a.y() == b.y(); } -inline bool operator!=(const FloatPoint& a, const FloatPoint& b) -{ - return a.x() != b.x() || a.y() != b.y(); +inline bool operator!=(const FloatPoint& a, const FloatPoint& b) { + return a.x() != b.x() || a.y() != b.y(); } -inline float operator*(const FloatPoint& a, const FloatPoint& b) -{ - // dot product - return a.dot(b); +inline float operator*(const FloatPoint& a, const FloatPoint& b) { + // dot product + return a.dot(b); } -inline IntPoint roundedIntPoint(const FloatPoint& p) -{ - return IntPoint(clampToInteger(roundf(p.x())), clampToInteger(roundf(p.y()))); +inline IntPoint roundedIntPoint(const FloatPoint& p) { + return IntPoint(clampToInteger(roundf(p.x())), clampToInteger(roundf(p.y()))); } -inline IntPoint flooredIntPoint(const FloatPoint& p) -{ - return IntPoint(clampToInteger(floorf(p.x())), clampToInteger(floorf(p.y()))); +inline IntPoint flooredIntPoint(const FloatPoint& p) { + return IntPoint(clampToInteger(floorf(p.x())), clampToInteger(floorf(p.y()))); } -inline IntPoint ceiledIntPoint(const FloatPoint& p) -{ - return IntPoint(clampToInteger(ceilf(p.x())), clampToInteger(ceilf(p.y()))); +inline IntPoint ceiledIntPoint(const FloatPoint& p) { + return IntPoint(clampToInteger(ceilf(p.x())), clampToInteger(ceilf(p.y()))); } -inline IntSize flooredIntSize(const FloatPoint& p) -{ - return IntSize(clampToInteger(floorf(p.x())), clampToInteger(floorf(p.y()))); +inline IntSize flooredIntSize(const FloatPoint& p) { + return IntSize(clampToInteger(floorf(p.x())), clampToInteger(floorf(p.y()))); } -inline FloatSize toFloatSize(const FloatPoint& a) -{ - return FloatSize(a.x(), a.y()); +inline FloatSize toFloatSize(const FloatPoint& a) { + return FloatSize(a.x(), a.y()); } -PLATFORM_EXPORT float findSlope(const FloatPoint& p1, const FloatPoint& p2, float& c); +PLATFORM_EXPORT float findSlope(const FloatPoint& p1, + const FloatPoint& p2, + float& c); -// Find point where lines through the two pairs of points intersect. Returns false if the lines don't intersect. -PLATFORM_EXPORT bool findIntersection(const FloatPoint& p1, const FloatPoint& p2, const FloatPoint& d1, const FloatPoint& d2, FloatPoint& intersection); +// Find point where lines through the two pairs of points intersect. Returns +// false if the lines don't intersect. +PLATFORM_EXPORT bool findIntersection(const FloatPoint& p1, + const FloatPoint& p2, + const FloatPoint& d1, + const FloatPoint& d2, + FloatPoint& intersection); -} +} // namespace blink #endif // SKY_ENGINE_PLATFORM_GEOMETRY_FLOATPOINT_H_ diff --git a/sky/engine/platform/geometry/FloatPoint3D.cpp b/sky/engine/platform/geometry/FloatPoint3D.cpp index 2b5afc6481b53..b36f282389cbc 100644 --- a/sky/engine/platform/geometry/FloatPoint3D.cpp +++ b/sky/engine/platform/geometry/FloatPoint3D.cpp @@ -19,23 +19,20 @@ Boston, MA 02110-1301, USA. */ - #include "flutter/sky/engine/platform/geometry/FloatPoint3D.h" #include namespace blink { -void FloatPoint3D::normalize() -{ - float tempLength = length(); +void FloatPoint3D::normalize() { + float tempLength = length(); - if (tempLength) { - m_x /= tempLength; - m_y /= tempLength; - m_z /= tempLength; - } + if (tempLength) { + m_x /= tempLength; + m_y /= tempLength; + m_z /= tempLength; + } } -} // namespace blink - +} // namespace blink diff --git a/sky/engine/platform/geometry/FloatPoint3D.h b/sky/engine/platform/geometry/FloatPoint3D.h index 7e5bb36dd022f..be82a1edbbd03 100644 --- a/sky/engine/platform/geometry/FloatPoint3D.h +++ b/sky/engine/platform/geometry/FloatPoint3D.h @@ -28,160 +28,121 @@ namespace blink { class PLATFORM_EXPORT FloatPoint3D { -public: - FloatPoint3D() - : m_x(0) - , m_y(0) - , m_z(0) - { - } - - FloatPoint3D(float x, float y, float z) - : m_x(x) - , m_y(y) - , m_z(z) - { - } - - FloatPoint3D(const FloatPoint& p) - : m_x(p.x()) - , m_y(p.y()) - , m_z(0) - { - } - - FloatPoint3D(const FloatPoint3D& p) - : m_x(p.x()) - , m_y(p.y()) - , m_z(p.z()) - { - } - - float x() const { return m_x; } - void setX(float x) { m_x = x; } - - float y() const { return m_y; } - void setY(float y) { m_y = y; } - - float z() const { return m_z; } - void setZ(float z) { m_z = z; } - void set(float x, float y, float z) - { - m_x = x; - m_y = y; - m_z = z; - } - void move(float dx, float dy, float dz) - { - m_x += dx; - m_y += dy; - m_z += dz; - } - void scale(float sx, float sy, float sz) - { - m_x *= sx; - m_y *= sy; - m_z *= sz; - } - - bool isZero() const - { - return !m_x && !m_y && !m_z; - } - - void normalize(); - - float dot(const FloatPoint3D& a) const - { - return m_x * a.x() + m_y * a.y() + m_z * a.z(); - } - - // Sets this FloatPoint3D to the cross product of the passed two. - // It is safe for "this" to be the same as either or both of the - // arguments. - void cross(const FloatPoint3D& a, const FloatPoint3D& b) - { - float x = a.y() * b.z() - a.z() * b.y(); - float y = a.z() * b.x() - a.x() * b.z(); - float z = a.x() * b.y() - a.y() * b.x(); - m_x = x; - m_y = y; - m_z = z; - } - - // Convenience function returning "this cross point" as a - // stack-allocated result. - FloatPoint3D cross(const FloatPoint3D& point) const - { - FloatPoint3D result; - result.cross(*this, point); - return result; - } - - float lengthSquared() const { return this->dot(*this); } - float length() const { return sqrtf(lengthSquared()); } - - float distanceTo(const FloatPoint3D& a) const; - -private: - float m_x; - float m_y; - float m_z; + public: + FloatPoint3D() : m_x(0), m_y(0), m_z(0) {} + + FloatPoint3D(float x, float y, float z) : m_x(x), m_y(y), m_z(z) {} + + FloatPoint3D(const FloatPoint& p) : m_x(p.x()), m_y(p.y()), m_z(0) {} + + FloatPoint3D(const FloatPoint3D& p) : m_x(p.x()), m_y(p.y()), m_z(p.z()) {} + + float x() const { return m_x; } + void setX(float x) { m_x = x; } + + float y() const { return m_y; } + void setY(float y) { m_y = y; } + + float z() const { return m_z; } + void setZ(float z) { m_z = z; } + void set(float x, float y, float z) { + m_x = x; + m_y = y; + m_z = z; + } + void move(float dx, float dy, float dz) { + m_x += dx; + m_y += dy; + m_z += dz; + } + void scale(float sx, float sy, float sz) { + m_x *= sx; + m_y *= sy; + m_z *= sz; + } + + bool isZero() const { return !m_x && !m_y && !m_z; } + + void normalize(); + + float dot(const FloatPoint3D& a) const { + return m_x * a.x() + m_y * a.y() + m_z * a.z(); + } + + // Sets this FloatPoint3D to the cross product of the passed two. + // It is safe for "this" to be the same as either or both of the + // arguments. + void cross(const FloatPoint3D& a, const FloatPoint3D& b) { + float x = a.y() * b.z() - a.z() * b.y(); + float y = a.z() * b.x() - a.x() * b.z(); + float z = a.x() * b.y() - a.y() * b.x(); + m_x = x; + m_y = y; + m_z = z; + } + + // Convenience function returning "this cross point" as a + // stack-allocated result. + FloatPoint3D cross(const FloatPoint3D& point) const { + FloatPoint3D result; + result.cross(*this, point); + return result; + } + + float lengthSquared() const { return this->dot(*this); } + float length() const { return sqrtf(lengthSquared()); } + + float distanceTo(const FloatPoint3D& a) const; + + private: + float m_x; + float m_y; + float m_z; }; -inline FloatPoint3D& operator +=(FloatPoint3D& a, const FloatPoint3D& b) -{ - a.move(b.x(), b.y(), b.z()); - return a; +inline FloatPoint3D& operator+=(FloatPoint3D& a, const FloatPoint3D& b) { + a.move(b.x(), b.y(), b.z()); + return a; } -inline FloatPoint3D& operator -=(FloatPoint3D& a, const FloatPoint3D& b) -{ - a.move(-b.x(), -b.y(), -b.z()); - return a; +inline FloatPoint3D& operator-=(FloatPoint3D& a, const FloatPoint3D& b) { + a.move(-b.x(), -b.y(), -b.z()); + return a; } -inline FloatPoint3D operator+(const FloatPoint3D& a, const FloatPoint3D& b) -{ - return FloatPoint3D(a.x() + b.x(), a.y() + b.y(), a.z() + b.z()); +inline FloatPoint3D operator+(const FloatPoint3D& a, const FloatPoint3D& b) { + return FloatPoint3D(a.x() + b.x(), a.y() + b.y(), a.z() + b.z()); } -inline FloatPoint3D operator-(const FloatPoint3D& a, const FloatPoint3D& b) -{ - return FloatPoint3D(a.x() - b.x(), a.y() - b.y(), a.z() - b.z()); +inline FloatPoint3D operator-(const FloatPoint3D& a, const FloatPoint3D& b) { + return FloatPoint3D(a.x() - b.x(), a.y() - b.y(), a.z() - b.z()); } -inline bool operator==(const FloatPoint3D& a, const FloatPoint3D& b) -{ - return a.x() == b.x() && a.y() == b.y() && a.z() == b.z(); +inline bool operator==(const FloatPoint3D& a, const FloatPoint3D& b) { + return a.x() == b.x() && a.y() == b.y() && a.z() == b.z(); } -inline bool operator!=(const FloatPoint3D& a, const FloatPoint3D& b) -{ - return a.x() != b.x() || a.y() != b.y() || a.z() != b.z(); +inline bool operator!=(const FloatPoint3D& a, const FloatPoint3D& b) { + return a.x() != b.x() || a.y() != b.y() || a.z() != b.z(); } -inline float operator*(const FloatPoint3D& a, const FloatPoint3D& b) -{ - // dot product - return a.dot(b); +inline float operator*(const FloatPoint3D& a, const FloatPoint3D& b) { + // dot product + return a.dot(b); } -inline FloatPoint3D operator*(float k, const FloatPoint3D& v) -{ - return FloatPoint3D(k * v.x(), k * v.y(), k * v.z()); +inline FloatPoint3D operator*(float k, const FloatPoint3D& v) { + return FloatPoint3D(k * v.x(), k * v.y(), k * v.z()); } -inline FloatPoint3D operator*(const FloatPoint3D& v, float k) -{ - return FloatPoint3D(k * v.x(), k * v.y(), k * v.z()); +inline FloatPoint3D operator*(const FloatPoint3D& v, float k) { + return FloatPoint3D(k * v.x(), k * v.y(), k * v.z()); } -inline float FloatPoint3D::distanceTo(const FloatPoint3D& a) const -{ - return (*this - a).length(); +inline float FloatPoint3D::distanceTo(const FloatPoint3D& a) const { + return (*this - a).length(); } -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_GEOMETRY_FLOATPOINT3D_H_ diff --git a/sky/engine/platform/geometry/FloatQuad.cpp b/sky/engine/platform/geometry/FloatQuad.cpp index 1ac6288c53666..6ba913eeaccfa 100644 --- a/sky/engine/platform/geometry/FloatQuad.cpp +++ b/sky/engine/platform/geometry/FloatQuad.cpp @@ -35,199 +35,205 @@ namespace blink { -static inline float min4(float a, float b, float c, float d) -{ - return std::min(std::min(a, b), std::min(c, d)); +static inline float min4(float a, float b, float c, float d) { + return std::min(std::min(a, b), std::min(c, d)); } -static inline float max4(float a, float b, float c, float d) -{ - return std::max(std::max(a, b), std::max(c, d)); +static inline float max4(float a, float b, float c, float d) { + return std::max(std::max(a, b), std::max(c, d)); } -inline float dot(const FloatSize& a, const FloatSize& b) -{ - return a.width() * b.width() + a.height() * b.height(); +inline float dot(const FloatSize& a, const FloatSize& b) { + return a.width() * b.width() + a.height() * b.height(); } -inline float determinant(const FloatSize& a, const FloatSize& b) -{ - return a.width() * b.height() - a.height() * b.width(); +inline float determinant(const FloatSize& a, const FloatSize& b) { + return a.width() * b.height() - a.height() * b.width(); } -inline bool isPointInTriangle(const FloatPoint& p, const FloatPoint& t1, const FloatPoint& t2, const FloatPoint& t3) -{ - // Compute vectors - FloatSize v0 = t3 - t1; - FloatSize v1 = t2 - t1; - FloatSize v2 = p - t1; +inline bool isPointInTriangle(const FloatPoint& p, + const FloatPoint& t1, + const FloatPoint& t2, + const FloatPoint& t3) { + // Compute vectors + FloatSize v0 = t3 - t1; + FloatSize v1 = t2 - t1; + FloatSize v2 = p - t1; - // Compute dot products - float dot00 = dot(v0, v0); - float dot01 = dot(v0, v1); - float dot02 = dot(v0, v2); - float dot11 = dot(v1, v1); - float dot12 = dot(v1, v2); + // Compute dot products + float dot00 = dot(v0, v0); + float dot01 = dot(v0, v1); + float dot02 = dot(v0, v2); + float dot11 = dot(v1, v1); + float dot12 = dot(v1, v2); - // Compute barycentric coordinates - float invDenom = 1.0f / (dot00 * dot11 - dot01 * dot01); - float u = (dot11 * dot02 - dot01 * dot12) * invDenom; - float v = (dot00 * dot12 - dot01 * dot02) * invDenom; + // Compute barycentric coordinates + float invDenom = 1.0f / (dot00 * dot11 - dot01 * dot01); + float u = (dot11 * dot02 - dot01 * dot12) * invDenom; + float v = (dot00 * dot12 - dot01 * dot02) * invDenom; - // Check if point is in triangle - return (u >= 0) && (v >= 0) && (u + v <= 1); + // Check if point is in triangle + return (u >= 0) && (v >= 0) && (u + v <= 1); } -FloatRect FloatQuad::boundingBox() const -{ - float left = min4(m_p1.x(), m_p2.x(), m_p3.x(), m_p4.x()); - float top = min4(m_p1.y(), m_p2.y(), m_p3.y(), m_p4.y()); +FloatRect FloatQuad::boundingBox() const { + float left = min4(m_p1.x(), m_p2.x(), m_p3.x(), m_p4.x()); + float top = min4(m_p1.y(), m_p2.y(), m_p3.y(), m_p4.y()); - float right = max4(m_p1.x(), m_p2.x(), m_p3.x(), m_p4.x()); - float bottom = max4(m_p1.y(), m_p2.y(), m_p3.y(), m_p4.y()); + float right = max4(m_p1.x(), m_p2.x(), m_p3.x(), m_p4.x()); + float bottom = max4(m_p1.y(), m_p2.y(), m_p3.y(), m_p4.y()); - return FloatRect(left, top, right - left, bottom - top); + return FloatRect(left, top, right - left, bottom - top); } -static inline bool withinEpsilon(float a, float b) -{ - return fabs(a - b) < std::numeric_limits::epsilon(); +static inline bool withinEpsilon(float a, float b) { + return fabs(a - b) < std::numeric_limits::epsilon(); } -bool FloatQuad::isRectilinear() const -{ - return (withinEpsilon(m_p1.x(), m_p2.x()) && withinEpsilon(m_p2.y(), m_p3.y()) && withinEpsilon(m_p3.x(), m_p4.x()) && withinEpsilon(m_p4.y(), m_p1.y())) - || (withinEpsilon(m_p1.y(), m_p2.y()) && withinEpsilon(m_p2.x(), m_p3.x()) && withinEpsilon(m_p3.y(), m_p4.y()) && withinEpsilon(m_p4.x(), m_p1.x())); +bool FloatQuad::isRectilinear() const { + return (withinEpsilon(m_p1.x(), m_p2.x()) && + withinEpsilon(m_p2.y(), m_p3.y()) && + withinEpsilon(m_p3.x(), m_p4.x()) && + withinEpsilon(m_p4.y(), m_p1.y())) || + (withinEpsilon(m_p1.y(), m_p2.y()) && + withinEpsilon(m_p2.x(), m_p3.x()) && + withinEpsilon(m_p3.y(), m_p4.y()) && + withinEpsilon(m_p4.x(), m_p1.x())); } -bool FloatQuad::containsPoint(const FloatPoint& p) const -{ - return isPointInTriangle(p, m_p1, m_p2, m_p3) || isPointInTriangle(p, m_p1, m_p3, m_p4); +bool FloatQuad::containsPoint(const FloatPoint& p) const { + return isPointInTriangle(p, m_p1, m_p2, m_p3) || + isPointInTriangle(p, m_p1, m_p3, m_p4); } // Note that we only handle convex quads here. -bool FloatQuad::containsQuad(const FloatQuad& other) const -{ - return containsPoint(other.p1()) && containsPoint(other.p2()) && containsPoint(other.p3()) && containsPoint(other.p4()); -} - -static inline FloatPoint rightMostCornerToVector(const FloatRect& rect, const FloatSize& vector) -{ - // Return the corner of the rectangle that if it is to the left of the vector - // would mean all of the rectangle is to the left of the vector. - // The vector here represents the side between two points in a clockwise convex polygon. - // - // Q XXX - // QQQ XXX If the lower left corner of X is left of the vector that goes from the top corner of Q to - // QQQ the right corner of Q, then all of X is left of the vector, and intersection impossible. - // Q - // - FloatPoint point; - if (vector.width() >= 0) - point.setY(rect.maxY()); - else - point.setY(rect.y()); - if (vector.height() >= 0) - point.setX(rect.x()); - else - point.setX(rect.maxX()); - return point; -} - -bool FloatQuad::intersectsRect(const FloatRect& rect) const -{ - // For each side of the quad clockwise we check if the rectangle is to the left of it - // since only content on the right can onlap with the quad. - // This only works if the quad is convex. - FloatSize v1, v2, v3, v4; - - // Ensure we use clockwise vectors. - if (!isCounterclockwise()) { - v1 = m_p2 - m_p1; - v2 = m_p3 - m_p2; - v3 = m_p4 - m_p3; - v4 = m_p1 - m_p4; - } else { - v1 = m_p4 - m_p1; - v2 = m_p1 - m_p2; - v3 = m_p2 - m_p3; - v4 = m_p3 - m_p4; - } - - FloatPoint p = rightMostCornerToVector(rect, v1); - if (determinant(v1, p - m_p1) < 0) - return false; - - p = rightMostCornerToVector(rect, v2); - if (determinant(v2, p - m_p2) < 0) - return false; - - p = rightMostCornerToVector(rect, v3); - if (determinant(v3, p - m_p3) < 0) - return false; - - p = rightMostCornerToVector(rect, v4); - if (determinant(v4, p - m_p4) < 0) - return false; - - // If not all of the rectangle is outside one of the quad's four sides, then that means at least - // a part of the rectangle is overlapping the quad. - return true; +bool FloatQuad::containsQuad(const FloatQuad& other) const { + return containsPoint(other.p1()) && containsPoint(other.p2()) && + containsPoint(other.p3()) && containsPoint(other.p4()); +} + +static inline FloatPoint rightMostCornerToVector(const FloatRect& rect, + const FloatSize& vector) { + // Return the corner of the rectangle that if it is to the left of the vector + // would mean all of the rectangle is to the left of the vector. + // The vector here represents the side between two points in a clockwise + // convex polygon. + // + // Q XXX + // QQQ XXX If the lower left corner of X is left of the vector that goes + // from the top corner of Q to + // QQQ the right corner of Q, then all of X is left of the vector, and + // intersection impossible. + // Q + // + FloatPoint point; + if (vector.width() >= 0) + point.setY(rect.maxY()); + else + point.setY(rect.y()); + if (vector.height() >= 0) + point.setX(rect.x()); + else + point.setX(rect.maxX()); + return point; +} + +bool FloatQuad::intersectsRect(const FloatRect& rect) const { + // For each side of the quad clockwise we check if the rectangle is to the + // left of it since only content on the right can onlap with the quad. This + // only works if the quad is convex. + FloatSize v1, v2, v3, v4; + + // Ensure we use clockwise vectors. + if (!isCounterclockwise()) { + v1 = m_p2 - m_p1; + v2 = m_p3 - m_p2; + v3 = m_p4 - m_p3; + v4 = m_p1 - m_p4; + } else { + v1 = m_p4 - m_p1; + v2 = m_p1 - m_p2; + v3 = m_p2 - m_p3; + v4 = m_p3 - m_p4; + } + + FloatPoint p = rightMostCornerToVector(rect, v1); + if (determinant(v1, p - m_p1) < 0) + return false; + + p = rightMostCornerToVector(rect, v2); + if (determinant(v2, p - m_p2) < 0) + return false; + + p = rightMostCornerToVector(rect, v3); + if (determinant(v3, p - m_p3) < 0) + return false; + + p = rightMostCornerToVector(rect, v4); + if (determinant(v4, p - m_p4) < 0) + return false; + + // If not all of the rectangle is outside one of the quad's four sides, then + // that means at least a part of the rectangle is overlapping the quad. + return true; } // Tests whether the line is contained by or intersected with the circle. -static inline bool lineIntersectsCircle(const FloatPoint& center, float radius, const FloatPoint& p0, const FloatPoint& p1) -{ - float x0 = p0.x() - center.x(), y0 = p0.y() - center.y(); - float x1 = p1.x() - center.x(), y1 = p1.y() - center.y(); - float radius2 = radius * radius; - if ((x0 * x0 + y0 * y0) <= radius2 || (x1 * x1 + y1 * y1) <= radius2) - return true; - if (p0 == p1) - return false; - - float a = y0 - y1; - float b = x1 - x0; - float c = x0 * y1 - x1 * y0; - float distance2 = c * c / (a * a + b * b); - // If distance between the center point and the line > the radius, - // the line doesn't cross (or is contained by) the ellipse. - if (distance2 > radius2) - return false; - - // The nearest point on the line is between p0 and p1? - float x = - a * c / (a * a + b * b); - float y = - b * c / (a * a + b * b); - return (((x0 <= x && x <= x1) || (x0 >= x && x >= x1)) - && ((y0 <= y && y <= y1) || (y1 <= y && y <= y0))); -} - -bool FloatQuad::intersectsCircle(const FloatPoint& center, float radius) const -{ - return containsPoint(center) // The circle may be totally contained by the quad. - || lineIntersectsCircle(center, radius, m_p1, m_p2) - || lineIntersectsCircle(center, radius, m_p2, m_p3) - || lineIntersectsCircle(center, radius, m_p3, m_p4) - || lineIntersectsCircle(center, radius, m_p4, m_p1); -} - -bool FloatQuad::intersectsEllipse(const FloatPoint& center, const FloatSize& radii) const -{ - // Transform the ellipse to an origin-centered circle whose radius is the product of major radius and minor radius. - // Here we apply the same transformation to the quad. - FloatQuad transformedQuad(*this); - transformedQuad.move(-center.x(), -center.y()); - transformedQuad.scale(radii.height(), radii.width()); - - FloatPoint originPoint; - return transformedQuad.intersectsCircle(originPoint, radii.height() * radii.width()); - -} - -bool FloatQuad::isCounterclockwise() const -{ - // Return if the two first vectors are turning clockwise. If the quad is convex then all following vectors will turn the same way. - return determinant(m_p2 - m_p1, m_p3 - m_p2) < 0; -} - -} // namespace blink +static inline bool lineIntersectsCircle(const FloatPoint& center, + float radius, + const FloatPoint& p0, + const FloatPoint& p1) { + float x0 = p0.x() - center.x(), y0 = p0.y() - center.y(); + float x1 = p1.x() - center.x(), y1 = p1.y() - center.y(); + float radius2 = radius * radius; + if ((x0 * x0 + y0 * y0) <= radius2 || (x1 * x1 + y1 * y1) <= radius2) + return true; + if (p0 == p1) + return false; + + float a = y0 - y1; + float b = x1 - x0; + float c = x0 * y1 - x1 * y0; + float distance2 = c * c / (a * a + b * b); + // If distance between the center point and the line > the radius, + // the line doesn't cross (or is contained by) the ellipse. + if (distance2 > radius2) + return false; + + // The nearest point on the line is between p0 and p1? + float x = -a * c / (a * a + b * b); + float y = -b * c / (a * a + b * b); + return (((x0 <= x && x <= x1) || (x0 >= x && x >= x1)) && + ((y0 <= y && y <= y1) || (y1 <= y && y <= y0))); +} + +bool FloatQuad::intersectsCircle(const FloatPoint& center, float radius) const { + return containsPoint( + center) // The circle may be totally contained by the quad. + || lineIntersectsCircle(center, radius, m_p1, m_p2) || + lineIntersectsCircle(center, radius, m_p2, m_p3) || + lineIntersectsCircle(center, radius, m_p3, m_p4) || + lineIntersectsCircle(center, radius, m_p4, m_p1); +} + +bool FloatQuad::intersectsEllipse(const FloatPoint& center, + const FloatSize& radii) const { + // Transform the ellipse to an origin-centered circle whose radius is the + // product of major radius and minor radius. Here we apply the same + // transformation to the quad. + FloatQuad transformedQuad(*this); + transformedQuad.move(-center.x(), -center.y()); + transformedQuad.scale(radii.height(), radii.width()); + + FloatPoint originPoint; + return transformedQuad.intersectsCircle(originPoint, + radii.height() * radii.width()); +} + +bool FloatQuad::isCounterclockwise() const { + // Return if the two first vectors are turning clockwise. If the quad is + // convex then all following vectors will turn the same way. + return determinant(m_p2 - m_p1, m_p3 - m_p2) < 0; +} + +} // namespace blink diff --git a/sky/engine/platform/geometry/FloatQuad.h b/sky/engine/platform/geometry/FloatQuad.h index ebf7f3b72c8ea..9597f386feb30 100644 --- a/sky/engine/platform/geometry/FloatQuad.h +++ b/sky/engine/platform/geometry/FloatQuad.h @@ -39,142 +39,124 @@ namespace blink { // mapping a rectangle through transforms. When initialized from a rect, the // points are in clockwise order from top left. class PLATFORM_EXPORT FloatQuad { -public: - FloatQuad() - { - } - - FloatQuad(const FloatPoint& p1, const FloatPoint& p2, const FloatPoint& p3, const FloatPoint& p4) - : m_p1(p1) - , m_p2(p2) - , m_p3(p3) - , m_p4(p4) - { - } - - FloatQuad(const FloatRect& inRect) - : m_p1(inRect.location()) - , m_p2(inRect.maxX(), inRect.y()) - , m_p3(inRect.maxX(), inRect.maxY()) - , m_p4(inRect.x(), inRect.maxY()) - { - } - - FloatPoint p1() const { return m_p1; } - FloatPoint p2() const { return m_p2; } - FloatPoint p3() const { return m_p3; } - FloatPoint p4() const { return m_p4; } - - void setP1(const FloatPoint& p) { m_p1 = p; } - void setP2(const FloatPoint& p) { m_p2 = p; } - void setP3(const FloatPoint& p) { m_p3 = p; } - void setP4(const FloatPoint& p) { m_p4 = p; } - - // isEmpty tests that the bounding box is empty. This will not identify - // "slanted" empty quads. - bool isEmpty() const { return boundingBox().isEmpty(); } - - // Tests whether this quad can be losslessly represented by a FloatRect, - // that is, if two edges are parallel to the x-axis and the other two - // are parallel to the y-axis. If this method returns true, the - // corresponding FloatRect can be retrieved with boundingBox(). - bool isRectilinear() const; - - // Tests whether the given point is inside, or on an edge or corner of this quad. - bool containsPoint(const FloatPoint&) const; - - // Tests whether the four corners of other are inside, or coincident with the sides of this quad. - // Note that this only works for convex quads, but that includes all quads that originate - // from transformed rects. - bool containsQuad(const FloatQuad&) const; - - // Tests whether any part of the rectangle intersects with this quad. - // This only works for convex quads. - bool intersectsRect(const FloatRect&) const; - - // Test whether any part of the circle/ellipse intersects with this quad. - // Note that these two functions only work for convex quads. - bool intersectsCircle(const FloatPoint& center, float radius) const; - bool intersectsEllipse(const FloatPoint& center, const FloatSize& radii) const; - - // The center of the quad. If the quad is the result of a affine-transformed rectangle this is the same as the original center transformed. - FloatPoint center() const - { - return FloatPoint((m_p1.x() + m_p2.x() + m_p3.x() + m_p4.x()) / 4.0, - (m_p1.y() + m_p2.y() + m_p3.y() + m_p4.y()) / 4.0); - } - - FloatRect boundingBox() const; - IntRect enclosingBoundingBox() const - { - return enclosingIntRect(boundingBox()); - } - - void move(const FloatSize& offset) - { - m_p1 += offset; - m_p2 += offset; - m_p3 += offset; - m_p4 += offset; - } - - void move(float dx, float dy) - { - m_p1.move(dx, dy); - m_p2.move(dx, dy); - m_p3.move(dx, dy); - m_p4.move(dx, dy); - } - - void scale(float dx, float dy) - { - m_p1.scale(dx, dy); - m_p2.scale(dx, dy); - m_p3.scale(dx, dy); - m_p4.scale(dx, dy); - } - - // Tests whether points are in clock-wise, or counter clock-wise order. - // Note that output is undefined when all points are colinear. - bool isCounterclockwise() const; - -private: - FloatPoint m_p1; - FloatPoint m_p2; - FloatPoint m_p3; - FloatPoint m_p4; + public: + FloatQuad() {} + + FloatQuad(const FloatPoint& p1, + const FloatPoint& p2, + const FloatPoint& p3, + const FloatPoint& p4) + : m_p1(p1), m_p2(p2), m_p3(p3), m_p4(p4) {} + + FloatQuad(const FloatRect& inRect) + : m_p1(inRect.location()), + m_p2(inRect.maxX(), inRect.y()), + m_p3(inRect.maxX(), inRect.maxY()), + m_p4(inRect.x(), inRect.maxY()) {} + + FloatPoint p1() const { return m_p1; } + FloatPoint p2() const { return m_p2; } + FloatPoint p3() const { return m_p3; } + FloatPoint p4() const { return m_p4; } + + void setP1(const FloatPoint& p) { m_p1 = p; } + void setP2(const FloatPoint& p) { m_p2 = p; } + void setP3(const FloatPoint& p) { m_p3 = p; } + void setP4(const FloatPoint& p) { m_p4 = p; } + + // isEmpty tests that the bounding box is empty. This will not identify + // "slanted" empty quads. + bool isEmpty() const { return boundingBox().isEmpty(); } + + // Tests whether this quad can be losslessly represented by a FloatRect, + // that is, if two edges are parallel to the x-axis and the other two + // are parallel to the y-axis. If this method returns true, the + // corresponding FloatRect can be retrieved with boundingBox(). + bool isRectilinear() const; + + // Tests whether the given point is inside, or on an edge or corner of this + // quad. + bool containsPoint(const FloatPoint&) const; + + // Tests whether the four corners of other are inside, or coincident with the + // sides of this quad. Note that this only works for convex quads, but that + // includes all quads that originate from transformed rects. + bool containsQuad(const FloatQuad&) const; + + // Tests whether any part of the rectangle intersects with this quad. + // This only works for convex quads. + bool intersectsRect(const FloatRect&) const; + + // Test whether any part of the circle/ellipse intersects with this quad. + // Note that these two functions only work for convex quads. + bool intersectsCircle(const FloatPoint& center, float radius) const; + bool intersectsEllipse(const FloatPoint& center, + const FloatSize& radii) const; + + // The center of the quad. If the quad is the result of a affine-transformed + // rectangle this is the same as the original center transformed. + FloatPoint center() const { + return FloatPoint((m_p1.x() + m_p2.x() + m_p3.x() + m_p4.x()) / 4.0, + (m_p1.y() + m_p2.y() + m_p3.y() + m_p4.y()) / 4.0); + } + + FloatRect boundingBox() const; + IntRect enclosingBoundingBox() const { + return enclosingIntRect(boundingBox()); + } + + void move(const FloatSize& offset) { + m_p1 += offset; + m_p2 += offset; + m_p3 += offset; + m_p4 += offset; + } + + void move(float dx, float dy) { + m_p1.move(dx, dy); + m_p2.move(dx, dy); + m_p3.move(dx, dy); + m_p4.move(dx, dy); + } + + void scale(float dx, float dy) { + m_p1.scale(dx, dy); + m_p2.scale(dx, dy); + m_p3.scale(dx, dy); + m_p4.scale(dx, dy); + } + + // Tests whether points are in clock-wise, or counter clock-wise order. + // Note that output is undefined when all points are colinear. + bool isCounterclockwise() const; + + private: + FloatPoint m_p1; + FloatPoint m_p2; + FloatPoint m_p3; + FloatPoint m_p4; }; -inline FloatQuad& operator+=(FloatQuad& a, const FloatSize& b) -{ - a.move(b); - return a; +inline FloatQuad& operator+=(FloatQuad& a, const FloatSize& b) { + a.move(b); + return a; } -inline FloatQuad& operator-=(FloatQuad& a, const FloatSize& b) -{ - a.move(-b.width(), -b.height()); - return a; +inline FloatQuad& operator-=(FloatQuad& a, const FloatSize& b) { + a.move(-b.width(), -b.height()); + return a; } -inline bool operator==(const FloatQuad& a, const FloatQuad& b) -{ - return a.p1() == b.p1() && - a.p2() == b.p2() && - a.p3() == b.p3() && - a.p4() == b.p4(); +inline bool operator==(const FloatQuad& a, const FloatQuad& b) { + return a.p1() == b.p1() && a.p2() == b.p2() && a.p3() == b.p3() && + a.p4() == b.p4(); } -inline bool operator!=(const FloatQuad& a, const FloatQuad& b) -{ - return a.p1() != b.p1() || - a.p2() != b.p2() || - a.p3() != b.p3() || - a.p4() != b.p4(); +inline bool operator!=(const FloatQuad& a, const FloatQuad& b) { + return a.p1() != b.p1() || a.p2() != b.p2() || a.p3() != b.p3() || + a.p4() != b.p4(); } -} // namespace blink - +} // namespace blink #endif // SKY_ENGINE_PLATFORM_GEOMETRY_FLOATQUAD_H_ - diff --git a/sky/engine/platform/geometry/FloatRect.cpp b/sky/engine/platform/geometry/FloatRect.cpp index 418db90f89124..1a176ea01fd73 100644 --- a/sky/engine/platform/geometry/FloatRect.cpp +++ b/sky/engine/platform/geometry/FloatRect.cpp @@ -37,226 +37,213 @@ namespace blink { -FloatRect::FloatRect(const IntRect& r) : m_location(r.location()), m_size(r.size()) -{ -} +FloatRect::FloatRect(const IntRect& r) + : m_location(r.location()), m_size(r.size()) {} -FloatRect::FloatRect(const LayoutRect& r) : m_location(r.location()), m_size(r.size()) -{ -} +FloatRect::FloatRect(const LayoutRect& r) + : m_location(r.location()), m_size(r.size()) {} -FloatRect::FloatRect(const SkRect& r) : m_location(r.fLeft, r.fTop), m_size(r.width(), r.height()) -{ -} +FloatRect::FloatRect(const SkRect& r) + : m_location(r.fLeft, r.fTop), m_size(r.width(), r.height()) {} -FloatRect FloatRect::narrowPrecision(double x, double y, double width, double height) -{ - return FloatRect(narrowPrecisionToFloat(x), narrowPrecisionToFloat(y), narrowPrecisionToFloat(width), narrowPrecisionToFloat(height)); +FloatRect FloatRect::narrowPrecision(double x, + double y, + double width, + double height) { + return FloatRect(narrowPrecisionToFloat(x), narrowPrecisionToFloat(y), + narrowPrecisionToFloat(width), + narrowPrecisionToFloat(height)); } -bool FloatRect::isExpressibleAsIntRect() const -{ - return isWithinIntRange(x()) && isWithinIntRange(y()) - && isWithinIntRange(width()) && isWithinIntRange(height()) - && isWithinIntRange(maxX()) && isWithinIntRange(maxY()); +bool FloatRect::isExpressibleAsIntRect() const { + return isWithinIntRange(x()) && isWithinIntRange(y()) && + isWithinIntRange(width()) && isWithinIntRange(height()) && + isWithinIntRange(maxX()) && isWithinIntRange(maxY()); } -bool FloatRect::intersects(const FloatRect& other) const -{ - // Checking emptiness handles negative widths as well as zero. - return !isEmpty() && !other.isEmpty() - && x() < other.maxX() && other.x() < maxX() - && y() < other.maxY() && other.y() < maxY(); +bool FloatRect::intersects(const FloatRect& other) const { + // Checking emptiness handles negative widths as well as zero. + return !isEmpty() && !other.isEmpty() && x() < other.maxX() && + other.x() < maxX() && y() < other.maxY() && other.y() < maxY(); } -bool FloatRect::contains(const FloatRect& other) const -{ - return x() <= other.x() && maxX() >= other.maxX() - && y() <= other.y() && maxY() >= other.maxY(); +bool FloatRect::contains(const FloatRect& other) const { + return x() <= other.x() && maxX() >= other.maxX() && y() <= other.y() && + maxY() >= other.maxY(); } -bool FloatRect::contains(const FloatPoint& point, ContainsMode containsMode) const -{ - if (containsMode == InsideOrOnStroke) - return contains(point.x(), point.y()); - return x() < point.x() && maxX() > point.x() && y() < point.y() && maxY() > point.y(); +bool FloatRect::contains(const FloatPoint& point, + ContainsMode containsMode) const { + if (containsMode == InsideOrOnStroke) + return contains(point.x(), point.y()); + return x() < point.x() && maxX() > point.x() && y() < point.y() && + maxY() > point.y(); } -void FloatRect::intersect(const FloatRect& other) -{ - float left = std::max(x(), other.x()); - float top = std::max(y(), other.y()); - float right = std::min(maxX(), other.maxX()); - float bottom = std::min(maxY(), other.maxY()); - - // Return a clean empty rectangle for non-intersecting cases. - if (left >= right || top >= bottom) { - left = 0; - top = 0; - right = 0; - bottom = 0; - } - - setLocationAndSizeFromEdges(left, top, right, bottom); +void FloatRect::intersect(const FloatRect& other) { + float left = std::max(x(), other.x()); + float top = std::max(y(), other.y()); + float right = std::min(maxX(), other.maxX()); + float bottom = std::min(maxY(), other.maxY()); + + // Return a clean empty rectangle for non-intersecting cases. + if (left >= right || top >= bottom) { + left = 0; + top = 0; + right = 0; + bottom = 0; + } + + setLocationAndSizeFromEdges(left, top, right, bottom); } -void FloatRect::unite(const FloatRect& other) -{ - // Handle empty special cases first. - if (other.isEmpty()) - return; - if (isEmpty()) { - *this = other; - return; - } - - uniteEvenIfEmpty(other); +void FloatRect::unite(const FloatRect& other) { + // Handle empty special cases first. + if (other.isEmpty()) + return; + if (isEmpty()) { + *this = other; + return; + } + + uniteEvenIfEmpty(other); } -void FloatRect::uniteEvenIfEmpty(const FloatRect& other) -{ - float minX = std::min(x(), other.x()); - float minY = std::min(y(), other.y()); - float maxX = std::max(this->maxX(), other.maxX()); - float maxY = std::max(this->maxY(), other.maxY()); +void FloatRect::uniteEvenIfEmpty(const FloatRect& other) { + float minX = std::min(x(), other.x()); + float minY = std::min(y(), other.y()); + float maxX = std::max(this->maxX(), other.maxX()); + float maxY = std::max(this->maxY(), other.maxY()); - setLocationAndSizeFromEdges(minX, minY, maxX, maxY); + setLocationAndSizeFromEdges(minX, minY, maxX, maxY); } -void FloatRect::uniteIfNonZero(const FloatRect& other) -{ - // Handle empty special cases first. - if (other.isZero()) - return; - if (isZero()) { - *this = other; - return; - } - - uniteEvenIfEmpty(other); +void FloatRect::uniteIfNonZero(const FloatRect& other) { + // Handle empty special cases first. + if (other.isZero()) + return; + if (isZero()) { + *this = other; + return; + } + + uniteEvenIfEmpty(other); } -void FloatRect::extend(const FloatPoint& p) -{ - float minX = std::min(x(), p.x()); - float minY = std::min(y(), p.y()); - float maxX = std::max(this->maxX(), p.x()); - float maxY = std::max(this->maxY(), p.y()); +void FloatRect::extend(const FloatPoint& p) { + float minX = std::min(x(), p.x()); + float minY = std::min(y(), p.y()); + float maxX = std::max(this->maxX(), p.x()); + float maxY = std::max(this->maxY(), p.y()); - setLocationAndSizeFromEdges(minX, minY, maxX, maxY); + setLocationAndSizeFromEdges(minX, minY, maxX, maxY); } -void FloatRect::scale(float sx, float sy) -{ - m_location.setX(x() * sx); - m_location.setY(y() * sy); - m_size.setWidth(width() * sx); - m_size.setHeight(height() * sy); +void FloatRect::scale(float sx, float sy) { + m_location.setX(x() * sx); + m_location.setY(y() * sy); + m_size.setWidth(width() * sx); + m_size.setHeight(height() * sy); } -FloatRect unionRect(const Vector& rects) -{ - FloatRect result; +FloatRect unionRect(const Vector& rects) { + FloatRect result; - size_t count = rects.size(); - for (size_t i = 0; i < count; ++i) - result.unite(rects[i]); + size_t count = rects.size(); + for (size_t i = 0; i < count; ++i) + result.unite(rects[i]); - return result; + return result; } -void FloatRect::fitToPoints(const FloatPoint& p0, const FloatPoint& p1) -{ - float left = std::min(p0.x(), p1.x()); - float top = std::min(p0.y(), p1.y()); - float right = std::max(p0.x(), p1.x()); - float bottom = std::max(p0.y(), p1.y()); +void FloatRect::fitToPoints(const FloatPoint& p0, const FloatPoint& p1) { + float left = std::min(p0.x(), p1.x()); + float top = std::min(p0.y(), p1.y()); + float right = std::max(p0.x(), p1.x()); + float bottom = std::max(p0.y(), p1.y()); - setLocationAndSizeFromEdges(left, top, right, bottom); + setLocationAndSizeFromEdges(left, top, right, bottom); } namespace { // Helpers for 3- and 4-way max and min. template -T min3(const T& v1, const T& v2, const T& v3) -{ - return std::min(std::min(v1, v2), v3); +T min3(const T& v1, const T& v2, const T& v3) { + return std::min(std::min(v1, v2), v3); } template -T max3(const T& v1, const T& v2, const T& v3) -{ - return std::max(std::max(v1, v2), v3); +T max3(const T& v1, const T& v2, const T& v3) { + return std::max(std::max(v1, v2), v3); } template -T min4(const T& v1, const T& v2, const T& v3, const T& v4) -{ - return std::min(std::min(v1, v2), std::min(v3, v4)); +T min4(const T& v1, const T& v2, const T& v3, const T& v4) { + return std::min(std::min(v1, v2), std::min(v3, v4)); } template -T max4(const T& v1, const T& v2, const T& v3, const T& v4) -{ - return std::max(std::max(v1, v2), std::max(v3, v4)); +T max4(const T& v1, const T& v2, const T& v3, const T& v4) { + return std::max(std::max(v1, v2), std::max(v3, v4)); } -} // anonymous namespace +} // anonymous namespace -void FloatRect::fitToPoints(const FloatPoint& p0, const FloatPoint& p1, const FloatPoint& p2) -{ - float left = min3(p0.x(), p1.x(), p2.x()); - float top = min3(p0.y(), p1.y(), p2.y()); - float right = max3(p0.x(), p1.x(), p2.x()); - float bottom = max3(p0.y(), p1.y(), p2.y()); +void FloatRect::fitToPoints(const FloatPoint& p0, + const FloatPoint& p1, + const FloatPoint& p2) { + float left = min3(p0.x(), p1.x(), p2.x()); + float top = min3(p0.y(), p1.y(), p2.y()); + float right = max3(p0.x(), p1.x(), p2.x()); + float bottom = max3(p0.y(), p1.y(), p2.y()); - setLocationAndSizeFromEdges(left, top, right, bottom); + setLocationAndSizeFromEdges(left, top, right, bottom); } -void FloatRect::fitToPoints(const FloatPoint& p0, const FloatPoint& p1, const FloatPoint& p2, const FloatPoint& p3) -{ - float left = min4(p0.x(), p1.x(), p2.x(), p3.x()); - float top = min4(p0.y(), p1.y(), p2.y(), p3.y()); - float right = max4(p0.x(), p1.x(), p2.x(), p3.x()); - float bottom = max4(p0.y(), p1.y(), p2.y(), p3.y()); +void FloatRect::fitToPoints(const FloatPoint& p0, + const FloatPoint& p1, + const FloatPoint& p2, + const FloatPoint& p3) { + float left = min4(p0.x(), p1.x(), p2.x(), p3.x()); + float top = min4(p0.y(), p1.y(), p2.y(), p3.y()); + float right = max4(p0.x(), p1.x(), p2.x(), p3.x()); + float bottom = max4(p0.y(), p1.y(), p2.y(), p3.y()); - setLocationAndSizeFromEdges(left, top, right, bottom); + setLocationAndSizeFromEdges(left, top, right, bottom); } -IntRect enclosingIntRect(const FloatRect& rect) -{ - IntPoint location = flooredIntPoint(rect.minXMinYCorner()); - IntPoint maxPoint = ceiledIntPoint(rect.maxXMaxYCorner()); +IntRect enclosingIntRect(const FloatRect& rect) { + IntPoint location = flooredIntPoint(rect.minXMinYCorner()); + IntPoint maxPoint = ceiledIntPoint(rect.maxXMaxYCorner()); - return IntRect(location, maxPoint - location); + return IntRect(location, maxPoint - location); } -IntRect enclosedIntRect(const FloatRect& rect) -{ - IntPoint location = ceiledIntPoint(rect.minXMinYCorner()); - IntPoint maxPoint = flooredIntPoint(rect.maxXMaxYCorner()); - IntSize size = maxPoint - location; - size.clampNegativeToZero(); +IntRect enclosedIntRect(const FloatRect& rect) { + IntPoint location = ceiledIntPoint(rect.minXMinYCorner()); + IntPoint maxPoint = flooredIntPoint(rect.maxXMaxYCorner()); + IntSize size = maxPoint - location; + size.clampNegativeToZero(); - return IntRect(location, size); + return IntRect(location, size); } -IntRect roundedIntRect(const FloatRect& rect) -{ - return IntRect(roundedIntPoint(rect.location()), roundedIntSize(rect.size())); +IntRect roundedIntRect(const FloatRect& rect) { + return IntRect(roundedIntPoint(rect.location()), roundedIntSize(rect.size())); } -FloatRect mapRect(const FloatRect& r, const FloatRect& srcRect, const FloatRect& destRect) -{ - if (!srcRect.width() || !srcRect.height()) - return FloatRect(); +FloatRect mapRect(const FloatRect& r, + const FloatRect& srcRect, + const FloatRect& destRect) { + if (!srcRect.width() || !srcRect.height()) + return FloatRect(); - float widthScale = destRect.width() / srcRect.width(); - float heightScale = destRect.height() / srcRect.height(); - return FloatRect(destRect.x() + (r.x() - srcRect.x()) * widthScale, - destRect.y() + (r.y() - srcRect.y()) * heightScale, - r.width() * widthScale, r.height() * heightScale); + float widthScale = destRect.width() / srcRect.width(); + float heightScale = destRect.height() / srcRect.height(); + return FloatRect(destRect.x() + (r.x() - srcRect.x()) * widthScale, + destRect.y() + (r.y() - srcRect.y()) * heightScale, + r.width() * widthScale, r.height() * heightScale); } -} +} // namespace blink diff --git a/sky/engine/platform/geometry/FloatRect.h b/sky/engine/platform/geometry/FloatRect.h index 515acdf8e6087..8c0947dc20df6 100644 --- a/sky/engine/platform/geometry/FloatRect.h +++ b/sky/engine/platform/geometry/FloatRect.h @@ -37,175 +37,188 @@ class LayoutRect; class IntRect; class PLATFORM_EXPORT FloatRect { -public: - enum ContainsMode { - InsideOrOnStroke, - InsideButNotOnStroke - }; - - FloatRect() { } - FloatRect(const FloatPoint& location, const FloatSize& size) - : m_location(location), m_size(size) { } - FloatRect(float x, float y, float width, float height) - : m_location(FloatPoint(x, y)), m_size(FloatSize(width, height)) { } - FloatRect(const IntRect&); - FloatRect(const LayoutRect&); - FloatRect(const SkRect&); - - static FloatRect narrowPrecision(double x, double y, double width, double height); - - FloatPoint location() const { return m_location; } - FloatSize size() const { return m_size; } - - void setLocation(const FloatPoint& location) { m_location = location; } - void setSize(const FloatSize& size) { m_size = size; } - - float x() const { return m_location.x(); } - float y() const { return m_location.y(); } - float maxX() const { return x() + width(); } - float maxY() const { return y() + height(); } - float width() const { return m_size.width(); } - float height() const { return m_size.height(); } - - void setX(float x) { m_location.setX(x); } - void setY(float y) { m_location.setY(y); } - void setWidth(float width) { m_size.setWidth(width); } - void setHeight(float height) { m_size.setHeight(height); } - - bool isEmpty() const { return m_size.isEmpty(); } - bool isZero() const { return m_size.isZero(); } - bool isExpressibleAsIntRect() const; - - FloatPoint center() const { return FloatPoint(x() + width() / 2, y() + height() / 2); } - - void move(const FloatSize& delta) { m_location += delta; } - void moveBy(const FloatPoint& delta) { m_location.move(delta.x(), delta.y()); } - void move(float dx, float dy) { m_location.move(dx, dy); } - - void expand(const FloatSize& size) { m_size += size; } - void expand(float dw, float dh) { m_size.expand(dw, dh); } - void contract(const FloatSize& size) { m_size -= size; } - void contract(float dw, float dh) { m_size.expand(-dw, -dh); } - - void shiftXEdgeTo(float edge) - { - float delta = edge - x(); - setX(edge); - setWidth(std::max(0.0f, width() - delta)); - } - void shiftMaxXEdgeTo(float edge) - { - float delta = edge - maxX(); - setWidth(std::max(0.0f, width() + delta)); - } - void shiftYEdgeTo(float edge) - { - float delta = edge - y(); - setY(edge); - setHeight(std::max(0.0f, height() - delta)); - } - void shiftMaxYEdgeTo(float edge) - { - float delta = edge - maxY(); - setHeight(std::max(0.0f, height() + delta)); - } - - FloatPoint minXMinYCorner() const { return m_location; } // typically topLeft - FloatPoint maxXMinYCorner() const { return FloatPoint(m_location.x() + m_size.width(), m_location.y()); } // typically topRight - FloatPoint minXMaxYCorner() const { return FloatPoint(m_location.x(), m_location.y() + m_size.height()); } // typically bottomLeft - FloatPoint maxXMaxYCorner() const { return FloatPoint(m_location.x() + m_size.width(), m_location.y() + m_size.height()); } // typically bottomRight - - bool intersects(const FloatRect&) const; - bool contains(const FloatRect&) const; - bool contains(const FloatPoint&, ContainsMode = InsideOrOnStroke) const; - - void intersect(const FloatRect&); - void unite(const FloatRect&); - void uniteEvenIfEmpty(const FloatRect&); - void uniteIfNonZero(const FloatRect&); - void extend(const FloatPoint&); - - // Note, this doesn't match what IntRect::contains(IntPoint&) does; the int version - // is really checking for containment of 1x1 rect, but that doesn't make sense with floats. - bool contains(float px, float py) const - { - return px >= x() && px <= maxX() && py >= y() && py <= maxY(); - } - - void inflateX(float dx) - { - m_location.setX(m_location.x() - dx); - m_size.setWidth(m_size.width() + dx + dx); - } - void inflateY(float dy) - { - m_location.setY(m_location.y() - dy); - m_size.setHeight(m_size.height() + dy + dy); - } - void inflate(float d) { inflateX(d); inflateY(d); } - void scale(float s) { scale(s, s); } - void scale(float sx, float sy); - - FloatRect transposedRect() const { return FloatRect(m_location.transposedPoint(), m_size.transposedSize()); } - - // Re-initializes this rectangle to fit the sets of passed points. - void fitToPoints(const FloatPoint& p0, const FloatPoint& p1); - void fitToPoints(const FloatPoint& p0, const FloatPoint& p1, const FloatPoint& p2); - void fitToPoints(const FloatPoint& p0, const FloatPoint& p1, const FloatPoint& p2, const FloatPoint& p3); - - operator SkRect() const { return SkRect::MakeXYWH(x(), y(), width(), height()); } - -private: - FloatPoint m_location; - FloatSize m_size; - - void setLocationAndSizeFromEdges(float left, float top, float right, float bottom) - { - m_location.set(left, top); - m_size.setWidth(right - left); - m_size.setHeight(bottom - top); - } + public: + enum ContainsMode { InsideOrOnStroke, InsideButNotOnStroke }; + + FloatRect() {} + FloatRect(const FloatPoint& location, const FloatSize& size) + : m_location(location), m_size(size) {} + FloatRect(float x, float y, float width, float height) + : m_location(FloatPoint(x, y)), m_size(FloatSize(width, height)) {} + FloatRect(const IntRect&); + FloatRect(const LayoutRect&); + FloatRect(const SkRect&); + + static FloatRect narrowPrecision(double x, + double y, + double width, + double height); + + FloatPoint location() const { return m_location; } + FloatSize size() const { return m_size; } + + void setLocation(const FloatPoint& location) { m_location = location; } + void setSize(const FloatSize& size) { m_size = size; } + + float x() const { return m_location.x(); } + float y() const { return m_location.y(); } + float maxX() const { return x() + width(); } + float maxY() const { return y() + height(); } + float width() const { return m_size.width(); } + float height() const { return m_size.height(); } + + void setX(float x) { m_location.setX(x); } + void setY(float y) { m_location.setY(y); } + void setWidth(float width) { m_size.setWidth(width); } + void setHeight(float height) { m_size.setHeight(height); } + + bool isEmpty() const { return m_size.isEmpty(); } + bool isZero() const { return m_size.isZero(); } + bool isExpressibleAsIntRect() const; + + FloatPoint center() const { + return FloatPoint(x() + width() / 2, y() + height() / 2); + } + + void move(const FloatSize& delta) { m_location += delta; } + void moveBy(const FloatPoint& delta) { + m_location.move(delta.x(), delta.y()); + } + void move(float dx, float dy) { m_location.move(dx, dy); } + + void expand(const FloatSize& size) { m_size += size; } + void expand(float dw, float dh) { m_size.expand(dw, dh); } + void contract(const FloatSize& size) { m_size -= size; } + void contract(float dw, float dh) { m_size.expand(-dw, -dh); } + + void shiftXEdgeTo(float edge) { + float delta = edge - x(); + setX(edge); + setWidth(std::max(0.0f, width() - delta)); + } + void shiftMaxXEdgeTo(float edge) { + float delta = edge - maxX(); + setWidth(std::max(0.0f, width() + delta)); + } + void shiftYEdgeTo(float edge) { + float delta = edge - y(); + setY(edge); + setHeight(std::max(0.0f, height() - delta)); + } + void shiftMaxYEdgeTo(float edge) { + float delta = edge - maxY(); + setHeight(std::max(0.0f, height() + delta)); + } + + FloatPoint minXMinYCorner() const { return m_location; } // typically topLeft + FloatPoint maxXMinYCorner() const { + return FloatPoint(m_location.x() + m_size.width(), m_location.y()); + } // typically topRight + FloatPoint minXMaxYCorner() const { + return FloatPoint(m_location.x(), m_location.y() + m_size.height()); + } // typically bottomLeft + FloatPoint maxXMaxYCorner() const { + return FloatPoint(m_location.x() + m_size.width(), + m_location.y() + m_size.height()); + } // typically bottomRight + + bool intersects(const FloatRect&) const; + bool contains(const FloatRect&) const; + bool contains(const FloatPoint&, ContainsMode = InsideOrOnStroke) const; + + void intersect(const FloatRect&); + void unite(const FloatRect&); + void uniteEvenIfEmpty(const FloatRect&); + void uniteIfNonZero(const FloatRect&); + void extend(const FloatPoint&); + + // Note, this doesn't match what IntRect::contains(IntPoint&) does; the int + // version is really checking for containment of 1x1 rect, but that doesn't + // make sense with floats. + bool contains(float px, float py) const { + return px >= x() && px <= maxX() && py >= y() && py <= maxY(); + } + + void inflateX(float dx) { + m_location.setX(m_location.x() - dx); + m_size.setWidth(m_size.width() + dx + dx); + } + void inflateY(float dy) { + m_location.setY(m_location.y() - dy); + m_size.setHeight(m_size.height() + dy + dy); + } + void inflate(float d) { + inflateX(d); + inflateY(d); + } + void scale(float s) { scale(s, s); } + void scale(float sx, float sy); + + FloatRect transposedRect() const { + return FloatRect(m_location.transposedPoint(), m_size.transposedSize()); + } + + // Re-initializes this rectangle to fit the sets of passed points. + void fitToPoints(const FloatPoint& p0, const FloatPoint& p1); + void fitToPoints(const FloatPoint& p0, + const FloatPoint& p1, + const FloatPoint& p2); + void fitToPoints(const FloatPoint& p0, + const FloatPoint& p1, + const FloatPoint& p2, + const FloatPoint& p3); + + operator SkRect() const { + return SkRect::MakeXYWH(x(), y(), width(), height()); + } + + private: + FloatPoint m_location; + FloatSize m_size; + + void setLocationAndSizeFromEdges(float left, + float top, + float right, + float bottom) { + m_location.set(left, top); + m_size.setWidth(right - left); + m_size.setHeight(bottom - top); + } }; -inline FloatRect intersection(const FloatRect& a, const FloatRect& b) -{ - FloatRect c = a; - c.intersect(b); - return c; +inline FloatRect intersection(const FloatRect& a, const FloatRect& b) { + FloatRect c = a; + c.intersect(b); + return c; } -inline FloatRect unionRect(const FloatRect& a, const FloatRect& b) -{ - FloatRect c = a; - c.unite(b); - return c; +inline FloatRect unionRect(const FloatRect& a, const FloatRect& b) { + FloatRect c = a; + c.unite(b); + return c; } FloatRect unionRect(const Vector&); -inline FloatRect& operator+=(FloatRect& a, const FloatRect& b) -{ - a.move(b.x(), b.y()); - a.setWidth(a.width() + b.width()); - a.setHeight(a.height() + b.height()); - return a; +inline FloatRect& operator+=(FloatRect& a, const FloatRect& b) { + a.move(b.x(), b.y()); + a.setWidth(a.width() + b.width()); + a.setHeight(a.height() + b.height()); + return a; } -inline FloatRect operator+(const FloatRect& a, const FloatRect& b) -{ - FloatRect c = a; - c += b; - return c; +inline FloatRect operator+(const FloatRect& a, const FloatRect& b) { + FloatRect c = a; + c += b; + return c; } -inline bool operator==(const FloatRect& a, const FloatRect& b) -{ - return a.location() == b.location() && a.size() == b.size(); +inline bool operator==(const FloatRect& a, const FloatRect& b) { + return a.location() == b.location() && a.size() == b.size(); } -inline bool operator!=(const FloatRect& a, const FloatRect& b) -{ - return a.location() != b.location() || a.size() != b.size(); +inline bool operator!=(const FloatRect& a, const FloatRect& b) { + return a.location() != b.location() || a.size() != b.size(); } PLATFORM_EXPORT IntRect enclosingIntRect(const FloatRect&); @@ -216,8 +229,10 @@ PLATFORM_EXPORT IntRect enclosedIntRect(const FloatRect&); PLATFORM_EXPORT IntRect roundedIntRect(const FloatRect&); // Map supplied rect from srcRect to an equivalent rect in destRect. -PLATFORM_EXPORT FloatRect mapRect(const FloatRect&, const FloatRect& srcRect, const FloatRect& destRect); +PLATFORM_EXPORT FloatRect mapRect(const FloatRect&, + const FloatRect& srcRect, + const FloatRect& destRect); -} +} // namespace blink #endif // SKY_ENGINE_PLATFORM_GEOMETRY_FLOATRECT_H_ diff --git a/sky/engine/platform/geometry/FloatRoundedRect.cpp b/sky/engine/platform/geometry/FloatRoundedRect.cpp index 4e092cc8830a3..2c754dc370703 100644 --- a/sky/engine/platform/geometry/FloatRoundedRect.cpp +++ b/sky/engine/platform/geometry/FloatRoundedRect.cpp @@ -34,106 +34,114 @@ namespace blink { FloatRoundedRect::FloatRoundedRect(float x, float y, float width, float height) - : m_rect(x, y, width, height) -{ -} + : m_rect(x, y, width, height) {} FloatRoundedRect::FloatRoundedRect(const FloatRect& rect, const Radii& radii) - : m_rect(rect) - , m_radii(radii) -{ -} - -FloatRoundedRect::FloatRoundedRect(const FloatRect& rect, const FloatSize& topLeft, const FloatSize& topRight, const FloatSize& bottomLeft, const FloatSize& bottomRight) - : m_rect(rect) - , m_radii(topLeft, topRight, bottomLeft, bottomRight) -{ + : m_rect(rect), m_radii(radii) {} + +FloatRoundedRect::FloatRoundedRect(const FloatRect& rect, + const FloatSize& topLeft, + const FloatSize& topRight, + const FloatSize& bottomLeft, + const FloatSize& bottomRight) + : m_rect(rect), m_radii(topLeft, topRight, bottomLeft, bottomRight) {} + +bool FloatRoundedRect::Radii::isZero() const { + return m_topLeft.isZero() && m_topRight.isZero() && m_bottomLeft.isZero() && + m_bottomRight.isZero(); } -bool FloatRoundedRect::Radii::isZero() const -{ - return m_topLeft.isZero() && m_topRight.isZero() && m_bottomLeft.isZero() && m_bottomRight.isZero(); -} - -void FloatRoundedRect::Radii::scale(float factor) -{ - if (factor == 1) - return; - - // If either radius on a corner becomes zero, reset both radii on that corner. - m_topLeft.scale(factor); - if (!m_topLeft.width() || !m_topLeft.height()) - m_topLeft = FloatSize(); - m_topRight.scale(factor); - if (!m_topRight.width() || !m_topRight.height()) - m_topRight = FloatSize(); - m_bottomLeft.scale(factor); - if (!m_bottomLeft.width() || !m_bottomLeft.height()) - m_bottomLeft = FloatSize(); - m_bottomRight.scale(factor); - if (!m_bottomRight.width() || !m_bottomRight.height()) - m_bottomRight = FloatSize(); - +void FloatRoundedRect::Radii::scale(float factor) { + if (factor == 1) + return; + + // If either radius on a corner becomes zero, reset both radii on that corner. + m_topLeft.scale(factor); + if (!m_topLeft.width() || !m_topLeft.height()) + m_topLeft = FloatSize(); + m_topRight.scale(factor); + if (!m_topRight.width() || !m_topRight.height()) + m_topRight = FloatSize(); + m_bottomLeft.scale(factor); + if (!m_bottomLeft.width() || !m_bottomLeft.height()) + m_bottomLeft = FloatSize(); + m_bottomRight.scale(factor); + if (!m_bottomRight.width() || !m_bottomRight.height()) + m_bottomRight = FloatSize(); } -void FloatRoundedRect::Radii::expand(float topWidth, float bottomWidth, float leftWidth, float rightWidth) -{ - if (m_topLeft.width() > 0 && m_topLeft.height() > 0) { - m_topLeft.setWidth(std::max(0, m_topLeft.width() + leftWidth)); - m_topLeft.setHeight(std::max(0, m_topLeft.height() + topWidth)); - } - if (m_topRight.width() > 0 && m_topRight.height() > 0) { - m_topRight.setWidth(std::max(0, m_topRight.width() + rightWidth)); - m_topRight.setHeight(std::max(0, m_topRight.height() + topWidth)); - } - if (m_bottomLeft.width() > 0 && m_bottomLeft.height() > 0) { - m_bottomLeft.setWidth(std::max(0, m_bottomLeft.width() + leftWidth)); - m_bottomLeft.setHeight(std::max(0, m_bottomLeft.height() + bottomWidth)); - } - if (m_bottomRight.width() > 0 && m_bottomRight.height() > 0) { - m_bottomRight.setWidth(std::max(0, m_bottomRight.width() + rightWidth)); - m_bottomRight.setHeight(std::max(0, m_bottomRight.height() + bottomWidth)); - } +void FloatRoundedRect::Radii::expand(float topWidth, + float bottomWidth, + float leftWidth, + float rightWidth) { + if (m_topLeft.width() > 0 && m_topLeft.height() > 0) { + m_topLeft.setWidth(std::max(0, m_topLeft.width() + leftWidth)); + m_topLeft.setHeight(std::max(0, m_topLeft.height() + topWidth)); + } + if (m_topRight.width() > 0 && m_topRight.height() > 0) { + m_topRight.setWidth(std::max(0, m_topRight.width() + rightWidth)); + m_topRight.setHeight(std::max(0, m_topRight.height() + topWidth)); + } + if (m_bottomLeft.width() > 0 && m_bottomLeft.height() > 0) { + m_bottomLeft.setWidth(std::max(0, m_bottomLeft.width() + leftWidth)); + m_bottomLeft.setHeight( + std::max(0, m_bottomLeft.height() + bottomWidth)); + } + if (m_bottomRight.width() > 0 && m_bottomRight.height() > 0) { + m_bottomRight.setWidth( + std::max(0, m_bottomRight.width() + rightWidth)); + m_bottomRight.setHeight( + std::max(0, m_bottomRight.height() + bottomWidth)); + } } -static inline float cornerRectIntercept(float y, const FloatRect& cornerRect) -{ - ASSERT(cornerRect.height() > 0); - return cornerRect.width() * sqrt(1 - (y * y) / (cornerRect.height() * cornerRect.height())); +static inline float cornerRectIntercept(float y, const FloatRect& cornerRect) { + ASSERT(cornerRect.height() > 0); + return cornerRect.width() * + sqrt(1 - (y * y) / (cornerRect.height() * cornerRect.height())); } -bool FloatRoundedRect::xInterceptsAtY(float y, float& minXIntercept, float& maxXIntercept) const -{ - if (y < rect().y() || y > rect().maxY()) - return false; - - if (!isRounded()) { - minXIntercept = rect().x(); - maxXIntercept = rect().maxX(); - return true; - } - - const FloatRect& topLeftRect = topLeftCorner(); - const FloatRect& bottomLeftRect = bottomLeftCorner(); - - if (!topLeftRect.isEmpty() && y >= topLeftRect.y() && y < topLeftRect.maxY()) - minXIntercept = topLeftRect.maxX() - cornerRectIntercept(topLeftRect.maxY() - y, topLeftRect); - else if (!bottomLeftRect.isEmpty() && y >= bottomLeftRect.y() && y <= bottomLeftRect.maxY()) - minXIntercept = bottomLeftRect.maxX() - cornerRectIntercept(y - bottomLeftRect.y(), bottomLeftRect); - else - minXIntercept = m_rect.x(); - - const FloatRect& topRightRect = topRightCorner(); - const FloatRect& bottomRightRect = bottomRightCorner(); - - if (!topRightRect.isEmpty() && y >= topRightRect.y() && y <= topRightRect.maxY()) - maxXIntercept = topRightRect.x() + cornerRectIntercept(topRightRect.maxY() - y, topRightRect); - else if (!bottomRightRect.isEmpty() && y >= bottomRightRect.y() && y <= bottomRightRect.maxY()) - maxXIntercept = bottomRightRect.x() + cornerRectIntercept(y - bottomRightRect.y(), bottomRightRect); - else - maxXIntercept = m_rect.maxX(); +bool FloatRoundedRect::xInterceptsAtY(float y, + float& minXIntercept, + float& maxXIntercept) const { + if (y < rect().y() || y > rect().maxY()) + return false; + if (!isRounded()) { + minXIntercept = rect().x(); + maxXIntercept = rect().maxX(); return true; + } + + const FloatRect& topLeftRect = topLeftCorner(); + const FloatRect& bottomLeftRect = bottomLeftCorner(); + + if (!topLeftRect.isEmpty() && y >= topLeftRect.y() && y < topLeftRect.maxY()) + minXIntercept = topLeftRect.maxX() - + cornerRectIntercept(topLeftRect.maxY() - y, topLeftRect); + else if (!bottomLeftRect.isEmpty() && y >= bottomLeftRect.y() && + y <= bottomLeftRect.maxY()) + minXIntercept = bottomLeftRect.maxX() - + cornerRectIntercept(y - bottomLeftRect.y(), bottomLeftRect); + else + minXIntercept = m_rect.x(); + + const FloatRect& topRightRect = topRightCorner(); + const FloatRect& bottomRightRect = bottomRightCorner(); + + if (!topRightRect.isEmpty() && y >= topRightRect.y() && + y <= topRightRect.maxY()) + maxXIntercept = topRightRect.x() + + cornerRectIntercept(topRightRect.maxY() - y, topRightRect); + else if (!bottomRightRect.isEmpty() && y >= bottomRightRect.y() && + y <= bottomRightRect.maxY()) + maxXIntercept = + bottomRightRect.x() + + cornerRectIntercept(y - bottomRightRect.y(), bottomRightRect); + else + maxXIntercept = m_rect.maxX(); + + return true; } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/geometry/FloatRoundedRect.h b/sky/engine/platform/geometry/FloatRoundedRect.h index 89f05ebd0bf86..365475e9dc1ad 100644 --- a/sky/engine/platform/geometry/FloatRoundedRect.h +++ b/sky/engine/platform/geometry/FloatRoundedRect.h @@ -37,101 +37,117 @@ namespace blink { class PLATFORM_EXPORT FloatRoundedRect { -public: - class PLATFORM_EXPORT Radii { - public: - Radii() { } - Radii(const FloatSize& topLeft, const FloatSize& topRight, const FloatSize& bottomLeft, const FloatSize& bottomRight) - : m_topLeft(topLeft) - , m_topRight(topRight) - , m_bottomLeft(bottomLeft) - , m_bottomRight(bottomRight) - { - } - - Radii(const RoundedRect::Radii& intRadii) - : m_topLeft(intRadii.topLeft()) - , m_topRight(intRadii.topRight()) - , m_bottomLeft(intRadii.bottomLeft()) - , m_bottomRight(intRadii.bottomRight()) - { - } - - void setTopLeft(const FloatSize& size) { m_topLeft = size; } - void setTopRight(const FloatSize& size) { m_topRight = size; } - void setBottomLeft(const FloatSize& size) { m_bottomLeft = size; } - void setBottomRight(const FloatSize& size) { m_bottomRight = size; } - const FloatSize& topLeft() const { return m_topLeft; } - const FloatSize& topRight() const { return m_topRight; } - const FloatSize& bottomLeft() const { return m_bottomLeft; } - const FloatSize& bottomRight() const { return m_bottomRight; } - - bool isZero() const; - - void scale(float factor); - void expand(float topWidth, float bottomWidth, float leftWidth, float rightWidth); - void expand(float size) { expand(size, size, size, size); } - void shrink(float topWidth, float bottomWidth, float leftWidth, float rightWidth) { expand(-topWidth, -bottomWidth, -leftWidth, -rightWidth); } - void shrink(float size) { shrink(size, size, size, size); } - - private: - FloatSize m_topLeft; - FloatSize m_topRight; - FloatSize m_bottomLeft; - FloatSize m_bottomRight; - }; - - explicit FloatRoundedRect(const FloatRect&, const Radii& = Radii()); - FloatRoundedRect(float x, float y, float width, float height); - FloatRoundedRect(const FloatRect&, const FloatSize& topLeft, const FloatSize& topRight, const FloatSize& bottomLeft, const FloatSize& bottomRight); - - const FloatRect& rect() const { return m_rect; } - const Radii& radii() const { return m_radii; } - bool isRounded() const { return !m_radii.isZero(); } - bool isEmpty() const { return m_rect.isEmpty(); } - - void setRect(const FloatRect& rect) { m_rect = rect; } - void setRadii(const Radii& radii) { m_radii = radii; } - - void move(const FloatSize& size) { m_rect.move(size); } - void inflate(float size) { m_rect.inflate(size); } - void expandRadii(float size) { m_radii.expand(size); } - void shrinkRadii(float size) { m_radii.shrink(size); } - - FloatRect topLeftCorner() const - { - return FloatRect(m_rect.x(), m_rect.y(), m_radii.topLeft().width(), m_radii.topLeft().height()); + public: + class PLATFORM_EXPORT Radii { + public: + Radii() {} + Radii(const FloatSize& topLeft, + const FloatSize& topRight, + const FloatSize& bottomLeft, + const FloatSize& bottomRight) + : m_topLeft(topLeft), + m_topRight(topRight), + m_bottomLeft(bottomLeft), + m_bottomRight(bottomRight) {} + + Radii(const RoundedRect::Radii& intRadii) + : m_topLeft(intRadii.topLeft()), + m_topRight(intRadii.topRight()), + m_bottomLeft(intRadii.bottomLeft()), + m_bottomRight(intRadii.bottomRight()) {} + + void setTopLeft(const FloatSize& size) { m_topLeft = size; } + void setTopRight(const FloatSize& size) { m_topRight = size; } + void setBottomLeft(const FloatSize& size) { m_bottomLeft = size; } + void setBottomRight(const FloatSize& size) { m_bottomRight = size; } + const FloatSize& topLeft() const { return m_topLeft; } + const FloatSize& topRight() const { return m_topRight; } + const FloatSize& bottomLeft() const { return m_bottomLeft; } + const FloatSize& bottomRight() const { return m_bottomRight; } + + bool isZero() const; + + void scale(float factor); + void expand(float topWidth, + float bottomWidth, + float leftWidth, + float rightWidth); + void expand(float size) { expand(size, size, size, size); } + void shrink(float topWidth, + float bottomWidth, + float leftWidth, + float rightWidth) { + expand(-topWidth, -bottomWidth, -leftWidth, -rightWidth); } - FloatRect topRightCorner() const - { - return FloatRect(m_rect.maxX() - m_radii.topRight().width(), m_rect.y(), m_radii.topRight().width(), m_radii.topRight().height()); - } - FloatRect bottomLeftCorner() const - { - return FloatRect(m_rect.x(), m_rect.maxY() - m_radii.bottomLeft().height(), m_radii.bottomLeft().width(), m_radii.bottomLeft().height()); - } - FloatRect bottomRightCorner() const - { - return FloatRect(m_rect.maxX() - m_radii.bottomRight().width(), m_rect.maxY() - m_radii.bottomRight().height(), m_radii.bottomRight().width(), m_radii.bottomRight().height()); - } - - bool xInterceptsAtY(float y, float& minXIntercept, float& maxXIntercept) const; - -private: - FloatRect m_rect; - Radii m_radii; + void shrink(float size) { shrink(size, size, size, size); } + + private: + FloatSize m_topLeft; + FloatSize m_topRight; + FloatSize m_bottomLeft; + FloatSize m_bottomRight; + }; + + explicit FloatRoundedRect(const FloatRect&, const Radii& = Radii()); + FloatRoundedRect(float x, float y, float width, float height); + FloatRoundedRect(const FloatRect&, + const FloatSize& topLeft, + const FloatSize& topRight, + const FloatSize& bottomLeft, + const FloatSize& bottomRight); + + const FloatRect& rect() const { return m_rect; } + const Radii& radii() const { return m_radii; } + bool isRounded() const { return !m_radii.isZero(); } + bool isEmpty() const { return m_rect.isEmpty(); } + + void setRect(const FloatRect& rect) { m_rect = rect; } + void setRadii(const Radii& radii) { m_radii = radii; } + + void move(const FloatSize& size) { m_rect.move(size); } + void inflate(float size) { m_rect.inflate(size); } + void expandRadii(float size) { m_radii.expand(size); } + void shrinkRadii(float size) { m_radii.shrink(size); } + + FloatRect topLeftCorner() const { + return FloatRect(m_rect.x(), m_rect.y(), m_radii.topLeft().width(), + m_radii.topLeft().height()); + } + FloatRect topRightCorner() const { + return FloatRect(m_rect.maxX() - m_radii.topRight().width(), m_rect.y(), + m_radii.topRight().width(), m_radii.topRight().height()); + } + FloatRect bottomLeftCorner() const { + return FloatRect(m_rect.x(), m_rect.maxY() - m_radii.bottomLeft().height(), + m_radii.bottomLeft().width(), + m_radii.bottomLeft().height()); + } + FloatRect bottomRightCorner() const { + return FloatRect(m_rect.maxX() - m_radii.bottomRight().width(), + m_rect.maxY() - m_radii.bottomRight().height(), + m_radii.bottomRight().width(), + m_radii.bottomRight().height()); + } + + bool xInterceptsAtY(float y, + float& minXIntercept, + float& maxXIntercept) const; + + private: + FloatRect m_rect; + Radii m_radii; }; -inline bool operator==(const FloatRoundedRect::Radii& a, const FloatRoundedRect::Radii& b) -{ - return a.topLeft() == b.topLeft() && a.topRight() == b.topRight() && a.bottomLeft() == b.bottomLeft() && a.bottomRight() == b.bottomRight(); +inline bool operator==(const FloatRoundedRect::Radii& a, + const FloatRoundedRect::Radii& b) { + return a.topLeft() == b.topLeft() && a.topRight() == b.topRight() && + a.bottomLeft() == b.bottomLeft() && a.bottomRight() == b.bottomRight(); } -inline bool operator==(const FloatRoundedRect& a, const FloatRoundedRect& b) -{ - return a.rect() == b.rect() && a.radii() == b.radii(); +inline bool operator==(const FloatRoundedRect& a, const FloatRoundedRect& b) { + return a.rect() == b.rect() && a.radii() == b.radii(); } -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_GEOMETRY_FLOATROUNDEDRECT_H_ diff --git a/sky/engine/platform/geometry/FloatRoundedRectTest.cpp b/sky/engine/platform/geometry/FloatRoundedRectTest.cpp index f799d2ad809b1..84c9ba746df55 100644 --- a/sky/engine/platform/geometry/FloatRoundedRectTest.cpp +++ b/sky/engine/platform/geometry/FloatRoundedRectTest.cpp @@ -27,7 +27,6 @@ * OF THE POSSIBILITY OF SUCH DAMAGE. */ - #include "flutter/sky/engine/platform/geometry/FloatRoundedRect.h" #include @@ -36,121 +35,112 @@ using namespace blink; namespace blink { -void PrintTo(const FloatSize& size, std::ostream* os) -{ - *os << "FloatSize(" - << size.width() << ", " - << size.height() << ")"; +void PrintTo(const FloatSize& size, std::ostream* os) { + *os << "FloatSize(" << size.width() << ", " << size.height() << ")"; } -void PrintTo(const FloatRect& rect, std::ostream* os) -{ - *os << "FloatRect(" - << rect.x() << ", " - << rect.y() << ", " - << rect.width() << ", " - << rect.height() << ")"; +void PrintTo(const FloatRect& rect, std::ostream* os) { + *os << "FloatRect(" << rect.x() << ", " << rect.y() << ", " << rect.width() + << ", " << rect.height() << ")"; } -void PrintTo(const FloatRoundedRect::Radii& radii, std::ostream* os) -{ - *os << "FloatRoundedRect::Radii(" - << ::testing::PrintToString(radii.topLeft()) << ", " - << ::testing::PrintToString(radii.topRight()) << ", " - << ::testing::PrintToString(radii.bottomRight()) << ", " - << ::testing::PrintToString(radii.bottomLeft()) << ")"; +void PrintTo(const FloatRoundedRect::Radii& radii, std::ostream* os) { + *os << "FloatRoundedRect::Radii(" << ::testing::PrintToString(radii.topLeft()) + << ", " << ::testing::PrintToString(radii.topRight()) << ", " + << ::testing::PrintToString(radii.bottomRight()) << ", " + << ::testing::PrintToString(radii.bottomLeft()) << ")"; } -void PrintTo(const FloatRoundedRect& roundedRect, std::ostream* os) -{ - *os << "FloatRoundedRect(" - << ::testing::PrintToString(roundedRect.rect()) << ", " - << ::testing::PrintToString(roundedRect.radii()) << ")"; +void PrintTo(const FloatRoundedRect& roundedRect, std::ostream* os) { + *os << "FloatRoundedRect(" << ::testing::PrintToString(roundedRect.rect()) + << ", " << ::testing::PrintToString(roundedRect.radii()) << ")"; } -} // namespace blink +} // namespace blink namespace { -#define TEST_INTERCEPTS(roundedRect, yCoordinate, expectedMinXIntercept, expectedMaxXIntercept) \ -{ \ - float minXIntercept; \ - float maxXIntercept; \ - EXPECT_TRUE(roundedRect.xInterceptsAtY(yCoordinate, minXIntercept, maxXIntercept)); \ - EXPECT_FLOAT_EQ(expectedMinXIntercept, minXIntercept); \ - EXPECT_FLOAT_EQ(expectedMaxXIntercept, maxXIntercept); \ -} - -TEST(FloatRoundedRectTest, zeroRadii) -{ - FloatRoundedRect r = FloatRoundedRect(1, 2, 3, 4); - - EXPECT_EQ(FloatRect(1, 2, 3, 4), r.rect()); - EXPECT_EQ(FloatSize(), r.radii().topLeft()); - EXPECT_EQ(FloatSize(), r.radii().topRight()); - EXPECT_EQ(FloatSize(), r.radii().bottomLeft()); - EXPECT_EQ(FloatSize(), r.radii().bottomRight()); - EXPECT_TRUE(r.radii().isZero()); - EXPECT_FALSE(r.isRounded()); - EXPECT_FALSE(r.isEmpty()); - - EXPECT_EQ(FloatRect(1, 2, 0, 0), r.topLeftCorner()); - EXPECT_EQ(FloatRect(4, 2, 0, 0), r.topRightCorner()); - EXPECT_EQ(FloatRect(4, 6, 0, 0), r.bottomRightCorner()); - EXPECT_EQ(FloatRect(1, 6, 0, 0), r.bottomLeftCorner()); - - TEST_INTERCEPTS(r, 2, r.rect().x(), r.rect().maxX()); - TEST_INTERCEPTS(r, 4, r.rect().x(), r.rect().maxX()); - TEST_INTERCEPTS(r, 6, r.rect().x(), r.rect().maxX()); - - float minXIntercept; - float maxXIntercept; - - EXPECT_FALSE(r.xInterceptsAtY(1, minXIntercept, maxXIntercept)); - EXPECT_FALSE(r.xInterceptsAtY(7, minXIntercept, maxXIntercept)); - - // The FloatRoundedRect::expandRadii() function doesn't change radii FloatSizes that - // are <= zero. Same as RoundedRect::expandRadii(). - r.expandRadii(20); - r.shrinkRadii(10); - EXPECT_TRUE(r.radii().isZero()); +#define TEST_INTERCEPTS(roundedRect, yCoordinate, expectedMinXIntercept, \ + expectedMaxXIntercept) \ + { \ + float minXIntercept; \ + float maxXIntercept; \ + EXPECT_TRUE(roundedRect.xInterceptsAtY(yCoordinate, minXIntercept, \ + maxXIntercept)); \ + EXPECT_FLOAT_EQ(expectedMinXIntercept, minXIntercept); \ + EXPECT_FLOAT_EQ(expectedMaxXIntercept, maxXIntercept); \ + } + +TEST(FloatRoundedRectTest, zeroRadii) { + FloatRoundedRect r = FloatRoundedRect(1, 2, 3, 4); + + EXPECT_EQ(FloatRect(1, 2, 3, 4), r.rect()); + EXPECT_EQ(FloatSize(), r.radii().topLeft()); + EXPECT_EQ(FloatSize(), r.radii().topRight()); + EXPECT_EQ(FloatSize(), r.radii().bottomLeft()); + EXPECT_EQ(FloatSize(), r.radii().bottomRight()); + EXPECT_TRUE(r.radii().isZero()); + EXPECT_FALSE(r.isRounded()); + EXPECT_FALSE(r.isEmpty()); + + EXPECT_EQ(FloatRect(1, 2, 0, 0), r.topLeftCorner()); + EXPECT_EQ(FloatRect(4, 2, 0, 0), r.topRightCorner()); + EXPECT_EQ(FloatRect(4, 6, 0, 0), r.bottomRightCorner()); + EXPECT_EQ(FloatRect(1, 6, 0, 0), r.bottomLeftCorner()); + + TEST_INTERCEPTS(r, 2, r.rect().x(), r.rect().maxX()); + TEST_INTERCEPTS(r, 4, r.rect().x(), r.rect().maxX()); + TEST_INTERCEPTS(r, 6, r.rect().x(), r.rect().maxX()); + + float minXIntercept; + float maxXIntercept; + + EXPECT_FALSE(r.xInterceptsAtY(1, minXIntercept, maxXIntercept)); + EXPECT_FALSE(r.xInterceptsAtY(7, minXIntercept, maxXIntercept)); + + // The FloatRoundedRect::expandRadii() function doesn't change radii + // FloatSizes that are <= zero. Same as RoundedRect::expandRadii(). + r.expandRadii(20); + r.shrinkRadii(10); + EXPECT_TRUE(r.radii().isZero()); } -TEST(FloatRoundedRectTest, circle) -{ - FloatSize cornerRadii(50, 50); - FloatRoundedRect r(FloatRect(0, 0, 100, 100), cornerRadii, cornerRadii, cornerRadii, cornerRadii); - - EXPECT_EQ(FloatRect(0, 0, 100, 100), r.rect()); - EXPECT_EQ(cornerRadii, r.radii().topLeft()); - EXPECT_EQ(cornerRadii, r.radii().topRight()); - EXPECT_EQ(cornerRadii, r.radii().bottomLeft()); - EXPECT_EQ(cornerRadii, r.radii().bottomRight()); - EXPECT_FALSE(r.radii().isZero()); - EXPECT_TRUE(r.isRounded()); - EXPECT_FALSE(r.isEmpty()); - - EXPECT_EQ(FloatRect(0, 0, 50, 50), r.topLeftCorner()); - EXPECT_EQ(FloatRect(50, 0, 50, 50), r.topRightCorner()); - EXPECT_EQ(FloatRect(0, 50, 50, 50), r.bottomLeftCorner()); - EXPECT_EQ(FloatRect(50, 50, 50, 50), r.bottomRightCorner()); - - TEST_INTERCEPTS(r, 0, 50, 50); - TEST_INTERCEPTS(r, 25, 6.69873, 93.3013); - TEST_INTERCEPTS(r, 50, 0, 100); - TEST_INTERCEPTS(r, 75, 6.69873, 93.3013); - TEST_INTERCEPTS(r, 100, 50, 50); - - float minXIntercept; - float maxXIntercept; - - EXPECT_FALSE(r.xInterceptsAtY(-1, minXIntercept, maxXIntercept)); - EXPECT_FALSE(r.xInterceptsAtY(101, minXIntercept, maxXIntercept)); +TEST(FloatRoundedRectTest, circle) { + FloatSize cornerRadii(50, 50); + FloatRoundedRect r(FloatRect(0, 0, 100, 100), cornerRadii, cornerRadii, + cornerRadii, cornerRadii); + + EXPECT_EQ(FloatRect(0, 0, 100, 100), r.rect()); + EXPECT_EQ(cornerRadii, r.radii().topLeft()); + EXPECT_EQ(cornerRadii, r.radii().topRight()); + EXPECT_EQ(cornerRadii, r.radii().bottomLeft()); + EXPECT_EQ(cornerRadii, r.radii().bottomRight()); + EXPECT_FALSE(r.radii().isZero()); + EXPECT_TRUE(r.isRounded()); + EXPECT_FALSE(r.isEmpty()); + + EXPECT_EQ(FloatRect(0, 0, 50, 50), r.topLeftCorner()); + EXPECT_EQ(FloatRect(50, 0, 50, 50), r.topRightCorner()); + EXPECT_EQ(FloatRect(0, 50, 50, 50), r.bottomLeftCorner()); + EXPECT_EQ(FloatRect(50, 50, 50, 50), r.bottomRightCorner()); + + TEST_INTERCEPTS(r, 0, 50, 50); + TEST_INTERCEPTS(r, 25, 6.69873, 93.3013); + TEST_INTERCEPTS(r, 50, 0, 100); + TEST_INTERCEPTS(r, 75, 6.69873, 93.3013); + TEST_INTERCEPTS(r, 100, 50, 50); + + float minXIntercept; + float maxXIntercept; + + EXPECT_FALSE(r.xInterceptsAtY(-1, minXIntercept, maxXIntercept)); + EXPECT_FALSE(r.xInterceptsAtY(101, minXIntercept, maxXIntercept)); } /* - * FloatRoundedRect geometry for this test. Corner radii are in parens, x and y intercepts - * for the elliptical corners are noted. The rectangle itself is at 0,0 with width and height 100. + * FloatRoundedRect geometry for this test. Corner radii are in parens, x and y + * intercepts for the elliptical corners are noted. The rectangle itself is at + * 0,0 with width and height 100. * * (10, 15) x=10 x=90 (10, 20) * (--+---------+--) @@ -161,39 +151,39 @@ TEST(FloatRoundedRectTest, circle) * (--+---------+--) * (25, 15) x=25 x=80 (20, 30) */ -TEST(FloatRoundedRectTest, ellipticalCorners) -{ - FloatSize cornerSize(10, 20); - FloatRoundedRect::Radii cornerRadii; - cornerRadii.setTopLeft(FloatSize(10, 15)); - cornerRadii.setTopRight(FloatSize(10, 20)); - cornerRadii.setBottomLeft(FloatSize(25, 15)); - cornerRadii.setBottomRight(FloatSize(20, 30)); - - FloatRoundedRect r(FloatRect(0, 0, 100, 100), cornerRadii); - - EXPECT_EQ(r.radii(), FloatRoundedRect::Radii(FloatSize(10, 15), FloatSize(10, 20), FloatSize(25, 15), FloatSize(20, 30))); - EXPECT_EQ(r, FloatRoundedRect(FloatRect(0, 0, 100, 100), cornerRadii)); - - EXPECT_EQ(FloatRect(0, 0, 10, 15), r.topLeftCorner()); - EXPECT_EQ(FloatRect(90, 0, 10, 20), r.topRightCorner()); - EXPECT_EQ(FloatRect(0, 85, 25, 15), r.bottomLeftCorner()); - EXPECT_EQ(FloatRect(80, 70, 20, 30), r.bottomRightCorner()); - - TEST_INTERCEPTS(r, 5, 2.5464401, 96.61438); - TEST_INTERCEPTS(r, 15, 0, 99.682457); - TEST_INTERCEPTS(r, 20, 0, 100); - TEST_INTERCEPTS(r, 50, 0, 100); - TEST_INTERCEPTS(r, 70, 0, 100); - TEST_INTERCEPTS(r, 85, 0, 97.320511); - TEST_INTERCEPTS(r, 95, 6.3661003, 91.05542); - - float minXIntercept; - float maxXIntercept; - - EXPECT_FALSE(r.xInterceptsAtY(-1, minXIntercept, maxXIntercept)); - EXPECT_FALSE(r.xInterceptsAtY(101, minXIntercept, maxXIntercept)); +TEST(FloatRoundedRectTest, ellipticalCorners) { + FloatSize cornerSize(10, 20); + FloatRoundedRect::Radii cornerRadii; + cornerRadii.setTopLeft(FloatSize(10, 15)); + cornerRadii.setTopRight(FloatSize(10, 20)); + cornerRadii.setBottomLeft(FloatSize(25, 15)); + cornerRadii.setBottomRight(FloatSize(20, 30)); + + FloatRoundedRect r(FloatRect(0, 0, 100, 100), cornerRadii); + + EXPECT_EQ(r.radii(), + FloatRoundedRect::Radii(FloatSize(10, 15), FloatSize(10, 20), + FloatSize(25, 15), FloatSize(20, 30))); + EXPECT_EQ(r, FloatRoundedRect(FloatRect(0, 0, 100, 100), cornerRadii)); + + EXPECT_EQ(FloatRect(0, 0, 10, 15), r.topLeftCorner()); + EXPECT_EQ(FloatRect(90, 0, 10, 20), r.topRightCorner()); + EXPECT_EQ(FloatRect(0, 85, 25, 15), r.bottomLeftCorner()); + EXPECT_EQ(FloatRect(80, 70, 20, 30), r.bottomRightCorner()); + + TEST_INTERCEPTS(r, 5, 2.5464401, 96.61438); + TEST_INTERCEPTS(r, 15, 0, 99.682457); + TEST_INTERCEPTS(r, 20, 0, 100); + TEST_INTERCEPTS(r, 50, 0, 100); + TEST_INTERCEPTS(r, 70, 0, 100); + TEST_INTERCEPTS(r, 85, 0, 97.320511); + TEST_INTERCEPTS(r, 95, 6.3661003, 91.05542); + + float minXIntercept; + float maxXIntercept; + + EXPECT_FALSE(r.xInterceptsAtY(-1, minXIntercept, maxXIntercept)); + EXPECT_FALSE(r.xInterceptsAtY(101, minXIntercept, maxXIntercept)); } -} // namespace - +} // namespace diff --git a/sky/engine/platform/geometry/FloatSize.cpp b/sky/engine/platform/geometry/FloatSize.cpp index 2c42707f6b95e..1de5dd0afa1d1 100644 --- a/sky/engine/platform/geometry/FloatSize.cpp +++ b/sky/engine/platform/geometry/FloatSize.cpp @@ -35,29 +35,24 @@ namespace blink { FloatSize::FloatSize(const LayoutSize& size) - : m_width(size.width().toFloat()) - , m_height(size.height().toFloat()) -{ -} + : m_width(size.width().toFloat()), m_height(size.height().toFloat()) {} -float FloatSize::diagonalLength() const -{ - return sqrtf(diagonalLengthSquared()); +float FloatSize::diagonalLength() const { + return sqrtf(diagonalLengthSquared()); } -bool FloatSize::isZero() const -{ - return fabs(m_width) < std::numeric_limits::epsilon() && fabs(m_height) < std::numeric_limits::epsilon(); +bool FloatSize::isZero() const { + return fabs(m_width) < std::numeric_limits::epsilon() && + fabs(m_height) < std::numeric_limits::epsilon(); } -bool FloatSize::isExpressibleAsIntSize() const -{ - return isWithinIntRange(m_width) && isWithinIntRange(m_height); +bool FloatSize::isExpressibleAsIntSize() const { + return isWithinIntRange(m_width) && isWithinIntRange(m_height); } -FloatSize FloatSize::narrowPrecision(double width, double height) -{ - return FloatSize(narrowPrecisionToFloat(width), narrowPrecisionToFloat(height)); +FloatSize FloatSize::narrowPrecision(double width, double height) { + return FloatSize(narrowPrecisionToFloat(width), + narrowPrecisionToFloat(height)); } -} +} // namespace blink diff --git a/sky/engine/platform/geometry/FloatSize.h b/sky/engine/platform/geometry/FloatSize.h index f414c9ac5f626..249aaa923e27b 100644 --- a/sky/engine/platform/geometry/FloatSize.h +++ b/sky/engine/platform/geometry/FloatSize.h @@ -37,146 +37,126 @@ class IntSize; class LayoutSize; class PLATFORM_EXPORT FloatSize { -public: - FloatSize() : m_width(0), m_height(0) { } - FloatSize(float width, float height) : m_width(width), m_height(height) { } - FloatSize(const IntSize& size) : m_width(size.width()), m_height(size.height()) { } - FloatSize(const LayoutSize&); - - static FloatSize narrowPrecision(double width, double height); - - float width() const { return m_width; } - float height() const { return m_height; } - - void setWidth(float width) { m_width = width; } - void setHeight(float height) { m_height = height; } - - bool isEmpty() const { return m_width <= 0 || m_height <= 0; } - bool isZero() const; - bool isExpressibleAsIntSize() const; - - float aspectRatio() const { return m_width / m_height; } - - void expand(float width, float height) - { - m_width += width; - m_height += height; - } - - void scale(float s) { scale(s, s); } - - void scale(float scaleX, float scaleY) - { - m_width *= scaleX; - m_height *= scaleY; - } - - FloatSize expandedTo(const FloatSize& other) const - { - return FloatSize(m_width > other.m_width ? m_width : other.m_width, - m_height > other.m_height ? m_height : other.m_height); - } - - FloatSize shrunkTo(const FloatSize& other) const - { - return FloatSize(m_width < other.m_width ? m_width : other.m_width, - m_height < other.m_height ? m_height : other.m_height); - } - - float diagonalLength() const; - float diagonalLengthSquared() const - { - return m_width * m_width + m_height * m_height; - } - - FloatSize transposedSize() const - { - return FloatSize(m_height, m_width); - } - - FloatSize scaledBy(float scale) const - { - return scaledBy(scale, scale); - } - - FloatSize scaledBy(float scaleX, float scaleY) const - { - return FloatSize(m_width * scaleX, m_height * scaleY); - } - -private: - float m_width, m_height; + public: + FloatSize() : m_width(0), m_height(0) {} + FloatSize(float width, float height) : m_width(width), m_height(height) {} + FloatSize(const IntSize& size) + : m_width(size.width()), m_height(size.height()) {} + FloatSize(const LayoutSize&); + + static FloatSize narrowPrecision(double width, double height); + + float width() const { return m_width; } + float height() const { return m_height; } + + void setWidth(float width) { m_width = width; } + void setHeight(float height) { m_height = height; } + + bool isEmpty() const { return m_width <= 0 || m_height <= 0; } + bool isZero() const; + bool isExpressibleAsIntSize() const; + + float aspectRatio() const { return m_width / m_height; } + + void expand(float width, float height) { + m_width += width; + m_height += height; + } + + void scale(float s) { scale(s, s); } + + void scale(float scaleX, float scaleY) { + m_width *= scaleX; + m_height *= scaleY; + } + + FloatSize expandedTo(const FloatSize& other) const { + return FloatSize(m_width > other.m_width ? m_width : other.m_width, + m_height > other.m_height ? m_height : other.m_height); + } + + FloatSize shrunkTo(const FloatSize& other) const { + return FloatSize(m_width < other.m_width ? m_width : other.m_width, + m_height < other.m_height ? m_height : other.m_height); + } + + float diagonalLength() const; + float diagonalLengthSquared() const { + return m_width * m_width + m_height * m_height; + } + + FloatSize transposedSize() const { return FloatSize(m_height, m_width); } + + FloatSize scaledBy(float scale) const { return scaledBy(scale, scale); } + + FloatSize scaledBy(float scaleX, float scaleY) const { + return FloatSize(m_width * scaleX, m_height * scaleY); + } + + private: + float m_width, m_height; }; -inline FloatSize& operator+=(FloatSize& a, const FloatSize& b) -{ - a.setWidth(a.width() + b.width()); - a.setHeight(a.height() + b.height()); - return a; +inline FloatSize& operator+=(FloatSize& a, const FloatSize& b) { + a.setWidth(a.width() + b.width()); + a.setHeight(a.height() + b.height()); + return a; } -inline FloatSize& operator-=(FloatSize& a, const FloatSize& b) -{ - a.setWidth(a.width() - b.width()); - a.setHeight(a.height() - b.height()); - return a; +inline FloatSize& operator-=(FloatSize& a, const FloatSize& b) { + a.setWidth(a.width() - b.width()); + a.setHeight(a.height() - b.height()); + return a; } -inline FloatSize operator+(const FloatSize& a, const FloatSize& b) -{ - return FloatSize(a.width() + b.width(), a.height() + b.height()); +inline FloatSize operator+(const FloatSize& a, const FloatSize& b) { + return FloatSize(a.width() + b.width(), a.height() + b.height()); } -inline FloatSize operator-(const FloatSize& a, const FloatSize& b) -{ - return FloatSize(a.width() - b.width(), a.height() - b.height()); +inline FloatSize operator-(const FloatSize& a, const FloatSize& b) { + return FloatSize(a.width() - b.width(), a.height() - b.height()); } -inline FloatSize operator-(const FloatSize& size) -{ - return FloatSize(-size.width(), -size.height()); +inline FloatSize operator-(const FloatSize& size) { + return FloatSize(-size.width(), -size.height()); } -inline FloatSize operator*(const FloatSize& a, const float b) -{ - return FloatSize(a.width() * b, a.height() * b); +inline FloatSize operator*(const FloatSize& a, const float b) { + return FloatSize(a.width() * b, a.height() * b); } -inline FloatSize operator*(const float a, const FloatSize& b) -{ - return FloatSize(a * b.width(), a * b.height()); +inline FloatSize operator*(const float a, const FloatSize& b) { + return FloatSize(a * b.width(), a * b.height()); } -inline bool operator==(const FloatSize& a, const FloatSize& b) -{ - return a.width() == b.width() && a.height() == b.height(); +inline bool operator==(const FloatSize& a, const FloatSize& b) { + return a.width() == b.width() && a.height() == b.height(); } -inline bool operator!=(const FloatSize& a, const FloatSize& b) -{ - return a.width() != b.width() || a.height() != b.height(); +inline bool operator!=(const FloatSize& a, const FloatSize& b) { + return a.width() != b.width() || a.height() != b.height(); } -inline IntSize roundedIntSize(const FloatSize& p) -{ - return IntSize(clampToInteger(roundf(p.width())), clampToInteger(roundf(p.height()))); +inline IntSize roundedIntSize(const FloatSize& p) { + return IntSize(clampToInteger(roundf(p.width())), + clampToInteger(roundf(p.height()))); } -inline IntSize flooredIntSize(const FloatSize& p) -{ - return IntSize(clampToInteger(floorf(p.width())), clampToInteger(floorf(p.height()))); +inline IntSize flooredIntSize(const FloatSize& p) { + return IntSize(clampToInteger(floorf(p.width())), + clampToInteger(floorf(p.height()))); } -inline IntSize expandedIntSize(const FloatSize& p) -{ - return IntSize(clampToInteger(ceilf(p.width())), clampToInteger(ceilf(p.height()))); +inline IntSize expandedIntSize(const FloatSize& p) { + return IntSize(clampToInteger(ceilf(p.width())), + clampToInteger(ceilf(p.height()))); } -inline IntPoint flooredIntPoint(const FloatSize& p) -{ - return IntPoint(clampToInteger(floorf(p.width())), clampToInteger(floorf(p.height()))); +inline IntPoint flooredIntPoint(const FloatSize& p) { + return IntPoint(clampToInteger(floorf(p.width())), + clampToInteger(floorf(p.height()))); } -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_GEOMETRY_FLOATSIZE_H_ diff --git a/sky/engine/platform/geometry/IntPoint.h b/sky/engine/platform/geometry/IntPoint.h index 1535164d1a0d9..61189ca1e86ec 100644 --- a/sky/engine/platform/geometry/IntPoint.h +++ b/sky/engine/platform/geometry/IntPoint.h @@ -34,114 +34,98 @@ namespace blink { class PLATFORM_EXPORT IntPoint { -public: - IntPoint() : m_x(0), m_y(0) { } - IntPoint(int x, int y) : m_x(x), m_y(y) { } - explicit IntPoint(const IntSize& size) : m_x(size.width()), m_y(size.height()) { } - - static IntPoint zero() { return IntPoint(); } - - int x() const { return m_x; } - int y() const { return m_y; } - - void setX(int x) { m_x = x; } - void setY(int y) { m_y = y; } - - void move(const IntSize& s) { move(s.width(), s.height()); } - void moveBy(const IntPoint& offset) { move(offset.x(), offset.y()); } - void move(int dx, int dy) { m_x += dx; m_y += dy; } - void scale(float sx, float sy) - { - m_x = lroundf(static_cast(m_x * sx)); - m_y = lroundf(static_cast(m_y * sy)); - } - - IntPoint expandedTo(const IntPoint& other) const - { - return IntPoint(m_x > other.m_x ? m_x : other.m_x, - m_y > other.m_y ? m_y : other.m_y); - } - - IntPoint shrunkTo(const IntPoint& other) const - { - return IntPoint(m_x < other.m_x ? m_x : other.m_x, - m_y < other.m_y ? m_y : other.m_y); - } - - int distanceSquaredToPoint(const IntPoint&) const; - - void clampNegativeToZero() - { - *this = expandedTo(zero()); - } - - IntPoint transposedPoint() const - { - return IntPoint(m_y, m_x); - } - -private: - int m_x, m_y; + public: + IntPoint() : m_x(0), m_y(0) {} + IntPoint(int x, int y) : m_x(x), m_y(y) {} + explicit IntPoint(const IntSize& size) + : m_x(size.width()), m_y(size.height()) {} + + static IntPoint zero() { return IntPoint(); } + + int x() const { return m_x; } + int y() const { return m_y; } + + void setX(int x) { m_x = x; } + void setY(int y) { m_y = y; } + + void move(const IntSize& s) { move(s.width(), s.height()); } + void moveBy(const IntPoint& offset) { move(offset.x(), offset.y()); } + void move(int dx, int dy) { + m_x += dx; + m_y += dy; + } + void scale(float sx, float sy) { + m_x = lroundf(static_cast(m_x * sx)); + m_y = lroundf(static_cast(m_y * sy)); + } + + IntPoint expandedTo(const IntPoint& other) const { + return IntPoint(m_x > other.m_x ? m_x : other.m_x, + m_y > other.m_y ? m_y : other.m_y); + } + + IntPoint shrunkTo(const IntPoint& other) const { + return IntPoint(m_x < other.m_x ? m_x : other.m_x, + m_y < other.m_y ? m_y : other.m_y); + } + + int distanceSquaredToPoint(const IntPoint&) const; + + void clampNegativeToZero() { *this = expandedTo(zero()); } + + IntPoint transposedPoint() const { return IntPoint(m_y, m_x); } + + private: + int m_x, m_y; }; -inline IntPoint& operator+=(IntPoint& a, const IntSize& b) -{ - a.move(b.width(), b.height()); - return a; +inline IntPoint& operator+=(IntPoint& a, const IntSize& b) { + a.move(b.width(), b.height()); + return a; } -inline IntPoint& operator-=(IntPoint& a, const IntSize& b) -{ - a.move(-b.width(), -b.height()); - return a; +inline IntPoint& operator-=(IntPoint& a, const IntSize& b) { + a.move(-b.width(), -b.height()); + return a; } -inline IntPoint operator+(const IntPoint& a, const IntSize& b) -{ - return IntPoint(a.x() + b.width(), a.y() + b.height()); +inline IntPoint operator+(const IntPoint& a, const IntSize& b) { + return IntPoint(a.x() + b.width(), a.y() + b.height()); } -inline IntPoint operator+(const IntPoint& a, const IntPoint& b) -{ - return IntPoint(a.x() + b.x(), a.y() + b.y()); +inline IntPoint operator+(const IntPoint& a, const IntPoint& b) { + return IntPoint(a.x() + b.x(), a.y() + b.y()); } -inline IntSize operator-(const IntPoint& a, const IntPoint& b) -{ - return IntSize(a.x() - b.x(), a.y() - b.y()); +inline IntSize operator-(const IntPoint& a, const IntPoint& b) { + return IntSize(a.x() - b.x(), a.y() - b.y()); } -inline IntPoint operator-(const IntPoint& a, const IntSize& b) -{ - return IntPoint(a.x() - b.width(), a.y() - b.height()); +inline IntPoint operator-(const IntPoint& a, const IntSize& b) { + return IntPoint(a.x() - b.width(), a.y() - b.height()); } -inline IntPoint operator-(const IntPoint& point) -{ - return IntPoint(-point.x(), -point.y()); +inline IntPoint operator-(const IntPoint& point) { + return IntPoint(-point.x(), -point.y()); } -inline bool operator==(const IntPoint& a, const IntPoint& b) -{ - return a.x() == b.x() && a.y() == b.y(); +inline bool operator==(const IntPoint& a, const IntPoint& b) { + return a.x() == b.x() && a.y() == b.y(); } -inline bool operator!=(const IntPoint& a, const IntPoint& b) -{ - return a.x() != b.x() || a.y() != b.y(); +inline bool operator!=(const IntPoint& a, const IntPoint& b) { + return a.x() != b.x() || a.y() != b.y(); } -inline IntSize toIntSize(const IntPoint& a) -{ - return IntSize(a.x(), a.y()); +inline IntSize toIntSize(const IntPoint& a) { + return IntSize(a.x(), a.y()); } -inline int IntPoint::distanceSquaredToPoint(const IntPoint& point) const -{ - return ((*this) - point).diagonalLengthSquared(); +inline int IntPoint::distanceSquaredToPoint(const IntPoint& point) const { + return ((*this) - point).diagonalLengthSquared(); } -} // namespace blink +} // namespace blink WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(blink::IntPoint); diff --git a/sky/engine/platform/geometry/IntRect.cpp b/sky/engine/platform/geometry/IntRect.cpp index ddf71ed72605b..8b6c9218ccf1f 100644 --- a/sky/engine/platform/geometry/IntRect.cpp +++ b/sky/engine/platform/geometry/IntRect.cpp @@ -34,148 +34,131 @@ namespace blink { IntRect::IntRect(const FloatRect& r) - : m_location(clampToInteger(r.x()), clampToInteger(r.y())) - , m_size(clampToInteger(r.width()), clampToInteger(r.height())) -{ -} + : m_location(clampToInteger(r.x()), clampToInteger(r.y())), + m_size(clampToInteger(r.width()), clampToInteger(r.height())) {} IntRect::IntRect(const LayoutRect& r) - : m_location(r.x(), r.y()) - , m_size(r.width(), r.height()) -{ -} + : m_location(r.x(), r.y()), m_size(r.width(), r.height()) {} -bool IntRect::intersects(const IntRect& other) const -{ - // Checking emptiness handles negative widths as well as zero. - return !isEmpty() && !other.isEmpty() - && x() < other.maxX() && other.x() < maxX() - && y() < other.maxY() && other.y() < maxY(); +bool IntRect::intersects(const IntRect& other) const { + // Checking emptiness handles negative widths as well as zero. + return !isEmpty() && !other.isEmpty() && x() < other.maxX() && + other.x() < maxX() && y() < other.maxY() && other.y() < maxY(); } -bool IntRect::contains(const IntRect& other) const -{ - return x() <= other.x() && maxX() >= other.maxX() - && y() <= other.y() && maxY() >= other.maxY(); +bool IntRect::contains(const IntRect& other) const { + return x() <= other.x() && maxX() >= other.maxX() && y() <= other.y() && + maxY() >= other.maxY(); } -void IntRect::intersect(const IntRect& other) -{ - int left = std::max(x(), other.x()); - int top = std::max(y(), other.y()); - int right = std::min(maxX(), other.maxX()); - int bottom = std::min(maxY(), other.maxY()); - - // Return a clean empty rectangle for non-intersecting cases. - if (left >= right || top >= bottom) { - left = 0; - top = 0; - right = 0; - bottom = 0; - } - - m_location.setX(left); - m_location.setY(top); - m_size.setWidth(right - left); - m_size.setHeight(bottom - top); +void IntRect::intersect(const IntRect& other) { + int left = std::max(x(), other.x()); + int top = std::max(y(), other.y()); + int right = std::min(maxX(), other.maxX()); + int bottom = std::min(maxY(), other.maxY()); + + // Return a clean empty rectangle for non-intersecting cases. + if (left >= right || top >= bottom) { + left = 0; + top = 0; + right = 0; + bottom = 0; + } + + m_location.setX(left); + m_location.setY(top); + m_size.setWidth(right - left); + m_size.setHeight(bottom - top); } -void IntRect::unite(const IntRect& other) -{ - // Handle empty special cases first. - if (other.isEmpty()) - return; - if (isEmpty()) { - *this = other; - return; - } - - int left = std::min(x(), other.x()); - int top = std::min(y(), other.y()); - int right = std::max(maxX(), other.maxX()); - int bottom = std::max(maxY(), other.maxY()); - - m_location.setX(left); - m_location.setY(top); - m_size.setWidth(right - left); - m_size.setHeight(bottom - top); +void IntRect::unite(const IntRect& other) { + // Handle empty special cases first. + if (other.isEmpty()) + return; + if (isEmpty()) { + *this = other; + return; + } + + int left = std::min(x(), other.x()); + int top = std::min(y(), other.y()); + int right = std::max(maxX(), other.maxX()); + int bottom = std::max(maxY(), other.maxY()); + + m_location.setX(left); + m_location.setY(top); + m_size.setWidth(right - left); + m_size.setHeight(bottom - top); } -void IntRect::uniteIfNonZero(const IntRect& other) -{ - // Handle empty special cases first. - if (!other.width() && !other.height()) - return; - if (!width() && !height()) { - *this = other; - return; - } - - int left = std::min(x(), other.x()); - int top = std::min(y(), other.y()); - int right = std::max(maxX(), other.maxX()); - int bottom = std::max(maxY(), other.maxY()); - - m_location.setX(left); - m_location.setY(top); - m_size.setWidth(right - left); - m_size.setHeight(bottom - top); +void IntRect::uniteIfNonZero(const IntRect& other) { + // Handle empty special cases first. + if (!other.width() && !other.height()) + return; + if (!width() && !height()) { + *this = other; + return; + } + + int left = std::min(x(), other.x()); + int top = std::min(y(), other.y()); + int right = std::max(maxX(), other.maxX()); + int bottom = std::max(maxY(), other.maxY()); + + m_location.setX(left); + m_location.setY(top); + m_size.setWidth(right - left); + m_size.setHeight(bottom - top); } -void IntRect::scale(float s) -{ - m_location.setX((int)(x() * s)); - m_location.setY((int)(y() * s)); - m_size.setWidth((int)(width() * s)); - m_size.setHeight((int)(height() * s)); +void IntRect::scale(float s) { + m_location.setX((int)(x() * s)); + m_location.setY((int)(y() * s)); + m_size.setWidth((int)(width() * s)); + m_size.setHeight((int)(height() * s)); } -static inline int distanceToInterval(int pos, int start, int end) -{ - if (pos < start) - return start - pos; - if (pos > end) - return end - pos; - return 0; +static inline int distanceToInterval(int pos, int start, int end) { + if (pos < start) + return start - pos; + if (pos > end) + return end - pos; + return 0; } -IntSize IntRect::differenceToPoint(const IntPoint& point) const -{ - int xdistance = distanceToInterval(point.x(), x(), maxX()); - int ydistance = distanceToInterval(point.y(), y(), maxY()); - return IntSize(xdistance, ydistance); +IntSize IntRect::differenceToPoint(const IntPoint& point) const { + int xdistance = distanceToInterval(point.x(), x(), maxX()); + int ydistance = distanceToInterval(point.y(), y(), maxY()); + return IntSize(xdistance, ydistance); } -IntRect::operator SkIRect() const -{ - SkIRect rect = { x(), y(), maxX(), maxY() }; - return rect; +IntRect::operator SkIRect() const { + SkIRect rect = {x(), y(), maxX(), maxY()}; + return rect; } -IntRect::operator SkRect() const -{ - SkRect rect; - rect.set(SkIntToScalar(x()), SkIntToScalar(y()), SkIntToScalar(maxX()), SkIntToScalar(maxY())); - return rect; +IntRect::operator SkRect() const { + SkRect rect; + rect.set(SkIntToScalar(x()), SkIntToScalar(y()), SkIntToScalar(maxX()), + SkIntToScalar(maxY())); + return rect; } -IntRect unionRect(const Vector& rects) -{ - IntRect result; +IntRect unionRect(const Vector& rects) { + IntRect result; - size_t count = rects.size(); - for (size_t i = 0; i < count; ++i) - result.unite(rects[i]); + size_t count = rects.size(); + for (size_t i = 0; i < count; ++i) + result.unite(rects[i]); - return result; + return result; } #ifndef NDEBUG - // Prints the rect to the screen. -void IntRect::show() const -{ - LayoutRect(*this).show(); +// Prints the rect to the screen. +void IntRect::show() const { + LayoutRect(*this).show(); } #endif -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/geometry/IntRect.h b/sky/engine/platform/geometry/IntRect.h index 025068d3b2413..f89515d179863 100644 --- a/sky/engine/platform/geometry/IntRect.h +++ b/sky/engine/platform/geometry/IntRect.h @@ -40,149 +40,164 @@ class FloatRect; class LayoutRect; class PLATFORM_EXPORT IntRect { - WTF_MAKE_FAST_ALLOCATED; -public: - IntRect() { } - IntRect(const IntPoint& location, const IntSize& size) - : m_location(location), m_size(size) { } - IntRect(int x, int y, int width, int height) - : m_location(IntPoint(x, y)), m_size(IntSize(width, height)) { } - - explicit IntRect(const FloatRect&); // don't do this implicitly since it's lossy - explicit IntRect(const LayoutRect&); // don't do this implicitly since it's lossy - - IntPoint location() const { return m_location; } - IntSize size() const { return m_size; } - - void setLocation(const IntPoint& location) { m_location = location; } - void setSize(const IntSize& size) { m_size = size; } - - int x() const { return m_location.x(); } - int y() const { return m_location.y(); } - int maxX() const { return x() + width(); } - int maxY() const { return y() + height(); } - int width() const { return m_size.width(); } - int height() const { return m_size.height(); } - - void setX(int x) { m_location.setX(x); } - void setY(int y) { m_location.setY(y); } - void setWidth(int width) { m_size.setWidth(width); } - void setHeight(int height) { m_size.setHeight(height); } - - bool isEmpty() const { return m_size.isEmpty(); } - - // NOTE: The result is rounded to integer values, and thus may be not the exact - // center point. - IntPoint center() const { return IntPoint(x() + width() / 2, y() + height() / 2); } - - void move(const IntSize& size) { m_location += size; } - void moveBy(const IntPoint& offset) { m_location.move(offset.x(), offset.y()); } - void move(int dx, int dy) { m_location.move(dx, dy); } - - void expand(const IntSize& size) { m_size += size; } - void expand(int dw, int dh) { m_size.expand(dw, dh); } - void contract(const IntSize& size) { m_size -= size; } - void contract(int dw, int dh) { m_size.expand(-dw, -dh); } - - void shiftXEdgeTo(int edge) - { - int delta = edge - x(); - setX(edge); - setWidth(std::max(0, width() - delta)); - } - void shiftMaxXEdgeTo(int edge) - { - int delta = edge - maxX(); - setWidth(std::max(0, width() + delta)); - } - void shiftYEdgeTo(int edge) - { - int delta = edge - y(); - setY(edge); - setHeight(std::max(0, height() - delta)); - } - void shiftMaxYEdgeTo(int edge) - { - int delta = edge - maxY(); - setHeight(std::max(0, height() + delta)); - } - - IntPoint minXMinYCorner() const { return m_location; } // typically topLeft - IntPoint maxXMinYCorner() const { return IntPoint(m_location.x() + m_size.width(), m_location.y()); } // typically topRight - IntPoint minXMaxYCorner() const { return IntPoint(m_location.x(), m_location.y() + m_size.height()); } // typically bottomLeft - IntPoint maxXMaxYCorner() const { return IntPoint(m_location.x() + m_size.width(), m_location.y() + m_size.height()); } // typically bottomRight - - bool intersects(const IntRect&) const; - bool contains(const IntRect&) const; - - // This checks to see if the rect contains x,y in the traditional sense. - // Equivalent to checking if the rect contains a 1x1 rect below and to the right of (px,py). - bool contains(int px, int py) const - { return px >= x() && px < maxX() && py >= y() && py < maxY(); } - bool contains(const IntPoint& point) const { return contains(point.x(), point.y()); } - - void intersect(const IntRect&); - void unite(const IntRect&); - void uniteIfNonZero(const IntRect&); - - void inflateX(int dx) - { - m_location.setX(m_location.x() - dx); - m_size.setWidth(m_size.width() + dx + dx); - } - void inflateY(int dy) - { - m_location.setY(m_location.y() - dy); - m_size.setHeight(m_size.height() + dy + dy); - } - void inflate(int d) { inflateX(d); inflateY(d); } - void scale(float s); - - IntSize differenceToPoint(const IntPoint&) const; - int distanceSquaredToPoint(const IntPoint& p) const { return differenceToPoint(p).diagonalLengthSquared(); } - - IntRect transposedRect() const { return IntRect(m_location.transposedPoint(), m_size.transposedSize()); } - - operator SkRect() const; - operator SkIRect() const; + WTF_MAKE_FAST_ALLOCATED; + + public: + IntRect() {} + IntRect(const IntPoint& location, const IntSize& size) + : m_location(location), m_size(size) {} + IntRect(int x, int y, int width, int height) + : m_location(IntPoint(x, y)), m_size(IntSize(width, height)) {} + + explicit IntRect( + const FloatRect&); // don't do this implicitly since it's lossy + explicit IntRect( + const LayoutRect&); // don't do this implicitly since it's lossy + + IntPoint location() const { return m_location; } + IntSize size() const { return m_size; } + + void setLocation(const IntPoint& location) { m_location = location; } + void setSize(const IntSize& size) { m_size = size; } + + int x() const { return m_location.x(); } + int y() const { return m_location.y(); } + int maxX() const { return x() + width(); } + int maxY() const { return y() + height(); } + int width() const { return m_size.width(); } + int height() const { return m_size.height(); } + + void setX(int x) { m_location.setX(x); } + void setY(int y) { m_location.setY(y); } + void setWidth(int width) { m_size.setWidth(width); } + void setHeight(int height) { m_size.setHeight(height); } + + bool isEmpty() const { return m_size.isEmpty(); } + + // NOTE: The result is rounded to integer values, and thus may be not the + // exact center point. + IntPoint center() const { + return IntPoint(x() + width() / 2, y() + height() / 2); + } + + void move(const IntSize& size) { m_location += size; } + void moveBy(const IntPoint& offset) { + m_location.move(offset.x(), offset.y()); + } + void move(int dx, int dy) { m_location.move(dx, dy); } + + void expand(const IntSize& size) { m_size += size; } + void expand(int dw, int dh) { m_size.expand(dw, dh); } + void contract(const IntSize& size) { m_size -= size; } + void contract(int dw, int dh) { m_size.expand(-dw, -dh); } + + void shiftXEdgeTo(int edge) { + int delta = edge - x(); + setX(edge); + setWidth(std::max(0, width() - delta)); + } + void shiftMaxXEdgeTo(int edge) { + int delta = edge - maxX(); + setWidth(std::max(0, width() + delta)); + } + void shiftYEdgeTo(int edge) { + int delta = edge - y(); + setY(edge); + setHeight(std::max(0, height() - delta)); + } + void shiftMaxYEdgeTo(int edge) { + int delta = edge - maxY(); + setHeight(std::max(0, height() + delta)); + } + + IntPoint minXMinYCorner() const { return m_location; } // typically topLeft + IntPoint maxXMinYCorner() const { + return IntPoint(m_location.x() + m_size.width(), m_location.y()); + } // typically topRight + IntPoint minXMaxYCorner() const { + return IntPoint(m_location.x(), m_location.y() + m_size.height()); + } // typically bottomLeft + IntPoint maxXMaxYCorner() const { + return IntPoint(m_location.x() + m_size.width(), + m_location.y() + m_size.height()); + } // typically bottomRight + + bool intersects(const IntRect&) const; + bool contains(const IntRect&) const; + + // This checks to see if the rect contains x,y in the traditional sense. + // Equivalent to checking if the rect contains a 1x1 rect below and to the + // right of (px,py). + bool contains(int px, int py) const { + return px >= x() && px < maxX() && py >= y() && py < maxY(); + } + bool contains(const IntPoint& point) const { + return contains(point.x(), point.y()); + } + + void intersect(const IntRect&); + void unite(const IntRect&); + void uniteIfNonZero(const IntRect&); + + void inflateX(int dx) { + m_location.setX(m_location.x() - dx); + m_size.setWidth(m_size.width() + dx + dx); + } + void inflateY(int dy) { + m_location.setY(m_location.y() - dy); + m_size.setHeight(m_size.height() + dy + dy); + } + void inflate(int d) { + inflateX(d); + inflateY(d); + } + void scale(float s); + + IntSize differenceToPoint(const IntPoint&) const; + int distanceSquaredToPoint(const IntPoint& p) const { + return differenceToPoint(p).diagonalLengthSquared(); + } + + IntRect transposedRect() const { + return IntRect(m_location.transposedPoint(), m_size.transposedSize()); + } + + operator SkRect() const; + operator SkIRect() const; #ifndef NDEBUG - // Prints the rect to the screen. - void show() const; + // Prints the rect to the screen. + void show() const; #endif -private: - IntPoint m_location; - IntSize m_size; + private: + IntPoint m_location; + IntSize m_size; }; -inline IntRect intersection(const IntRect& a, const IntRect& b) -{ - IntRect c = a; - c.intersect(b); - return c; +inline IntRect intersection(const IntRect& a, const IntRect& b) { + IntRect c = a; + c.intersect(b); + return c; } -inline IntRect unionRect(const IntRect& a, const IntRect& b) -{ - IntRect c = a; - c.unite(b); - return c; +inline IntRect unionRect(const IntRect& a, const IntRect& b) { + IntRect c = a; + c.unite(b); + return c; } PLATFORM_EXPORT IntRect unionRect(const Vector&); -inline bool operator==(const IntRect& a, const IntRect& b) -{ - return a.location() == b.location() && a.size() == b.size(); +inline bool operator==(const IntRect& a, const IntRect& b) { + return a.location() == b.location() && a.size() == b.size(); } -inline bool operator!=(const IntRect& a, const IntRect& b) -{ - return a.location() != b.location() || a.size() != b.size(); +inline bool operator!=(const IntRect& a, const IntRect& b) { + return a.location() != b.location() || a.size() != b.size(); } -} // namespace blink +} // namespace blink WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(blink::IntRect); diff --git a/sky/engine/platform/geometry/IntRectExtent.h b/sky/engine/platform/geometry/IntRectExtent.h index 726949d6d7d5e..f34c2b33ca8d3 100644 --- a/sky/engine/platform/geometry/IntRectExtent.h +++ b/sky/engine/platform/geometry/IntRectExtent.h @@ -35,75 +35,57 @@ namespace blink { class IntRectExtent { -public: - IntRectExtent() - : m_top(0) - , m_right(0) - , m_bottom(0) - , m_left(0) - { - } - - IntRectExtent(int top, int right, int bottom, int left) - : m_top(top) - , m_right(right) - , m_bottom(bottom) - , m_left(left) - { - } - - int top() const { return m_top; } - void setTop(int top) { m_top = top; } - - int right() const { return m_right; } - void setRight(int right) { m_right = right; } - - int bottom() const { return m_bottom; } - void setBottom(int bottom) { m_bottom = bottom; } - - int left() const { return m_left; } - void setLeft(int left) { m_left = left; } - - bool isZero() const { return !left() && !right() && !top() && !bottom(); } - - void expandRect(LayoutRect& rect) const - { - if (isZero()) - return; - - rect.move(-left(), -top()); - rect.expand(left() + right(), top() + bottom()); - } - -private: - int m_top; - int m_right; - int m_bottom; - int m_left; + public: + IntRectExtent() : m_top(0), m_right(0), m_bottom(0), m_left(0) {} + + IntRectExtent(int top, int right, int bottom, int left) + : m_top(top), m_right(right), m_bottom(bottom), m_left(left) {} + + int top() const { return m_top; } + void setTop(int top) { m_top = top; } + + int right() const { return m_right; } + void setRight(int right) { m_right = right; } + + int bottom() const { return m_bottom; } + void setBottom(int bottom) { m_bottom = bottom; } + + int left() const { return m_left; } + void setLeft(int left) { m_left = left; } + + bool isZero() const { return !left() && !right() && !top() && !bottom(); } + + void expandRect(LayoutRect& rect) const { + if (isZero()) + return; + + rect.move(-left(), -top()); + rect.expand(left() + right(), top() + bottom()); + } + + private: + int m_top; + int m_right; + int m_bottom; + int m_left; }; -inline bool operator==(const IntRectExtent& a, const IntRectExtent& b) -{ - return a.top() == b.top() - && a.right() == b.right() - && a.bottom() == b.bottom() - && a.left() == b.left(); +inline bool operator==(const IntRectExtent& a, const IntRectExtent& b) { + return a.top() == b.top() && a.right() == b.right() && + a.bottom() == b.bottom() && a.left() == b.left(); } -inline bool operator!=(const IntRectExtent& a, const IntRectExtent& b) -{ - return !(a == b); +inline bool operator!=(const IntRectExtent& a, const IntRectExtent& b) { + return !(a == b); } -inline void operator+=(IntRectExtent& a, const IntRectExtent& b) -{ - a.setTop(a.top() + b.top()); - a.setRight(a.right() + b.right()); - a.setBottom(a.bottom() + b.bottom()); - a.setLeft(a.left() + b.left()); +inline void operator+=(IntRectExtent& a, const IntRectExtent& b) { + a.setTop(a.top() + b.top()); + a.setRight(a.right() + b.right()); + a.setBottom(a.bottom() + b.bottom()); + a.setLeft(a.left() + b.left()); } -} // namespace blink - +} // namespace blink #endif // SKY_ENGINE_PLATFORM_GEOMETRY_INTRECTEXTENT_H_ diff --git a/sky/engine/platform/geometry/IntSize.h b/sky/engine/platform/geometry/IntSize.h index 54d842472df80..59841d027dc88 100644 --- a/sky/engine/platform/geometry/IntSize.h +++ b/sky/engine/platform/geometry/IntSize.h @@ -1,5 +1,6 @@ /* - * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. + * All rights reserved. * Copyright (C) 2013 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -33,122 +34,99 @@ namespace blink { class PLATFORM_EXPORT IntSize { -public: - IntSize() : m_width(0), m_height(0) { } - IntSize(int width, int height) : m_width(width), m_height(height) { } - - int width() const { return m_width; } - int height() const { return m_height; } - - void setWidth(int width) { m_width = width; } - void setHeight(int height) { m_height = height; } - - bool isEmpty() const { return m_width <= 0 || m_height <= 0; } - bool isZero() const { return !m_width && !m_height; } - - float aspectRatio() const { return static_cast(m_width) / static_cast(m_height); } - - void expand(int width, int height) - { - m_width += width; - m_height += height; - } - - void scale(float widthScale, float heightScale) - { - m_width = static_cast(static_cast(m_width) * widthScale); - m_height = static_cast(static_cast(m_height) * heightScale); - } - - void scale(float scale) - { - this->scale(scale, scale); - } - - IntSize expandedTo(const IntSize& other) const - { - return IntSize(m_width > other.m_width ? m_width : other.m_width, - m_height > other.m_height ? m_height : other.m_height); - } - - IntSize shrunkTo(const IntSize& other) const - { - return IntSize(m_width < other.m_width ? m_width : other.m_width, - m_height < other.m_height ? m_height : other.m_height); - } - - void clampNegativeToZero() - { - *this = expandedTo(IntSize()); - } - - void clampToMinimumSize(const IntSize& minimumSize) - { - if (m_width < minimumSize.width()) - m_width = minimumSize.width(); - if (m_height < minimumSize.height()) - m_height = minimumSize.height(); - } - - // Return area in a uint64_t to avoid overflow. - uint64_t area() const - { - return static_cast(width()) * height(); - } - - int diagonalLengthSquared() const - { - return m_width * m_width + m_height * m_height; - } - - IntSize transposedSize() const - { - return IntSize(m_height, m_width); - } - -private: - int m_width, m_height; + public: + IntSize() : m_width(0), m_height(0) {} + IntSize(int width, int height) : m_width(width), m_height(height) {} + + int width() const { return m_width; } + int height() const { return m_height; } + + void setWidth(int width) { m_width = width; } + void setHeight(int height) { m_height = height; } + + bool isEmpty() const { return m_width <= 0 || m_height <= 0; } + bool isZero() const { return !m_width && !m_height; } + + float aspectRatio() const { + return static_cast(m_width) / static_cast(m_height); + } + + void expand(int width, int height) { + m_width += width; + m_height += height; + } + + void scale(float widthScale, float heightScale) { + m_width = static_cast(static_cast(m_width) * widthScale); + m_height = static_cast(static_cast(m_height) * heightScale); + } + + void scale(float scale) { this->scale(scale, scale); } + + IntSize expandedTo(const IntSize& other) const { + return IntSize(m_width > other.m_width ? m_width : other.m_width, + m_height > other.m_height ? m_height : other.m_height); + } + + IntSize shrunkTo(const IntSize& other) const { + return IntSize(m_width < other.m_width ? m_width : other.m_width, + m_height < other.m_height ? m_height : other.m_height); + } + + void clampNegativeToZero() { *this = expandedTo(IntSize()); } + + void clampToMinimumSize(const IntSize& minimumSize) { + if (m_width < minimumSize.width()) + m_width = minimumSize.width(); + if (m_height < minimumSize.height()) + m_height = minimumSize.height(); + } + + // Return area in a uint64_t to avoid overflow. + uint64_t area() const { return static_cast(width()) * height(); } + + int diagonalLengthSquared() const { + return m_width * m_width + m_height * m_height; + } + + IntSize transposedSize() const { return IntSize(m_height, m_width); } + + private: + int m_width, m_height; }; -inline IntSize& operator+=(IntSize& a, const IntSize& b) -{ - a.setWidth(a.width() + b.width()); - a.setHeight(a.height() + b.height()); - return a; +inline IntSize& operator+=(IntSize& a, const IntSize& b) { + a.setWidth(a.width() + b.width()); + a.setHeight(a.height() + b.height()); + return a; } -inline IntSize& operator-=(IntSize& a, const IntSize& b) -{ - a.setWidth(a.width() - b.width()); - a.setHeight(a.height() - b.height()); - return a; +inline IntSize& operator-=(IntSize& a, const IntSize& b) { + a.setWidth(a.width() - b.width()); + a.setHeight(a.height() - b.height()); + return a; } -inline IntSize operator+(const IntSize& a, const IntSize& b) -{ - return IntSize(a.width() + b.width(), a.height() + b.height()); +inline IntSize operator+(const IntSize& a, const IntSize& b) { + return IntSize(a.width() + b.width(), a.height() + b.height()); } -inline IntSize operator-(const IntSize& a, const IntSize& b) -{ - return IntSize(a.width() - b.width(), a.height() - b.height()); +inline IntSize operator-(const IntSize& a, const IntSize& b) { + return IntSize(a.width() - b.width(), a.height() - b.height()); } -inline IntSize operator-(const IntSize& size) -{ - return IntSize(-size.width(), -size.height()); +inline IntSize operator-(const IntSize& size) { + return IntSize(-size.width(), -size.height()); } -inline bool operator==(const IntSize& a, const IntSize& b) -{ - return a.width() == b.width() && a.height() == b.height(); +inline bool operator==(const IntSize& a, const IntSize& b) { + return a.width() == b.width() && a.height() == b.height(); } -inline bool operator!=(const IntSize& a, const IntSize& b) -{ - return a.width() != b.width() || a.height() != b.height(); +inline bool operator!=(const IntSize& a, const IntSize& b) { + return a.width() != b.width() || a.height() != b.height(); } -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_GEOMETRY_INTSIZE_H_ diff --git a/sky/engine/platform/geometry/IntSizeHash.h b/sky/engine/platform/geometry/IntSizeHash.h index e26c4c1354c03..c8ffa01482e66 100644 --- a/sky/engine/platform/geometry/IntSizeHash.h +++ b/sky/engine/platform/geometry/IntSizeHash.h @@ -26,23 +26,34 @@ namespace WTF { -template<> struct IntHash { - static unsigned hash(const blink::IntSize& key) { return pairIntHash(key.width(), key.height()); } - static bool equal(const blink::IntSize& a, const blink::IntSize& b) { return a == b; } - static const bool safeToCompareToEmptyOrDeleted = true; +template <> +struct IntHash { + static unsigned hash(const blink::IntSize& key) { + return pairIntHash(key.width(), key.height()); + } + static bool equal(const blink::IntSize& a, const blink::IntSize& b) { + return a == b; + } + static const bool safeToCompareToEmptyOrDeleted = true; }; -template<> struct DefaultHash { - typedef IntHash Hash; +template <> +struct DefaultHash { + typedef IntHash Hash; }; -template<> struct HashTraits : GenericHashTraits { - static const bool emptyValueIsZero = true; - static const bool needsDestruction = false; - static void constructDeletedValue(blink::IntSize& slot, bool) { new (NotNull, &slot) blink::IntSize(-1, -1); } - static bool isDeletedValue(const blink::IntSize& value) { return value.width() == -1 && value.height() == -1; } +template <> +struct HashTraits : GenericHashTraits { + static const bool emptyValueIsZero = true; + static const bool needsDestruction = false; + static void constructDeletedValue(blink::IntSize& slot, bool) { + new (NotNull, &slot) blink::IntSize(-1, -1); + } + static bool isDeletedValue(const blink::IntSize& value) { + return value.width() == -1 && value.height() == -1; + } }; -} // namespace WTF +} // namespace WTF #endif // SKY_ENGINE_PLATFORM_GEOMETRY_INTSIZEHASH_H_ diff --git a/sky/engine/platform/geometry/LayoutBoxExtent.cpp b/sky/engine/platform/geometry/LayoutBoxExtent.cpp index ac4b8d52dcfba..648cd6e45e352 100644 --- a/sky/engine/platform/geometry/LayoutBoxExtent.cpp +++ b/sky/engine/platform/geometry/LayoutBoxExtent.cpp @@ -34,94 +34,78 @@ namespace blink { -LayoutUnit LayoutBoxExtent::logicalTop() const -{ - return m_top; +LayoutUnit LayoutBoxExtent::logicalTop() const { + return m_top; } -LayoutUnit LayoutBoxExtent::logicalBottom() const -{ - return m_bottom; +LayoutUnit LayoutBoxExtent::logicalBottom() const { + return m_bottom; } -LayoutUnit LayoutBoxExtent::logicalLeft() const -{ - return m_left; +LayoutUnit LayoutBoxExtent::logicalLeft() const { + return m_left; } -LayoutUnit LayoutBoxExtent::logicalRight() const -{ - return m_right; +LayoutUnit LayoutBoxExtent::logicalRight() const { + return m_right; } -LayoutUnit LayoutBoxExtent::before() const -{ - // FIXME(sky): Remove - return m_top; +LayoutUnit LayoutBoxExtent::before() const { + // FIXME(sky): Remove + return m_top; } -LayoutUnit LayoutBoxExtent::after() const -{ - // FIXME(sky): Remove - return m_bottom; +LayoutUnit LayoutBoxExtent::after() const { + // FIXME(sky): Remove + return m_bottom; } -LayoutUnit LayoutBoxExtent::start(TextDirection direction) const -{ - return isLeftToRightDirection(direction) ? m_left : m_right; +LayoutUnit LayoutBoxExtent::start(TextDirection direction) const { + return isLeftToRightDirection(direction) ? m_left : m_right; } -LayoutUnit LayoutBoxExtent::end(TextDirection direction) const -{ - return isLeftToRightDirection(direction) ? m_right : m_left; +LayoutUnit LayoutBoxExtent::end(TextDirection direction) const { + return isLeftToRightDirection(direction) ? m_right : m_left; } -void LayoutBoxExtent::setBefore(LayoutUnit value) -{ - // FIXME(sky): Remove - m_top = value; +void LayoutBoxExtent::setBefore(LayoutUnit value) { + // FIXME(sky): Remove + m_top = value; } -void LayoutBoxExtent::setAfter(LayoutUnit value) -{ - // FIXME(sky): Remove - m_bottom = value; +void LayoutBoxExtent::setAfter(LayoutUnit value) { + // FIXME(sky): Remove + m_bottom = value; } -void LayoutBoxExtent::setStart(TextDirection direction, LayoutUnit value) -{ - if (isLeftToRightDirection(direction)) - m_left = value; - else - m_right = value; +void LayoutBoxExtent::setStart(TextDirection direction, LayoutUnit value) { + if (isLeftToRightDirection(direction)) + m_left = value; + else + m_right = value; } -void LayoutBoxExtent::setEnd(TextDirection direction, LayoutUnit value) -{ - if (isLeftToRightDirection(direction)) - m_right = value; - else - m_left = value; +void LayoutBoxExtent::setEnd(TextDirection direction, LayoutUnit value) { + if (isLeftToRightDirection(direction)) + m_right = value; + else + m_left = value; } -LayoutUnit& LayoutBoxExtent::mutableLogicalLeft() -{ - return m_left; +LayoutUnit& LayoutBoxExtent::mutableLogicalLeft() { + return m_left; } -LayoutUnit& LayoutBoxExtent::mutableLogicalRight() -{ - return m_right; +LayoutUnit& LayoutBoxExtent::mutableLogicalRight() { + return m_right; } -LayoutUnit& LayoutBoxExtent::mutableBefore() -{ - return m_top; +LayoutUnit& LayoutBoxExtent::mutableBefore() { + return m_top; } -LayoutUnit& LayoutBoxExtent::mutableAfter() -{ - return m_bottom; +LayoutUnit& LayoutBoxExtent::mutableAfter() { + return m_bottom; } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/geometry/LayoutBoxExtent.h b/sky/engine/platform/geometry/LayoutBoxExtent.h index 1a8242c14d6a7..cb565c7d10262 100644 --- a/sky/engine/platform/geometry/LayoutBoxExtent.h +++ b/sky/engine/platform/geometry/LayoutBoxExtent.h @@ -38,49 +38,52 @@ namespace blink { class PLATFORM_EXPORT LayoutBoxExtent { -public: - LayoutBoxExtent() : m_top(0), m_right(0), m_bottom(0), m_left(0) { } - LayoutBoxExtent(LayoutUnit top, LayoutUnit right, LayoutUnit bottom, LayoutUnit left) - : m_top(top), m_right(right), m_bottom(bottom), m_left(left) { } + public: + LayoutBoxExtent() : m_top(0), m_right(0), m_bottom(0), m_left(0) {} + LayoutBoxExtent(LayoutUnit top, + LayoutUnit right, + LayoutUnit bottom, + LayoutUnit left) + : m_top(top), m_right(right), m_bottom(bottom), m_left(left) {} - inline LayoutUnit top() const { return m_top; } - inline LayoutUnit right() const { return m_right; } - inline LayoutUnit bottom() const { return m_bottom; } - inline LayoutUnit left() const { return m_left; } + inline LayoutUnit top() const { return m_top; } + inline LayoutUnit right() const { return m_right; } + inline LayoutUnit bottom() const { return m_bottom; } + inline LayoutUnit left() const { return m_left; } - inline void setTop(LayoutUnit value) { m_top = value; } - inline void setRight(LayoutUnit value) { m_right = value; } - inline void setBottom(LayoutUnit value) { m_bottom = value; } - inline void setLeft(LayoutUnit value) { m_left = value; } + inline void setTop(LayoutUnit value) { m_top = value; } + inline void setRight(LayoutUnit value) { m_right = value; } + inline void setBottom(LayoutUnit value) { m_bottom = value; } + inline void setLeft(LayoutUnit value) { m_left = value; } - LayoutUnit logicalTop() const; - LayoutUnit logicalBottom() const; - LayoutUnit logicalLeft() const; - LayoutUnit logicalRight() const; + LayoutUnit logicalTop() const; + LayoutUnit logicalBottom() const; + LayoutUnit logicalLeft() const; + LayoutUnit logicalRight() const; - LayoutUnit before() const; - LayoutUnit after() const; - LayoutUnit start(TextDirection) const; - LayoutUnit end(TextDirection) const; + LayoutUnit before() const; + LayoutUnit after() const; + LayoutUnit start(TextDirection) const; + LayoutUnit end(TextDirection) const; - void setBefore(LayoutUnit); - void setAfter(LayoutUnit); - void setStart(TextDirection, LayoutUnit); - void setEnd(TextDirection, LayoutUnit); + void setBefore(LayoutUnit); + void setAfter(LayoutUnit); + void setStart(TextDirection, LayoutUnit); + void setEnd(TextDirection, LayoutUnit); - LayoutUnit& mutableLogicalLeft(); - LayoutUnit& mutableLogicalRight(); + LayoutUnit& mutableLogicalLeft(); + LayoutUnit& mutableLogicalRight(); - LayoutUnit& mutableBefore(); - LayoutUnit& mutableAfter(); + LayoutUnit& mutableBefore(); + LayoutUnit& mutableAfter(); -private: - LayoutUnit m_top; - LayoutUnit m_right; - LayoutUnit m_bottom; - LayoutUnit m_left; + private: + LayoutUnit m_top; + LayoutUnit m_right; + LayoutUnit m_bottom; + LayoutUnit m_left; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_GEOMETRY_LAYOUTBOXEXTENT_H_ diff --git a/sky/engine/platform/geometry/LayoutPoint.h b/sky/engine/platform/geometry/LayoutPoint.h index 19db896dd1946..46b4e09a4e885 100644 --- a/sky/engine/platform/geometry/LayoutPoint.h +++ b/sky/engine/platform/geometry/LayoutPoint.h @@ -38,167 +38,143 @@ namespace blink { class LayoutPoint { -public: - LayoutPoint() { } - LayoutPoint(LayoutUnit x, LayoutUnit y) : m_x(x), m_y(y) { } - LayoutPoint(const IntPoint& point) : m_x(point.x()), m_y(point.y()) { } - explicit LayoutPoint(const FloatPoint& size) : m_x(size.x()), m_y(size.y()) { } - explicit LayoutPoint(const LayoutSize& size) : m_x(size.width()), m_y(size.height()) { } - - static LayoutPoint zero() { return LayoutPoint(); } - - LayoutUnit x() const { return m_x; } - LayoutUnit y() const { return m_y; } - - void setX(LayoutUnit x) { m_x = x; } - void setY(LayoutUnit y) { m_y = y; } - - void move(const LayoutSize& s) { move(s.width(), s.height()); } - void moveBy(const LayoutPoint& offset) { move(offset.x(), offset.y()); } - void move(LayoutUnit dx, LayoutUnit dy) { m_x += dx; m_y += dy; } - void scale(float sx, float sy) - { - m_x *= sx; - m_y *= sy; - } - - LayoutPoint expandedTo(const LayoutPoint& other) const - { - return LayoutPoint(std::max(m_x, other.m_x), std::max(m_y, other.m_y)); - } - - LayoutPoint shrunkTo(const LayoutPoint& other) const - { - return LayoutPoint(std::min(m_x, other.m_x), std::min(m_y, other.m_y)); - } - - void clampNegativeToZero() - { - *this = expandedTo(zero()); - } - - LayoutPoint transposedPoint() const - { - return LayoutPoint(m_y, m_x); - } - -private: - LayoutUnit m_x, m_y; + public: + LayoutPoint() {} + LayoutPoint(LayoutUnit x, LayoutUnit y) : m_x(x), m_y(y) {} + LayoutPoint(const IntPoint& point) : m_x(point.x()), m_y(point.y()) {} + explicit LayoutPoint(const FloatPoint& size) : m_x(size.x()), m_y(size.y()) {} + explicit LayoutPoint(const LayoutSize& size) + : m_x(size.width()), m_y(size.height()) {} + + static LayoutPoint zero() { return LayoutPoint(); } + + LayoutUnit x() const { return m_x; } + LayoutUnit y() const { return m_y; } + + void setX(LayoutUnit x) { m_x = x; } + void setY(LayoutUnit y) { m_y = y; } + + void move(const LayoutSize& s) { move(s.width(), s.height()); } + void moveBy(const LayoutPoint& offset) { move(offset.x(), offset.y()); } + void move(LayoutUnit dx, LayoutUnit dy) { + m_x += dx; + m_y += dy; + } + void scale(float sx, float sy) { + m_x *= sx; + m_y *= sy; + } + + LayoutPoint expandedTo(const LayoutPoint& other) const { + return LayoutPoint(std::max(m_x, other.m_x), std::max(m_y, other.m_y)); + } + + LayoutPoint shrunkTo(const LayoutPoint& other) const { + return LayoutPoint(std::min(m_x, other.m_x), std::min(m_y, other.m_y)); + } + + void clampNegativeToZero() { *this = expandedTo(zero()); } + + LayoutPoint transposedPoint() const { return LayoutPoint(m_y, m_x); } + + private: + LayoutUnit m_x, m_y; }; -ALWAYS_INLINE LayoutPoint& operator+=(LayoutPoint& a, const LayoutSize& b) -{ - a.move(b.width(), b.height()); - return a; +ALWAYS_INLINE LayoutPoint& operator+=(LayoutPoint& a, const LayoutSize& b) { + a.move(b.width(), b.height()); + return a; } -ALWAYS_INLINE LayoutPoint& operator-=(LayoutPoint& a, const LayoutSize& b) -{ - a.move(-b.width(), -b.height()); - return a; +ALWAYS_INLINE LayoutPoint& operator-=(LayoutPoint& a, const LayoutSize& b) { + a.move(-b.width(), -b.height()); + return a; } -inline LayoutPoint operator+(const LayoutPoint& a, const LayoutSize& b) -{ - return LayoutPoint(a.x() + b.width(), a.y() + b.height()); +inline LayoutPoint operator+(const LayoutPoint& a, const LayoutSize& b) { + return LayoutPoint(a.x() + b.width(), a.y() + b.height()); } -ALWAYS_INLINE LayoutPoint operator+(const LayoutPoint& a, const LayoutPoint& b) -{ - return LayoutPoint(a.x() + b.x(), a.y() + b.y()); +ALWAYS_INLINE LayoutPoint operator+(const LayoutPoint& a, + const LayoutPoint& b) { + return LayoutPoint(a.x() + b.x(), a.y() + b.y()); } -ALWAYS_INLINE LayoutSize operator-(const LayoutPoint& a, const LayoutPoint& b) -{ - return LayoutSize(a.x() - b.x(), a.y() - b.y()); +ALWAYS_INLINE LayoutSize operator-(const LayoutPoint& a, const LayoutPoint& b) { + return LayoutSize(a.x() - b.x(), a.y() - b.y()); } -inline LayoutPoint operator-(const LayoutPoint& a, const LayoutSize& b) -{ - return LayoutPoint(a.x() - b.width(), a.y() - b.height()); +inline LayoutPoint operator-(const LayoutPoint& a, const LayoutSize& b) { + return LayoutPoint(a.x() - b.width(), a.y() - b.height()); } -inline LayoutPoint operator-(const LayoutPoint& point) -{ - return LayoutPoint(-point.x(), -point.y()); +inline LayoutPoint operator-(const LayoutPoint& point) { + return LayoutPoint(-point.x(), -point.y()); } -ALWAYS_INLINE bool operator==(const LayoutPoint& a, const LayoutPoint& b) -{ - return a.x() == b.x() && a.y() == b.y(); +ALWAYS_INLINE bool operator==(const LayoutPoint& a, const LayoutPoint& b) { + return a.x() == b.x() && a.y() == b.y(); } -inline bool operator!=(const LayoutPoint& a, const LayoutPoint& b) -{ - return a.x() != b.x() || a.y() != b.y(); +inline bool operator!=(const LayoutPoint& a, const LayoutPoint& b) { + return a.x() != b.x() || a.y() != b.y(); } -inline LayoutPoint toPoint(const LayoutSize& size) -{ - return LayoutPoint(size.width(), size.height()); +inline LayoutPoint toPoint(const LayoutSize& size) { + return LayoutPoint(size.width(), size.height()); } -inline LayoutPoint toLayoutPoint(const LayoutSize& p) -{ - return LayoutPoint(p.width(), p.height()); +inline LayoutPoint toLayoutPoint(const LayoutSize& p) { + return LayoutPoint(p.width(), p.height()); } -inline LayoutSize toSize(const LayoutPoint& a) -{ - return LayoutSize(a.x(), a.y()); +inline LayoutSize toSize(const LayoutPoint& a) { + return LayoutSize(a.x(), a.y()); } -inline IntPoint flooredIntPoint(const LayoutPoint& point) -{ - return IntPoint(point.x().floor(), point.y().floor()); +inline IntPoint flooredIntPoint(const LayoutPoint& point) { + return IntPoint(point.x().floor(), point.y().floor()); } -inline IntPoint roundedIntPoint(const LayoutPoint& point) -{ - return IntPoint(point.x().round(), point.y().round()); +inline IntPoint roundedIntPoint(const LayoutPoint& point) { + return IntPoint(point.x().round(), point.y().round()); } -inline IntPoint roundedIntPoint(const LayoutSize& size) -{ - return IntPoint(size.width().round(), size.height().round()); +inline IntPoint roundedIntPoint(const LayoutSize& size) { + return IntPoint(size.width().round(), size.height().round()); } -inline IntPoint ceiledIntPoint(const LayoutPoint& point) -{ - return IntPoint(point.x().ceil(), point.y().ceil()); +inline IntPoint ceiledIntPoint(const LayoutPoint& point) { + return IntPoint(point.x().ceil(), point.y().ceil()); } -inline LayoutPoint flooredLayoutPoint(const FloatPoint& p) -{ - return LayoutPoint(LayoutUnit::fromFloatFloor(p.x()), LayoutUnit::fromFloatFloor(p.y())); +inline LayoutPoint flooredLayoutPoint(const FloatPoint& p) { + return LayoutPoint(LayoutUnit::fromFloatFloor(p.x()), + LayoutUnit::fromFloatFloor(p.y())); } -inline LayoutPoint ceiledLayoutPoint(const FloatPoint& p) -{ - return LayoutPoint(LayoutUnit::fromFloatCeil(p.x()), LayoutUnit::fromFloatCeil(p.y())); +inline LayoutPoint ceiledLayoutPoint(const FloatPoint& p) { + return LayoutPoint(LayoutUnit::fromFloatCeil(p.x()), + LayoutUnit::fromFloatCeil(p.y())); } -inline IntSize pixelSnappedIntSize(const LayoutSize& s, const LayoutPoint& p) -{ - return IntSize(snapSizeToPixel(s.width(), p.x()), snapSizeToPixel(s.height(), p.y())); +inline IntSize pixelSnappedIntSize(const LayoutSize& s, const LayoutPoint& p) { + return IntSize(snapSizeToPixel(s.width(), p.x()), + snapSizeToPixel(s.height(), p.y())); } -inline LayoutPoint roundedLayoutPoint(const FloatPoint& p) -{ - return LayoutPoint(p); +inline LayoutPoint roundedLayoutPoint(const FloatPoint& p) { + return LayoutPoint(p); } -inline LayoutSize toLayoutSize(const LayoutPoint& p) -{ - return LayoutSize(p.x(), p.y()); +inline LayoutSize toLayoutSize(const LayoutPoint& p) { + return LayoutSize(p.x(), p.y()); } -inline LayoutPoint flooredLayoutPoint(const FloatSize& s) -{ - return flooredLayoutPoint(FloatPoint(s)); +inline LayoutPoint flooredLayoutPoint(const FloatSize& s) { + return flooredLayoutPoint(FloatPoint(s)); } - -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_GEOMETRY_LAYOUTPOINT_H_ diff --git a/sky/engine/platform/geometry/LayoutRect.cpp b/sky/engine/platform/geometry/LayoutRect.cpp index 456b1065d62dc..da821e662d40b 100644 --- a/sky/engine/platform/geometry/LayoutRect.cpp +++ b/sky/engine/platform/geometry/LayoutRect.cpp @@ -38,120 +38,113 @@ namespace blink { LayoutRect::LayoutRect(const FloatRect& r) - : m_location(LayoutPoint(r.location())) - , m_size(LayoutSize(r.size())) -{ -} + : m_location(LayoutPoint(r.location())), m_size(LayoutSize(r.size())) {} -bool LayoutRect::intersects(const LayoutRect& other) const -{ - // Checking emptiness handles negative widths as well as zero. - return !isEmpty() && !other.isEmpty() - && x() < other.maxX() && other.x() < maxX() - && y() < other.maxY() && other.y() < maxY(); +bool LayoutRect::intersects(const LayoutRect& other) const { + // Checking emptiness handles negative widths as well as zero. + return !isEmpty() && !other.isEmpty() && x() < other.maxX() && + other.x() < maxX() && y() < other.maxY() && other.y() < maxY(); } -bool LayoutRect::contains(const LayoutRect& other) const -{ - return x() <= other.x() && maxX() >= other.maxX() - && y() <= other.y() && maxY() >= other.maxY(); +bool LayoutRect::contains(const LayoutRect& other) const { + return x() <= other.x() && maxX() >= other.maxX() && y() <= other.y() && + maxY() >= other.maxY(); } -void LayoutRect::intersect(const LayoutRect& other) -{ - LayoutPoint newLocation(std::max(x(), other.x()), std::max(y(), other.y())); - LayoutPoint newMaxPoint(std::min(maxX(), other.maxX()), std::min(maxY(), other.maxY())); +void LayoutRect::intersect(const LayoutRect& other) { + LayoutPoint newLocation(std::max(x(), other.x()), std::max(y(), other.y())); + LayoutPoint newMaxPoint(std::min(maxX(), other.maxX()), + std::min(maxY(), other.maxY())); - // Return a clean empty rectangle for non-intersecting cases. - if (newLocation.x() >= newMaxPoint.x() || newLocation.y() >= newMaxPoint.y()) { - newLocation = LayoutPoint(0, 0); - newMaxPoint = LayoutPoint(0, 0); - } + // Return a clean empty rectangle for non-intersecting cases. + if (newLocation.x() >= newMaxPoint.x() || + newLocation.y() >= newMaxPoint.y()) { + newLocation = LayoutPoint(0, 0); + newMaxPoint = LayoutPoint(0, 0); + } - m_location = newLocation; - m_size = newMaxPoint - newLocation; + m_location = newLocation; + m_size = newMaxPoint - newLocation; } -void LayoutRect::unite(const LayoutRect& other) -{ - // Handle empty special cases first. - if (other.isEmpty()) - return; - if (isEmpty()) { - *this = other; - return; - } - - LayoutPoint newLocation(std::min(x(), other.x()), std::min(y(), other.y())); - LayoutPoint newMaxPoint(std::max(maxX(), other.maxX()), std::max(maxY(), other.maxY())); - - m_location = newLocation; - m_size = newMaxPoint - newLocation; +void LayoutRect::unite(const LayoutRect& other) { + // Handle empty special cases first. + if (other.isEmpty()) + return; + if (isEmpty()) { + *this = other; + return; + } + + LayoutPoint newLocation(std::min(x(), other.x()), std::min(y(), other.y())); + LayoutPoint newMaxPoint(std::max(maxX(), other.maxX()), + std::max(maxY(), other.maxY())); + + m_location = newLocation; + m_size = newMaxPoint - newLocation; } -void LayoutRect::uniteIfNonZero(const LayoutRect& other) -{ - // Handle empty special cases first. - if (!other.width() && !other.height()) - return; - if (!width() && !height()) { - *this = other; - return; - } - - LayoutPoint newLocation(std::min(x(), other.x()), std::min(y(), other.y())); - LayoutPoint newMaxPoint(std::max(maxX(), other.maxX()), std::max(maxY(), other.maxY())); - - m_location = newLocation; - m_size = newMaxPoint - newLocation; +void LayoutRect::uniteIfNonZero(const LayoutRect& other) { + // Handle empty special cases first. + if (!other.width() && !other.height()) + return; + if (!width() && !height()) { + *this = other; + return; + } + + LayoutPoint newLocation(std::min(x(), other.x()), std::min(y(), other.y())); + LayoutPoint newMaxPoint(std::max(maxX(), other.maxX()), + std::max(maxY(), other.maxY())); + + m_location = newLocation; + m_size = newMaxPoint - newLocation; } -void LayoutRect::scale(float s) -{ - m_location.scale(s, s); - m_size.scale(s); +void LayoutRect::scale(float s) { + m_location.scale(s, s); + m_size.scale(s); } -void LayoutRect::scale(float xAxisScale, float yAxisScale) -{ - m_location.scale(xAxisScale, yAxisScale); - m_size.scale(xAxisScale, yAxisScale); +void LayoutRect::scale(float xAxisScale, float yAxisScale) { + m_location.scale(xAxisScale, yAxisScale); + m_size.scale(xAxisScale, yAxisScale); } #ifndef NDEBUG -void LayoutRect::show(bool showRawValue) const -{ - if (showRawValue) - printf("Rect (in raw layout units): [x=%d y=%d maxX=%d maxY=%d]\n", x().rawValue(), y().rawValue(), maxX().rawValue(), maxY().rawValue()); - else - printf("Rect (in pixels): [x=%lf y=%lf maxX=%lf maxY=%lf]\n", x().toDouble(), y().toDouble(), maxX().toDouble(), maxY().toDouble()); +void LayoutRect::show(bool showRawValue) const { + if (showRawValue) + printf("Rect (in raw layout units): [x=%d y=%d maxX=%d maxY=%d]\n", + x().rawValue(), y().rawValue(), maxX().rawValue(), + maxY().rawValue()); + else + printf("Rect (in pixels): [x=%lf y=%lf maxX=%lf maxY=%lf]\n", + x().toDouble(), y().toDouble(), maxX().toDouble(), + maxY().toDouble()); } #endif -LayoutRect unionRect(const Vector& rects) -{ - LayoutRect result; +LayoutRect unionRect(const Vector& rects) { + LayoutRect result; - size_t count = rects.size(); - for (size_t i = 0; i < count; ++i) - result.unite(rects[i]); + size_t count = rects.size(); + for (size_t i = 0; i < count; ++i) + result.unite(rects[i]); - return result; + return result; } -IntRect enclosingIntRect(const LayoutRect& rect) -{ - IntPoint location = flooredIntPoint(rect.minXMinYCorner()); - IntPoint maxPoint = ceiledIntPoint(rect.maxXMaxYCorner()); +IntRect enclosingIntRect(const LayoutRect& rect) { + IntPoint location = flooredIntPoint(rect.minXMinYCorner()); + IntPoint maxPoint = ceiledIntPoint(rect.maxXMaxYCorner()); - return IntRect(location, maxPoint - location); + return IntRect(location, maxPoint - location); } -LayoutRect enclosingLayoutRect(const FloatRect& rect) -{ - LayoutPoint location = flooredLayoutPoint(rect.minXMinYCorner()); - LayoutPoint maxPoint = ceiledLayoutPoint(rect.maxXMaxYCorner()); - return LayoutRect(location, maxPoint - location); +LayoutRect enclosingLayoutRect(const FloatRect& rect) { + LayoutPoint location = flooredLayoutPoint(rect.minXMinYCorner()); + LayoutPoint maxPoint = ceiledLayoutPoint(rect.maxXMaxYCorner()); + return LayoutRect(location, maxPoint - location); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/geometry/LayoutRect.h b/sky/engine/platform/geometry/LayoutRect.h index a64ae43c65b5c..099d467ac5d73 100644 --- a/sky/engine/platform/geometry/LayoutRect.h +++ b/sky/engine/platform/geometry/LayoutRect.h @@ -41,195 +41,220 @@ namespace blink { class FloatRect; class PLATFORM_EXPORT LayoutRect { -public: - LayoutRect() { } - LayoutRect(const LayoutPoint& location, const LayoutSize& size) - : m_location(location), m_size(size) { } - LayoutRect(LayoutUnit x, LayoutUnit y, LayoutUnit width, LayoutUnit height) - : m_location(LayoutPoint(x, y)), m_size(LayoutSize(width, height)) { } - LayoutRect(const FloatPoint& location, const FloatSize& size) - : m_location(location), m_size(size) { } - LayoutRect(const IntRect& rect) : m_location(rect.location()), m_size(rect.size()) { } - - explicit LayoutRect(const FloatRect&); // don't do this implicitly since it's lossy - - LayoutPoint location() const { return m_location; } - LayoutSize size() const { return m_size; } - - IntPoint pixelSnappedLocation() const { return roundedIntPoint(m_location); } - IntSize pixelSnappedSize() const { return IntSize(snapSizeToPixel(m_size.width(), m_location.x()), snapSizeToPixel(m_size.height(), m_location.y())); } - - void setLocation(const LayoutPoint& location) { m_location = location; } - void setSize(const LayoutSize& size) { m_size = size; } - - ALWAYS_INLINE LayoutUnit x() const { return m_location.x(); } - ALWAYS_INLINE LayoutUnit y() const { return m_location.y(); } - ALWAYS_INLINE LayoutUnit maxX() const { return x() + width(); } - ALWAYS_INLINE LayoutUnit maxY() const { return y() + height(); } - LayoutUnit width() const { return m_size.width(); } - LayoutUnit height() const { return m_size.height(); } - - int pixelSnappedX() const { return x().round(); } - int pixelSnappedY() const { return y().round(); } - int pixelSnappedWidth() const { return snapSizeToPixel(width(), x()); } - int pixelSnappedHeight() const { return snapSizeToPixel(height(), y()); } - int pixelSnappedMaxX() const { return (m_location.x() + m_size.width()).round(); } - int pixelSnappedMaxY() const { return (m_location.y() + m_size.height()).round(); } - - void setX(LayoutUnit x) { m_location.setX(x); } - void setY(LayoutUnit y) { m_location.setY(y); } - void setWidth(LayoutUnit width) { m_size.setWidth(width); } - void setHeight(LayoutUnit height) { m_size.setHeight(height); } - - ALWAYS_INLINE bool isEmpty() const { return m_size.isEmpty(); } - - // NOTE: The result is rounded to integer values, and thus may be not the exact - // center point. - LayoutPoint center() const { return LayoutPoint(x() + width() / 2, y() + height() / 2); } - - void move(const LayoutSize& size) { m_location += size; } - void moveBy(const LayoutPoint& offset) { m_location.move(offset.x(), offset.y()); } - void move(LayoutUnit dx, LayoutUnit dy) { m_location.move(dx, dy); } - - void expand(const LayoutSize& size) { m_size += size; } - void expand(const LayoutBoxExtent& box) - { - m_location.move(-box.left(), -box.top()); - m_size.expand(box.left() + box.right(), box.top() + box.bottom()); - } - void expand(LayoutUnit dw, LayoutUnit dh) { m_size.expand(dw, dh); } - void contract(const LayoutSize& size) { m_size -= size; } - void contract(const LayoutBoxExtent& box) - { - m_location.move(box.left(), box.top()); - m_size.shrink(box.left() + box.right(), box.top() + box.bottom()); - } - void contract(LayoutUnit dw, LayoutUnit dh) { m_size.expand(-dw, -dh); } - - void shiftXEdgeTo(LayoutUnit edge) - { - LayoutUnit delta = edge - x(); - setX(edge); - setWidth(std::max(0, width() - delta)); - } - void shiftMaxXEdgeTo(LayoutUnit edge) - { - LayoutUnit delta = edge - maxX(); - setWidth(std::max(0, width() + delta)); - } - void shiftYEdgeTo(LayoutUnit edge) - { - LayoutUnit delta = edge - y(); - setY(edge); - setHeight(std::max(0, height() - delta)); - } - void shiftMaxYEdgeTo(LayoutUnit edge) - { - LayoutUnit delta = edge - maxY(); - setHeight(std::max(0, height() + delta)); - } - - LayoutPoint minXMinYCorner() const { return m_location; } // typically topLeft - LayoutPoint maxXMinYCorner() const { return LayoutPoint(m_location.x() + m_size.width(), m_location.y()); } // typically topRight - LayoutPoint minXMaxYCorner() const { return LayoutPoint(m_location.x(), m_location.y() + m_size.height()); } // typically bottomLeft - LayoutPoint maxXMaxYCorner() const { return LayoutPoint(m_location.x() + m_size.width(), m_location.y() + m_size.height()); } // typically bottomRight - - bool intersects(const LayoutRect&) const; - bool contains(const LayoutRect&) const; - - // This checks to see if the rect contains x,y in the traditional sense. - // Equivalent to checking if the rect contains a 1x1 rect below and to the right of (px,py). - bool contains(LayoutUnit px, LayoutUnit py) const - { return px >= x() && px < maxX() && py >= y() && py < maxY(); } - bool contains(const LayoutPoint& point) const { return contains(point.x(), point.y()); } - - void intersect(const LayoutRect&); - void unite(const LayoutRect&); - void uniteIfNonZero(const LayoutRect&); - - void inflateX(LayoutUnit dx) - { - m_location.setX(m_location.x() - dx); - m_size.setWidth(m_size.width() + dx + dx); - } - void inflateY(LayoutUnit dy) - { - m_location.setY(m_location.y() - dy); - m_size.setHeight(m_size.height() + dy + dy); - } - void inflate(LayoutUnit d) { inflateX(d); inflateY(d); } - void scale(float s); - void scale(float xAxisScale, float yAxisScale); - - LayoutRect transposedRect() const { return LayoutRect(m_location.transposedPoint(), m_size.transposedSize()); } - - static LayoutRect infiniteRect() - { - // Return a rect that is slightly smaller than the true max rect to allow pixelSnapping to round up to the nearest IntRect without overflowing. - return LayoutRect(LayoutUnit::nearlyMin() / 2, LayoutUnit::nearlyMin() / 2, LayoutUnit::nearlyMax(), LayoutUnit::nearlyMax()); - } + public: + LayoutRect() {} + LayoutRect(const LayoutPoint& location, const LayoutSize& size) + : m_location(location), m_size(size) {} + LayoutRect(LayoutUnit x, LayoutUnit y, LayoutUnit width, LayoutUnit height) + : m_location(LayoutPoint(x, y)), m_size(LayoutSize(width, height)) {} + LayoutRect(const FloatPoint& location, const FloatSize& size) + : m_location(location), m_size(size) {} + LayoutRect(const IntRect& rect) + : m_location(rect.location()), m_size(rect.size()) {} + + explicit LayoutRect( + const FloatRect&); // don't do this implicitly since it's lossy + + LayoutPoint location() const { return m_location; } + LayoutSize size() const { return m_size; } + + IntPoint pixelSnappedLocation() const { return roundedIntPoint(m_location); } + IntSize pixelSnappedSize() const { + return IntSize(snapSizeToPixel(m_size.width(), m_location.x()), + snapSizeToPixel(m_size.height(), m_location.y())); + } + + void setLocation(const LayoutPoint& location) { m_location = location; } + void setSize(const LayoutSize& size) { m_size = size; } + + ALWAYS_INLINE LayoutUnit x() const { return m_location.x(); } + ALWAYS_INLINE LayoutUnit y() const { return m_location.y(); } + ALWAYS_INLINE LayoutUnit maxX() const { return x() + width(); } + ALWAYS_INLINE LayoutUnit maxY() const { return y() + height(); } + LayoutUnit width() const { return m_size.width(); } + LayoutUnit height() const { return m_size.height(); } + + int pixelSnappedX() const { return x().round(); } + int pixelSnappedY() const { return y().round(); } + int pixelSnappedWidth() const { return snapSizeToPixel(width(), x()); } + int pixelSnappedHeight() const { return snapSizeToPixel(height(), y()); } + int pixelSnappedMaxX() const { + return (m_location.x() + m_size.width()).round(); + } + int pixelSnappedMaxY() const { + return (m_location.y() + m_size.height()).round(); + } + + void setX(LayoutUnit x) { m_location.setX(x); } + void setY(LayoutUnit y) { m_location.setY(y); } + void setWidth(LayoutUnit width) { m_size.setWidth(width); } + void setHeight(LayoutUnit height) { m_size.setHeight(height); } + + ALWAYS_INLINE bool isEmpty() const { return m_size.isEmpty(); } + + // NOTE: The result is rounded to integer values, and thus may be not the + // exact center point. + LayoutPoint center() const { + return LayoutPoint(x() + width() / 2, y() + height() / 2); + } + + void move(const LayoutSize& size) { m_location += size; } + void moveBy(const LayoutPoint& offset) { + m_location.move(offset.x(), offset.y()); + } + void move(LayoutUnit dx, LayoutUnit dy) { m_location.move(dx, dy); } + + void expand(const LayoutSize& size) { m_size += size; } + void expand(const LayoutBoxExtent& box) { + m_location.move(-box.left(), -box.top()); + m_size.expand(box.left() + box.right(), box.top() + box.bottom()); + } + void expand(LayoutUnit dw, LayoutUnit dh) { m_size.expand(dw, dh); } + void contract(const LayoutSize& size) { m_size -= size; } + void contract(const LayoutBoxExtent& box) { + m_location.move(box.left(), box.top()); + m_size.shrink(box.left() + box.right(), box.top() + box.bottom()); + } + void contract(LayoutUnit dw, LayoutUnit dh) { m_size.expand(-dw, -dh); } + + void shiftXEdgeTo(LayoutUnit edge) { + LayoutUnit delta = edge - x(); + setX(edge); + setWidth(std::max(0, width() - delta)); + } + void shiftMaxXEdgeTo(LayoutUnit edge) { + LayoutUnit delta = edge - maxX(); + setWidth(std::max(0, width() + delta)); + } + void shiftYEdgeTo(LayoutUnit edge) { + LayoutUnit delta = edge - y(); + setY(edge); + setHeight(std::max(0, height() - delta)); + } + void shiftMaxYEdgeTo(LayoutUnit edge) { + LayoutUnit delta = edge - maxY(); + setHeight(std::max(0, height() + delta)); + } + + LayoutPoint minXMinYCorner() const { + return m_location; + } // typically topLeft + LayoutPoint maxXMinYCorner() const { + return LayoutPoint(m_location.x() + m_size.width(), m_location.y()); + } // typically topRight + LayoutPoint minXMaxYCorner() const { + return LayoutPoint(m_location.x(), m_location.y() + m_size.height()); + } // typically bottomLeft + LayoutPoint maxXMaxYCorner() const { + return LayoutPoint(m_location.x() + m_size.width(), + m_location.y() + m_size.height()); + } // typically bottomRight + + bool intersects(const LayoutRect&) const; + bool contains(const LayoutRect&) const; + + // This checks to see if the rect contains x,y in the traditional sense. + // Equivalent to checking if the rect contains a 1x1 rect below and to the + // right of (px,py). + bool contains(LayoutUnit px, LayoutUnit py) const { + return px >= x() && px < maxX() && py >= y() && py < maxY(); + } + bool contains(const LayoutPoint& point) const { + return contains(point.x(), point.y()); + } + + void intersect(const LayoutRect&); + void unite(const LayoutRect&); + void uniteIfNonZero(const LayoutRect&); + + void inflateX(LayoutUnit dx) { + m_location.setX(m_location.x() - dx); + m_size.setWidth(m_size.width() + dx + dx); + } + void inflateY(LayoutUnit dy) { + m_location.setY(m_location.y() - dy); + m_size.setHeight(m_size.height() + dy + dy); + } + void inflate(LayoutUnit d) { + inflateX(d); + inflateY(d); + } + void scale(float s); + void scale(float xAxisScale, float yAxisScale); + + LayoutRect transposedRect() const { + return LayoutRect(m_location.transposedPoint(), m_size.transposedSize()); + } + + static LayoutRect infiniteRect() { + // Return a rect that is slightly smaller than the true max rect to allow + // pixelSnapping to round up to the nearest IntRect without overflowing. + return LayoutRect(LayoutUnit::nearlyMin() / 2, LayoutUnit::nearlyMin() / 2, + LayoutUnit::nearlyMax(), LayoutUnit::nearlyMax()); + } #ifndef NDEBUG - // Prints the rect to the screen. - void show(bool showRawValue = false) const; + // Prints the rect to the screen. + void show(bool showRawValue = false) const; #endif -private: - LayoutPoint m_location; - LayoutSize m_size; + private: + LayoutPoint m_location; + LayoutSize m_size; }; -inline LayoutRect intersection(const LayoutRect& a, const LayoutRect& b) -{ - LayoutRect c = a; - c.intersect(b); - return c; +inline LayoutRect intersection(const LayoutRect& a, const LayoutRect& b) { + LayoutRect c = a; + c.intersect(b); + return c; } -inline LayoutRect unionRect(const LayoutRect& a, const LayoutRect& b) -{ - LayoutRect c = a; - c.unite(b); - return c; +inline LayoutRect unionRect(const LayoutRect& a, const LayoutRect& b) { + LayoutRect c = a; + c.unite(b); + return c; } PLATFORM_EXPORT LayoutRect unionRect(const Vector&); -ALWAYS_INLINE bool operator==(const LayoutRect& a, const LayoutRect& b) -{ - return a.location() == b.location() && a.size() == b.size(); +ALWAYS_INLINE bool operator==(const LayoutRect& a, const LayoutRect& b) { + return a.location() == b.location() && a.size() == b.size(); } -inline bool operator!=(const LayoutRect& a, const LayoutRect& b) -{ - return a.location() != b.location() || a.size() != b.size(); +inline bool operator!=(const LayoutRect& a, const LayoutRect& b) { + return a.location() != b.location() || a.size() != b.size(); } -inline IntRect pixelSnappedIntRect(const LayoutRect& rect) -{ - return IntRect(roundedIntPoint(rect.location()), IntSize( - snapSizeToPixel(rect.width(), rect.x()), - snapSizeToPixel(rect.height(), rect.y()))); +inline IntRect pixelSnappedIntRect(const LayoutRect& rect) { + return IntRect(roundedIntPoint(rect.location()), + IntSize(snapSizeToPixel(rect.width(), rect.x()), + snapSizeToPixel(rect.height(), rect.y()))); } PLATFORM_EXPORT IntRect enclosingIntRect(const LayoutRect&); PLATFORM_EXPORT LayoutRect enclosingLayoutRect(const FloatRect&); -inline IntRect pixelSnappedIntRect(LayoutUnit left, LayoutUnit top, LayoutUnit width, LayoutUnit height) -{ - return IntRect(left.round(), top.round(), snapSizeToPixel(width, left), snapSizeToPixel(height, top)); +inline IntRect pixelSnappedIntRect(LayoutUnit left, + LayoutUnit top, + LayoutUnit width, + LayoutUnit height) { + return IntRect(left.round(), top.round(), snapSizeToPixel(width, left), + snapSizeToPixel(height, top)); } -inline IntRect pixelSnappedIntRectFromEdges(LayoutUnit left, LayoutUnit top, LayoutUnit right, LayoutUnit bottom) -{ - return IntRect(left.round(), top.round(), snapSizeToPixel(right - left, left), snapSizeToPixel(bottom - top, top)); +inline IntRect pixelSnappedIntRectFromEdges(LayoutUnit left, + LayoutUnit top, + LayoutUnit right, + LayoutUnit bottom) { + return IntRect(left.round(), top.round(), snapSizeToPixel(right - left, left), + snapSizeToPixel(bottom - top, top)); } -inline IntRect pixelSnappedIntRect(LayoutPoint location, LayoutSize size) -{ - return IntRect(roundedIntPoint(location), pixelSnappedIntSize(size, location)); +inline IntRect pixelSnappedIntRect(LayoutPoint location, LayoutSize size) { + return IntRect(roundedIntPoint(location), + pixelSnappedIntSize(size, location)); } -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_GEOMETRY_LAYOUTRECT_H_ diff --git a/sky/engine/platform/geometry/LayoutSize.h b/sky/engine/platform/geometry/LayoutSize.h index 84fcc20d6ba6d..c8c5cb0fa4852 100644 --- a/sky/engine/platform/geometry/LayoutSize.h +++ b/sky/engine/platform/geometry/LayoutSize.h @@ -37,156 +37,136 @@ namespace blink { -enum AspectRatioFit { - AspectRatioFitShrink, - AspectRatioFitGrow -}; +enum AspectRatioFit { AspectRatioFitShrink, AspectRatioFitGrow }; class LayoutSize { -public: - LayoutSize() { } - LayoutSize(const IntSize& size) : m_width(size.width()), m_height(size.height()) { } - LayoutSize(LayoutUnit width, LayoutUnit height) : m_width(width), m_height(height) { } - - explicit LayoutSize(const FloatSize& size) : m_width(size.width()), m_height(size.height()) { } - - LayoutUnit width() const { return m_width; } - LayoutUnit height() const { return m_height; } - - void setWidth(LayoutUnit width) { m_width = width; } - void setHeight(LayoutUnit height) { m_height = height; } - - bool isEmpty() const { return m_width.rawValue() <= 0 || m_height.rawValue() <= 0; } - bool isZero() const { return !m_width && !m_height; } - - float aspectRatio() const { return m_width.toFloat() / m_height.toFloat(); } - - void expand(LayoutUnit width, LayoutUnit height) - { - m_width += width; - m_height += height; - } - - void shrink(LayoutUnit width, LayoutUnit height) - { - m_width -= width; - m_height -= height; - } - - void scale(float scale) - { - m_width *= scale; - m_height *= scale; - } - - void scale(float widthScale, float heightScale) - { - m_width *= widthScale; - m_height *= heightScale; - } - - LayoutSize expandedTo(const LayoutSize& other) const - { - return LayoutSize(m_width > other.m_width ? m_width : other.m_width, - m_height > other.m_height ? m_height : other.m_height); - } - - LayoutSize shrunkTo(const LayoutSize& other) const - { - return LayoutSize(m_width < other.m_width ? m_width : other.m_width, - m_height < other.m_height ? m_height : other.m_height); - } - - void clampNegativeToZero() - { - *this = expandedTo(LayoutSize()); - } - - void clampToMinimumSize(const LayoutSize& minimumSize) - { - if (m_width < minimumSize.width()) - m_width = minimumSize.width(); - if (m_height < minimumSize.height()) - m_height = minimumSize.height(); - } - - LayoutSize transposedSize() const - { - return LayoutSize(m_height, m_width); - } - - LayoutSize fitToAspectRatio(const LayoutSize& aspectRatio, AspectRatioFit fit) const - { - float heightScale = height().toFloat() / aspectRatio.height().toFloat(); - float widthScale = width().toFloat() / aspectRatio.width().toFloat(); - if ((widthScale > heightScale) != (fit == AspectRatioFitGrow)) - return LayoutSize(height() * aspectRatio.width() / aspectRatio.height(), height()); - return LayoutSize(width(), width() * aspectRatio.height() / aspectRatio.width()); - } - - LayoutSize fraction() const - { - return LayoutSize(m_width.fraction(), m_height.fraction()); - } - -private: - LayoutUnit m_width, m_height; + public: + LayoutSize() {} + LayoutSize(const IntSize& size) + : m_width(size.width()), m_height(size.height()) {} + LayoutSize(LayoutUnit width, LayoutUnit height) + : m_width(width), m_height(height) {} + + explicit LayoutSize(const FloatSize& size) + : m_width(size.width()), m_height(size.height()) {} + + LayoutUnit width() const { return m_width; } + LayoutUnit height() const { return m_height; } + + void setWidth(LayoutUnit width) { m_width = width; } + void setHeight(LayoutUnit height) { m_height = height; } + + bool isEmpty() const { + return m_width.rawValue() <= 0 || m_height.rawValue() <= 0; + } + bool isZero() const { return !m_width && !m_height; } + + float aspectRatio() const { return m_width.toFloat() / m_height.toFloat(); } + + void expand(LayoutUnit width, LayoutUnit height) { + m_width += width; + m_height += height; + } + + void shrink(LayoutUnit width, LayoutUnit height) { + m_width -= width; + m_height -= height; + } + + void scale(float scale) { + m_width *= scale; + m_height *= scale; + } + + void scale(float widthScale, float heightScale) { + m_width *= widthScale; + m_height *= heightScale; + } + + LayoutSize expandedTo(const LayoutSize& other) const { + return LayoutSize(m_width > other.m_width ? m_width : other.m_width, + m_height > other.m_height ? m_height : other.m_height); + } + + LayoutSize shrunkTo(const LayoutSize& other) const { + return LayoutSize(m_width < other.m_width ? m_width : other.m_width, + m_height < other.m_height ? m_height : other.m_height); + } + + void clampNegativeToZero() { *this = expandedTo(LayoutSize()); } + + void clampToMinimumSize(const LayoutSize& minimumSize) { + if (m_width < minimumSize.width()) + m_width = minimumSize.width(); + if (m_height < minimumSize.height()) + m_height = minimumSize.height(); + } + + LayoutSize transposedSize() const { return LayoutSize(m_height, m_width); } + + LayoutSize fitToAspectRatio(const LayoutSize& aspectRatio, + AspectRatioFit fit) const { + float heightScale = height().toFloat() / aspectRatio.height().toFloat(); + float widthScale = width().toFloat() / aspectRatio.width().toFloat(); + if ((widthScale > heightScale) != (fit == AspectRatioFitGrow)) + return LayoutSize(height() * aspectRatio.width() / aspectRatio.height(), + height()); + return LayoutSize(width(), + width() * aspectRatio.height() / aspectRatio.width()); + } + + LayoutSize fraction() const { + return LayoutSize(m_width.fraction(), m_height.fraction()); + } + + private: + LayoutUnit m_width, m_height; }; -inline LayoutSize& operator+=(LayoutSize& a, const LayoutSize& b) -{ - a.setWidth(a.width() + b.width()); - a.setHeight(a.height() + b.height()); - return a; +inline LayoutSize& operator+=(LayoutSize& a, const LayoutSize& b) { + a.setWidth(a.width() + b.width()); + a.setHeight(a.height() + b.height()); + return a; } -inline LayoutSize& operator-=(LayoutSize& a, const LayoutSize& b) -{ - a.setWidth(a.width() - b.width()); - a.setHeight(a.height() - b.height()); - return a; +inline LayoutSize& operator-=(LayoutSize& a, const LayoutSize& b) { + a.setWidth(a.width() - b.width()); + a.setHeight(a.height() - b.height()); + return a; } -inline LayoutSize operator+(const LayoutSize& a, const LayoutSize& b) -{ - return LayoutSize(a.width() + b.width(), a.height() + b.height()); +inline LayoutSize operator+(const LayoutSize& a, const LayoutSize& b) { + return LayoutSize(a.width() + b.width(), a.height() + b.height()); } -inline LayoutSize operator-(const LayoutSize& a, const LayoutSize& b) -{ - return LayoutSize(a.width() - b.width(), a.height() - b.height()); +inline LayoutSize operator-(const LayoutSize& a, const LayoutSize& b) { + return LayoutSize(a.width() - b.width(), a.height() - b.height()); } -inline LayoutSize operator-(const LayoutSize& size) -{ - return LayoutSize(-size.width(), -size.height()); +inline LayoutSize operator-(const LayoutSize& size) { + return LayoutSize(-size.width(), -size.height()); } -inline bool operator==(const LayoutSize& a, const LayoutSize& b) -{ - return a.width() == b.width() && a.height() == b.height(); +inline bool operator==(const LayoutSize& a, const LayoutSize& b) { + return a.width() == b.width() && a.height() == b.height(); } -inline bool operator!=(const LayoutSize& a, const LayoutSize& b) -{ - return a.width() != b.width() || a.height() != b.height(); +inline bool operator!=(const LayoutSize& a, const LayoutSize& b) { + return a.width() != b.width() || a.height() != b.height(); } -inline IntSize flooredIntSize(const LayoutSize& s) -{ - return IntSize(s.width().floor(), s.height().floor()); +inline IntSize flooredIntSize(const LayoutSize& s) { + return IntSize(s.width().floor(), s.height().floor()); } -inline IntSize roundedIntSize(const LayoutSize& s) -{ - return IntSize(s.width().round(), s.height().round()); +inline IntSize roundedIntSize(const LayoutSize& s) { + return IntSize(s.width().round(), s.height().round()); } -inline LayoutSize roundedLayoutSize(const FloatSize& s) -{ - return LayoutSize(s); +inline LayoutSize roundedLayoutSize(const FloatSize& s) { + return LayoutSize(s); } -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_GEOMETRY_LAYOUTSIZE_H_ diff --git a/sky/engine/platform/geometry/Region.cpp b/sky/engine/platform/geometry/Region.cpp index 593eabd703484..cac9ee883b3ce 100644 --- a/sky/engine/platform/geometry/Region.cpp +++ b/sky/engine/platform/geometry/Region.cpp @@ -35,596 +35,601 @@ namespace blink { -Region::Region() -{ -} +Region::Region() {} -Region::Region(const IntRect& rect) - : m_bounds(rect) - , m_shape(rect) -{ -} +Region::Region(const IntRect& rect) : m_bounds(rect), m_shape(rect) {} -Vector Region::rects() const -{ - Vector rects; +Vector Region::rects() const { + Vector rects; - for (Shape::SpanIterator span = m_shape.spansBegin(), end = m_shape.spansEnd(); span != end && span + 1 != end; ++span) { - int y = span->y; - int height = (span + 1)->y - y; + for (Shape::SpanIterator span = m_shape.spansBegin(), + end = m_shape.spansEnd(); + span != end && span + 1 != end; ++span) { + int y = span->y; + int height = (span + 1)->y - y; - for (Shape::SegmentIterator segment = m_shape.segmentsBegin(span), end = m_shape.segmentsEnd(span); segment != end && segment + 1 != end; segment += 2) { - int x = *segment; - int width = *(segment + 1) - x; + for (Shape::SegmentIterator segment = m_shape.segmentsBegin(span), + end = m_shape.segmentsEnd(span); + segment != end && segment + 1 != end; segment += 2) { + int x = *segment; + int width = *(segment + 1) - x; - rects.append(IntRect(x, y, width, height)); - } + rects.append(IntRect(x, y, width, height)); } + } - return rects; -} - -bool Region::contains(const Region& region) const -{ - if (!m_bounds.contains(region.m_bounds)) - return false; - - return Shape::compareShapes(m_shape, region.m_shape); + return rects; } -bool Region::contains(const IntPoint& point) const -{ - if (!m_bounds.contains(point)) - return false; - - for (Shape::SpanIterator span = m_shape.spansBegin(), end = m_shape.spansEnd(); span != end && span + 1 != end; ++span) { - int y = span->y; - int maxY = (span + 1)->y; - - if (y > point.y()) - break; - if (maxY <= point.y()) - continue; - - for (Shape::SegmentIterator segment = m_shape.segmentsBegin(span), end = m_shape.segmentsEnd(span); segment != end && segment + 1 != end; segment += 2) { - int x = *segment; - int maxX = *(segment + 1); - - if (x > point.x()) - break; - if (maxX > point.x()) - return true; - } - } - +bool Region::contains(const Region& region) const { + if (!m_bounds.contains(region.m_bounds)) return false; -} -bool Region::intersects(const Region& region) const -{ - if (!m_bounds.intersects(region.m_bounds)) - return false; - - return Shape::compareShapes(m_shape, region.m_shape); + return Shape::compareShapes(m_shape, + region.m_shape); } -unsigned Region::totalArea() const -{ - Vector rects = this->rects(); - size_t size = rects.size(); - unsigned totalArea = 0; +bool Region::contains(const IntPoint& point) const { + if (!m_bounds.contains(point)) + return false; - for (size_t i = 0; i < size; ++i) { - IntRect rect = rects[i]; - totalArea += (rect.width() * rect.height()); + for (Shape::SpanIterator span = m_shape.spansBegin(), + end = m_shape.spansEnd(); + span != end && span + 1 != end; ++span) { + int y = span->y; + int maxY = (span + 1)->y; + + if (y > point.y()) + break; + if (maxY <= point.y()) + continue; + + for (Shape::SegmentIterator segment = m_shape.segmentsBegin(span), + end = m_shape.segmentsEnd(span); + segment != end && segment + 1 != end; segment += 2) { + int x = *segment; + int maxX = *(segment + 1); + + if (x > point.x()) + break; + if (maxX > point.x()) + return true; } + } - return totalArea; + return false; } -template -bool Region::Shape::compareShapes(const Shape& aShape, const Shape& bShape) -{ - bool result = CompareOperation::defaultResult; - - Shape::SpanIterator aSpan = aShape.spansBegin(); - Shape::SpanIterator aSpanEnd = aShape.spansEnd(); - Shape::SpanIterator bSpan = bShape.spansBegin(); - Shape::SpanIterator bSpanEnd = bShape.spansEnd(); - - bool aHadSegmentInPreviousSpan = false; - bool bHadSegmentInPreviousSpan = false; - while (aSpan != aSpanEnd && aSpan + 1 != aSpanEnd && bSpan != bSpanEnd && bSpan + 1 != bSpanEnd) { - int aY = aSpan->y; - int aMaxY = (aSpan + 1)->y; - int bY = bSpan->y; - int bMaxY = (bSpan + 1)->y; - - Shape::SegmentIterator aSegment = aShape.segmentsBegin(aSpan); - Shape::SegmentIterator aSegmentEnd = aShape.segmentsEnd(aSpan); - Shape::SegmentIterator bSegment = bShape.segmentsBegin(bSpan); - Shape::SegmentIterator bSegmentEnd = bShape.segmentsEnd(bSpan); - - // Look for a non-overlapping part of the spans. If B had a segment in its previous span, then we already tested A against B within that span. - bool aHasSegmentInSpan = aSegment != aSegmentEnd; - bool bHasSegmentInSpan = bSegment != bSegmentEnd; - if (aY < bY && !bHadSegmentInPreviousSpan && aHasSegmentInSpan && CompareOperation::aOutsideB(result)) - return result; - if (bY < aY && !aHadSegmentInPreviousSpan && bHasSegmentInSpan && CompareOperation::bOutsideA(result)) - return result; - - aHadSegmentInPreviousSpan = aHasSegmentInSpan; - bHadSegmentInPreviousSpan = bHasSegmentInSpan; - - bool spansOverlap = bMaxY > aY && bY < aMaxY; - if (spansOverlap) { - while (aSegment != aSegmentEnd && bSegment != bSegmentEnd) { - int aX = *aSegment; - int aMaxX = *(aSegment + 1); - int bX = *bSegment; - int bMaxX = *(bSegment + 1); - - bool segmentsOverlap = bMaxX > aX && bX < aMaxX; - if (segmentsOverlap && CompareOperation::aOverlapsB(result)) - return result; - if (aX < bX && CompareOperation::aOutsideB(result)) - return result; - if (bX < aX && CompareOperation::bOutsideA(result)) - return result; - - if (aMaxX < bMaxX) { - aSegment += 2; - } else if (bMaxX < aMaxX) { - bSegment += 2; - } else { - aSegment += 2; - bSegment += 2; - } - } - - if (aSegment != aSegmentEnd && CompareOperation::aOutsideB(result)) - return result; - if (bSegment != bSegmentEnd && CompareOperation::bOutsideA(result)) - return result; - } +bool Region::intersects(const Region& region) const { + if (!m_bounds.intersects(region.m_bounds)) + return false; - if (aMaxY < bMaxY) { - aSpan += 1; - } else if (bMaxY < aMaxY) { - bSpan += 1; + return Shape::compareShapes( + m_shape, region.m_shape); +} + +unsigned Region::totalArea() const { + Vector rects = this->rects(); + size_t size = rects.size(); + unsigned totalArea = 0; + + for (size_t i = 0; i < size; ++i) { + IntRect rect = rects[i]; + totalArea += (rect.width() * rect.height()); + } + + return totalArea; +} + +template +bool Region::Shape::compareShapes(const Shape& aShape, const Shape& bShape) { + bool result = CompareOperation::defaultResult; + + Shape::SpanIterator aSpan = aShape.spansBegin(); + Shape::SpanIterator aSpanEnd = aShape.spansEnd(); + Shape::SpanIterator bSpan = bShape.spansBegin(); + Shape::SpanIterator bSpanEnd = bShape.spansEnd(); + + bool aHadSegmentInPreviousSpan = false; + bool bHadSegmentInPreviousSpan = false; + while (aSpan != aSpanEnd && aSpan + 1 != aSpanEnd && bSpan != bSpanEnd && + bSpan + 1 != bSpanEnd) { + int aY = aSpan->y; + int aMaxY = (aSpan + 1)->y; + int bY = bSpan->y; + int bMaxY = (bSpan + 1)->y; + + Shape::SegmentIterator aSegment = aShape.segmentsBegin(aSpan); + Shape::SegmentIterator aSegmentEnd = aShape.segmentsEnd(aSpan); + Shape::SegmentIterator bSegment = bShape.segmentsBegin(bSpan); + Shape::SegmentIterator bSegmentEnd = bShape.segmentsEnd(bSpan); + + // Look for a non-overlapping part of the spans. If B had a segment in its + // previous span, then we already tested A against B within that span. + bool aHasSegmentInSpan = aSegment != aSegmentEnd; + bool bHasSegmentInSpan = bSegment != bSegmentEnd; + if (aY < bY && !bHadSegmentInPreviousSpan && aHasSegmentInSpan && + CompareOperation::aOutsideB(result)) + return result; + if (bY < aY && !aHadSegmentInPreviousSpan && bHasSegmentInSpan && + CompareOperation::bOutsideA(result)) + return result; + + aHadSegmentInPreviousSpan = aHasSegmentInSpan; + bHadSegmentInPreviousSpan = bHasSegmentInSpan; + + bool spansOverlap = bMaxY > aY && bY < aMaxY; + if (spansOverlap) { + while (aSegment != aSegmentEnd && bSegment != bSegmentEnd) { + int aX = *aSegment; + int aMaxX = *(aSegment + 1); + int bX = *bSegment; + int bMaxX = *(bSegment + 1); + + bool segmentsOverlap = bMaxX > aX && bX < aMaxX; + if (segmentsOverlap && CompareOperation::aOverlapsB(result)) + return result; + if (aX < bX && CompareOperation::aOutsideB(result)) + return result; + if (bX < aX && CompareOperation::bOutsideA(result)) + return result; + + if (aMaxX < bMaxX) { + aSegment += 2; + } else if (bMaxX < aMaxX) { + bSegment += 2; } else { - aSpan += 1; - bSpan += 1; + aSegment += 2; + bSegment += 2; } - } + } - if (aSpan != aSpanEnd && aSpan + 1 != aSpanEnd && CompareOperation::aOutsideB(result)) + if (aSegment != aSegmentEnd && CompareOperation::aOutsideB(result)) return result; - if (bSpan != bSpanEnd && bSpan + 1 != bSpanEnd && CompareOperation::bOutsideA(result)) + if (bSegment != bSegmentEnd && CompareOperation::bOutsideA(result)) return result; + } + if (aMaxY < bMaxY) { + aSpan += 1; + } else if (bMaxY < aMaxY) { + bSpan += 1; + } else { + aSpan += 1; + bSpan += 1; + } + } + + if (aSpan != aSpanEnd && aSpan + 1 != aSpanEnd && + CompareOperation::aOutsideB(result)) return result; + if (bSpan != bSpanEnd && bSpan + 1 != bSpanEnd && + CompareOperation::bOutsideA(result)) + return result; + + return result; } -void Region::Shape::trimCapacities() -{ - m_segments.shrinkToReasonableCapacity(); - m_spans.shrinkToReasonableCapacity(); +void Region::Shape::trimCapacities() { + m_segments.shrinkToReasonableCapacity(); + m_spans.shrinkToReasonableCapacity(); } struct Region::Shape::CompareContainsOperation { - const static bool defaultResult = true; - inline static bool aOutsideB(bool& /* result */) { return false; } - inline static bool bOutsideA(bool& result) { result = false; return true; } - inline static bool aOverlapsB(bool& /* result */) { return false; } + const static bool defaultResult = true; + inline static bool aOutsideB(bool& /* result */) { return false; } + inline static bool bOutsideA(bool& result) { + result = false; + return true; + } + inline static bool aOverlapsB(bool& /* result */) { return false; } }; struct Region::Shape::CompareIntersectsOperation { - const static bool defaultResult = false; - inline static bool aOutsideB(bool& /* result */) { return false; } - inline static bool bOutsideA(bool& /* result */) { return false; } - inline static bool aOverlapsB(bool& result) { result = true; return true; } + const static bool defaultResult = false; + inline static bool aOutsideB(bool& /* result */) { return false; } + inline static bool bOutsideA(bool& /* result */) { return false; } + inline static bool aOverlapsB(bool& result) { + result = true; + return true; + } }; -Region::Shape::Shape() -{ -} +Region::Shape::Shape() {} -Region::Shape::Shape(const IntRect& rect) -{ - appendSpan(rect.y()); - appendSegment(rect.x()); - appendSegment(rect.maxX()); - appendSpan(rect.maxY()); +Region::Shape::Shape(const IntRect& rect) { + appendSpan(rect.y()); + appendSegment(rect.x()); + appendSegment(rect.maxX()); + appendSpan(rect.maxY()); } -Region::Shape::Shape(size_t segmentsCapacity, size_t spansCapacity) -{ - m_segments.reserveCapacity(segmentsCapacity); - m_spans.reserveCapacity(spansCapacity); +Region::Shape::Shape(size_t segmentsCapacity, size_t spansCapacity) { + m_segments.reserveCapacity(segmentsCapacity); + m_spans.reserveCapacity(spansCapacity); } -void Region::Shape::appendSpan(int y) -{ - m_spans.append(Span(y, m_segments.size())); +void Region::Shape::appendSpan(int y) { + m_spans.append(Span(y, m_segments.size())); } -bool Region::Shape::canCoalesce(SegmentIterator begin, SegmentIterator end) -{ - if (m_spans.isEmpty()) - return false; +bool Region::Shape::canCoalesce(SegmentIterator begin, SegmentIterator end) { + if (m_spans.isEmpty()) + return false; - SegmentIterator lastSpanBegin = m_segments.data() + m_spans.last().segmentIndex; - SegmentIterator lastSpanEnd = m_segments.data() + m_segments.size(); + SegmentIterator lastSpanBegin = + m_segments.data() + m_spans.last().segmentIndex; + SegmentIterator lastSpanEnd = m_segments.data() + m_segments.size(); - // Check if both spans have an equal number of segments. - if (lastSpanEnd - lastSpanBegin != end - begin) - return false; + // Check if both spans have an equal number of segments. + if (lastSpanEnd - lastSpanBegin != end - begin) + return false; - // Check if both spans are equal. - if (!std::equal(begin, end, lastSpanBegin)) - return false; + // Check if both spans are equal. + if (!std::equal(begin, end, lastSpanBegin)) + return false; - // Since the segments are equal the second segment can just be ignored. - return true; + // Since the segments are equal the second segment can just be ignored. + return true; } -void Region::Shape::appendSpan(int y, SegmentIterator begin, SegmentIterator end) -{ - if (canCoalesce(begin, end)) - return; +void Region::Shape::appendSpan(int y, + SegmentIterator begin, + SegmentIterator end) { + if (canCoalesce(begin, end)) + return; - appendSpan(y); - m_segments.appendRange(begin, end); + appendSpan(y); + m_segments.appendRange(begin, end); } -void Region::Shape::appendSpans(const Shape& shape, SpanIterator begin, SpanIterator end) -{ - for (SpanIterator it = begin; it != end; ++it) - appendSpan(it->y, shape.segmentsBegin(it), shape.segmentsEnd(it)); +void Region::Shape::appendSpans(const Shape& shape, + SpanIterator begin, + SpanIterator end) { + for (SpanIterator it = begin; it != end; ++it) + appendSpan(it->y, shape.segmentsBegin(it), shape.segmentsEnd(it)); } -void Region::Shape::appendSegment(int x) -{ - m_segments.append(x); +void Region::Shape::appendSegment(int x) { + m_segments.append(x); } -Region::Shape::SpanIterator Region::Shape::spansBegin() const -{ - return m_spans.data(); +Region::Shape::SpanIterator Region::Shape::spansBegin() const { + return m_spans.data(); } -Region::Shape::SpanIterator Region::Shape::spansEnd() const -{ - return m_spans.data() + m_spans.size(); +Region::Shape::SpanIterator Region::Shape::spansEnd() const { + return m_spans.data() + m_spans.size(); } -Region::Shape::SegmentIterator Region::Shape::segmentsBegin(SpanIterator it) const -{ - ASSERT(it >= m_spans.data()); - ASSERT(it < m_spans.data() + m_spans.size()); +Region::Shape::SegmentIterator Region::Shape::segmentsBegin( + SpanIterator it) const { + ASSERT(it >= m_spans.data()); + ASSERT(it < m_spans.data() + m_spans.size()); - // Check if this span has any segments. - if (it->segmentIndex == m_segments.size()) - return 0; + // Check if this span has any segments. + if (it->segmentIndex == m_segments.size()) + return 0; - return &m_segments[it->segmentIndex]; + return &m_segments[it->segmentIndex]; } -Region::Shape::SegmentIterator Region::Shape::segmentsEnd(SpanIterator it) const -{ - ASSERT(it >= m_spans.data()); - ASSERT(it < m_spans.data() + m_spans.size()); +Region::Shape::SegmentIterator Region::Shape::segmentsEnd( + SpanIterator it) const { + ASSERT(it >= m_spans.data()); + ASSERT(it < m_spans.data() + m_spans.size()); - // Check if this span has any segments. - if (it->segmentIndex == m_segments.size()) - return 0; + // Check if this span has any segments. + if (it->segmentIndex == m_segments.size()) + return 0; - ASSERT(it + 1 < m_spans.data() + m_spans.size()); - size_t segmentIndex = (it + 1)->segmentIndex; + ASSERT(it + 1 < m_spans.data() + m_spans.size()); + size_t segmentIndex = (it + 1)->segmentIndex; - ASSERT_WITH_SECURITY_IMPLICATION(segmentIndex <= m_segments.size()); - return m_segments.data() + segmentIndex; + ASSERT_WITH_SECURITY_IMPLICATION(segmentIndex <= m_segments.size()); + return m_segments.data() + segmentIndex; } #ifndef NDEBUG -void Region::Shape::dump() const -{ - for (Shape::SpanIterator span = spansBegin(), end = spansEnd(); span != end; ++span) { - printf("%6d: (", span->y); - - for (Shape::SegmentIterator segment = segmentsBegin(span), end = segmentsEnd(span); segment != end; ++segment) - printf("%d ", *segment); - printf(")\n"); - } +void Region::Shape::dump() const { + for (Shape::SpanIterator span = spansBegin(), end = spansEnd(); span != end; + ++span) { + printf("%6d: (", span->y); - printf("\n"); + for (Shape::SegmentIterator segment = segmentsBegin(span), + end = segmentsEnd(span); + segment != end; ++segment) + printf("%d ", *segment); + printf(")\n"); + } + + printf("\n"); } #endif -IntRect Region::Shape::bounds() const -{ - if (isEmpty()) - return IntRect(); - - SpanIterator span = spansBegin(); - int minY = span->y; +IntRect Region::Shape::bounds() const { + if (isEmpty()) + return IntRect(); - SpanIterator lastSpan = spansEnd() - 1; - int maxY = lastSpan->y; + SpanIterator span = spansBegin(); + int minY = span->y; - int minX = std::numeric_limits::max(); - int maxX = std::numeric_limits::min(); + SpanIterator lastSpan = spansEnd() - 1; + int maxY = lastSpan->y; - while (span != lastSpan) { - SegmentIterator firstSegment = segmentsBegin(span); - SegmentIterator lastSegment = segmentsEnd(span) - 1; + int minX = std::numeric_limits::max(); + int maxX = std::numeric_limits::min(); - if (firstSegment && lastSegment) { - ASSERT(firstSegment != lastSegment); + while (span != lastSpan) { + SegmentIterator firstSegment = segmentsBegin(span); + SegmentIterator lastSegment = segmentsEnd(span) - 1; - if (*firstSegment < minX) - minX = *firstSegment; + if (firstSegment && lastSegment) { + ASSERT(firstSegment != lastSegment); - if (*lastSegment > maxX) - maxX = *lastSegment; - } + if (*firstSegment < minX) + minX = *firstSegment; - ++span; + if (*lastSegment > maxX) + maxX = *lastSegment; } - ASSERT(minX <= maxX); - ASSERT(minY <= maxY); + ++span; + } + + ASSERT(minX <= maxX); + ASSERT(minY <= maxY); - return IntRect(minX, minY, maxX - minX, maxY - minY); + return IntRect(minX, minY, maxX - minX, maxY - minY); } -void Region::Shape::translate(const IntSize& offset) -{ - for (size_t i = 0; i < m_segments.size(); ++i) - m_segments[i] += offset.width(); - for (size_t i = 0; i < m_spans.size(); ++i) - m_spans[i].y += offset.height(); +void Region::Shape::translate(const IntSize& offset) { + for (size_t i = 0; i < m_segments.size(); ++i) + m_segments[i] += offset.width(); + for (size_t i = 0; i < m_spans.size(); ++i) + m_spans[i].y += offset.height(); } -void Region::Shape::swap(Shape& other) -{ - m_segments.swap(other.m_segments); - m_spans.swap(other.m_spans); +void Region::Shape::swap(Shape& other) { + m_segments.swap(other.m_segments); + m_spans.swap(other.m_spans); } enum { - Shape1, - Shape2, + Shape1, + Shape2, }; -template -Region::Shape Region::Shape::shapeOperation(const Shape& shape1, const Shape& shape2) -{ - COMPILE_ASSERT(!(!Operation::shouldAddRemainingSegmentsFromSpan1 && Operation::shouldAddRemainingSegmentsFromSpan2), invalid_segment_combination); - COMPILE_ASSERT(!(!Operation::shouldAddRemainingSpansFromShape1 && Operation::shouldAddRemainingSpansFromShape2), invalid_span_combination); - - size_t segmentsCapacity = shape1.segmentsSize() + shape2.segmentsSize(); - size_t spansCapacity = shape1.spansSize() + shape2.spansSize(); - Shape result(segmentsCapacity, spansCapacity); - if (Operation::trySimpleOperation(shape1, shape2, result)) - return result; - - SpanIterator spans1 = shape1.spansBegin(); - SpanIterator spans1End = shape1.spansEnd(); - - SpanIterator spans2 = shape2.spansBegin(); - SpanIterator spans2End = shape2.spansEnd(); - - SegmentIterator segments1 = 0; - SegmentIterator segments1End = 0; - - SegmentIterator segments2 = 0; - SegmentIterator segments2End = 0; - - Vector segments; - segments.reserveCapacity(std::max(shape1.segmentsSize(), shape2.segmentsSize())); - - // Iterate over all spans. - while (spans1 != spans1End && spans2 != spans2End) { - int y = 0; - int test = spans1->y - spans2->y; +template +Region::Shape Region::Shape::shapeOperation(const Shape& shape1, + const Shape& shape2) { + COMPILE_ASSERT(!(!Operation::shouldAddRemainingSegmentsFromSpan1 && + Operation::shouldAddRemainingSegmentsFromSpan2), + invalid_segment_combination); + COMPILE_ASSERT(!(!Operation::shouldAddRemainingSpansFromShape1 && + Operation::shouldAddRemainingSpansFromShape2), + invalid_span_combination); + + size_t segmentsCapacity = shape1.segmentsSize() + shape2.segmentsSize(); + size_t spansCapacity = shape1.spansSize() + shape2.spansSize(); + Shape result(segmentsCapacity, spansCapacity); + if (Operation::trySimpleOperation(shape1, shape2, result)) + return result; - if (test <= 0) { - y = spans1->y; + SpanIterator spans1 = shape1.spansBegin(); + SpanIterator spans1End = shape1.spansEnd(); - segments1 = shape1.segmentsBegin(spans1); - segments1End = shape1.segmentsEnd(spans1); - ++spans1; - } - if (test >= 0) { - y = spans2->y; + SpanIterator spans2 = shape2.spansBegin(); + SpanIterator spans2End = shape2.spansEnd(); - segments2 = shape2.segmentsBegin(spans2); - segments2End = shape2.segmentsEnd(spans2); - ++spans2; - } + SegmentIterator segments1 = 0; + SegmentIterator segments1End = 0; - int flag = 0; - int oldFlag = 0; + SegmentIterator segments2 = 0; + SegmentIterator segments2End = 0; - SegmentIterator s1 = segments1; - SegmentIterator s2 = segments2; + Vector segments; + segments.reserveCapacity( + std::max(shape1.segmentsSize(), shape2.segmentsSize())); - // Clear vector without dropping capacity. - segments.resize(0); - ASSERT(segments.capacity()); + // Iterate over all spans. + while (spans1 != spans1End && spans2 != spans2End) { + int y = 0; + int test = spans1->y - spans2->y; - // Now iterate over the segments in each span and construct a new vector of segments. - while (s1 != segments1End && s2 != segments2End) { - int test = *s1 - *s2; - int x; + if (test <= 0) { + y = spans1->y; - if (test <= 0) { - x = *s1; - flag = flag ^ 1; - ++s1; - } - if (test >= 0) { - x = *s2; - flag = flag ^ 2; - ++s2; - } + segments1 = shape1.segmentsBegin(spans1); + segments1End = shape1.segmentsEnd(spans1); + ++spans1; + } + if (test >= 0) { + y = spans2->y; - if (flag == Operation::opCode || oldFlag == Operation::opCode) - segments.append(x); + segments2 = shape2.segmentsBegin(spans2); + segments2End = shape2.segmentsEnd(spans2); + ++spans2; + } - oldFlag = flag; - } + int flag = 0; + int oldFlag = 0; + + SegmentIterator s1 = segments1; + SegmentIterator s2 = segments2; + + // Clear vector without dropping capacity. + segments.resize(0); + ASSERT(segments.capacity()); + + // Now iterate over the segments in each span and construct a new vector of + // segments. + while (s1 != segments1End && s2 != segments2End) { + int test = *s1 - *s2; + int x; + + if (test <= 0) { + x = *s1; + flag = flag ^ 1; + ++s1; + } + if (test >= 0) { + x = *s2; + flag = flag ^ 2; + ++s2; + } + + if (flag == Operation::opCode || oldFlag == Operation::opCode) + segments.append(x); + + oldFlag = flag; + } - // Add any remaining segments. - if (Operation::shouldAddRemainingSegmentsFromSpan1 && s1 != segments1End) - segments.appendRange(s1, segments1End); - else if (Operation::shouldAddRemainingSegmentsFromSpan2 && s2 != segments2End) - segments.appendRange(s2, segments2End); + // Add any remaining segments. + if (Operation::shouldAddRemainingSegmentsFromSpan1 && s1 != segments1End) + segments.appendRange(s1, segments1End); + else if (Operation::shouldAddRemainingSegmentsFromSpan2 && + s2 != segments2End) + segments.appendRange(s2, segments2End); - // Add the span. - if (!segments.isEmpty() || !result.isEmpty()) - result.appendSpan(y, segments.data(), segments.data() + segments.size()); - } + // Add the span. + if (!segments.isEmpty() || !result.isEmpty()) + result.appendSpan(y, segments.data(), segments.data() + segments.size()); + } - // Add any remaining spans. - if (Operation::shouldAddRemainingSpansFromShape1 && spans1 != spans1End) - result.appendSpans(shape1, spans1, spans1End); - else if (Operation::shouldAddRemainingSpansFromShape2 && spans2 != spans2End) - result.appendSpans(shape2, spans2, spans2End); + // Add any remaining spans. + if (Operation::shouldAddRemainingSpansFromShape1 && spans1 != spans1End) + result.appendSpans(shape1, spans1, spans1End); + else if (Operation::shouldAddRemainingSpansFromShape2 && spans2 != spans2End) + result.appendSpans(shape2, spans2, spans2End); - result.trimCapacities(); + result.trimCapacities(); - return result; + return result; } struct Region::Shape::UnionOperation { - static bool trySimpleOperation(const Shape& shape1, const Shape& shape2, Shape& result) - { - if (shape1.isEmpty()) { - result = shape2; - return true; - } - - return false; + static bool trySimpleOperation(const Shape& shape1, + const Shape& shape2, + Shape& result) { + if (shape1.isEmpty()) { + result = shape2; + return true; } - static const int opCode = 0; + return false; + } - static const bool shouldAddRemainingSegmentsFromSpan1 = true; - static const bool shouldAddRemainingSegmentsFromSpan2 = true; - static const bool shouldAddRemainingSpansFromShape1 = true; - static const bool shouldAddRemainingSpansFromShape2 = true; + static const int opCode = 0; + + static const bool shouldAddRemainingSegmentsFromSpan1 = true; + static const bool shouldAddRemainingSegmentsFromSpan2 = true; + static const bool shouldAddRemainingSpansFromShape1 = true; + static const bool shouldAddRemainingSpansFromShape2 = true; }; -Region::Shape Region::Shape::unionShapes(const Shape& shape1, const Shape& shape2) -{ - return shapeOperation(shape1, shape2); +Region::Shape Region::Shape::unionShapes(const Shape& shape1, + const Shape& shape2) { + return shapeOperation(shape1, shape2); } struct Region::Shape::IntersectOperation { - static bool trySimpleOperation(const Shape&, const Shape&, Shape&) - { - return false; - } + static bool trySimpleOperation(const Shape&, const Shape&, Shape&) { + return false; + } - static const int opCode = 3; + static const int opCode = 3; - static const bool shouldAddRemainingSegmentsFromSpan1 = false; - static const bool shouldAddRemainingSegmentsFromSpan2 = false; - static const bool shouldAddRemainingSpansFromShape1 = false; - static const bool shouldAddRemainingSpansFromShape2 = false; + static const bool shouldAddRemainingSegmentsFromSpan1 = false; + static const bool shouldAddRemainingSegmentsFromSpan2 = false; + static const bool shouldAddRemainingSpansFromShape1 = false; + static const bool shouldAddRemainingSpansFromShape2 = false; }; -Region::Shape Region::Shape::intersectShapes(const Shape& shape1, const Shape& shape2) -{ - return shapeOperation(shape1, shape2); +Region::Shape Region::Shape::intersectShapes(const Shape& shape1, + const Shape& shape2) { + return shapeOperation(shape1, shape2); } struct Region::Shape::SubtractOperation { - static bool trySimpleOperation(const Shape&, const Shape&, Region::Shape&) - { - return false; - } + static bool trySimpleOperation(const Shape&, const Shape&, Region::Shape&) { + return false; + } - static const int opCode = 1; + static const int opCode = 1; - static const bool shouldAddRemainingSegmentsFromSpan1 = true; - static const bool shouldAddRemainingSegmentsFromSpan2 = false; - static const bool shouldAddRemainingSpansFromShape1 = true; - static const bool shouldAddRemainingSpansFromShape2 = false; + static const bool shouldAddRemainingSegmentsFromSpan1 = true; + static const bool shouldAddRemainingSegmentsFromSpan2 = false; + static const bool shouldAddRemainingSpansFromShape1 = true; + static const bool shouldAddRemainingSpansFromShape2 = false; }; -Region::Shape Region::Shape::subtractShapes(const Shape& shape1, const Shape& shape2) -{ - return shapeOperation(shape1, shape2); +Region::Shape Region::Shape::subtractShapes(const Shape& shape1, + const Shape& shape2) { + return shapeOperation(shape1, shape2); } #ifndef NDEBUG -void Region::dump() const -{ - printf("Bounds: (%d, %d, %d, %d)\n", m_bounds.x(), m_bounds.y(), m_bounds.width(), m_bounds.height()); - m_shape.dump(); +void Region::dump() const { + printf("Bounds: (%d, %d, %d, %d)\n", m_bounds.x(), m_bounds.y(), + m_bounds.width(), m_bounds.height()); + m_shape.dump(); } #endif -void Region::intersect(const Region& region) -{ - if (m_bounds.isEmpty()) - return; - if (!m_bounds.intersects(region.m_bounds)) { - m_shape = Shape(); - m_bounds = IntRect(); - return; - } +void Region::intersect(const Region& region) { + if (m_bounds.isEmpty()) + return; + if (!m_bounds.intersects(region.m_bounds)) { + m_shape = Shape(); + m_bounds = IntRect(); + return; + } - Shape intersectedShape = Shape::intersectShapes(m_shape, region.m_shape); + Shape intersectedShape = Shape::intersectShapes(m_shape, region.m_shape); - m_shape.swap(intersectedShape); - m_bounds = m_shape.bounds(); + m_shape.swap(intersectedShape); + m_bounds = m_shape.bounds(); } -void Region::unite(const Region& region) -{ - if (region.isEmpty()) - return; - if (isRect() && m_bounds.contains(region.m_bounds)) - return; - if (region.isRect() && region.m_bounds.contains(m_bounds)) { - m_shape = region.m_shape; - m_bounds = region.m_bounds; - return; - } - // FIXME: We may want another way to construct a Region without doing this test when we expect it to be false. - if (!isRect() && contains(region)) - return; +void Region::unite(const Region& region) { + if (region.isEmpty()) + return; + if (isRect() && m_bounds.contains(region.m_bounds)) + return; + if (region.isRect() && region.m_bounds.contains(m_bounds)) { + m_shape = region.m_shape; + m_bounds = region.m_bounds; + return; + } + // FIXME: We may want another way to construct a Region without doing this + // test when we expect it to be false. + if (!isRect() && contains(region)) + return; - Shape unitedShape = Shape::unionShapes(m_shape, region.m_shape); + Shape unitedShape = Shape::unionShapes(m_shape, region.m_shape); - m_shape.swap(unitedShape); - m_bounds.unite(region.m_bounds); + m_shape.swap(unitedShape); + m_bounds.unite(region.m_bounds); } -void Region::subtract(const Region& region) -{ - if (m_bounds.isEmpty()) - return; - if (region.isEmpty()) - return; - if (!m_bounds.intersects(region.m_bounds)) - return; +void Region::subtract(const Region& region) { + if (m_bounds.isEmpty()) + return; + if (region.isEmpty()) + return; + if (!m_bounds.intersects(region.m_bounds)) + return; - Shape subtractedShape = Shape::subtractShapes(m_shape, region.m_shape); + Shape subtractedShape = Shape::subtractShapes(m_shape, region.m_shape); - m_shape.swap(subtractedShape); - m_bounds = m_shape.bounds(); + m_shape.swap(subtractedShape); + m_bounds = m_shape.bounds(); } -void Region::translate(const IntSize& offset) -{ - m_bounds.move(offset); - m_shape.translate(offset); +void Region::translate(const IntSize& offset) { + m_bounds.move(offset); + m_shape.translate(offset); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/geometry/Region.h b/sky/engine/platform/geometry/Region.h index 8bbee7de0a2ff..323e599f3f247 100644 --- a/sky/engine/platform/geometry/Region.h +++ b/sky/engine/platform/geometry/Region.h @@ -33,153 +33,146 @@ namespace blink { class PLATFORM_EXPORT Region { -public: - Region(); - Region(const IntRect&); + public: + Region(); + Region(const IntRect&); - IntRect bounds() const { return m_bounds; } - bool isEmpty() const { return m_bounds.isEmpty(); } - bool isRect() const { return m_shape.isRect(); } + IntRect bounds() const { return m_bounds; } + bool isEmpty() const { return m_bounds.isEmpty(); } + bool isRect() const { return m_shape.isRect(); } - Vector rects() const; + Vector rects() const; - void unite(const Region&); - void intersect(const Region&); - void subtract(const Region&); + void unite(const Region&); + void intersect(const Region&); + void subtract(const Region&); - void translate(const IntSize&); + void translate(const IntSize&); - // Returns true if the query region is a subset of this region. - bool contains(const Region&) const; + // Returns true if the query region is a subset of this region. + bool contains(const Region&) const; - bool contains(const IntPoint&) const; + bool contains(const IntPoint&) const; - // Returns true if the query region intersects any part of this region. - bool intersects(const Region&) const; + // Returns true if the query region intersects any part of this region. + bool intersects(const Region&) const; - unsigned totalArea() const; + unsigned totalArea() const; #ifndef NDEBUG - void dump() const; + void dump() const; #endif -private: - struct Span { - Span(int y, size_t segmentIndex) - : y(y), segmentIndex(segmentIndex) - { - } + private: + struct Span { + Span(int y, size_t segmentIndex) : y(y), segmentIndex(segmentIndex) {} - int y; - size_t segmentIndex; - }; + int y; + size_t segmentIndex; + }; - class Shape { - public: - Shape(); - Shape(const IntRect&); - Shape(size_t segmentsCapacity, size_t spansCapacity); + class Shape { + public: + Shape(); + Shape(const IntRect&); + Shape(size_t segmentsCapacity, size_t spansCapacity); - IntRect bounds() const; - bool isEmpty() const { return m_spans.isEmpty(); } - bool isRect() const { return m_spans.size() <= 2 && m_segments.size() <= 2; } + IntRect bounds() const; + bool isEmpty() const { return m_spans.isEmpty(); } + bool isRect() const { + return m_spans.size() <= 2 && m_segments.size() <= 2; + } - typedef const Span* SpanIterator; - SpanIterator spansBegin() const; - SpanIterator spansEnd() const; - size_t spansSize() const { return m_spans.size(); } + typedef const Span* SpanIterator; + SpanIterator spansBegin() const; + SpanIterator spansEnd() const; + size_t spansSize() const { return m_spans.size(); } - typedef const int* SegmentIterator; - SegmentIterator segmentsBegin(SpanIterator) const; - SegmentIterator segmentsEnd(SpanIterator) const; - size_t segmentsSize() const { return m_segments.size(); } + typedef const int* SegmentIterator; + SegmentIterator segmentsBegin(SpanIterator) const; + SegmentIterator segmentsEnd(SpanIterator) const; + size_t segmentsSize() const { return m_segments.size(); } - static Shape unionShapes(const Shape& shape1, const Shape& shape2); - static Shape intersectShapes(const Shape& shape1, const Shape& shape2); - static Shape subtractShapes(const Shape& shape1, const Shape& shape2); + static Shape unionShapes(const Shape& shape1, const Shape& shape2); + static Shape intersectShapes(const Shape& shape1, const Shape& shape2); + static Shape subtractShapes(const Shape& shape1, const Shape& shape2); - void translate(const IntSize&); - void swap(Shape&); + void translate(const IntSize&); + void swap(Shape&); - struct CompareContainsOperation; - struct CompareIntersectsOperation; + struct CompareContainsOperation; + struct CompareIntersectsOperation; - template - static bool compareShapes(const Shape& shape1, const Shape& shape2); - void trimCapacities(); + template + static bool compareShapes(const Shape& shape1, const Shape& shape2); + void trimCapacities(); #ifndef NDEBUG - void dump() const; + void dump() const; #endif - private: - struct UnionOperation; - struct IntersectOperation; - struct SubtractOperation; + private: + struct UnionOperation; + struct IntersectOperation; + struct SubtractOperation; - template - static Shape shapeOperation(const Shape& shape1, const Shape& shape2); + template + static Shape shapeOperation(const Shape& shape1, const Shape& shape2); - void appendSegment(int x); - void appendSpan(int y); - void appendSpan(int y, SegmentIterator begin, SegmentIterator end); - void appendSpans(const Shape&, SpanIterator begin, SpanIterator end); + void appendSegment(int x); + void appendSpan(int y); + void appendSpan(int y, SegmentIterator begin, SegmentIterator end); + void appendSpans(const Shape&, SpanIterator begin, SpanIterator end); - bool canCoalesce(SegmentIterator begin, SegmentIterator end); + bool canCoalesce(SegmentIterator begin, SegmentIterator end); - Vector m_segments; - Vector m_spans; + Vector m_segments; + Vector m_spans; - friend bool operator==(const Shape&, const Shape&); - }; + friend bool operator==(const Shape&, const Shape&); + }; - IntRect m_bounds; - Shape m_shape; + IntRect m_bounds; + Shape m_shape; - friend bool operator==(const Region&, const Region&); - friend bool operator==(const Shape&, const Shape&); - friend bool operator==(const Span&, const Span&); + friend bool operator==(const Region&, const Region&); + friend bool operator==(const Shape&, const Shape&); + friend bool operator==(const Span&, const Span&); }; -static inline Region intersect(const Region& a, const Region& b) -{ - Region result(a); - result.intersect(b); +static inline Region intersect(const Region& a, const Region& b) { + Region result(a); + result.intersect(b); - return result; + return result; } -static inline Region subtract(const Region& a, const Region& b) -{ - Region result(a); - result.subtract(b); +static inline Region subtract(const Region& a, const Region& b) { + Region result(a); + result.subtract(b); - return result; + return result; } -static inline Region translate(const Region& region, const IntSize& offset) -{ - Region result(region); - result.translate(offset); +static inline Region translate(const Region& region, const IntSize& offset) { + Region result(region); + result.translate(offset); - return result; + return result; } -inline bool operator==(const Region& a, const Region& b) -{ - return a.m_bounds == b.m_bounds && a.m_shape == b.m_shape; +inline bool operator==(const Region& a, const Region& b) { + return a.m_bounds == b.m_bounds && a.m_shape == b.m_shape; } -inline bool operator==(const Region::Shape& a, const Region::Shape& b) -{ - return a.m_spans == b.m_spans && a.m_segments == b.m_segments; +inline bool operator==(const Region::Shape& a, const Region::Shape& b) { + return a.m_spans == b.m_spans && a.m_segments == b.m_segments; } -inline bool operator==(const Region::Span& a, const Region::Span& b) -{ - return a.y == b.y && a.segmentIndex == b.segmentIndex; +inline bool operator==(const Region::Span& a, const Region::Span& b) { + return a.y == b.y && a.segmentIndex == b.segmentIndex; } -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_GEOMETRY_REGION_H_ diff --git a/sky/engine/platform/geometry/RegionTest.cpp b/sky/engine/platform/geometry/RegionTest.cpp index 7e9e8f99ef421..6624db7096c07 100644 --- a/sky/engine/platform/geometry/RegionTest.cpp +++ b/sky/engine/platform/geometry/RegionTest.cpp @@ -10,19 +10,18 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #include "flutter/sky/engine/platform/geometry/Region.h" #include @@ -31,363 +30,357 @@ using namespace blink; namespace { -#define TEST_INSIDE_RECT(r, x, y, w, h) \ - EXPECT_TRUE(r.contains(IntPoint(x, y))); \ - EXPECT_TRUE(r.contains(IntPoint(x + w - 1, y))); \ - EXPECT_TRUE(r.contains(IntPoint(x, y + h - 1))); \ - EXPECT_TRUE(r.contains(IntPoint(x + w - 1, y + h - 1))); \ - EXPECT_TRUE(r.contains(IntPoint(x, y + h / 2))); \ - EXPECT_TRUE(r.contains(IntPoint(x + w - 1, y + h / 2))); \ - EXPECT_TRUE(r.contains(IntPoint(x + w / 2, y))); \ - EXPECT_TRUE(r.contains(IntPoint(x + w / 2, y + h - 1))); \ - EXPECT_TRUE(r.contains(IntPoint(x + w / 2, y + h / 2))); \ - -#define TEST_LEFT_OF_RECT(r, x, y, w, h) \ - EXPECT_FALSE(r.contains(IntPoint(x - 1, y))); \ - EXPECT_FALSE(r.contains(IntPoint(x - 1, y + h - 1))); \ - -#define TEST_RIGHT_OF_RECT(r, x, y, w, h) \ - EXPECT_FALSE(r.contains(IntPoint(x + w, y))); \ - EXPECT_FALSE(r.contains(IntPoint(x + w, y + h - 1))); \ - -#define TEST_TOP_OF_RECT(r, x, y, w, h) \ - EXPECT_FALSE(r.contains(IntPoint(x, y - 1))); \ - EXPECT_FALSE(r.contains(IntPoint(x + w - 1, y - 1))); \ - -#define TEST_BOTTOM_OF_RECT(r, x, y, w, h) \ - EXPECT_FALSE(r.contains(IntPoint(x, y + h))); \ - EXPECT_FALSE(r.contains(IntPoint(x + w - 1, y + h))); \ - -TEST(RegionTest, containsPoint) -{ - Region r; - - EXPECT_FALSE(r.contains(IntPoint(0, 0))); - - r.unite(IntRect(35, 35, 1, 1)); - TEST_INSIDE_RECT(r, 35, 35, 1, 1); - TEST_LEFT_OF_RECT(r, 35, 35, 1, 1); - TEST_RIGHT_OF_RECT(r, 35, 35, 1, 1); - TEST_TOP_OF_RECT(r, 35, 35, 1, 1); - TEST_BOTTOM_OF_RECT(r, 35, 35, 1, 1); - - r.unite(IntRect(30, 30, 10, 10)); - TEST_INSIDE_RECT(r, 30, 30, 10, 10); - TEST_LEFT_OF_RECT(r, 30, 30, 10, 10); - TEST_RIGHT_OF_RECT(r, 30, 30, 10, 10); - TEST_TOP_OF_RECT(r, 30, 30, 10, 10); - TEST_BOTTOM_OF_RECT(r, 30, 30, 10, 10); - - r.unite(IntRect(31, 40, 10, 10)); - EXPECT_FALSE(r.contains(IntPoint(30, 40))); - EXPECT_TRUE(r.contains(IntPoint(31, 40))); - EXPECT_FALSE(r.contains(IntPoint(40, 39))); - EXPECT_TRUE(r.contains(IntPoint(40, 40))); - - TEST_INSIDE_RECT(r, 30, 30, 10, 10); - TEST_LEFT_OF_RECT(r, 30, 30, 10, 10); - TEST_RIGHT_OF_RECT(r, 30, 30, 10, 10); - TEST_TOP_OF_RECT(r, 30, 30, 10, 10); - TEST_INSIDE_RECT(r, 31, 40, 10, 10); - TEST_LEFT_OF_RECT(r, 31, 40, 10, 10); - TEST_RIGHT_OF_RECT(r, 31, 40, 10, 10); - TEST_BOTTOM_OF_RECT(r, 31, 40, 10, 10); - - r.unite(IntRect(42, 40, 10, 10)); - - TEST_INSIDE_RECT(r, 42, 40, 10, 10); - TEST_LEFT_OF_RECT(r, 42, 40, 10, 10); - TEST_RIGHT_OF_RECT(r, 42, 40, 10, 10); - TEST_TOP_OF_RECT(r, 42, 40, 10, 10); - TEST_BOTTOM_OF_RECT(r, 42, 40, 10, 10); - - TEST_INSIDE_RECT(r, 30, 30, 10, 10); - TEST_LEFT_OF_RECT(r, 30, 30, 10, 10); - TEST_RIGHT_OF_RECT(r, 30, 30, 10, 10); - TEST_TOP_OF_RECT(r, 30, 30, 10, 10); - TEST_INSIDE_RECT(r, 31, 40, 10, 10); - TEST_LEFT_OF_RECT(r, 31, 40, 10, 10); - TEST_RIGHT_OF_RECT(r, 31, 40, 10, 10); - TEST_BOTTOM_OF_RECT(r, 31, 40, 10, 10); +#define TEST_INSIDE_RECT(r, x, y, w, h) \ + EXPECT_TRUE(r.contains(IntPoint(x, y))); \ + EXPECT_TRUE(r.contains(IntPoint(x + w - 1, y))); \ + EXPECT_TRUE(r.contains(IntPoint(x, y + h - 1))); \ + EXPECT_TRUE(r.contains(IntPoint(x + w - 1, y + h - 1))); \ + EXPECT_TRUE(r.contains(IntPoint(x, y + h / 2))); \ + EXPECT_TRUE(r.contains(IntPoint(x + w - 1, y + h / 2))); \ + EXPECT_TRUE(r.contains(IntPoint(x + w / 2, y))); \ + EXPECT_TRUE(r.contains(IntPoint(x + w / 2, y + h - 1))); \ + EXPECT_TRUE(r.contains(IntPoint(x + w / 2, y + h / 2))); + +#define TEST_LEFT_OF_RECT(r, x, y, w, h) \ + EXPECT_FALSE(r.contains(IntPoint(x - 1, y))); \ + EXPECT_FALSE(r.contains(IntPoint(x - 1, y + h - 1))); + +#define TEST_RIGHT_OF_RECT(r, x, y, w, h) \ + EXPECT_FALSE(r.contains(IntPoint(x + w, y))); \ + EXPECT_FALSE(r.contains(IntPoint(x + w, y + h - 1))); + +#define TEST_TOP_OF_RECT(r, x, y, w, h) \ + EXPECT_FALSE(r.contains(IntPoint(x, y - 1))); \ + EXPECT_FALSE(r.contains(IntPoint(x + w - 1, y - 1))); + +#define TEST_BOTTOM_OF_RECT(r, x, y, w, h) \ + EXPECT_FALSE(r.contains(IntPoint(x, y + h))); \ + EXPECT_FALSE(r.contains(IntPoint(x + w - 1, y + h))); + +TEST(RegionTest, containsPoint) { + Region r; + + EXPECT_FALSE(r.contains(IntPoint(0, 0))); + + r.unite(IntRect(35, 35, 1, 1)); + TEST_INSIDE_RECT(r, 35, 35, 1, 1); + TEST_LEFT_OF_RECT(r, 35, 35, 1, 1); + TEST_RIGHT_OF_RECT(r, 35, 35, 1, 1); + TEST_TOP_OF_RECT(r, 35, 35, 1, 1); + TEST_BOTTOM_OF_RECT(r, 35, 35, 1, 1); + + r.unite(IntRect(30, 30, 10, 10)); + TEST_INSIDE_RECT(r, 30, 30, 10, 10); + TEST_LEFT_OF_RECT(r, 30, 30, 10, 10); + TEST_RIGHT_OF_RECT(r, 30, 30, 10, 10); + TEST_TOP_OF_RECT(r, 30, 30, 10, 10); + TEST_BOTTOM_OF_RECT(r, 30, 30, 10, 10); + + r.unite(IntRect(31, 40, 10, 10)); + EXPECT_FALSE(r.contains(IntPoint(30, 40))); + EXPECT_TRUE(r.contains(IntPoint(31, 40))); + EXPECT_FALSE(r.contains(IntPoint(40, 39))); + EXPECT_TRUE(r.contains(IntPoint(40, 40))); + + TEST_INSIDE_RECT(r, 30, 30, 10, 10); + TEST_LEFT_OF_RECT(r, 30, 30, 10, 10); + TEST_RIGHT_OF_RECT(r, 30, 30, 10, 10); + TEST_TOP_OF_RECT(r, 30, 30, 10, 10); + TEST_INSIDE_RECT(r, 31, 40, 10, 10); + TEST_LEFT_OF_RECT(r, 31, 40, 10, 10); + TEST_RIGHT_OF_RECT(r, 31, 40, 10, 10); + TEST_BOTTOM_OF_RECT(r, 31, 40, 10, 10); + + r.unite(IntRect(42, 40, 10, 10)); + + TEST_INSIDE_RECT(r, 42, 40, 10, 10); + TEST_LEFT_OF_RECT(r, 42, 40, 10, 10); + TEST_RIGHT_OF_RECT(r, 42, 40, 10, 10); + TEST_TOP_OF_RECT(r, 42, 40, 10, 10); + TEST_BOTTOM_OF_RECT(r, 42, 40, 10, 10); + + TEST_INSIDE_RECT(r, 30, 30, 10, 10); + TEST_LEFT_OF_RECT(r, 30, 30, 10, 10); + TEST_RIGHT_OF_RECT(r, 30, 30, 10, 10); + TEST_TOP_OF_RECT(r, 30, 30, 10, 10); + TEST_INSIDE_RECT(r, 31, 40, 10, 10); + TEST_LEFT_OF_RECT(r, 31, 40, 10, 10); + TEST_RIGHT_OF_RECT(r, 31, 40, 10, 10); + TEST_BOTTOM_OF_RECT(r, 31, 40, 10, 10); } -TEST(RegionTest, emptySpan) -{ - Region r; - r.unite(IntRect(5, 0, 10, 10)); - r.unite(IntRect(0, 5, 10, 10)); - r.subtract(IntRect(7, 7, 10, 0)); +TEST(RegionTest, emptySpan) { + Region r; + r.unite(IntRect(5, 0, 10, 10)); + r.unite(IntRect(0, 5, 10, 10)); + r.subtract(IntRect(7, 7, 10, 0)); - Vector rects = r.rects(); - for (size_t i = 0; i < rects.size(); ++i) - EXPECT_FALSE(rects[i].isEmpty()); + Vector rects = r.rects(); + for (size_t i = 0; i < rects.size(); ++i) + EXPECT_FALSE(rects[i].isEmpty()); } #define TEST_NO_INTERSECT(a, b) \ -{ \ + { \ Region ar = a; \ Region br = b; \ EXPECT_FALSE(ar.intersects(br)); \ EXPECT_FALSE(br.intersects(ar)); \ -} + } #define TEST_INTERSECT(a, b) \ -{ \ + { \ Region ar = a; \ Region br = b; \ EXPECT_TRUE(ar.intersects(br)); \ EXPECT_TRUE(br.intersects(ar)); \ + } + +TEST(RegionTest, intersectsRegion) { + Region r; + + TEST_NO_INTERSECT(IntRect(), IntRect()); + TEST_NO_INTERSECT(IntRect(), IntRect(0, 0, 1, 1)); + TEST_NO_INTERSECT(IntRect(), IntRect(1, 1, 1, 1)); + + r.unite(IntRect(0, 0, 1, 1)); + TEST_NO_INTERSECT(r, IntRect()); + TEST_INTERSECT(r, IntRect(0, 0, 1, 1)); + TEST_INTERSECT(r, IntRect(0, 0, 2, 2)); + TEST_INTERSECT(r, IntRect(-1, 0, 2, 2)); + TEST_INTERSECT(r, IntRect(-1, -1, 2, 2)); + TEST_INTERSECT(r, IntRect(0, -1, 2, 2)); + TEST_INTERSECT(r, IntRect(-1, -1, 3, 3)); + + r.unite(IntRect(0, 0, 3, 3)); + r.unite(IntRect(10, 0, 3, 3)); + r.unite(IntRect(0, 10, 13, 3)); + TEST_NO_INTERSECT(r, IntRect()); + TEST_INTERSECT(r, IntRect(1, 1, 1, 1)); + TEST_INTERSECT(r, IntRect(0, 0, 2, 2)); + TEST_INTERSECT(r, IntRect(1, 0, 2, 2)); + TEST_INTERSECT(r, IntRect(1, 1, 2, 2)); + TEST_INTERSECT(r, IntRect(0, 1, 2, 2)); + TEST_INTERSECT(r, IntRect(0, 0, 3, 3)); + TEST_INTERSECT(r, IntRect(-1, -1, 2, 2)); + TEST_INTERSECT(r, IntRect(2, -1, 2, 2)); + TEST_INTERSECT(r, IntRect(2, 2, 2, 2)); + TEST_INTERSECT(r, IntRect(-1, 2, 2, 2)); + + TEST_INTERSECT(r, IntRect(11, 1, 1, 1)); + TEST_INTERSECT(r, IntRect(10, 0, 2, 2)); + TEST_INTERSECT(r, IntRect(11, 0, 2, 2)); + TEST_INTERSECT(r, IntRect(11, 1, 2, 2)); + TEST_INTERSECT(r, IntRect(10, 1, 2, 2)); + TEST_INTERSECT(r, IntRect(10, 0, 3, 3)); + TEST_INTERSECT(r, IntRect(9, -1, 2, 2)); + TEST_INTERSECT(r, IntRect(12, -1, 2, 2)); + TEST_INTERSECT(r, IntRect(12, 2, 2, 2)); + TEST_INTERSECT(r, IntRect(9, 2, 2, 2)); + + TEST_INTERSECT(r, IntRect(0, -1, 13, 5)); + TEST_INTERSECT(r, IntRect(1, -1, 11, 5)); + TEST_INTERSECT(r, IntRect(2, -1, 9, 5)); + TEST_INTERSECT(r, IntRect(2, -1, 8, 5)); + TEST_INTERSECT(r, IntRect(3, -1, 8, 5)); + TEST_NO_INTERSECT(r, IntRect(3, -1, 7, 5)); + + TEST_INTERSECT(r, IntRect(0, 1, 13, 1)); + TEST_INTERSECT(r, IntRect(1, 1, 11, 1)); + TEST_INTERSECT(r, IntRect(2, 1, 9, 1)); + TEST_INTERSECT(r, IntRect(2, 1, 8, 1)); + TEST_INTERSECT(r, IntRect(3, 1, 8, 1)); + TEST_NO_INTERSECT(r, IntRect(3, 1, 7, 1)); + + TEST_INTERSECT(r, IntRect(0, 0, 13, 13)); + TEST_INTERSECT(r, IntRect(0, 1, 13, 11)); + TEST_INTERSECT(r, IntRect(0, 2, 13, 9)); + TEST_INTERSECT(r, IntRect(0, 2, 13, 8)); + TEST_INTERSECT(r, IntRect(0, 3, 13, 8)); + TEST_NO_INTERSECT(r, IntRect(0, 3, 13, 7)); } -TEST(RegionTest, intersectsRegion) -{ - Region r; - - TEST_NO_INTERSECT(IntRect(), IntRect()); - TEST_NO_INTERSECT(IntRect(), IntRect(0, 0, 1, 1)); - TEST_NO_INTERSECT(IntRect(), IntRect(1, 1, 1, 1)); - - r.unite(IntRect(0, 0, 1, 1)); - TEST_NO_INTERSECT(r, IntRect()); - TEST_INTERSECT(r, IntRect(0, 0, 1, 1)); - TEST_INTERSECT(r, IntRect(0, 0, 2, 2)); - TEST_INTERSECT(r, IntRect(-1, 0, 2, 2)); - TEST_INTERSECT(r, IntRect(-1, -1, 2, 2)); - TEST_INTERSECT(r, IntRect(0, -1, 2, 2)); - TEST_INTERSECT(r, IntRect(-1, -1, 3, 3)); - - r.unite(IntRect(0, 0, 3, 3)); - r.unite(IntRect(10, 0, 3, 3)); - r.unite(IntRect(0, 10, 13, 3)); - TEST_NO_INTERSECT(r, IntRect()); - TEST_INTERSECT(r, IntRect(1, 1, 1, 1)); - TEST_INTERSECT(r, IntRect(0, 0, 2, 2)); - TEST_INTERSECT(r, IntRect(1, 0, 2, 2)); - TEST_INTERSECT(r, IntRect(1, 1, 2, 2)); - TEST_INTERSECT(r, IntRect(0, 1, 2, 2)); - TEST_INTERSECT(r, IntRect(0, 0, 3, 3)); - TEST_INTERSECT(r, IntRect(-1, -1, 2, 2)); - TEST_INTERSECT(r, IntRect(2, -1, 2, 2)); - TEST_INTERSECT(r, IntRect(2, 2, 2, 2)); - TEST_INTERSECT(r, IntRect(-1, 2, 2, 2)); - - TEST_INTERSECT(r, IntRect(11, 1, 1, 1)); - TEST_INTERSECT(r, IntRect(10, 0, 2, 2)); - TEST_INTERSECT(r, IntRect(11, 0, 2, 2)); - TEST_INTERSECT(r, IntRect(11, 1, 2, 2)); - TEST_INTERSECT(r, IntRect(10, 1, 2, 2)); - TEST_INTERSECT(r, IntRect(10, 0, 3, 3)); - TEST_INTERSECT(r, IntRect(9, -1, 2, 2)); - TEST_INTERSECT(r, IntRect(12, -1, 2, 2)); - TEST_INTERSECT(r, IntRect(12, 2, 2, 2)); - TEST_INTERSECT(r, IntRect(9, 2, 2, 2)); - - TEST_INTERSECT(r, IntRect(0, -1, 13, 5)); - TEST_INTERSECT(r, IntRect(1, -1, 11, 5)); - TEST_INTERSECT(r, IntRect(2, -1, 9, 5)); - TEST_INTERSECT(r, IntRect(2, -1, 8, 5)); - TEST_INTERSECT(r, IntRect(3, -1, 8, 5)); - TEST_NO_INTERSECT(r, IntRect(3, -1, 7, 5)); - - TEST_INTERSECT(r, IntRect(0, 1, 13, 1)); - TEST_INTERSECT(r, IntRect(1, 1, 11, 1)); - TEST_INTERSECT(r, IntRect(2, 1, 9, 1)); - TEST_INTERSECT(r, IntRect(2, 1, 8, 1)); - TEST_INTERSECT(r, IntRect(3, 1, 8, 1)); - TEST_NO_INTERSECT(r, IntRect(3, 1, 7, 1)); - - TEST_INTERSECT(r, IntRect(0, 0, 13, 13)); - TEST_INTERSECT(r, IntRect(0, 1, 13, 11)); - TEST_INTERSECT(r, IntRect(0, 2, 13, 9)); - TEST_INTERSECT(r, IntRect(0, 2, 13, 8)); - TEST_INTERSECT(r, IntRect(0, 3, 13, 8)); - TEST_NO_INTERSECT(r, IntRect(0, 3, 13, 7)); -} - -TEST(RegionTest, ReadPastFullSpanVectorInIntersectsTest) -{ - Region r; - - // This region has enough spans to fill its allocated Vector exactly. - r.unite(IntRect(400, 300, 1, 800)); - r.unite(IntRect(785, 585, 1, 1)); - r.unite(IntRect(787, 585, 1, 1)); - r.unite(IntRect(0, 587, 16, 162)); - r.unite(IntRect(26, 590, 300, 150)); - r.unite(IntRect(196, 750, 1, 1)); - r.unite(IntRect(0, 766, 1, 1)); - r.unite(IntRect(0, 782, 1, 1)); - r.unite(IntRect(745, 798, 1, 1)); - r.unite(IntRect(795, 882, 10, 585)); - r.unite(IntRect(100, 1499, 586, 1)); - r.unite(IntRect(100, 1500, 585, 784)); - // This query rect goes past the bottom of the Region, causing the - // test to reach the last span and try go past it. It should not read - // memory off the end of the span Vector. - TEST_NO_INTERSECT(r, IntRect(0, 2184, 1, 150)); +TEST(RegionTest, ReadPastFullSpanVectorInIntersectsTest) { + Region r; + + // This region has enough spans to fill its allocated Vector exactly. + r.unite(IntRect(400, 300, 1, 800)); + r.unite(IntRect(785, 585, 1, 1)); + r.unite(IntRect(787, 585, 1, 1)); + r.unite(IntRect(0, 587, 16, 162)); + r.unite(IntRect(26, 590, 300, 150)); + r.unite(IntRect(196, 750, 1, 1)); + r.unite(IntRect(0, 766, 1, 1)); + r.unite(IntRect(0, 782, 1, 1)); + r.unite(IntRect(745, 798, 1, 1)); + r.unite(IntRect(795, 882, 10, 585)); + r.unite(IntRect(100, 1499, 586, 1)); + r.unite(IntRect(100, 1500, 585, 784)); + // This query rect goes past the bottom of the Region, causing the + // test to reach the last span and try go past it. It should not read + // memory off the end of the span Vector. + TEST_NO_INTERSECT(r, IntRect(0, 2184, 1, 150)); } #define TEST_NO_CONTAINS(a, b) \ -{ \ + { \ Region ar = a; \ Region br = b; \ EXPECT_FALSE(ar.contains(br)); \ -} + } #define TEST_CONTAINS(a, b) \ -{ \ + { \ Region ar = a; \ Region br = b; \ EXPECT_TRUE(ar.contains(br)); \ + } + +TEST(RegionTest, containsRegion) { + TEST_CONTAINS(IntRect(), IntRect()); + TEST_NO_CONTAINS(IntRect(), IntRect(0, 0, 1, 1)); + TEST_NO_CONTAINS(IntRect(), IntRect(1, 1, 1, 1)); + + TEST_NO_CONTAINS(IntRect(10, 10, 1, 1), IntRect(11, 10, 1, 1)); + TEST_NO_CONTAINS(IntRect(10, 10, 1, 1), IntRect(10, 11, 1, 1)); + TEST_NO_CONTAINS(IntRect(10, 10, 1, 1), IntRect(9, 10, 1, 1)); + TEST_NO_CONTAINS(IntRect(10, 10, 1, 1), IntRect(10, 9, 1, 1)); + TEST_NO_CONTAINS(IntRect(10, 10, 1, 1), IntRect(9, 9, 2, 2)); + TEST_NO_CONTAINS(IntRect(10, 10, 1, 1), IntRect(10, 9, 2, 2)); + TEST_NO_CONTAINS(IntRect(10, 10, 1, 1), IntRect(9, 10, 2, 2)); + TEST_NO_CONTAINS(IntRect(10, 10, 1, 1), IntRect(10, 10, 2, 2)); + TEST_NO_CONTAINS(IntRect(10, 10, 1, 1), IntRect(9, 9, 3, 3)); + + Region hLines; + for (int i = 10; i < 20; i += 2) + hLines.unite(IntRect(i, 10, 1, 10)); + + TEST_CONTAINS(IntRect(10, 10, 9, 10), hLines); + TEST_NO_CONTAINS(IntRect(10, 10, 9, 9), hLines); + TEST_NO_CONTAINS(IntRect(10, 11, 9, 9), hLines); + TEST_NO_CONTAINS(IntRect(10, 10, 8, 10), hLines); + TEST_NO_CONTAINS(IntRect(11, 10, 8, 10), hLines); + + Region vLines; + for (int i = 10; i < 20; i += 2) + vLines.unite(IntRect(10, i, 10, 1)); + + TEST_CONTAINS(IntRect(10, 10, 10, 9), vLines); + TEST_NO_CONTAINS(IntRect(10, 10, 9, 9), vLines); + TEST_NO_CONTAINS(IntRect(11, 10, 9, 9), vLines); + TEST_NO_CONTAINS(IntRect(10, 10, 10, 8), vLines); + TEST_NO_CONTAINS(IntRect(10, 11, 10, 8), vLines); + + Region grid; + for (int i = 10; i < 20; i += 2) + for (int j = 10; j < 20; j += 2) + grid.unite(IntRect(i, j, 1, 1)); + + TEST_CONTAINS(IntRect(10, 10, 9, 9), grid); + TEST_NO_CONTAINS(IntRect(10, 10, 9, 8), grid); + TEST_NO_CONTAINS(IntRect(10, 11, 9, 8), grid); + TEST_NO_CONTAINS(IntRect(10, 10, 8, 9), grid); + TEST_NO_CONTAINS(IntRect(11, 10, 8, 9), grid); + + TEST_CONTAINS(hLines, hLines); + TEST_CONTAINS(vLines, vLines); + TEST_NO_CONTAINS(vLines, hLines); + TEST_NO_CONTAINS(hLines, vLines); + TEST_CONTAINS(grid, grid); + TEST_CONTAINS(hLines, grid); + TEST_CONTAINS(vLines, grid); + TEST_NO_CONTAINS(grid, hLines); + TEST_NO_CONTAINS(grid, vLines); + + for (int i = 10; i < 20; i += 2) + TEST_CONTAINS(hLines, IntRect(i, 10, 1, 10)); + + for (int i = 10; i < 20; i += 2) + TEST_CONTAINS(vLines, IntRect(10, i, 10, 1)); + + for (int i = 10; i < 20; i += 2) + for (int j = 10; j < 20; j += 2) + TEST_CONTAINS(grid, IntRect(i, j, 1, 1)); + + Region container; + container.unite(IntRect(0, 0, 40, 20)); + container.unite(IntRect(0, 20, 41, 20)); + TEST_CONTAINS(container, IntRect(5, 5, 30, 30)); + + container = Region(); + container.unite(IntRect(0, 0, 10, 10)); + container.unite(IntRect(0, 30, 10, 10)); + container.unite(IntRect(30, 30, 10, 10)); + container.unite(IntRect(30, 0, 10, 10)); + TEST_NO_CONTAINS(container, IntRect(5, 5, 30, 30)); + + container = Region(); + container.unite(IntRect(0, 0, 10, 10)); + container.unite(IntRect(0, 30, 10, 10)); + container.unite(IntRect(30, 0, 10, 40)); + TEST_NO_CONTAINS(container, IntRect(5, 5, 30, 30)); + + container = Region(); + container.unite(IntRect(30, 0, 10, 10)); + container.unite(IntRect(30, 30, 10, 10)); + container.unite(IntRect(0, 0, 10, 40)); + TEST_NO_CONTAINS(container, IntRect(5, 5, 30, 30)); + + container = Region(); + container.unite(IntRect(0, 0, 10, 40)); + container.unite(IntRect(30, 0, 10, 40)); + TEST_NO_CONTAINS(container, IntRect(5, 5, 30, 30)); + + container = Region(); + container.unite(IntRect(0, 0, 40, 40)); + TEST_NO_CONTAINS(container, IntRect(10, -1, 20, 10)); + + container = Region(); + container.unite(IntRect(0, 0, 40, 40)); + TEST_NO_CONTAINS(container, IntRect(10, 31, 20, 10)); + + container = Region(); + container.unite(IntRect(0, 0, 40, 20)); + container.unite(IntRect(0, 20, 41, 20)); + TEST_NO_CONTAINS(container, IntRect(-1, 10, 10, 20)); + + container = Region(); + container.unite(IntRect(0, 0, 40, 20)); + container.unite(IntRect(0, 20, 41, 20)); + TEST_NO_CONTAINS(container, IntRect(31, 10, 10, 20)); + + container = Region(); + container.unite(IntRect(0, 0, 40, 40)); + container.subtract(IntRect(0, 20, 60, 0)); + TEST_NO_CONTAINS(container, IntRect(31, 10, 10, 20)); } -TEST(RegionTest, containsRegion) -{ - TEST_CONTAINS(IntRect(), IntRect()); - TEST_NO_CONTAINS(IntRect(), IntRect(0, 0, 1, 1)); - TEST_NO_CONTAINS(IntRect(), IntRect(1, 1, 1, 1)); - - TEST_NO_CONTAINS(IntRect(10, 10, 1, 1), IntRect(11, 10, 1, 1)); - TEST_NO_CONTAINS(IntRect(10, 10, 1, 1), IntRect(10, 11, 1, 1)); - TEST_NO_CONTAINS(IntRect(10, 10, 1, 1), IntRect(9, 10, 1, 1)); - TEST_NO_CONTAINS(IntRect(10, 10, 1, 1), IntRect(10, 9, 1, 1)); - TEST_NO_CONTAINS(IntRect(10, 10, 1, 1), IntRect(9, 9, 2, 2)); - TEST_NO_CONTAINS(IntRect(10, 10, 1, 1), IntRect(10, 9, 2, 2)); - TEST_NO_CONTAINS(IntRect(10, 10, 1, 1), IntRect(9, 10, 2, 2)); - TEST_NO_CONTAINS(IntRect(10, 10, 1, 1), IntRect(10, 10, 2, 2)); - TEST_NO_CONTAINS(IntRect(10, 10, 1, 1), IntRect(9, 9, 3, 3)); - - Region hLines; - for (int i = 10; i < 20; i += 2) - hLines.unite(IntRect(i, 10, 1, 10)); - - TEST_CONTAINS(IntRect(10, 10, 9, 10), hLines); - TEST_NO_CONTAINS(IntRect(10, 10, 9, 9), hLines); - TEST_NO_CONTAINS(IntRect(10, 11, 9, 9), hLines); - TEST_NO_CONTAINS(IntRect(10, 10, 8, 10), hLines); - TEST_NO_CONTAINS(IntRect(11, 10, 8, 10), hLines); - - Region vLines; - for (int i = 10; i < 20; i += 2) - vLines.unite(IntRect(10, i, 10, 1)); - - TEST_CONTAINS(IntRect(10, 10, 10, 9), vLines); - TEST_NO_CONTAINS(IntRect(10, 10, 9, 9), vLines); - TEST_NO_CONTAINS(IntRect(11, 10, 9, 9), vLines); - TEST_NO_CONTAINS(IntRect(10, 10, 10, 8), vLines); - TEST_NO_CONTAINS(IntRect(10, 11, 10, 8), vLines); - - Region grid; - for (int i = 10; i < 20; i += 2) - for (int j = 10; j < 20; j += 2) - grid.unite(IntRect(i, j, 1, 1)); - - TEST_CONTAINS(IntRect(10, 10, 9, 9), grid); - TEST_NO_CONTAINS(IntRect(10, 10, 9, 8), grid); - TEST_NO_CONTAINS(IntRect(10, 11, 9, 8), grid); - TEST_NO_CONTAINS(IntRect(10, 10, 8, 9), grid); - TEST_NO_CONTAINS(IntRect(11, 10, 8, 9), grid); - - TEST_CONTAINS(hLines, hLines); - TEST_CONTAINS(vLines, vLines); - TEST_NO_CONTAINS(vLines, hLines); - TEST_NO_CONTAINS(hLines, vLines); - TEST_CONTAINS(grid, grid); - TEST_CONTAINS(hLines, grid); - TEST_CONTAINS(vLines, grid); - TEST_NO_CONTAINS(grid, hLines); - TEST_NO_CONTAINS(grid, vLines); - - for (int i = 10; i < 20; i += 2) - TEST_CONTAINS(hLines, IntRect(i, 10, 1, 10)); - - for (int i = 10; i < 20; i += 2) - TEST_CONTAINS(vLines, IntRect(10, i, 10, 1)); - - for (int i = 10; i < 20; i += 2) - for (int j = 10; j < 20; j += 2) - TEST_CONTAINS(grid, IntRect(i, j, 1, 1)); - - Region container; - container.unite(IntRect(0, 0, 40, 20)); - container.unite(IntRect(0, 20, 41, 20)); - TEST_CONTAINS(container, IntRect(5, 5, 30, 30)); - - container = Region(); - container.unite(IntRect(0, 0, 10, 10)); - container.unite(IntRect(0, 30, 10, 10)); - container.unite(IntRect(30, 30, 10, 10)); - container.unite(IntRect(30, 0, 10, 10)); - TEST_NO_CONTAINS(container, IntRect(5, 5, 30, 30)); - - container = Region(); - container.unite(IntRect(0, 0, 10, 10)); - container.unite(IntRect(0, 30, 10, 10)); - container.unite(IntRect(30, 0, 10, 40)); - TEST_NO_CONTAINS(container, IntRect(5, 5, 30, 30)); - - container = Region(); - container.unite(IntRect(30, 0, 10, 10)); - container.unite(IntRect(30, 30, 10, 10)); - container.unite(IntRect(0, 0, 10, 40)); - TEST_NO_CONTAINS(container, IntRect(5, 5, 30, 30)); - - container = Region(); - container.unite(IntRect(0, 0, 10, 40)); - container.unite(IntRect(30, 0, 10, 40)); - TEST_NO_CONTAINS(container, IntRect(5, 5, 30, 30)); - - container = Region(); - container.unite(IntRect(0, 0, 40, 40)); - TEST_NO_CONTAINS(container, IntRect(10, -1, 20, 10)); - - container = Region(); - container.unite(IntRect(0, 0, 40, 40)); - TEST_NO_CONTAINS(container, IntRect(10, 31, 20, 10)); - - container = Region(); - container.unite(IntRect(0, 0, 40, 20)); - container.unite(IntRect(0, 20, 41, 20)); - TEST_NO_CONTAINS(container, IntRect(-1, 10, 10, 20)); - - container = Region(); - container.unite(IntRect(0, 0, 40, 20)); - container.unite(IntRect(0, 20, 41, 20)); - TEST_NO_CONTAINS(container, IntRect(31, 10, 10, 20)); - - container = Region(); - container.unite(IntRect(0, 0, 40, 40)); - container.subtract(IntRect(0, 20, 60, 0)); - TEST_NO_CONTAINS(container, IntRect(31, 10, 10, 20)); -} - -TEST(RegionTest, unite) -{ - Region r; - Region r2; - - // A rect uniting a contained rect does not change the region. - r2 = r = IntRect(0, 0, 50, 50); - r2.unite(IntRect(20, 20, 10, 10)); - EXPECT_EQ(r, r2); - - // A rect uniting a containing rect gives back the containing rect. - r = IntRect(0, 0, 50, 50); - r.unite(IntRect(0, 0, 100, 100)); - EXPECT_EQ(Region(IntRect(0, 0, 100, 100)), r); - - // A complex region uniting a contained rect does not change the region. - r = IntRect(0, 0, 50, 50); - r.unite(IntRect(100, 0, 50, 50)); - r2 = r; - r2.unite(IntRect(20, 20, 10, 10)); - EXPECT_EQ(r, r2); - - // A complex region uniting a containing rect gives back the containing rect. - r = IntRect(0, 0, 50, 50); - r.unite(IntRect(100, 0, 50, 50)); - r. unite(IntRect(0, 0, 500, 500)); - EXPECT_EQ(Region(IntRect(0, 0, 500, 500)), r); +TEST(RegionTest, unite) { + Region r; + Region r2; + + // A rect uniting a contained rect does not change the region. + r2 = r = IntRect(0, 0, 50, 50); + r2.unite(IntRect(20, 20, 10, 10)); + EXPECT_EQ(r, r2); + + // A rect uniting a containing rect gives back the containing rect. + r = IntRect(0, 0, 50, 50); + r.unite(IntRect(0, 0, 100, 100)); + EXPECT_EQ(Region(IntRect(0, 0, 100, 100)), r); + + // A complex region uniting a contained rect does not change the region. + r = IntRect(0, 0, 50, 50); + r.unite(IntRect(100, 0, 50, 50)); + r2 = r; + r2.unite(IntRect(20, 20, 10, 10)); + EXPECT_EQ(r, r2); + + // A complex region uniting a containing rect gives back the containing rect. + r = IntRect(0, 0, 50, 50); + r.unite(IntRect(100, 0, 50, 50)); + r.unite(IntRect(0, 0, 500, 500)); + EXPECT_EQ(Region(IntRect(0, 0, 500, 500)), r); } -} // namespace +} // namespace diff --git a/sky/engine/platform/geometry/RoundedRect.cpp b/sky/engine/platform/geometry/RoundedRect.cpp index cd50294fd410c..f11958ff494e2 100644 --- a/sky/engine/platform/geometry/RoundedRect.cpp +++ b/sky/engine/platform/geometry/RoundedRect.cpp @@ -32,185 +32,203 @@ namespace blink { -bool RoundedRect::Radii::isZero() const -{ - return m_topLeft.isZero() && m_topRight.isZero() && m_bottomLeft.isZero() && m_bottomRight.isZero(); +bool RoundedRect::Radii::isZero() const { + return m_topLeft.isZero() && m_topRight.isZero() && m_bottomLeft.isZero() && + m_bottomRight.isZero(); } -void RoundedRect::Radii::scale(float factor) -{ - if (factor == 1) - return; - - // If either radius on a corner becomes zero, reset both radii on that corner. - m_topLeft.scale(factor); - if (!m_topLeft.width() || !m_topLeft.height()) - m_topLeft = IntSize(); - m_topRight.scale(factor); - if (!m_topRight.width() || !m_topRight.height()) - m_topRight = IntSize(); - m_bottomLeft.scale(factor); - if (!m_bottomLeft.width() || !m_bottomLeft.height()) - m_bottomLeft = IntSize(); - m_bottomRight.scale(factor); - if (!m_bottomRight.width() || !m_bottomRight.height()) - m_bottomRight = IntSize(); - +void RoundedRect::Radii::scale(float factor) { + if (factor == 1) + return; + + // If either radius on a corner becomes zero, reset both radii on that corner. + m_topLeft.scale(factor); + if (!m_topLeft.width() || !m_topLeft.height()) + m_topLeft = IntSize(); + m_topRight.scale(factor); + if (!m_topRight.width() || !m_topRight.height()) + m_topRight = IntSize(); + m_bottomLeft.scale(factor); + if (!m_bottomLeft.width() || !m_bottomLeft.height()) + m_bottomLeft = IntSize(); + m_bottomRight.scale(factor); + if (!m_bottomRight.width() || !m_bottomRight.height()) + m_bottomRight = IntSize(); } -void RoundedRect::Radii::expand(int topWidth, int bottomWidth, int leftWidth, int rightWidth) -{ - if (m_topLeft.width() > 0 && m_topLeft.height() > 0) { - m_topLeft.setWidth(std::max(0, m_topLeft.width() + leftWidth)); - m_topLeft.setHeight(std::max(0, m_topLeft.height() + topWidth)); - } - if (m_topRight.width() > 0 && m_topRight.height() > 0) { - m_topRight.setWidth(std::max(0, m_topRight.width() + rightWidth)); - m_topRight.setHeight(std::max(0, m_topRight.height() + topWidth)); - } - if (m_bottomLeft.width() > 0 && m_bottomLeft.height() > 0) { - m_bottomLeft.setWidth(std::max(0, m_bottomLeft.width() + leftWidth)); - m_bottomLeft.setHeight(std::max(0, m_bottomLeft.height() + bottomWidth)); - } - if (m_bottomRight.width() > 0 && m_bottomRight.height() > 0) { - m_bottomRight.setWidth(std::max(0, m_bottomRight.width() + rightWidth)); - m_bottomRight.setHeight(std::max(0, m_bottomRight.height() + bottomWidth)); - } +void RoundedRect::Radii::expand(int topWidth, + int bottomWidth, + int leftWidth, + int rightWidth) { + if (m_topLeft.width() > 0 && m_topLeft.height() > 0) { + m_topLeft.setWidth(std::max(0, m_topLeft.width() + leftWidth)); + m_topLeft.setHeight(std::max(0, m_topLeft.height() + topWidth)); + } + if (m_topRight.width() > 0 && m_topRight.height() > 0) { + m_topRight.setWidth(std::max(0, m_topRight.width() + rightWidth)); + m_topRight.setHeight(std::max(0, m_topRight.height() + topWidth)); + } + if (m_bottomLeft.width() > 0 && m_bottomLeft.height() > 0) { + m_bottomLeft.setWidth(std::max(0, m_bottomLeft.width() + leftWidth)); + m_bottomLeft.setHeight( + std::max(0, m_bottomLeft.height() + bottomWidth)); + } + if (m_bottomRight.width() > 0 && m_bottomRight.height() > 0) { + m_bottomRight.setWidth( + std::max(0, m_bottomRight.width() + rightWidth)); + m_bottomRight.setHeight( + std::max(0, m_bottomRight.height() + bottomWidth)); + } } -void RoundedRect::inflateWithRadii(int size) -{ - IntRect old = m_rect; +void RoundedRect::inflateWithRadii(int size) { + IntRect old = m_rect; - m_rect.inflate(size); - // Considering the inflation factor of shorter size to scale the radii seems appropriate here - float factor; - if (m_rect.width() < m_rect.height()) - factor = old.width() ? (float)m_rect.width() / old.width() : int(0); - else - factor = old.height() ? (float)m_rect.height() / old.height() : int(0); + m_rect.inflate(size); + // Considering the inflation factor of shorter size to scale the radii seems + // appropriate here + float factor; + if (m_rect.width() < m_rect.height()) + factor = old.width() ? (float)m_rect.width() / old.width() : int(0); + else + factor = old.height() ? (float)m_rect.height() / old.height() : int(0); - m_radii.scale(factor); + m_radii.scale(factor); } -void RoundedRect::Radii::includeLogicalEdges(const RoundedRect::Radii& edges, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) -{ - if (includeLogicalLeftEdge) { - m_bottomLeft = edges.bottomLeft(); - m_topLeft = edges.topLeft(); - } - - if (includeLogicalRightEdge) { - m_topRight = edges.topRight(); - m_bottomRight = edges.bottomRight(); - } +void RoundedRect::Radii::includeLogicalEdges(const RoundedRect::Radii& edges, + bool includeLogicalLeftEdge, + bool includeLogicalRightEdge) { + if (includeLogicalLeftEdge) { + m_bottomLeft = edges.bottomLeft(); + m_topLeft = edges.topLeft(); + } + + if (includeLogicalRightEdge) { + m_topRight = edges.topRight(); + m_bottomRight = edges.bottomRight(); + } } RoundedRect::RoundedRect(int x, int y, int width, int height) - : m_rect(x, y, width, height) -{ -} + : m_rect(x, y, width, height) {} RoundedRect::RoundedRect(const IntRect& rect, const Radii& radii) - : m_rect(rect) - , m_radii(radii) -{ -} - -RoundedRect::RoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight) - : m_rect(rect) - , m_radii(topLeft, topRight, bottomLeft, bottomRight) -{ + : m_rect(rect), m_radii(radii) {} + +RoundedRect::RoundedRect(const IntRect& rect, + const IntSize& topLeft, + const IntSize& topRight, + const IntSize& bottomLeft, + const IntSize& bottomRight) + : m_rect(rect), m_radii(topLeft, topRight, bottomLeft, bottomRight) {} + +IntRect RoundedRect::radiusCenterRect() const { + ASSERT(isRenderable()); + int minX = m_rect.x() + + std::max(m_radii.topLeft().width(), m_radii.bottomLeft().width()); + int minY = m_rect.y() + + std::max(m_radii.topLeft().height(), m_radii.topRight().height()); + int maxX = m_rect.maxX() - std::max(m_radii.topRight().width(), + m_radii.bottomRight().width()); + int maxY = m_rect.maxY() - std::max(m_radii.bottomLeft().height(), + m_radii.bottomRight().height()); + return IntRect(minX, minY, maxX - minX, maxY - minY); } -IntRect RoundedRect::radiusCenterRect() const -{ - ASSERT(isRenderable()); - int minX = m_rect.x() + std::max(m_radii.topLeft().width(), m_radii.bottomLeft().width()); - int minY = m_rect.y() + std::max(m_radii.topLeft().height(), m_radii.topRight().height()); - int maxX = m_rect.maxX() - std::max(m_radii.topRight().width(), m_radii.bottomRight().width()); - int maxY = m_rect.maxY() - std::max(m_radii.bottomLeft().height(), m_radii.bottomRight().height()); - return IntRect(minX, minY, maxX - minX, maxY - minY); +void RoundedRect::includeLogicalEdges(const Radii& edges, + bool includeLogicalLeftEdge, + bool includeLogicalRightEdge) { + m_radii.includeLogicalEdges(edges, includeLogicalLeftEdge, + includeLogicalRightEdge); } -void RoundedRect::includeLogicalEdges(const Radii& edges, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) -{ - m_radii.includeLogicalEdges(edges, includeLogicalLeftEdge, includeLogicalRightEdge); +bool RoundedRect::isRenderable() const { + return m_radii.topLeft().width() + m_radii.topRight().width() <= + m_rect.width() && + m_radii.bottomLeft().width() + m_radii.bottomRight().width() <= + m_rect.width() && + m_radii.topLeft().height() + m_radii.bottomLeft().height() <= + m_rect.height() && + m_radii.topRight().height() + m_radii.bottomRight().height() <= + m_rect.height(); } -bool RoundedRect::isRenderable() const -{ - return m_radii.topLeft().width() + m_radii.topRight().width() <= m_rect.width() - && m_radii.bottomLeft().width() + m_radii.bottomRight().width() <= m_rect.width() - && m_radii.topLeft().height() + m_radii.bottomLeft().height() <= m_rect.height() - && m_radii.topRight().height() + m_radii.bottomRight().height() <= m_rect.height(); +void RoundedRect::adjustRadii() { + int maxRadiusWidth = + std::max(m_radii.topLeft().width() + m_radii.topRight().width(), + m_radii.bottomLeft().width() + m_radii.bottomRight().width()); + int maxRadiusHeight = + std::max(m_radii.topLeft().height() + m_radii.bottomLeft().height(), + m_radii.topRight().height() + m_radii.bottomRight().height()); + + if (maxRadiusWidth <= 0 || maxRadiusHeight <= 0) { + m_radii.scale(0.0f); + return; + } + float widthRatio = static_cast(m_rect.width()) / maxRadiusWidth; + float heightRatio = static_cast(m_rect.height()) / maxRadiusHeight; + m_radii.scale(widthRatio < heightRatio ? widthRatio : heightRatio); } -void RoundedRect::adjustRadii() -{ - int maxRadiusWidth = std::max(m_radii.topLeft().width() + m_radii.topRight().width(), m_radii.bottomLeft().width() + m_radii.bottomRight().width()); - int maxRadiusHeight = std::max(m_radii.topLeft().height() + m_radii.bottomLeft().height(), m_radii.topRight().height() + m_radii.bottomRight().height()); - - if (maxRadiusWidth <= 0 || maxRadiusHeight <= 0) { - m_radii.scale(0.0f); - return; - } - float widthRatio = static_cast(m_rect.width()) / maxRadiusWidth; - float heightRatio = static_cast(m_rect.height()) / maxRadiusHeight; - m_radii.scale(widthRatio < heightRatio ? widthRatio : heightRatio); -} - -bool RoundedRect::intersectsQuad(const FloatQuad& quad) const -{ - FloatRect rect(m_rect); - if (!quad.intersectsRect(rect)) +bool RoundedRect::intersectsQuad(const FloatQuad& quad) const { + FloatRect rect(m_rect); + if (!quad.intersectsRect(rect)) + return false; + + const IntSize& topLeft = m_radii.topLeft(); + if (!topLeft.isEmpty()) { + FloatRect rect(m_rect.x(), m_rect.y(), topLeft.width(), topLeft.height()); + if (quad.intersectsRect(rect)) { + FloatPoint center(m_rect.x() + topLeft.width(), + m_rect.y() + topLeft.height()); + FloatSize size(topLeft.width(), topLeft.height()); + if (!quad.intersectsEllipse(center, size)) return false; - - const IntSize& topLeft = m_radii.topLeft(); - if (!topLeft.isEmpty()) { - FloatRect rect(m_rect.x(), m_rect.y(), topLeft.width(), topLeft.height()); - if (quad.intersectsRect(rect)) { - FloatPoint center(m_rect.x() + topLeft.width(), m_rect.y() + topLeft.height()); - FloatSize size(topLeft.width(), topLeft.height()); - if (!quad.intersectsEllipse(center, size)) - return false; - } } - - const IntSize& topRight = m_radii.topRight(); - if (!topRight.isEmpty()) { - FloatRect rect(m_rect.maxX() - topRight.width(), m_rect.y(), topRight.width(), topRight.height()); - if (quad.intersectsRect(rect)) { - FloatPoint center(m_rect.maxX() - topRight.width(), m_rect.y() + topRight.height()); - FloatSize size(topRight.width(), topRight.height()); - if (!quad.intersectsEllipse(center, size)) - return false; - } + } + + const IntSize& topRight = m_radii.topRight(); + if (!topRight.isEmpty()) { + FloatRect rect(m_rect.maxX() - topRight.width(), m_rect.y(), + topRight.width(), topRight.height()); + if (quad.intersectsRect(rect)) { + FloatPoint center(m_rect.maxX() - topRight.width(), + m_rect.y() + topRight.height()); + FloatSize size(topRight.width(), topRight.height()); + if (!quad.intersectsEllipse(center, size)) + return false; } - - const IntSize& bottomLeft = m_radii.bottomLeft(); - if (!bottomLeft.isEmpty()) { - FloatRect rect(m_rect.x(), m_rect.maxY() - bottomLeft.height(), bottomLeft.width(), bottomLeft.height()); - if (quad.intersectsRect(rect)) { - FloatPoint center(m_rect.x() + bottomLeft.width(), m_rect.maxY() - bottomLeft.height()); - FloatSize size(bottomLeft.width(), bottomLeft.height()); - if (!quad.intersectsEllipse(center, size)) - return false; - } + } + + const IntSize& bottomLeft = m_radii.bottomLeft(); + if (!bottomLeft.isEmpty()) { + FloatRect rect(m_rect.x(), m_rect.maxY() - bottomLeft.height(), + bottomLeft.width(), bottomLeft.height()); + if (quad.intersectsRect(rect)) { + FloatPoint center(m_rect.x() + bottomLeft.width(), + m_rect.maxY() - bottomLeft.height()); + FloatSize size(bottomLeft.width(), bottomLeft.height()); + if (!quad.intersectsEllipse(center, size)) + return false; } - - const IntSize& bottomRight = m_radii.bottomRight(); - if (!bottomRight.isEmpty()) { - FloatRect rect(m_rect.maxX() - bottomRight.width(), m_rect.maxY() - bottomRight.height(), bottomRight.width(), bottomRight.height()); - if (quad.intersectsRect(rect)) { - FloatPoint center(m_rect.maxX() - bottomRight.width(), m_rect.maxY() - bottomRight.height()); - FloatSize size(bottomRight.width(), bottomRight.height()); - if (!quad.intersectsEllipse(center, size)) - return false; - } + } + + const IntSize& bottomRight = m_radii.bottomRight(); + if (!bottomRight.isEmpty()) { + FloatRect rect(m_rect.maxX() - bottomRight.width(), + m_rect.maxY() - bottomRight.height(), bottomRight.width(), + bottomRight.height()); + if (quad.intersectsRect(rect)) { + FloatPoint center(m_rect.maxX() - bottomRight.width(), + m_rect.maxY() - bottomRight.height()); + FloatSize size(bottomRight.width(), bottomRight.height()); + if (!quad.intersectsEllipse(center, size)) + return false; } + } - return true; + return true; } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/geometry/RoundedRect.h b/sky/engine/platform/geometry/RoundedRect.h index b8035e3be118e..6bdf4ae8167f6 100644 --- a/sky/engine/platform/geometry/RoundedRect.h +++ b/sky/engine/platform/geometry/RoundedRect.h @@ -36,95 +36,105 @@ namespace blink { // used for painting. It uses integer units because using layout units leads to // blurry rounded corners. class PLATFORM_EXPORT RoundedRect { -public: - class PLATFORM_EXPORT Radii { - public: - Radii() { } - Radii(const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight) - : m_topLeft(topLeft) - , m_topRight(topRight) - , m_bottomLeft(bottomLeft) - , m_bottomRight(bottomRight) - { - } - - void setTopLeft(const IntSize& size) { m_topLeft = size; } - void setTopRight(const IntSize& size) { m_topRight = size; } - void setBottomLeft(const IntSize& size) { m_bottomLeft = size; } - void setBottomRight(const IntSize& size) { m_bottomRight = size; } - const IntSize& topLeft() const { return m_topLeft; } - const IntSize& topRight() const { return m_topRight; } - const IntSize& bottomLeft() const { return m_bottomLeft; } - const IntSize& bottomRight() const { return m_bottomRight; } - - bool isZero() const; - - void includeLogicalEdges(const Radii& edges, bool includeLogicalLeftEdge, bool includeLogicalRightEdge); - - void scale(float factor); - void expand(int topWidth, int bottomWidth, int leftWidth, int rightWidth); - void expand(int size) { expand(size, size, size, size); } - void shrink(int topWidth, int bottomWidth, int leftWidth, int rightWidth) { expand(-topWidth, -bottomWidth, -leftWidth, -rightWidth); } - void shrink(int size) { shrink(size, size, size, size); } - - private: - IntSize m_topLeft; - IntSize m_topRight; - IntSize m_bottomLeft; - IntSize m_bottomRight; - }; - - explicit RoundedRect(const IntRect&, const Radii& = Radii()); - RoundedRect(int x, int y, int width, int height); - RoundedRect(const IntRect&, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight); - - const IntRect& rect() const { return m_rect; } - const Radii& radii() const { return m_radii; } - bool isRounded() const { return !m_radii.isZero(); } - bool isEmpty() const { return m_rect.isEmpty(); } - - // Returns a quickly computed rect enclosed by the rounded rect. - IntRect radiusCenterRect() const; - - void setRect(const IntRect& rect) { m_rect = rect; } - void setRadii(const Radii& radii) { m_radii = radii; } - - void move(const IntSize& size) { m_rect.move(size); } - void inflate(int size) { m_rect.inflate(size); } - void inflateWithRadii(int size); - void expandRadii(int size) { m_radii.expand(size); } - void shrinkRadii(int size) { m_radii.shrink(size); } - - void includeLogicalEdges(const Radii& edges, bool includeLogicalLeftEdge, bool includeLogicalRightEdge); - - bool isRenderable() const; - void adjustRadii(); - - // Tests whether the quad intersects any part of this rounded rectangle. - // This only works for convex quads. - bool intersectsQuad(const FloatQuad&) const; - -private: - IntRect m_rect; - Radii m_radii; + public: + class PLATFORM_EXPORT Radii { + public: + Radii() {} + Radii(const IntSize& topLeft, + const IntSize& topRight, + const IntSize& bottomLeft, + const IntSize& bottomRight) + : m_topLeft(topLeft), + m_topRight(topRight), + m_bottomLeft(bottomLeft), + m_bottomRight(bottomRight) {} + + void setTopLeft(const IntSize& size) { m_topLeft = size; } + void setTopRight(const IntSize& size) { m_topRight = size; } + void setBottomLeft(const IntSize& size) { m_bottomLeft = size; } + void setBottomRight(const IntSize& size) { m_bottomRight = size; } + const IntSize& topLeft() const { return m_topLeft; } + const IntSize& topRight() const { return m_topRight; } + const IntSize& bottomLeft() const { return m_bottomLeft; } + const IntSize& bottomRight() const { return m_bottomRight; } + + bool isZero() const; + + void includeLogicalEdges(const Radii& edges, + bool includeLogicalLeftEdge, + bool includeLogicalRightEdge); + + void scale(float factor); + void expand(int topWidth, int bottomWidth, int leftWidth, int rightWidth); + void expand(int size) { expand(size, size, size, size); } + void shrink(int topWidth, int bottomWidth, int leftWidth, int rightWidth) { + expand(-topWidth, -bottomWidth, -leftWidth, -rightWidth); + } + void shrink(int size) { shrink(size, size, size, size); } + + private: + IntSize m_topLeft; + IntSize m_topRight; + IntSize m_bottomLeft; + IntSize m_bottomRight; + }; + + explicit RoundedRect(const IntRect&, const Radii& = Radii()); + RoundedRect(int x, int y, int width, int height); + RoundedRect(const IntRect&, + const IntSize& topLeft, + const IntSize& topRight, + const IntSize& bottomLeft, + const IntSize& bottomRight); + + const IntRect& rect() const { return m_rect; } + const Radii& radii() const { return m_radii; } + bool isRounded() const { return !m_radii.isZero(); } + bool isEmpty() const { return m_rect.isEmpty(); } + + // Returns a quickly computed rect enclosed by the rounded rect. + IntRect radiusCenterRect() const; + + void setRect(const IntRect& rect) { m_rect = rect; } + void setRadii(const Radii& radii) { m_radii = radii; } + + void move(const IntSize& size) { m_rect.move(size); } + void inflate(int size) { m_rect.inflate(size); } + void inflateWithRadii(int size); + void expandRadii(int size) { m_radii.expand(size); } + void shrinkRadii(int size) { m_radii.shrink(size); } + + void includeLogicalEdges(const Radii& edges, + bool includeLogicalLeftEdge, + bool includeLogicalRightEdge); + + bool isRenderable() const; + void adjustRadii(); + + // Tests whether the quad intersects any part of this rounded rectangle. + // This only works for convex quads. + bool intersectsQuad(const FloatQuad&) const; + + private: + IntRect m_rect; + Radii m_radii; }; -inline bool operator==(const RoundedRect::Radii& a, const RoundedRect::Radii& b) -{ - return a.topLeft() == b.topLeft() && a.topRight() == b.topRight() && a.bottomLeft() == b.bottomLeft() && a.bottomRight() == b.bottomRight(); +inline bool operator==(const RoundedRect::Radii& a, + const RoundedRect::Radii& b) { + return a.topLeft() == b.topLeft() && a.topRight() == b.topRight() && + a.bottomLeft() == b.bottomLeft() && a.bottomRight() == b.bottomRight(); } -inline bool operator!=(const RoundedRect::Radii& a, const RoundedRect::Radii& b) -{ - return !(a == b); +inline bool operator!=(const RoundedRect::Radii& a, + const RoundedRect::Radii& b) { + return !(a == b); } -inline bool operator==(const RoundedRect& a, const RoundedRect& b) -{ - return a.rect() == b.rect() && a.radii() == b.radii(); +inline bool operator==(const RoundedRect& a, const RoundedRect& b) { + return a.rect() == b.rect() && a.radii() == b.radii(); } - -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_GEOMETRY_ROUNDEDRECT_H_ diff --git a/sky/engine/platform/geometry/RoundedRectTest.cpp b/sky/engine/platform/geometry/RoundedRectTest.cpp index 7f1f2b7b90ec1..80bc7bbf253d7 100644 --- a/sky/engine/platform/geometry/RoundedRectTest.cpp +++ b/sky/engine/platform/geometry/RoundedRectTest.cpp @@ -38,45 +38,40 @@ using namespace blink; namespace blink { // FIXME: Move this somewhere more generic. -void PrintTo(const IntRect& rect, std::ostream* os) -{ - *os << "IntRect(" - << rect.x() << ", " - << rect.y() << ", " - << rect.width() << ", " - << rect.height() << ")"; +void PrintTo(const IntRect& rect, std::ostream* os) { + *os << "IntRect(" << rect.x() << ", " << rect.y() << ", " << rect.width() + << ", " << rect.height() << ")"; } -} // namespace blink +} // namespace blink namespace { -TEST(RoundedRectTest, RadiusCenterRectZeroRadius) -{ - RoundedRect rr(100, 200, 300, 400); - EXPECT_TRUE(rr.rect().contains(rr.radiusCenterRect())); - EXPECT_EQ(IntRect(100, 200, 300, 400), rr.radiusCenterRect()); +TEST(RoundedRectTest, RadiusCenterRectZeroRadius) { + RoundedRect rr(100, 200, 300, 400); + EXPECT_TRUE(rr.rect().contains(rr.radiusCenterRect())); + EXPECT_EQ(IntRect(100, 200, 300, 400), rr.radiusCenterRect()); } -TEST(RoundedRectTest, RadiusCenterRectEqualRadius) -{ - RoundedRect rr(IntRect(100, 200, 300, 400), IntSize(10, 10), IntSize(10, 10), IntSize(10, 10), IntSize(10, 10)); - EXPECT_TRUE(rr.rect().contains(rr.radiusCenterRect())); - EXPECT_EQ(IntRect(110, 210, 280, 380), rr.radiusCenterRect()); +TEST(RoundedRectTest, RadiusCenterRectEqualRadius) { + RoundedRect rr(IntRect(100, 200, 300, 400), IntSize(10, 10), IntSize(10, 10), + IntSize(10, 10), IntSize(10, 10)); + EXPECT_TRUE(rr.rect().contains(rr.radiusCenterRect())); + EXPECT_EQ(IntRect(110, 210, 280, 380), rr.radiusCenterRect()); } -TEST(RoundedRectTest, RadiusCenterRectUnequalRadius) -{ - RoundedRect rr(IntRect(100, 200, 300, 400), IntSize(5, 5), IntSize(10, 10), IntSize(15, 15), IntSize(20, 20)); - EXPECT_TRUE(rr.rect().contains(rr.radiusCenterRect())); - EXPECT_EQ(IntRect(115, 210, 265, 370), rr.radiusCenterRect()); +TEST(RoundedRectTest, RadiusCenterRectUnequalRadius) { + RoundedRect rr(IntRect(100, 200, 300, 400), IntSize(5, 5), IntSize(10, 10), + IntSize(15, 15), IntSize(20, 20)); + EXPECT_TRUE(rr.rect().contains(rr.radiusCenterRect())); + EXPECT_EQ(IntRect(115, 210, 265, 370), rr.radiusCenterRect()); } -TEST(RoundedRectTest, RadiusCenterRectElliptical) -{ - RoundedRect rr(IntRect(100, 200, 300, 400), IntSize(20, 10), IntSize(20, 10), IntSize(10, 20), IntSize(10, 20)); - EXPECT_TRUE(rr.rect().contains(rr.radiusCenterRect())); - EXPECT_EQ(IntRect(120, 210, 260, 370), rr.radiusCenterRect()); +TEST(RoundedRectTest, RadiusCenterRectElliptical) { + RoundedRect rr(IntRect(100, 200, 300, 400), IntSize(20, 10), IntSize(20, 10), + IntSize(10, 20), IntSize(10, 20)); + EXPECT_TRUE(rr.rect().contains(rr.radiusCenterRect())); + EXPECT_EQ(IntRect(120, 210, 260, 370), rr.radiusCenterRect()); } -} // namespace +} // namespace diff --git a/sky/engine/platform/geometry/TransformState.cpp b/sky/engine/platform/geometry/TransformState.cpp index 0d4f514816418..32fe3e7dbce4e 100644 --- a/sky/engine/platform/geometry/TransformState.cpp +++ b/sky/engine/platform/geometry/TransformState.cpp @@ -29,182 +29,195 @@ namespace blink { -TransformState& TransformState::operator=(const TransformState& other) -{ - m_accumulatedOffset = other.m_accumulatedOffset; - m_mapPoint = other.m_mapPoint; - m_mapQuad = other.m_mapQuad; - if (m_mapPoint) - m_lastPlanarPoint = other.m_lastPlanarPoint; - if (m_mapQuad) - m_lastPlanarQuad = other.m_lastPlanarQuad; - m_accumulatingTransform = other.m_accumulatingTransform; - m_direction = other.m_direction; - - m_accumulatedTransform.clear(); - - if (other.m_accumulatedTransform) - m_accumulatedTransform = adoptPtr(new TransformationMatrix(*other.m_accumulatedTransform)); - - return *this; +TransformState& TransformState::operator=(const TransformState& other) { + m_accumulatedOffset = other.m_accumulatedOffset; + m_mapPoint = other.m_mapPoint; + m_mapQuad = other.m_mapQuad; + if (m_mapPoint) + m_lastPlanarPoint = other.m_lastPlanarPoint; + if (m_mapQuad) + m_lastPlanarQuad = other.m_lastPlanarQuad; + m_accumulatingTransform = other.m_accumulatingTransform; + m_direction = other.m_direction; + + m_accumulatedTransform.clear(); + + if (other.m_accumulatedTransform) + m_accumulatedTransform = + adoptPtr(new TransformationMatrix(*other.m_accumulatedTransform)); + + return *this; } -void TransformState::translateTransform(const LayoutSize& offset) -{ - if (m_direction == ApplyTransformDirection) - m_accumulatedTransform->translateRight(offset.width().toDouble(), offset.height().toDouble()); - else - m_accumulatedTransform->translate(offset.width().toDouble(), offset.height().toDouble()); +void TransformState::translateTransform(const LayoutSize& offset) { + if (m_direction == ApplyTransformDirection) + m_accumulatedTransform->translateRight(offset.width().toDouble(), + offset.height().toDouble()); + else + m_accumulatedTransform->translate(offset.width().toDouble(), + offset.height().toDouble()); } -void TransformState::translateMappedCoordinates(const LayoutSize& offset) -{ - LayoutSize adjustedOffset = (m_direction == ApplyTransformDirection) ? offset : -offset; - if (m_mapPoint) - m_lastPlanarPoint.move(adjustedOffset); - if (m_mapQuad) - m_lastPlanarQuad.move(adjustedOffset); +void TransformState::translateMappedCoordinates(const LayoutSize& offset) { + LayoutSize adjustedOffset = + (m_direction == ApplyTransformDirection) ? offset : -offset; + if (m_mapPoint) + m_lastPlanarPoint.move(adjustedOffset); + if (m_mapQuad) + m_lastPlanarQuad.move(adjustedOffset); } -void TransformState::move(const LayoutSize& offset, TransformAccumulation accumulate) -{ - if (accumulate == FlattenTransform || !m_accumulatedTransform) { - m_accumulatedOffset += offset; +void TransformState::move(const LayoutSize& offset, + TransformAccumulation accumulate) { + if (accumulate == FlattenTransform || !m_accumulatedTransform) { + m_accumulatedOffset += offset; + } else { + applyAccumulatedOffset(); + if (m_accumulatingTransform && m_accumulatedTransform) { + // If we're accumulating into an existing transform, apply the + // translation. + translateTransform(offset); + + // Then flatten if necessary. + if (accumulate == FlattenTransform) + flatten(); } else { - applyAccumulatedOffset(); - if (m_accumulatingTransform && m_accumulatedTransform) { - // If we're accumulating into an existing transform, apply the translation. - translateTransform(offset); - - // Then flatten if necessary. - if (accumulate == FlattenTransform) - flatten(); - } else { - // Just move the point and/or quad. - translateMappedCoordinates(offset); - } + // Just move the point and/or quad. + translateMappedCoordinates(offset); } - m_accumulatingTransform = accumulate == AccumulateTransform; + } + m_accumulatingTransform = accumulate == AccumulateTransform; } -void TransformState::applyAccumulatedOffset() -{ - LayoutSize offset = m_accumulatedOffset; - m_accumulatedOffset = LayoutSize(); - if (!offset.isZero()) { - if (m_accumulatedTransform) { - translateTransform(offset); - flatten(); - } else { - translateMappedCoordinates(offset); - } +void TransformState::applyAccumulatedOffset() { + LayoutSize offset = m_accumulatedOffset; + m_accumulatedOffset = LayoutSize(); + if (!offset.isZero()) { + if (m_accumulatedTransform) { + translateTransform(offset); + flatten(); + } else { + translateMappedCoordinates(offset); } + } } -// FIXME: We transform AffineTransform to TransformationMatrix. This is rather inefficient. -void TransformState::applyTransform(const AffineTransform& transformFromContainer, TransformAccumulation accumulate, bool* wasClamped) -{ - applyTransform(transformFromContainer.toTransformationMatrix(), accumulate, wasClamped); +// FIXME: We transform AffineTransform to TransformationMatrix. This is rather +// inefficient. +void TransformState::applyTransform( + const AffineTransform& transformFromContainer, + TransformAccumulation accumulate, + bool* wasClamped) { + applyTransform(transformFromContainer.toTransformationMatrix(), accumulate, + wasClamped); } -void TransformState::applyTransform(const TransformationMatrix& transformFromContainer, TransformAccumulation accumulate, bool* wasClamped) -{ - if (wasClamped) - *wasClamped = false; - - if (transformFromContainer.isIntegerTranslation()) { - move(LayoutSize(transformFromContainer.e(), transformFromContainer.f()), accumulate); - return; - } +void TransformState::applyTransform( + const TransformationMatrix& transformFromContainer, + TransformAccumulation accumulate, + bool* wasClamped) { + if (wasClamped) + *wasClamped = false; - applyAccumulatedOffset(); + if (transformFromContainer.isIntegerTranslation()) { + move(LayoutSize(transformFromContainer.e(), transformFromContainer.f()), + accumulate); + return; + } - // If we have an accumulated transform from last time, multiply in this transform - if (m_accumulatedTransform) { - if (m_direction == ApplyTransformDirection) - m_accumulatedTransform = adoptPtr(new TransformationMatrix(transformFromContainer * *m_accumulatedTransform)); - else - m_accumulatedTransform->multiply(transformFromContainer); - } else if (accumulate == AccumulateTransform) { - // Make one if we started to accumulate - m_accumulatedTransform = adoptPtr(new TransformationMatrix(transformFromContainer)); - } + applyAccumulatedOffset(); - if (accumulate == FlattenTransform) { - const TransformationMatrix* finalTransform = m_accumulatedTransform ? m_accumulatedTransform.get() : &transformFromContainer; - flattenWithTransform(*finalTransform, wasClamped); - } - m_accumulatingTransform = accumulate == AccumulateTransform; + // If we have an accumulated transform from last time, multiply in this + // transform + if (m_accumulatedTransform) { + if (m_direction == ApplyTransformDirection) + m_accumulatedTransform = adoptPtr(new TransformationMatrix( + transformFromContainer * *m_accumulatedTransform)); + else + m_accumulatedTransform->multiply(transformFromContainer); + } else if (accumulate == AccumulateTransform) { + // Make one if we started to accumulate + m_accumulatedTransform = + adoptPtr(new TransformationMatrix(transformFromContainer)); + } + + if (accumulate == FlattenTransform) { + const TransformationMatrix* finalTransform = + m_accumulatedTransform ? m_accumulatedTransform.get() + : &transformFromContainer; + flattenWithTransform(*finalTransform, wasClamped); + } + m_accumulatingTransform = accumulate == AccumulateTransform; } -void TransformState::flatten(bool* wasClamped) -{ - if (wasClamped) - *wasClamped = false; +void TransformState::flatten(bool* wasClamped) { + if (wasClamped) + *wasClamped = false; - applyAccumulatedOffset(); + applyAccumulatedOffset(); - if (!m_accumulatedTransform) { - m_accumulatingTransform = false; - return; - } + if (!m_accumulatedTransform) { + m_accumulatingTransform = false; + return; + } - flattenWithTransform(*m_accumulatedTransform, wasClamped); + flattenWithTransform(*m_accumulatedTransform, wasClamped); } -FloatPoint TransformState::mappedPoint(bool* wasClamped) const -{ - if (wasClamped) - *wasClamped = false; +FloatPoint TransformState::mappedPoint(bool* wasClamped) const { + if (wasClamped) + *wasClamped = false; - FloatPoint point = m_lastPlanarPoint; - point.move((m_direction == ApplyTransformDirection) ? m_accumulatedOffset : -m_accumulatedOffset); - if (!m_accumulatedTransform) - return point; + FloatPoint point = m_lastPlanarPoint; + point.move((m_direction == ApplyTransformDirection) ? m_accumulatedOffset + : -m_accumulatedOffset); + if (!m_accumulatedTransform) + return point; - if (m_direction == ApplyTransformDirection) - return m_accumulatedTransform->mapPoint(point); + if (m_direction == ApplyTransformDirection) + return m_accumulatedTransform->mapPoint(point); - return m_accumulatedTransform->inverse().projectPoint(point, wasClamped); + return m_accumulatedTransform->inverse().projectPoint(point, wasClamped); } -FloatQuad TransformState::mappedQuad(bool* wasClamped) const -{ - if (wasClamped) - *wasClamped = false; +FloatQuad TransformState::mappedQuad(bool* wasClamped) const { + if (wasClamped) + *wasClamped = false; - FloatQuad quad = m_lastPlanarQuad; - quad.move((m_direction == ApplyTransformDirection) ? m_accumulatedOffset : -m_accumulatedOffset); - if (!m_accumulatedTransform) - return quad; + FloatQuad quad = m_lastPlanarQuad; + quad.move((m_direction == ApplyTransformDirection) ? m_accumulatedOffset + : -m_accumulatedOffset); + if (!m_accumulatedTransform) + return quad; - if (m_direction == ApplyTransformDirection) - return m_accumulatedTransform->mapQuad(quad); + if (m_direction == ApplyTransformDirection) + return m_accumulatedTransform->mapQuad(quad); - return m_accumulatedTransform->inverse().projectQuad(quad, wasClamped); + return m_accumulatedTransform->inverse().projectQuad(quad, wasClamped); } -void TransformState::flattenWithTransform(const TransformationMatrix& t, bool* wasClamped) -{ - if (m_direction == ApplyTransformDirection) { - if (m_mapPoint) - m_lastPlanarPoint = t.mapPoint(m_lastPlanarPoint); - if (m_mapQuad) - m_lastPlanarQuad = t.mapQuad(m_lastPlanarQuad); - } else { - TransformationMatrix inverseTransform = t.inverse(); - if (m_mapPoint) - m_lastPlanarPoint = inverseTransform.projectPoint(m_lastPlanarPoint); - if (m_mapQuad) - m_lastPlanarQuad = inverseTransform.projectQuad(m_lastPlanarQuad, wasClamped); - } - - // We could throw away m_accumulatedTransform if we wanted to here, but that - // would cause thrash when traversing hierarchies with alternating - // preserve-3d and flat elements. - if (m_accumulatedTransform) - m_accumulatedTransform->makeIdentity(); - m_accumulatingTransform = false; +void TransformState::flattenWithTransform(const TransformationMatrix& t, + bool* wasClamped) { + if (m_direction == ApplyTransformDirection) { + if (m_mapPoint) + m_lastPlanarPoint = t.mapPoint(m_lastPlanarPoint); + if (m_mapQuad) + m_lastPlanarQuad = t.mapQuad(m_lastPlanarQuad); + } else { + TransformationMatrix inverseTransform = t.inverse(); + if (m_mapPoint) + m_lastPlanarPoint = inverseTransform.projectPoint(m_lastPlanarPoint); + if (m_mapQuad) + m_lastPlanarQuad = + inverseTransform.projectQuad(m_lastPlanarQuad, wasClamped); + } + + // We could throw away m_accumulatedTransform if we wanted to here, but that + // would cause thrash when traversing hierarchies with alternating + // preserve-3d and flat elements. + if (m_accumulatedTransform) + m_accumulatedTransform->makeIdentity(); + m_accumulatingTransform = false; } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/geometry/TransformState.h b/sky/engine/platform/geometry/TransformState.h index c2e244bf63f40..653c0b4bdc290 100644 --- a/sky/engine/platform/geometry/TransformState.h +++ b/sky/engine/platform/geometry/TransformState.h @@ -37,86 +37,90 @@ namespace blink { class PLATFORM_EXPORT TransformState { -public: - enum TransformDirection { ApplyTransformDirection, UnapplyInverseTransformDirection }; - enum TransformAccumulation { FlattenTransform, AccumulateTransform }; - - TransformState(TransformDirection mappingDirection, const FloatPoint& p, const FloatQuad& quad) - : m_lastPlanarPoint(p) - , m_lastPlanarQuad(quad) - , m_accumulatingTransform(false) - , m_mapPoint(true) - , m_mapQuad(true) - , m_direction(mappingDirection) - { - } - - TransformState(TransformDirection mappingDirection, const FloatPoint& p) - : m_lastPlanarPoint(p) - , m_accumulatingTransform(false) - , m_mapPoint(true) - , m_mapQuad(false) - , m_direction(mappingDirection) - { - } - - TransformState(TransformDirection mappingDirection, const FloatQuad& quad) - : m_lastPlanarQuad(quad) - , m_accumulatingTransform(false) - , m_mapPoint(false) - , m_mapQuad(true) - , m_direction(mappingDirection) - { - } - - TransformState(const TransformState& other) { *this = other; } - - TransformState& operator=(const TransformState&); - - void setQuad(const FloatQuad& quad) - { - // FIXME: this assumes that the quad being added is in the coordinate system of the current state. - // This breaks if we're simultaneously mapping a point. https://bugs.webkit.org/show_bug.cgi?id=106680 - ASSERT(!m_mapPoint); - m_accumulatedOffset = LayoutSize(); - m_lastPlanarQuad = quad; - } - - void move(LayoutUnit x, LayoutUnit y, TransformAccumulation accumulate = FlattenTransform) - { - move(LayoutSize(x, y), accumulate); - } - - void move(const LayoutSize&, TransformAccumulation = FlattenTransform); - void applyTransform(const AffineTransform& transformFromContainer, TransformAccumulation = FlattenTransform, bool* wasClamped = 0); - void applyTransform(const TransformationMatrix& transformFromContainer, TransformAccumulation = FlattenTransform, bool* wasClamped = 0); - void flatten(bool* wasClamped = 0); - - // Return the coords of the point or quad in the last flattened layer - FloatPoint lastPlanarPoint() const { return m_lastPlanarPoint; } - FloatQuad lastPlanarQuad() const { return m_lastPlanarQuad; } - - // Return the point or quad mapped through the current transform - FloatPoint mappedPoint(bool* wasClamped = 0) const; - FloatQuad mappedQuad(bool* wasClamped = 0) const; - -private: - void translateTransform(const LayoutSize&); - void translateMappedCoordinates(const LayoutSize&); - void flattenWithTransform(const TransformationMatrix&, bool* wasClamped); - void applyAccumulatedOffset(); - - FloatPoint m_lastPlanarPoint; - FloatQuad m_lastPlanarQuad; - - // We only allocate the transform if we need to - OwnPtr m_accumulatedTransform; - LayoutSize m_accumulatedOffset; - bool m_accumulatingTransform; - bool m_mapPoint, m_mapQuad; - TransformDirection m_direction; + public: + enum TransformDirection { + ApplyTransformDirection, + UnapplyInverseTransformDirection + }; + enum TransformAccumulation { FlattenTransform, AccumulateTransform }; + + TransformState(TransformDirection mappingDirection, + const FloatPoint& p, + const FloatQuad& quad) + : m_lastPlanarPoint(p), + m_lastPlanarQuad(quad), + m_accumulatingTransform(false), + m_mapPoint(true), + m_mapQuad(true), + m_direction(mappingDirection) {} + + TransformState(TransformDirection mappingDirection, const FloatPoint& p) + : m_lastPlanarPoint(p), + m_accumulatingTransform(false), + m_mapPoint(true), + m_mapQuad(false), + m_direction(mappingDirection) {} + + TransformState(TransformDirection mappingDirection, const FloatQuad& quad) + : m_lastPlanarQuad(quad), + m_accumulatingTransform(false), + m_mapPoint(false), + m_mapQuad(true), + m_direction(mappingDirection) {} + + TransformState(const TransformState& other) { *this = other; } + + TransformState& operator=(const TransformState&); + + void setQuad(const FloatQuad& quad) { + // FIXME: this assumes that the quad being added is in the coordinate system + // of the current state. This breaks if we're simultaneously mapping a + // point. https://bugs.webkit.org/show_bug.cgi?id=106680 + ASSERT(!m_mapPoint); + m_accumulatedOffset = LayoutSize(); + m_lastPlanarQuad = quad; + } + + void move(LayoutUnit x, + LayoutUnit y, + TransformAccumulation accumulate = FlattenTransform) { + move(LayoutSize(x, y), accumulate); + } + + void move(const LayoutSize&, TransformAccumulation = FlattenTransform); + void applyTransform(const AffineTransform& transformFromContainer, + TransformAccumulation = FlattenTransform, + bool* wasClamped = 0); + void applyTransform(const TransformationMatrix& transformFromContainer, + TransformAccumulation = FlattenTransform, + bool* wasClamped = 0); + void flatten(bool* wasClamped = 0); + + // Return the coords of the point or quad in the last flattened layer + FloatPoint lastPlanarPoint() const { return m_lastPlanarPoint; } + FloatQuad lastPlanarQuad() const { return m_lastPlanarQuad; } + + // Return the point or quad mapped through the current transform + FloatPoint mappedPoint(bool* wasClamped = 0) const; + FloatQuad mappedQuad(bool* wasClamped = 0) const; + + private: + void translateTransform(const LayoutSize&); + void translateMappedCoordinates(const LayoutSize&); + void flattenWithTransform(const TransformationMatrix&, bool* wasClamped); + void applyAccumulatedOffset(); + + FloatPoint m_lastPlanarPoint; + FloatQuad m_lastPlanarQuad; + + // We only allocate the transform if we need to + OwnPtr m_accumulatedTransform; + LayoutSize m_accumulatedOffset; + bool m_accumulatingTransform; + bool m_mapPoint, m_mapQuad; + TransformDirection m_direction; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_GEOMETRY_TRANSFORMSTATE_H_ diff --git a/sky/engine/platform/graphics/Color.cpp b/sky/engine/platform/graphics/Color.cpp index e710a1d934f1c..150ebfc199af4 100644 --- a/sky/engine/platform/graphics/Color.cpp +++ b/sky/engine/platform/graphics/Color.cpp @@ -34,7 +34,8 @@ namespace blink { -// FIXME: Use C++11 strong enums to avoid static data member with initializer definition problems. +// FIXME: Use C++11 strong enums to avoid static data member with initializer +// definition problems. const RGBA32 Color::black; const RGBA32 Color::white; const RGBA32 Color::darkGray; @@ -45,47 +46,45 @@ const RGBA32 Color::transparent; static const RGBA32 lightenedBlack = 0xFF545454; static const RGBA32 darkenedWhite = 0xFFABABAB; -RGBA32 makeRGB(int r, int g, int b) -{ - return 0xFF000000 | std::max(0, std::min(r, 255)) << 16 | std::max(0, std::min(g, 255)) << 8 | std::max(0, std::min(b, 255)); +RGBA32 makeRGB(int r, int g, int b) { + return 0xFF000000 | std::max(0, std::min(r, 255)) << 16 | + std::max(0, std::min(g, 255)) << 8 | std::max(0, std::min(b, 255)); } -RGBA32 makeRGBA(int r, int g, int b, int a) -{ - return std::max(0, std::min(a, 255)) << 24 | std::max(0, std::min(r, 255)) << 16 | std::max(0, std::min(g, 255)) << 8 | std::max(0, std::min(b, 255)); +RGBA32 makeRGBA(int r, int g, int b, int a) { + return std::max(0, std::min(a, 255)) << 24 | + std::max(0, std::min(r, 255)) << 16 | + std::max(0, std::min(g, 255)) << 8 | std::max(0, std::min(b, 255)); } -static int colorFloatToRGBAByte(float f) -{ - // We use lroundf and 255 instead of nextafterf(256, 0) to match CG's rounding - return std::max(0, std::min(static_cast(lroundf(255.0f * f)), 255)); +static int colorFloatToRGBAByte(float f) { + // We use lroundf and 255 instead of nextafterf(256, 0) to match CG's rounding + return std::max(0, std::min(static_cast(lroundf(255.0f * f)), 255)); } -RGBA32 makeRGBA32FromFloats(float r, float g, float b, float a) -{ - return colorFloatToRGBAByte(a) << 24 | colorFloatToRGBAByte(r) << 16 | colorFloatToRGBAByte(g) << 8 | colorFloatToRGBAByte(b); +RGBA32 makeRGBA32FromFloats(float r, float g, float b, float a) { + return colorFloatToRGBAByte(a) << 24 | colorFloatToRGBAByte(r) << 16 | + colorFloatToRGBAByte(g) << 8 | colorFloatToRGBAByte(b); } -RGBA32 colorWithOverrideAlpha(RGBA32 color, float overrideAlpha) -{ - RGBA32 rgbOnly = color & 0x00FFFFFF; - RGBA32 rgba = rgbOnly | colorFloatToRGBAByte(overrideAlpha) << 24; - return rgba; +RGBA32 colorWithOverrideAlpha(RGBA32 color, float overrideAlpha) { + RGBA32 rgbOnly = color & 0x00FFFFFF; + RGBA32 rgba = rgbOnly | colorFloatToRGBAByte(overrideAlpha) << 24; + return rgba; } -static double calcHue(double temp1, double temp2, double hueVal) -{ - if (hueVal < 0.0) - hueVal++; - else if (hueVal > 1.0) - hueVal--; - if (hueVal * 6.0 < 1.0) - return temp1 + (temp2 - temp1) * hueVal * 6.0; - if (hueVal * 2.0 < 1.0) - return temp2; - if (hueVal * 3.0 < 2.0) - return temp1 + (temp2 - temp1) * (2.0 / 3.0 - hueVal) * 6.0; - return temp1; +static double calcHue(double temp1, double temp2, double hueVal) { + if (hueVal < 0.0) + hueVal++; + else if (hueVal > 1.0) + hueVal--; + if (hueVal * 6.0 < 1.0) + return temp1 + (temp2 - temp1) * hueVal * 6.0; + if (hueVal * 2.0 < 1.0) + return temp2; + if (hueVal * 3.0 < 2.0) + return temp1 + (temp2 - temp1) * (2.0 / 3.0 - hueVal) * 6.0; + return temp1; } // Explanation of this algorithm can be found in the CSS3 Color Module @@ -93,328 +92,320 @@ static double calcHue(double temp1, double temp2, double hueVal) // explanation available at http://en.wikipedia.org/wiki/HSL_color_space // all values are in the range of 0 to 1.0 -RGBA32 makeRGBAFromHSLA(double hue, double saturation, double lightness, double alpha) -{ - const double scaleFactor = nextafter(256.0, 0.0); - - if (!saturation) { - int greyValue = static_cast(lightness * scaleFactor); - return makeRGBA(greyValue, greyValue, greyValue, static_cast(alpha * scaleFactor)); - } - - double temp2 = lightness < 0.5 ? lightness * (1.0 + saturation) : lightness + saturation - lightness * saturation; - double temp1 = 2.0 * lightness - temp2; - - return makeRGBA(static_cast(calcHue(temp1, temp2, hue + 1.0 / 3.0) * scaleFactor), - static_cast(calcHue(temp1, temp2, hue) * scaleFactor), - static_cast(calcHue(temp1, temp2, hue - 1.0 / 3.0) * scaleFactor), +RGBA32 makeRGBAFromHSLA(double hue, + double saturation, + double lightness, + double alpha) { + const double scaleFactor = nextafter(256.0, 0.0); + + if (!saturation) { + int greyValue = static_cast(lightness * scaleFactor); + return makeRGBA(greyValue, greyValue, greyValue, static_cast(alpha * scaleFactor)); + } + + double temp2 = lightness < 0.5 + ? lightness * (1.0 + saturation) + : lightness + saturation - lightness * saturation; + double temp1 = 2.0 * lightness - temp2; + + return makeRGBA( + static_cast(calcHue(temp1, temp2, hue + 1.0 / 3.0) * scaleFactor), + static_cast(calcHue(temp1, temp2, hue) * scaleFactor), + static_cast(calcHue(temp1, temp2, hue - 1.0 / 3.0) * scaleFactor), + static_cast(alpha * scaleFactor)); } -RGBA32 makeRGBAFromCMYKA(float c, float m, float y, float k, float a) -{ - double colors = 1 - k; - int r = static_cast(nextafter(256, 0) * (colors * (1 - c))); - int g = static_cast(nextafter(256, 0) * (colors * (1 - m))); - int b = static_cast(nextafter(256, 0) * (colors * (1 - y))); - return makeRGBA(r, g, b, static_cast(nextafter(256, 0) * a)); +RGBA32 makeRGBAFromCMYKA(float c, float m, float y, float k, float a) { + double colors = 1 - k; + int r = static_cast(nextafter(256, 0) * (colors * (1 - c))); + int g = static_cast(nextafter(256, 0) * (colors * (1 - m))); + int b = static_cast(nextafter(256, 0) * (colors * (1 - y))); + return makeRGBA(r, g, b, static_cast(nextafter(256, 0) * a)); } // originally moved here from the CSS parser template -static inline bool parseHexColorInternal(const CharacterType* name, unsigned length, RGBA32& rgb) -{ - if (length != 3 && length != 6) - return false; - unsigned value = 0; - for (unsigned i = 0; i < length; ++i) { - if (!isASCIIHexDigit(name[i])) - return false; - value <<= 4; - value |= toASCIIHexValue(name[i]); - } - if (length == 6) { - rgb = 0xFF000000 | value; - return true; - } - // #abc converts to #aabbcc - rgb = 0xFF000000 - | (value & 0xF00) << 12 | (value & 0xF00) << 8 - | (value & 0xF0) << 8 | (value & 0xF0) << 4 - | (value & 0xF) << 4 | (value & 0xF); +static inline bool parseHexColorInternal(const CharacterType* name, + unsigned length, + RGBA32& rgb) { + if (length != 3 && length != 6) + return false; + unsigned value = 0; + for (unsigned i = 0; i < length; ++i) { + if (!isASCIIHexDigit(name[i])) + return false; + value <<= 4; + value |= toASCIIHexValue(name[i]); + } + if (length == 6) { + rgb = 0xFF000000 | value; return true; + } + // #abc converts to #aabbcc + rgb = 0xFF000000 | (value & 0xF00) << 12 | (value & 0xF00) << 8 | + (value & 0xF0) << 8 | (value & 0xF0) << 4 | (value & 0xF) << 4 | + (value & 0xF); + return true; } -bool Color::parseHexColor(const LChar* name, unsigned length, RGBA32& rgb) -{ - return parseHexColorInternal(name, length, rgb); +bool Color::parseHexColor(const LChar* name, unsigned length, RGBA32& rgb) { + return parseHexColorInternal(name, length, rgb); } -bool Color::parseHexColor(const UChar* name, unsigned length, RGBA32& rgb) -{ - return parseHexColorInternal(name, length, rgb); +bool Color::parseHexColor(const UChar* name, unsigned length, RGBA32& rgb) { + return parseHexColorInternal(name, length, rgb); } -bool Color::parseHexColor(const String& name, RGBA32& rgb) -{ - unsigned length = name.length(); +bool Color::parseHexColor(const String& name, RGBA32& rgb) { + unsigned length = name.length(); - if (!length) - return false; - if (name.is8Bit()) - return parseHexColor(name.characters8(), name.length(), rgb); - return parseHexColor(name.characters16(), name.length(), rgb); + if (!length) + return false; + if (name.is8Bit()) + return parseHexColor(name.characters8(), name.length(), rgb); + return parseHexColor(name.characters16(), name.length(), rgb); } -String Color::serializedAsCSSComponentValue() const -{ - StringBuilder result; - result.reserveCapacity(32); - bool colorHasAlpha = hasAlpha(); - if (colorHasAlpha) - result.appendLiteral("rgba("); - else - result.appendLiteral("rgb("); - - result.appendNumber(static_cast(red())); - result.appendLiteral(", "); - - result.appendNumber(static_cast(green())); - result.appendLiteral(", "); - - result.appendNumber(static_cast(blue())); - if (colorHasAlpha) { - result.appendLiteral(", "); - - NumberToStringBuffer buffer; - const char* alphaString = numberToFixedPrecisionString(alpha() / 255.0f, 6, buffer, true); - result.append(alphaString, strlen(alphaString)); - } +String Color::serializedAsCSSComponentValue() const { + StringBuilder result; + result.reserveCapacity(32); + bool colorHasAlpha = hasAlpha(); + if (colorHasAlpha) + result.appendLiteral("rgba("); + else + result.appendLiteral("rgb("); - result.append(')'); - return result.toString(); -} + result.appendNumber(static_cast(red())); + result.appendLiteral(", "); -String Color::serialized() const -{ - if (!hasAlpha()) { - StringBuilder builder; - builder.reserveCapacity(7); - builder.append('#'); - appendByteAsHex(red(), builder, Lowercase); - appendByteAsHex(green(), builder, Lowercase); - appendByteAsHex(blue(), builder, Lowercase); - return builder.toString(); - } - - StringBuilder result; - result.reserveCapacity(28); + result.appendNumber(static_cast(green())); + result.appendLiteral(", "); - result.appendLiteral("rgba("); - result.appendNumber(red()); - result.appendLiteral(", "); - result.appendNumber(green()); - result.appendLiteral(", "); - result.appendNumber(blue()); + result.appendNumber(static_cast(blue())); + if (colorHasAlpha) { result.appendLiteral(", "); - if (!alpha()) - result.append('0'); - else { - result.append(Decimal::fromDouble(alpha() / 255.0).toString()); - } + NumberToStringBuffer buffer; + const char* alphaString = + numberToFixedPrecisionString(alpha() / 255.0f, 6, buffer, true); + result.append(alphaString, strlen(alphaString)); + } + + result.append(')'); + return result.toString(); +} - result.append(')'); - return result.toString(); +String Color::serialized() const { + if (!hasAlpha()) { + StringBuilder builder; + builder.reserveCapacity(7); + builder.append('#'); + appendByteAsHex(red(), builder, Lowercase); + appendByteAsHex(green(), builder, Lowercase); + appendByteAsHex(blue(), builder, Lowercase); + return builder.toString(); + } + + StringBuilder result; + result.reserveCapacity(28); + + result.appendLiteral("rgba("); + result.appendNumber(red()); + result.appendLiteral(", "); + result.appendNumber(green()); + result.appendLiteral(", "); + result.appendNumber(blue()); + result.appendLiteral(", "); + + if (!alpha()) + result.append('0'); + else { + result.append(Decimal::fromDouble(alpha() / 255.0).toString()); + } + + result.append(')'); + return result.toString(); } -String Color::nameForRenderTreeAsText() const -{ - if (alpha() < 0xFF) - return String::format("#%02X%02X%02X%02X", red(), green(), blue(), alpha()); - return String::format("#%02X%02X%02X", red(), green(), blue()); +String Color::nameForRenderTreeAsText() const { + if (alpha() < 0xFF) + return String::format("#%02X%02X%02X%02X", red(), green(), blue(), alpha()); + return String::format("#%02X%02X%02X", red(), green(), blue()); } -Color Color::light() const -{ - // Hardcode this common case for speed. - if (m_color == black) - return lightenedBlack; +Color Color::light() const { + // Hardcode this common case for speed. + if (m_color == black) + return lightenedBlack; - const float scaleFactor = nextafterf(256.0f, 0.0f); + const float scaleFactor = nextafterf(256.0f, 0.0f); - float r, g, b, a; - getRGBA(r, g, b, a); + float r, g, b, a; + getRGBA(r, g, b, a); - float v = std::max(r, std::max(g, b)); + float v = std::max(r, std::max(g, b)); - if (v == 0.0f) - // Lightened black with alpha. - return Color(0x54, 0x54, 0x54, alpha()); + if (v == 0.0f) + // Lightened black with alpha. + return Color(0x54, 0x54, 0x54, alpha()); - float multiplier = std::min(1.0f, v + 0.33f) / v; + float multiplier = std::min(1.0f, v + 0.33f) / v; - return Color(static_cast(multiplier * r * scaleFactor), - static_cast(multiplier * g * scaleFactor), - static_cast(multiplier * b * scaleFactor), - alpha()); + return Color(static_cast(multiplier * r * scaleFactor), + static_cast(multiplier * g * scaleFactor), + static_cast(multiplier * b * scaleFactor), alpha()); } -Color Color::dark() const -{ - // Hardcode this common case for speed. - if (m_color == white) - return darkenedWhite; +Color Color::dark() const { + // Hardcode this common case for speed. + if (m_color == white) + return darkenedWhite; - const float scaleFactor = nextafterf(256.0f, 0.0f); + const float scaleFactor = nextafterf(256.0f, 0.0f); - float r, g, b, a; - getRGBA(r, g, b, a); + float r, g, b, a; + getRGBA(r, g, b, a); - float v = std::max(r, std::max(g, b)); - float multiplier = std::max(0.0f, (v - 0.33f) / v); + float v = std::max(r, std::max(g, b)); + float multiplier = std::max(0.0f, (v - 0.33f) / v); - return Color(static_cast(multiplier * r * scaleFactor), - static_cast(multiplier * g * scaleFactor), - static_cast(multiplier * b * scaleFactor), - alpha()); + return Color(static_cast(multiplier * r * scaleFactor), + static_cast(multiplier * g * scaleFactor), + static_cast(multiplier * b * scaleFactor), alpha()); } -Color Color::combineWithAlpha(float otherAlpha) const -{ - return colorWithOverrideAlpha(rgb(), (alpha() / 255.f) * otherAlpha); +Color Color::combineWithAlpha(float otherAlpha) const { + return colorWithOverrideAlpha(rgb(), (alpha() / 255.f) * otherAlpha); } -static int blendComponent(int c, int a) -{ - // We use white. - float alpha = a / 255.0f; - int whiteBlend = 255 - a; - c -= whiteBlend; - return static_cast(c / alpha); +static int blendComponent(int c, int a) { + // We use white. + float alpha = a / 255.0f; + int whiteBlend = 255 - a; + c -= whiteBlend; + return static_cast(c / alpha); } -const int cStartAlpha = 153; // 60% -const int cEndAlpha = 204; // 80%; -const int cAlphaIncrement = 17; // Increments in between. - -Color Color::blend(const Color& source) const -{ - if (!alpha() || !source.hasAlpha()) - return source; - - if (!source.alpha()) - return *this; - - int d = 255 * (alpha() + source.alpha()) - alpha() * source.alpha(); - int a = d / 255; - int r = (red() * alpha() * (255 - source.alpha()) + 255 * source.alpha() * source.red()) / d; - int g = (green() * alpha() * (255 - source.alpha()) + 255 * source.alpha() * source.green()) / d; - int b = (blue() * alpha() * (255 - source.alpha()) + 255 * source.alpha() * source.blue()) / d; - return Color(r, g, b, a); +const int cStartAlpha = 153; // 60% +const int cEndAlpha = 204; // 80%; +const int cAlphaIncrement = 17; // Increments in between. + +Color Color::blend(const Color& source) const { + if (!alpha() || !source.hasAlpha()) + return source; + + if (!source.alpha()) + return *this; + + int d = 255 * (alpha() + source.alpha()) - alpha() * source.alpha(); + int a = d / 255; + int r = (red() * alpha() * (255 - source.alpha()) + + 255 * source.alpha() * source.red()) / + d; + int g = (green() * alpha() * (255 - source.alpha()) + + 255 * source.alpha() * source.green()) / + d; + int b = (blue() * alpha() * (255 - source.alpha()) + + 255 * source.alpha() * source.blue()) / + d; + return Color(r, g, b, a); } -Color Color::blendWithWhite() const -{ - // If the color contains alpha already, we leave it alone. - if (hasAlpha()) - return *this; - - Color newColor; - for (int alpha = cStartAlpha; alpha <= cEndAlpha; alpha += cAlphaIncrement) { - // We have a solid color. Convert to an equivalent color that looks the same when blended with white - // at the current alpha. Try using less transparency if the numbers end up being negative. - int r = blendComponent(red(), alpha); - int g = blendComponent(green(), alpha); - int b = blendComponent(blue(), alpha); - - newColor = Color(r, g, b, alpha); - - if (r >= 0 && g >= 0 && b >= 0) - break; - } - return newColor; +Color Color::blendWithWhite() const { + // If the color contains alpha already, we leave it alone. + if (hasAlpha()) + return *this; + + Color newColor; + for (int alpha = cStartAlpha; alpha <= cEndAlpha; alpha += cAlphaIncrement) { + // We have a solid color. Convert to an equivalent color that looks the + // same when blended with white at the current alpha. Try using less + // transparency if the numbers end up being negative. + int r = blendComponent(red(), alpha); + int g = blendComponent(green(), alpha); + int b = blendComponent(blue(), alpha); + + newColor = Color(r, g, b, alpha); + + if (r >= 0 && g >= 0 && b >= 0) + break; + } + return newColor; } -void Color::getRGBA(float& r, float& g, float& b, float& a) const -{ - r = red() / 255.0f; - g = green() / 255.0f; - b = blue() / 255.0f; - a = alpha() / 255.0f; +void Color::getRGBA(float& r, float& g, float& b, float& a) const { + r = red() / 255.0f; + g = green() / 255.0f; + b = blue() / 255.0f; + a = alpha() / 255.0f; } -void Color::getRGBA(double& r, double& g, double& b, double& a) const -{ - r = red() / 255.0; - g = green() / 255.0; - b = blue() / 255.0; - a = alpha() / 255.0; +void Color::getRGBA(double& r, double& g, double& b, double& a) const { + r = red() / 255.0; + g = green() / 255.0; + b = blue() / 255.0; + a = alpha() / 255.0; } -void Color::getHSL(double& hue, double& saturation, double& lightness) const -{ - // http://en.wikipedia.org/wiki/HSL_color_space. This is a direct copy of - // the algorithm therein, although it's 360^o based and we end up wanting - // [0...1) based. It's clearer if we stick to 360^o until the end. - double r = static_cast(red()) / 255.0; - double g = static_cast(green()) / 255.0; - double b = static_cast(blue()) / 255.0; - double max = std::max(std::max(r, g), b); - double min = std::min(std::min(r, g), b); - - if (max == min) - hue = 0.0; - else if (max == r) - hue = (60.0 * ((g - b) / (max - min))) + 360.0; - else if (max == g) - hue = (60.0 * ((b - r) / (max - min))) + 120.0; - else - hue = (60.0 * ((r - g) / (max - min))) + 240.0; - - if (hue >= 360.0) - hue -= 360.0; - - // makeRGBAFromHSLA assumes that hue is in [0...1). - hue /= 360.0; - - lightness = 0.5 * (max + min); - if (max == min) - saturation = 0.0; - else if (lightness <= 0.5) - saturation = ((max - min) / (max + min)); - else - saturation = ((max - min) / (2.0 - (max + min))); +void Color::getHSL(double& hue, double& saturation, double& lightness) const { + // http://en.wikipedia.org/wiki/HSL_color_space. This is a direct copy of + // the algorithm therein, although it's 360^o based and we end up wanting + // [0...1) based. It's clearer if we stick to 360^o until the end. + double r = static_cast(red()) / 255.0; + double g = static_cast(green()) / 255.0; + double b = static_cast(blue()) / 255.0; + double max = std::max(std::max(r, g), b); + double min = std::min(std::min(r, g), b); + + if (max == min) + hue = 0.0; + else if (max == r) + hue = (60.0 * ((g - b) / (max - min))) + 360.0; + else if (max == g) + hue = (60.0 * ((b - r) / (max - min))) + 120.0; + else + hue = (60.0 * ((r - g) / (max - min))) + 240.0; + + if (hue >= 360.0) + hue -= 360.0; + + // makeRGBAFromHSLA assumes that hue is in [0...1). + hue /= 360.0; + + lightness = 0.5 * (max + min); + if (max == min) + saturation = 0.0; + else if (lightness <= 0.5) + saturation = ((max - min) / (max + min)); + else + saturation = ((max - min) / (2.0 - (max + min))); } -Color colorFromPremultipliedARGB(RGBA32 pixelColor) -{ - int alpha = alphaChannel(pixelColor); - if (alpha && alpha < 255) { - return Color::createUnchecked( - redChannel(pixelColor) * 255 / alpha, - greenChannel(pixelColor) * 255 / alpha, - blueChannel(pixelColor) * 255 / alpha, - alpha); - } else - return Color(pixelColor); +Color colorFromPremultipliedARGB(RGBA32 pixelColor) { + int alpha = alphaChannel(pixelColor); + if (alpha && alpha < 255) { + return Color::createUnchecked(redChannel(pixelColor) * 255 / alpha, + greenChannel(pixelColor) * 255 / alpha, + blueChannel(pixelColor) * 255 / alpha, alpha); + } else + return Color(pixelColor); } -RGBA32 premultipliedARGBFromColor(const Color& color) -{ - unsigned pixelColor; - - unsigned alpha = color.alpha(); - if (alpha < 255) { - pixelColor = Color::createUnchecked( - (color.red() * alpha + 254) / 255, - (color.green() * alpha + 254) / 255, - (color.blue() * alpha + 254) / 255, - alpha).rgb(); - } else - pixelColor = color.rgb(); - - return pixelColor; +RGBA32 premultipliedARGBFromColor(const Color& color) { + unsigned pixelColor; + + unsigned alpha = color.alpha(); + if (alpha < 255) { + pixelColor = + Color::createUnchecked((color.red() * alpha + 254) / 255, + (color.green() * alpha + 254) / 255, + (color.blue() * alpha + 254) / 255, alpha) + .rgb(); + } else + pixelColor = color.rgb(); + + return pixelColor; } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/graphics/Color.h b/sky/engine/platform/graphics/Color.h index a4fe4b89677cd..a0e2f130053e3 100644 --- a/sky/engine/platform/graphics/Color.h +++ b/sky/engine/platform/graphics/Color.h @@ -35,128 +35,144 @@ namespace blink { class Color; -typedef unsigned RGBA32; // RGBA quadruplet +typedef unsigned RGBA32; // RGBA quadruplet PLATFORM_EXPORT RGBA32 makeRGB(int r, int g, int b); PLATFORM_EXPORT RGBA32 makeRGBA(int r, int g, int b, int a); -PLATFORM_EXPORT RGBA32 colorWithOverrideAlpha(RGBA32 color, float overrideAlpha); +PLATFORM_EXPORT RGBA32 colorWithOverrideAlpha(RGBA32 color, + float overrideAlpha); PLATFORM_EXPORT RGBA32 makeRGBA32FromFloats(float r, float g, float b, float a); PLATFORM_EXPORT RGBA32 makeRGBAFromHSLA(double h, double s, double l, double a); -PLATFORM_EXPORT RGBA32 makeRGBAFromCMYKA(float c, float m, float y, float k, float a); +PLATFORM_EXPORT RGBA32 +makeRGBAFromCMYKA(float c, float m, float y, float k, float a); -inline int redChannel(RGBA32 color) { return (color >> 16) & 0xFF; } -inline int greenChannel(RGBA32 color) { return (color >> 8) & 0xFF; } -inline int blueChannel(RGBA32 color) { return color & 0xFF; } -inline int alphaChannel(RGBA32 color) { return (color >> 24) & 0xFF; } +inline int redChannel(RGBA32 color) { + return (color >> 16) & 0xFF; +} +inline int greenChannel(RGBA32 color) { + return (color >> 8) & 0xFF; +} +inline int blueChannel(RGBA32 color) { + return color & 0xFF; +} +inline int alphaChannel(RGBA32 color) { + return (color >> 24) & 0xFF; +} class PLATFORM_EXPORT Color { - WTF_MAKE_FAST_ALLOCATED; -public: - Color() : m_color(Color::transparent) { } - Color(RGBA32 color) : m_color(color) { } - Color(int r, int g, int b) : m_color(makeRGB(r, g, b)) { } - Color(int r, int g, int b, int a) : m_color(makeRGBA(r, g, b, a)) { } - // Color is currently limited to 32bit RGBA, perhaps some day we'll support better colors - Color(float r, float g, float b, float a) : m_color(makeRGBA32FromFloats(r, g, b, a)) { } - // Creates a new color from the specific CMYK and alpha values. - Color(float c, float m, float y, float k, float a) : m_color(makeRGBAFromCMYKA(c, m, y, k, a)) { } - - static Color createUnchecked(int r, int g, int b) - { - RGBA32 color = 0xFF000000 | r << 16 | g << 8 | b; - return Color(color); - } - static Color createUnchecked(int r, int g, int b, int a) - { - RGBA32 color = a << 24 | r << 16 | g << 8 | b; - return Color(color); - } - - // Returns the color serialized according to HTML5 - // - http://www.whatwg.org/specs/web-apps/current-work/#serialization-of-a-color - String serialized() const; - - // Returns the color serialized according to CSSOM - // - http://dev.w3.org/csswg/cssom/#serialize-a-css-component-value - String serializedAsCSSComponentValue() const; - - // Returns the color serialized as either #RRGGBB or #RRGGBBAA - // The latter format is not a valid CSS color, and should only be seen in DRT dumps. - String nameForRenderTreeAsText() const; - - bool hasAlpha() const { return alpha() < 255; } - - int red() const { return redChannel(m_color); } - int green() const { return greenChannel(m_color); } - int blue() const { return blueChannel(m_color); } - int alpha() const { return alphaChannel(m_color); } - - RGBA32 rgb() const { return m_color; } // Preserve the alpha. - void setRGB(int r, int g, int b) { m_color = makeRGB(r, g, b); } - void setRGB(RGBA32 rgb) { m_color = rgb; } - void getRGBA(float& r, float& g, float& b, float& a) const; - void getRGBA(double& r, double& g, double& b, double& a) const; - void getHSL(double& h, double& s, double& l) const; - - Color light() const; - Color dark() const; - - Color combineWithAlpha(float otherAlpha) const; - - // This is an implementation of Porter-Duff's "source-over" equation - Color blend(const Color&) const; - Color blendWithWhite() const; - - static bool parseHexColor(const String&, RGBA32&); - static bool parseHexColor(const LChar*, unsigned, RGBA32&); - static bool parseHexColor(const UChar*, unsigned, RGBA32&); - - static const RGBA32 black = 0xFF000000; - static const RGBA32 white = 0xFFFFFFFF; - static const RGBA32 darkGray = 0xFF808080; - static const RGBA32 gray = 0xFFA0A0A0; - static const RGBA32 lightGray = 0xFFC0C0C0; - static const RGBA32 transparent = 0x00000000; - -private: - RGBA32 m_color; + WTF_MAKE_FAST_ALLOCATED; + + public: + Color() : m_color(Color::transparent) {} + Color(RGBA32 color) : m_color(color) {} + Color(int r, int g, int b) : m_color(makeRGB(r, g, b)) {} + Color(int r, int g, int b, int a) : m_color(makeRGBA(r, g, b, a)) {} + // Color is currently limited to 32bit RGBA, perhaps some day we'll support + // better colors + Color(float r, float g, float b, float a) + : m_color(makeRGBA32FromFloats(r, g, b, a)) {} + // Creates a new color from the specific CMYK and alpha values. + Color(float c, float m, float y, float k, float a) + : m_color(makeRGBAFromCMYKA(c, m, y, k, a)) {} + + static Color createUnchecked(int r, int g, int b) { + RGBA32 color = 0xFF000000 | r << 16 | g << 8 | b; + return Color(color); + } + static Color createUnchecked(int r, int g, int b, int a) { + RGBA32 color = a << 24 | r << 16 | g << 8 | b; + return Color(color); + } + + // Returns the color serialized according to HTML5 + // - + // http://www.whatwg.org/specs/web-apps/current-work/#serialization-of-a-color + String serialized() const; + + // Returns the color serialized according to CSSOM + // - http://dev.w3.org/csswg/cssom/#serialize-a-css-component-value + String serializedAsCSSComponentValue() const; + + // Returns the color serialized as either #RRGGBB or #RRGGBBAA + // The latter format is not a valid CSS color, and should only be seen in DRT + // dumps. + String nameForRenderTreeAsText() const; + + bool hasAlpha() const { return alpha() < 255; } + + int red() const { return redChannel(m_color); } + int green() const { return greenChannel(m_color); } + int blue() const { return blueChannel(m_color); } + int alpha() const { return alphaChannel(m_color); } + + RGBA32 rgb() const { return m_color; } // Preserve the alpha. + void setRGB(int r, int g, int b) { m_color = makeRGB(r, g, b); } + void setRGB(RGBA32 rgb) { m_color = rgb; } + void getRGBA(float& r, float& g, float& b, float& a) const; + void getRGBA(double& r, double& g, double& b, double& a) const; + void getHSL(double& h, double& s, double& l) const; + + Color light() const; + Color dark() const; + + Color combineWithAlpha(float otherAlpha) const; + + // This is an implementation of Porter-Duff's "source-over" equation + Color blend(const Color&) const; + Color blendWithWhite() const; + + static bool parseHexColor(const String&, RGBA32&); + static bool parseHexColor(const LChar*, unsigned, RGBA32&); + static bool parseHexColor(const UChar*, unsigned, RGBA32&); + + static const RGBA32 black = 0xFF000000; + static const RGBA32 white = 0xFFFFFFFF; + static const RGBA32 darkGray = 0xFF808080; + static const RGBA32 gray = 0xFFA0A0A0; + static const RGBA32 lightGray = 0xFFC0C0C0; + static const RGBA32 transparent = 0x00000000; + + private: + RGBA32 m_color; }; -inline bool operator==(const Color& a, const Color& b) -{ - return a.rgb() == b.rgb(); +inline bool operator==(const Color& a, const Color& b) { + return a.rgb() == b.rgb(); } -inline bool operator!=(const Color& a, const Color& b) -{ - return !(a == b); +inline bool operator!=(const Color& a, const Color& b) { + return !(a == b); } PLATFORM_EXPORT Color colorFromPremultipliedARGB(RGBA32); PLATFORM_EXPORT RGBA32 premultipliedARGBFromColor(const Color&); -inline Color blend(const Color& from, const Color& to, double progress, bool blendPremultiplied = true) -{ - if (blendPremultiplied) { - // Contrary to the name, RGBA32 actually stores ARGB, so we can initialize Color directly from premultipliedARGBFromColor(). - // Also, premultipliedARGBFromColor() bails on zero alpha, so special-case that. - Color premultFrom = from.alpha() ? premultipliedARGBFromColor(from) : 0; - Color premultTo = to.alpha() ? premultipliedARGBFromColor(to) : 0; - - Color premultBlended(blend(premultFrom.red(), premultTo.red(), progress), - blend(premultFrom.green(), premultTo.green(), progress), - blend(premultFrom.blue(), premultTo.blue(), progress), - blend(premultFrom.alpha(), premultTo.alpha(), progress)); - - return Color(colorFromPremultipliedARGB(premultBlended.rgb())); - } - - return Color(blend(from.red(), to.red(), progress), - blend(from.green(), to.green(), progress), - blend(from.blue(), to.blue(), progress), - blend(from.alpha(), to.alpha(), progress)); +inline Color blend(const Color& from, + const Color& to, + double progress, + bool blendPremultiplied = true) { + if (blendPremultiplied) { + // Contrary to the name, RGBA32 actually stores ARGB, so we can initialize + // Color directly from premultipliedARGBFromColor(). Also, + // premultipliedARGBFromColor() bails on zero alpha, so special-case that. + Color premultFrom = from.alpha() ? premultipliedARGBFromColor(from) : 0; + Color premultTo = to.alpha() ? premultipliedARGBFromColor(to) : 0; + + Color premultBlended( + blend(premultFrom.red(), premultTo.red(), progress), + blend(premultFrom.green(), premultTo.green(), progress), + blend(premultFrom.blue(), premultTo.blue(), progress), + blend(premultFrom.alpha(), premultTo.alpha(), progress)); + + return Color(colorFromPremultipliedARGB(premultBlended.rgb())); + } + + return Color(blend(from.red(), to.red(), progress), + blend(from.green(), to.green(), progress), + blend(from.blue(), to.blue(), progress), + blend(from.alpha(), to.alpha(), progress)); } -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_GRAPHICS_COLOR_H_ diff --git a/sky/engine/platform/graphics/ColorSpace.cpp b/sky/engine/platform/graphics/ColorSpace.cpp index c280996ac8f84..226dfb587bbe3 100644 --- a/sky/engine/platform/graphics/ColorSpace.cpp +++ b/sky/engine/platform/graphics/ColorSpace.cpp @@ -38,69 +38,72 @@ namespace blink { namespace ColorSpaceUtilities { -static const uint8_t* getLinearRgbLUT() -{ - static uint8_t linearRgbLUT[256]; - static bool initialized; - if (!initialized) { - for (unsigned i = 0; i < 256; i++) { - float color = i / 255.0f; - color = (color <= 0.04045f ? color / 12.92f : pow((color + 0.055f) / 1.055f, 2.4f)); - color = std::max(0.0f, color); - color = std::min(1.0f, color); - linearRgbLUT[i] = static_cast(round(color * 255)); - } - initialized = true; +static const uint8_t* getLinearRgbLUT() { + static uint8_t linearRgbLUT[256]; + static bool initialized; + if (!initialized) { + for (unsigned i = 0; i < 256; i++) { + float color = i / 255.0f; + color = (color <= 0.04045f ? color / 12.92f + : pow((color + 0.055f) / 1.055f, 2.4f)); + color = std::max(0.0f, color); + color = std::min(1.0f, color); + linearRgbLUT[i] = static_cast(round(color * 255)); } - return linearRgbLUT; + initialized = true; + } + return linearRgbLUT; } -static const uint8_t* getDeviceRgbLUT() -{ - static uint8_t deviceRgbLUT[256]; - static bool initialized; - if (!initialized) { - for (unsigned i = 0; i < 256; i++) { - float color = i / 255.0f; - color = (powf(color, 1.0f / 2.4f) * 1.055f) - 0.055f; - color = std::max(0.0f, color); - color = std::min(1.0f, color); - deviceRgbLUT[i] = static_cast(round(color * 255)); - } - initialized = true; +static const uint8_t* getDeviceRgbLUT() { + static uint8_t deviceRgbLUT[256]; + static bool initialized; + if (!initialized) { + for (unsigned i = 0; i < 256; i++) { + float color = i / 255.0f; + color = (powf(color, 1.0f / 2.4f) * 1.055f) - 0.055f; + color = std::max(0.0f, color); + color = std::min(1.0f, color); + deviceRgbLUT[i] = static_cast(round(color * 255)); } - return deviceRgbLUT; + initialized = true; + } + return deviceRgbLUT; } -const uint8_t* getConversionLUT(ColorSpace dstColorSpace, ColorSpace srcColorSpace) -{ - // Identity. - if (srcColorSpace == dstColorSpace) - return 0; +const uint8_t* getConversionLUT(ColorSpace dstColorSpace, + ColorSpace srcColorSpace) { + // Identity. + if (srcColorSpace == dstColorSpace) + return 0; - // Only sRGB/DeviceRGB <-> linearRGB are supported at the moment. - if ((srcColorSpace != ColorSpaceLinearRGB && srcColorSpace != ColorSpaceDeviceRGB) - || (dstColorSpace != ColorSpaceLinearRGB && dstColorSpace != ColorSpaceDeviceRGB)) - return 0; + // Only sRGB/DeviceRGB <-> linearRGB are supported at the moment. + if ((srcColorSpace != ColorSpaceLinearRGB && + srcColorSpace != ColorSpaceDeviceRGB) || + (dstColorSpace != ColorSpaceLinearRGB && + dstColorSpace != ColorSpaceDeviceRGB)) + return 0; - if (dstColorSpace == ColorSpaceLinearRGB) - return getLinearRgbLUT(); - if (dstColorSpace == ColorSpaceDeviceRGB) - return getDeviceRgbLUT(); + if (dstColorSpace == ColorSpaceLinearRGB) + return getLinearRgbLUT(); + if (dstColorSpace == ColorSpaceDeviceRGB) + return getDeviceRgbLUT(); - ASSERT_NOT_REACHED(); - return 0; + ASSERT_NOT_REACHED(); + return 0; } -Color convertColor(const Color& srcColor, ColorSpace dstColorSpace, ColorSpace srcColorSpace) -{ - const uint8_t* lookupTable = getConversionLUT(dstColorSpace, srcColorSpace); - if (!lookupTable) - return srcColor; +Color convertColor(const Color& srcColor, + ColorSpace dstColorSpace, + ColorSpace srcColorSpace) { + const uint8_t* lookupTable = getConversionLUT(dstColorSpace, srcColorSpace); + if (!lookupTable) + return srcColor; - return Color(lookupTable[srcColor.red()], lookupTable[srcColor.green()], lookupTable[srcColor.blue()], srcColor.alpha()); + return Color(lookupTable[srcColor.red()], lookupTable[srcColor.green()], + lookupTable[srcColor.blue()], srcColor.alpha()); } -} // namespace ColorSpaceUtilities +} // namespace ColorSpaceUtilities -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/graphics/ColorSpace.h b/sky/engine/platform/graphics/ColorSpace.h index 2592a82b83e72..19b23a2bf1b19 100644 --- a/sky/engine/platform/graphics/ColorSpace.h +++ b/sky/engine/platform/graphics/ColorSpace.h @@ -31,11 +31,7 @@ namespace blink { -enum ColorSpace { - ColorSpaceDeviceRGB, - ColorSpaceSRGB, - ColorSpaceLinearRGB -}; +enum ColorSpace { ColorSpaceDeviceRGB, ColorSpaceSRGB, ColorSpaceLinearRGB }; namespace ColorSpaceUtilities { @@ -44,13 +40,17 @@ namespace ColorSpaceUtilities { // If the conversion cannot be performed, or is a no-op (identity transform), // then 0 is returned. // (Note that a round-trip - f(B,A)[f(A,B)[x]] - is not lossless in general.) -const uint8_t* getConversionLUT(ColorSpace dstColorSpace, ColorSpace srcColorSpace = ColorSpaceDeviceRGB); +const uint8_t* getConversionLUT(ColorSpace dstColorSpace, + ColorSpace srcColorSpace = ColorSpaceDeviceRGB); -// Convert a Color assumed to be in the |srcColorSpace| into the |dstColorSpace|. -Color convertColor(const Color& srcColor, ColorSpace dstColorSpace, ColorSpace srcColorSpace = ColorSpaceDeviceRGB); +// Convert a Color assumed to be in the |srcColorSpace| into the +// |dstColorSpace|. +Color convertColor(const Color& srcColor, + ColorSpace dstColorSpace, + ColorSpace srcColorSpace = ColorSpaceDeviceRGB); -} // namespace ColorSpaceUtilities +} // namespace ColorSpaceUtilities -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_GRAPHICS_COLORSPACE_H_ diff --git a/sky/engine/platform/graphics/DrawLooperBuilder.cpp b/sky/engine/platform/graphics/DrawLooperBuilder.cpp index 1050727c0293f..fdf0925c26a94 100644 --- a/sky/engine/platform/graphics/DrawLooperBuilder.cpp +++ b/sky/engine/platform/graphics/DrawLooperBuilder.cpp @@ -42,72 +42,73 @@ namespace blink { -DrawLooperBuilder::DrawLooperBuilder() { } +DrawLooperBuilder::DrawLooperBuilder() {} -DrawLooperBuilder::~DrawLooperBuilder() { } +DrawLooperBuilder::~DrawLooperBuilder() {} -PassOwnPtr DrawLooperBuilder::create() -{ - return adoptPtr(new DrawLooperBuilder); +PassOwnPtr DrawLooperBuilder::create() { + return adoptPtr(new DrawLooperBuilder); } -sk_sp DrawLooperBuilder::detachDrawLooper() -{ - return m_skDrawLooperBuilder.detach(); +sk_sp DrawLooperBuilder::detachDrawLooper() { + return m_skDrawLooperBuilder.detach(); } -void DrawLooperBuilder::addUnmodifiedContent() -{ - SkLayerDrawLooper::LayerInfo info; - m_skDrawLooperBuilder.addLayerOnTop(info); +void DrawLooperBuilder::addUnmodifiedContent() { + SkLayerDrawLooper::LayerInfo info; + m_skDrawLooperBuilder.addLayerOnTop(info); } -// This replicates the old skia behavior when it used to take radius for blur. Now it takes sigma. -static SkScalar RadiusToSigma(SkScalar radius) -{ - SkASSERT(radius > 0); - return 0.57735f * radius + 0.5f; +// This replicates the old skia behavior when it used to take radius for blur. +// Now it takes sigma. +static SkScalar RadiusToSigma(SkScalar radius) { + SkASSERT(radius > 0); + return 0.57735f * radius + 0.5f; } -void DrawLooperBuilder::addShadow(const FloatSize& offset, float blur, const Color& color, - ShadowTransformMode shadowTransformMode, ShadowAlphaMode shadowAlphaMode) -{ - // Detect when there's no effective shadow. - if (!color.alpha()) - return; +void DrawLooperBuilder::addShadow(const FloatSize& offset, + float blur, + const Color& color, + ShadowTransformMode shadowTransformMode, + ShadowAlphaMode shadowAlphaMode) { + // Detect when there's no effective shadow. + if (!color.alpha()) + return; - SkColor skColor = color.rgb(); + SkColor skColor = color.rgb(); - SkLayerDrawLooper::LayerInfo info; + SkLayerDrawLooper::LayerInfo info; - switch (shadowAlphaMode) { + switch (shadowAlphaMode) { case ShadowRespectsAlpha: - info.fColorMode = SkBlendMode::kDst; - break; + info.fColorMode = SkBlendMode::kDst; + break; case ShadowIgnoresAlpha: - info.fColorMode = SkBlendMode::kSrc; - break; + info.fColorMode = SkBlendMode::kSrc; + break; default: - ASSERT_NOT_REACHED(); - } - - if (blur) - info.fPaintBits |= SkLayerDrawLooper::kMaskFilter_Bit; // our blur - info.fPaintBits |= SkLayerDrawLooper::kColorFilter_Bit; - info.fOffset.set(offset.width(), offset.height()); - info.fPostTranslate = (shadowTransformMode == ShadowIgnoresTransforms); - - SkPaint* paint = m_skDrawLooperBuilder.addLayerOnTop(info); - - if (blur) { - const SkScalar sigma = RadiusToSigma(blur / 2); - uint32_t mfFlags = SkBlurMaskFilter::kHighQuality_BlurFlag; - if (shadowTransformMode == ShadowIgnoresTransforms) - mfFlags |= SkBlurMaskFilter::kIgnoreTransform_BlurFlag; - paint->setMaskFilter(SkBlurMaskFilter::Make(kNormal_SkBlurStyle, sigma, mfFlags)); - } - - paint->setColorFilter(SkColorFilter::MakeModeFilter(skColor, SkBlendMode::kSrcIn)); + ASSERT_NOT_REACHED(); + } + + if (blur) + info.fPaintBits |= SkLayerDrawLooper::kMaskFilter_Bit; // our blur + info.fPaintBits |= SkLayerDrawLooper::kColorFilter_Bit; + info.fOffset.set(offset.width(), offset.height()); + info.fPostTranslate = (shadowTransformMode == ShadowIgnoresTransforms); + + SkPaint* paint = m_skDrawLooperBuilder.addLayerOnTop(info); + + if (blur) { + const SkScalar sigma = RadiusToSigma(blur / 2); + uint32_t mfFlags = SkBlurMaskFilter::kHighQuality_BlurFlag; + if (shadowTransformMode == ShadowIgnoresTransforms) + mfFlags |= SkBlurMaskFilter::kIgnoreTransform_BlurFlag; + paint->setMaskFilter( + SkBlurMaskFilter::Make(kNormal_SkBlurStyle, sigma, mfFlags)); + } + + paint->setColorFilter( + SkColorFilter::MakeModeFilter(skColor, SkBlendMode::kSrcIn)); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/graphics/DrawLooperBuilder.h b/sky/engine/platform/graphics/DrawLooperBuilder.h index b34d365d04033..09e2ee245fc3b 100644 --- a/sky/engine/platform/graphics/DrawLooperBuilder.h +++ b/sky/engine/platform/graphics/DrawLooperBuilder.h @@ -45,38 +45,37 @@ class Color; class FloatSize; class PLATFORM_EXPORT DrawLooperBuilder final { - // Implementing the copy constructor properly would require writing code to - // copy the underlying SkLayerDrawLooper::Builder. - WTF_MAKE_NONCOPYABLE(DrawLooperBuilder); + // Implementing the copy constructor properly would require writing code to + // copy the underlying SkLayerDrawLooper::Builder. + WTF_MAKE_NONCOPYABLE(DrawLooperBuilder); -public: - enum ShadowTransformMode { - ShadowRespectsTransforms, - ShadowIgnoresTransforms - }; - enum ShadowAlphaMode { - ShadowRespectsAlpha, - ShadowIgnoresAlpha - }; + public: + enum ShadowTransformMode { + ShadowRespectsTransforms, + ShadowIgnoresTransforms + }; + enum ShadowAlphaMode { ShadowRespectsAlpha, ShadowIgnoresAlpha }; - DrawLooperBuilder(); - ~DrawLooperBuilder(); + DrawLooperBuilder(); + ~DrawLooperBuilder(); - static PassOwnPtr create(); + static PassOwnPtr create(); - // Creates the SkDrawLooper and passes ownership to the caller. The builder - // should not be used any more after calling this method. - sk_sp detachDrawLooper(); + // Creates the SkDrawLooper and passes ownership to the caller. The builder + // should not be used any more after calling this method. + sk_sp detachDrawLooper(); - void addUnmodifiedContent(); - void addShadow(const FloatSize& offset, float blur, const Color&, - ShadowTransformMode = ShadowRespectsTransforms, - ShadowAlphaMode = ShadowRespectsAlpha); + void addUnmodifiedContent(); + void addShadow(const FloatSize& offset, + float blur, + const Color&, + ShadowTransformMode = ShadowRespectsTransforms, + ShadowAlphaMode = ShadowRespectsAlpha); -private: - SkLayerDrawLooper::Builder m_skDrawLooperBuilder; + private: + SkLayerDrawLooper::Builder m_skDrawLooperBuilder; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_GRAPHICS_DRAWLOOPERBUILDER_H_ diff --git a/sky/engine/platform/graphics/FrameData.cpp b/sky/engine/platform/graphics/FrameData.cpp index 79f3dabec4205..6abb81227d6aa 100644 --- a/sky/engine/platform/graphics/FrameData.cpp +++ b/sky/engine/platform/graphics/FrameData.cpp @@ -29,23 +29,19 @@ namespace blink { FrameData::FrameData() - : m_orientation(DefaultImageOrientation) - , m_duration(0) - , m_haveMetadata(false) - , m_isComplete(false) - , m_hasAlpha(true) - , m_frameBytes(0) -{ -} + : m_orientation(DefaultImageOrientation), + m_duration(0), + m_haveMetadata(false), + m_isComplete(false), + m_hasAlpha(true), + m_frameBytes(0) {} -FrameData::~FrameData() -{ - clear(true); +FrameData::~FrameData() { + clear(true); } -bool FrameData::clear(bool clearMetadata) -{ - return false; +bool FrameData::clear(bool clearMetadata) { + return false; } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/graphics/FrameData.h b/sky/engine/platform/graphics/FrameData.h index 133466a7088bc..13c450cf03dca 100644 --- a/sky/engine/platform/graphics/FrameData.h +++ b/sky/engine/platform/graphics/FrameData.h @@ -36,29 +36,33 @@ namespace blink { struct FrameData { - WTF_MAKE_NONCOPYABLE(FrameData); -public: - FrameData(); - ~FrameData(); + WTF_MAKE_NONCOPYABLE(FrameData); - // Clear the cached image data on the frame, and (optionally) the metadata. - // Returns whether there was cached image data to clear. - bool clear(bool clearMetadata); + public: + FrameData(); + ~FrameData(); - ImageOrientation m_orientation; - float m_duration; - bool m_haveMetadata : 1; - bool m_isComplete : 1; - bool m_hasAlpha : 1; - unsigned m_frameBytes; + // Clear the cached image data on the frame, and (optionally) the metadata. + // Returns whether there was cached image data to clear. + bool clear(bool clearMetadata); + + ImageOrientation m_orientation; + float m_duration; + bool m_haveMetadata : 1; + bool m_isComplete : 1; + bool m_hasAlpha : 1; + unsigned m_frameBytes; }; -} // namespace blink +} // namespace blink namespace WTF { -template<> struct VectorTraits : public SimpleClassVectorTraits { - static const bool canInitializeWithMemset = false; // Not all FrameData members initialize to 0. +template <> +struct VectorTraits + : public SimpleClassVectorTraits { + static const bool canInitializeWithMemset = + false; // Not all FrameData members initialize to 0. }; -} +} // namespace WTF #endif // SKY_ENGINE_PLATFORM_GRAPHICS_FRAMEDATA_H_ diff --git a/sky/engine/platform/graphics/GeneratedImage.cpp b/sky/engine/platform/graphics/GeneratedImage.cpp index ffb08d2b18de4..b62afb37c1b10 100644 --- a/sky/engine/platform/graphics/GeneratedImage.cpp +++ b/sky/engine/platform/graphics/GeneratedImage.cpp @@ -32,13 +32,14 @@ #include "flutter/sky/engine/platform/geometry/FloatSize.h" - namespace blink { -void GeneratedImage::computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio) -{ - Image::computeIntrinsicDimensions(intrinsicWidth, intrinsicHeight, intrinsicRatio); - intrinsicRatio = FloatSize(); +void GeneratedImage::computeIntrinsicDimensions(Length& intrinsicWidth, + Length& intrinsicHeight, + FloatSize& intrinsicRatio) { + Image::computeIntrinsicDimensions(intrinsicWidth, intrinsicHeight, + intrinsicRatio); + intrinsicRatio = FloatSize(); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/graphics/GeneratedImage.h b/sky/engine/platform/graphics/GeneratedImage.h index 8a8e6ac9b5452..035fd9b45e181 100644 --- a/sky/engine/platform/graphics/GeneratedImage.h +++ b/sky/engine/platform/graphics/GeneratedImage.h @@ -33,31 +33,38 @@ namespace blink { class PLATFORM_EXPORT GeneratedImage : public Image { -public: - virtual void setContainerSize(const IntSize& size) override { m_size = size; } - virtual bool usesContainerSize() const override { return true; } - virtual bool hasRelativeWidth() const override { return true; } - virtual bool hasRelativeHeight() const override { return true; } - virtual void computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio) override; + public: + virtual void setContainerSize(const IntSize& size) override { m_size = size; } + virtual bool usesContainerSize() const override { return true; } + virtual bool hasRelativeWidth() const override { return true; } + virtual bool hasRelativeHeight() const override { return true; } + virtual void computeIntrinsicDimensions(Length& intrinsicWidth, + Length& intrinsicHeight, + FloatSize& intrinsicRatio) override; - virtual IntSize size() const override { return m_size; } + virtual IntSize size() const override { return m_size; } - // Assume that generated content has no decoded data we need to worry about - virtual void destroyDecodedData(bool) override { } + // Assume that generated content has no decoded data we need to worry about + virtual void destroyDecodedData(bool) override {} -protected: - virtual void drawPattern(GraphicsContext*, const FloatRect&, - const FloatSize&, const FloatPoint&, CompositeOperator, - const FloatRect&, WebBlendMode, const IntSize& repeatSpacing) override = 0; + protected: + virtual void drawPattern(GraphicsContext*, + const FloatRect&, + const FloatSize&, + const FloatPoint&, + CompositeOperator, + const FloatRect&, + WebBlendMode, + const IntSize& repeatSpacing) override = 0; - // FIXME: Implement this to be less conservative. - virtual bool currentFrameKnownToBeOpaque() override { return false; } + // FIXME: Implement this to be less conservative. + virtual bool currentFrameKnownToBeOpaque() override { return false; } - GeneratedImage() { } + GeneratedImage() {} - IntSize m_size; + IntSize m_size; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_GRAPHICS_GENERATEDIMAGE_H_ diff --git a/sky/engine/platform/graphics/Gradient.cpp b/sky/engine/platform/graphics/Gradient.cpp index 3fce3823f04a5..f1bc095023c9b 100644 --- a/sky/engine/platform/graphics/Gradient.cpp +++ b/sky/engine/platform/graphics/Gradient.cpp @@ -40,124 +40,117 @@ typedef Vector ColorStopColorVector; namespace blink { Gradient::Gradient(const FloatPoint& p0, const FloatPoint& p1) - : m_p0(p0) - , m_p1(p1) - , m_r0(0) - , m_r1(0) - , m_aspectRatio(1) - , m_radial(false) - , m_stopsSorted(false) - , m_drawInPMColorSpace(false) - , m_spreadMethod(SpreadMethodPad) -{ + : m_p0(p0), + m_p1(p1), + m_r0(0), + m_r1(0), + m_aspectRatio(1), + m_radial(false), + m_stopsSorted(false), + m_drawInPMColorSpace(false), + m_spreadMethod(SpreadMethodPad) {} + +Gradient::Gradient(const FloatPoint& p0, + float r0, + const FloatPoint& p1, + float r1, + float aspectRatio) + : m_p0(p0), + m_p1(p1), + m_r0(r0), + m_r1(r1), + m_aspectRatio(aspectRatio), + m_radial(true), + m_stopsSorted(false), + m_drawInPMColorSpace(false), + m_spreadMethod(SpreadMethodPad) {} + +Gradient::~Gradient() {} + +static inline bool compareStops(const Gradient::ColorStop& a, + const Gradient::ColorStop& b) { + return a.stop < b.stop; } -Gradient::Gradient(const FloatPoint& p0, float r0, const FloatPoint& p1, float r1, float aspectRatio) - : m_p0(p0) - , m_p1(p1) - , m_r0(r0) - , m_r1(r1) - , m_aspectRatio(aspectRatio) - , m_radial(true) - , m_stopsSorted(false) - , m_drawInPMColorSpace(false) - , m_spreadMethod(SpreadMethodPad) -{ -} - -Gradient::~Gradient() -{ -} - -static inline bool compareStops(const Gradient::ColorStop& a, const Gradient::ColorStop& b) -{ - return a.stop < b.stop; -} - -void Gradient::addColorStop(const Gradient::ColorStop& stop) -{ - if (m_stops.isEmpty()) { - m_stopsSorted = true; - } else { - m_stopsSorted = m_stopsSorted && compareStops(m_stops.last(), stop); - } +void Gradient::addColorStop(const Gradient::ColorStop& stop) { + if (m_stops.isEmpty()) { + m_stopsSorted = true; + } else { + m_stopsSorted = m_stopsSorted && compareStops(m_stops.last(), stop); + } - m_stops.append(stop); - m_gradient = nullptr; + m_stops.append(stop); + m_gradient = nullptr; } -void Gradient::sortStopsIfNecessary() -{ - if (m_stopsSorted) - return; +void Gradient::sortStopsIfNecessary() { + if (m_stopsSorted) + return; - m_stopsSorted = true; + m_stopsSorted = true; - if (!m_stops.size()) - return; + if (!m_stops.size()) + return; - std::stable_sort(m_stops.begin(), m_stops.end(), compareStops); + std::stable_sort(m_stops.begin(), m_stops.end(), compareStops); } -bool Gradient::hasAlpha() const -{ - for (size_t i = 0; i < m_stops.size(); i++) { - if (m_stops[i].color.hasAlpha()) - return true; - } +bool Gradient::hasAlpha() const { + for (size_t i = 0; i < m_stops.size(); i++) { + if (m_stops[i].color.hasAlpha()) + return true; + } - return false; + return false; } -void Gradient::setSpreadMethod(GradientSpreadMethod spreadMethod) -{ - // FIXME: Should it become necessary, allow calls to this method after m_gradient has been set. - ASSERT(!m_gradient); +void Gradient::setSpreadMethod(GradientSpreadMethod spreadMethod) { + // FIXME: Should it become necessary, allow calls to this method after + // m_gradient has been set. + ASSERT(!m_gradient); - if (m_spreadMethod == spreadMethod) - return; + if (m_spreadMethod == spreadMethod) + return; - m_spreadMethod = spreadMethod; + m_spreadMethod = spreadMethod; } -void Gradient::setDrawsInPMColorSpace(bool drawInPMColorSpace) -{ - if (drawInPMColorSpace == m_drawInPMColorSpace) - return; +void Gradient::setDrawsInPMColorSpace(bool drawInPMColorSpace) { + if (drawInPMColorSpace == m_drawInPMColorSpace) + return; - m_drawInPMColorSpace = drawInPMColorSpace; - m_gradient = nullptr; + m_drawInPMColorSpace = drawInPMColorSpace; + m_gradient = nullptr; } -void Gradient::setGradientSpaceTransform(const AffineTransform& gradientSpaceTransformation) -{ - if (m_gradientSpaceTransformation == gradientSpaceTransformation) - return; +void Gradient::setGradientSpaceTransform( + const AffineTransform& gradientSpaceTransformation) { + if (m_gradientSpaceTransformation == gradientSpaceTransformation) + return; - m_gradientSpaceTransformation = gradientSpaceTransformation; - m_gradient = nullptr; + m_gradientSpaceTransformation = gradientSpaceTransformation; + m_gradient = nullptr; } // Determine the total number of stops needed, including pseudo-stops at the // ends as necessary. -static size_t totalStopsNeeded(const Gradient::ColorStop* stopData, size_t count) -{ - // N.B.: The tests in this function should kept in sync with the ones in - // fillStops(), or badness happens. - const Gradient::ColorStop* stop = stopData; - size_t countUsed = count; - if (count < 1 || stop->stop > 0.0) - countUsed++; - stop += count - 1; - if (count < 1 || stop->stop < 1.0) - countUsed++; - return countUsed; +static size_t totalStopsNeeded(const Gradient::ColorStop* stopData, + size_t count) { + // N.B.: The tests in this function should kept in sync with the ones in + // fillStops(), or badness happens. + const Gradient::ColorStop* stop = stopData; + size_t countUsed = count; + if (count < 1 || stop->stop > 0.0) + countUsed++; + stop += count - 1; + if (count < 1 || stop->stop < 1.0) + countUsed++; + return countUsed; } // FIXME: This would be more at home as Color::operator SkColor. -static inline SkColor makeSkColor(const Color& c) -{ - return SkColorSetARGB(c.alpha(), c.red(), c.green(), c.blue()); +static inline SkColor makeSkColor(const Color& c) { + return SkColorSetARGB(c.alpha(), c.red(), c.green(), c.blue()); } // Collect sorted stop position and color information into the pos and colors @@ -165,102 +158,114 @@ static inline SkColor makeSkColor(const Color& c) // enough to hold information for all stops, including the new endpoints if // stops at 0.0 and 1.0 aren't already included. static void fillStops(const Gradient::ColorStop* stopData, - size_t count, ColorStopOffsetVector& pos, ColorStopColorVector& colors) -{ - const Gradient::ColorStop* stop = stopData; - size_t start = 0; - if (count < 1) { - // A gradient with no stops must be transparent black. - pos[0] = WebCoreFloatToSkScalar(0.0); - colors[0] = SK_ColorTRANSPARENT; - start = 1; - } else if (stop->stop > 0.0) { - // Copy the first stop to 0.0. The first stop position may have a slight - // rounding error, but we don't care in this float comparison, since - // 0.0 comes through cleanly and people aren't likely to want a gradient - // with a stop at (0 + epsilon). - pos[0] = WebCoreFloatToSkScalar(0.0); - colors[0] = makeSkColor(stop->color); - start = 1; - } - - for (size_t i = start; i < start + count; i++) { - pos[i] = WebCoreFloatToSkScalar(stop->stop); - colors[i] = makeSkColor(stop->color); - ++stop; - } - - // Copy the last stop to 1.0 if needed. See comment above about this float - // comparison. - if (count < 1 || (--stop)->stop < 1.0) { - pos[start + count] = WebCoreFloatToSkScalar(1.0); - colors[start + count] = colors[start + count - 1]; - } + size_t count, + ColorStopOffsetVector& pos, + ColorStopColorVector& colors) { + const Gradient::ColorStop* stop = stopData; + size_t start = 0; + if (count < 1) { + // A gradient with no stops must be transparent black. + pos[0] = WebCoreFloatToSkScalar(0.0); + colors[0] = SK_ColorTRANSPARENT; + start = 1; + } else if (stop->stop > 0.0) { + // Copy the first stop to 0.0. The first stop position may have a slight + // rounding error, but we don't care in this float comparison, since + // 0.0 comes through cleanly and people aren't likely to want a gradient + // with a stop at (0 + epsilon). + pos[0] = WebCoreFloatToSkScalar(0.0); + colors[0] = makeSkColor(stop->color); + start = 1; + } + + for (size_t i = start; i < start + count; i++) { + pos[i] = WebCoreFloatToSkScalar(stop->stop); + colors[i] = makeSkColor(stop->color); + ++stop; + } + + // Copy the last stop to 1.0 if needed. See comment above about this float + // comparison. + if (count < 1 || (--stop)->stop < 1.0) { + pos[start + count] = WebCoreFloatToSkScalar(1.0); + colors[start + count] = colors[start + count - 1]; + } } -sk_sp Gradient::shader() -{ - if (m_gradient) - return m_gradient; +sk_sp Gradient::shader() { + if (m_gradient) + return m_gradient; - sortStopsIfNecessary(); - ASSERT(m_stopsSorted); + sortStopsIfNecessary(); + ASSERT(m_stopsSorted); - size_t countUsed = totalStopsNeeded(m_stops.data(), m_stops.size()); - ASSERT(countUsed >= 2); - ASSERT(countUsed >= m_stops.size()); + size_t countUsed = totalStopsNeeded(m_stops.data(), m_stops.size()); + ASSERT(countUsed >= 2); + ASSERT(countUsed >= m_stops.size()); - ColorStopOffsetVector pos(countUsed); - ColorStopColorVector colors(countUsed); - fillStops(m_stops.data(), m_stops.size(), pos, colors); + ColorStopOffsetVector pos(countUsed); + ColorStopColorVector colors(countUsed); + fillStops(m_stops.data(), m_stops.size(), pos, colors); - SkShader::TileMode tile = SkShader::kClamp_TileMode; - switch (m_spreadMethod) { + SkShader::TileMode tile = SkShader::kClamp_TileMode; + switch (m_spreadMethod) { case SpreadMethodReflect: - tile = SkShader::kMirror_TileMode; - break; + tile = SkShader::kMirror_TileMode; + break; case SpreadMethodRepeat: - tile = SkShader::kRepeat_TileMode; - break; + tile = SkShader::kRepeat_TileMode; + break; case SpreadMethodPad: - tile = SkShader::kClamp_TileMode; - break; + tile = SkShader::kClamp_TileMode; + break; + } + + uint32_t shouldDrawInPMColorSpace = + m_drawInPMColorSpace ? SkGradientShader::kInterpolateColorsInPremul_Flag + : 0; + if (m_radial) { + if (aspectRatio() != 1) { + // CSS3 elliptical gradients: apply the elliptical scaling at the + // gradient center point. + m_gradientSpaceTransformation.translate(m_p0.x(), m_p0.y()); + m_gradientSpaceTransformation.scale(1, 1 / aspectRatio()); + m_gradientSpaceTransformation.translate(-m_p0.x(), -m_p0.y()); + ASSERT(m_p0 == m_p1); } - - uint32_t shouldDrawInPMColorSpace = m_drawInPMColorSpace ? SkGradientShader::kInterpolateColorsInPremul_Flag : 0; - if (m_radial) { - if (aspectRatio() != 1) { - // CSS3 elliptical gradients: apply the elliptical scaling at the - // gradient center point. - m_gradientSpaceTransformation.translate(m_p0.x(), m_p0.y()); - m_gradientSpaceTransformation.scale(1, 1 / aspectRatio()); - m_gradientSpaceTransformation.translate(-m_p0.x(), -m_p0.y()); - ASSERT(m_p0 == m_p1); - } - SkMatrix localMatrix = affineTransformToSkMatrix(m_gradientSpaceTransformation); - - // Since the two-point radial gradient is slower than the plain radial, - // only use it if we have to. - if (m_p0 == m_p1 && m_r0 <= 0.0f) { - m_gradient = SkGradientShader::MakeRadial(m_p1.data(), m_r1, colors.data(), pos.data(), static_cast(countUsed), tile, shouldDrawInPMColorSpace, &localMatrix); - } else { - // The radii we give to Skia must be positive. If we're given a - // negative radius, ask for zero instead. - SkScalar radius0 = m_r0 >= 0.0f ? WebCoreFloatToSkScalar(m_r0) : 0; - SkScalar radius1 = m_r1 >= 0.0f ? WebCoreFloatToSkScalar(m_r1) : 0; - m_gradient = SkGradientShader::MakeTwoPointConical(m_p0.data(), radius0, m_p1.data(), radius1, colors.data(), pos.data(), static_cast(countUsed), tile, shouldDrawInPMColorSpace, &localMatrix); - } + SkMatrix localMatrix = + affineTransformToSkMatrix(m_gradientSpaceTransformation); + + // Since the two-point radial gradient is slower than the plain radial, + // only use it if we have to. + if (m_p0 == m_p1 && m_r0 <= 0.0f) { + m_gradient = SkGradientShader::MakeRadial( + m_p1.data(), m_r1, colors.data(), pos.data(), + static_cast(countUsed), tile, shouldDrawInPMColorSpace, + &localMatrix); } else { - SkPoint pts[2] = { m_p0.data(), m_p1.data() }; - SkMatrix localMatrix = affineTransformToSkMatrix(m_gradientSpaceTransformation); - m_gradient = SkGradientShader::MakeLinear(pts, colors.data(), pos.data(), static_cast(countUsed), tile, shouldDrawInPMColorSpace, &localMatrix); + // The radii we give to Skia must be positive. If we're given a + // negative radius, ask for zero instead. + SkScalar radius0 = m_r0 >= 0.0f ? WebCoreFloatToSkScalar(m_r0) : 0; + SkScalar radius1 = m_r1 >= 0.0f ? WebCoreFloatToSkScalar(m_r1) : 0; + m_gradient = SkGradientShader::MakeTwoPointConical( + m_p0.data(), radius0, m_p1.data(), radius1, colors.data(), pos.data(), + static_cast(countUsed), tile, shouldDrawInPMColorSpace, + &localMatrix); } - - if (!m_gradient) { - // use last color, since our "geometry" was degenerate (e.g. radius==0) - m_gradient = SkShader::MakeColorShader(colors[countUsed - 1]); - } - return m_gradient; + } else { + SkPoint pts[2] = {m_p0.data(), m_p1.data()}; + SkMatrix localMatrix = + affineTransformToSkMatrix(m_gradientSpaceTransformation); + m_gradient = SkGradientShader::MakeLinear( + pts, colors.data(), pos.data(), static_cast(countUsed), tile, + shouldDrawInPMColorSpace, &localMatrix); + } + + if (!m_gradient) { + // use last color, since our "geometry" was degenerate (e.g. radius==0) + m_gradient = SkShader::MakeColorShader(colors[countUsed - 1]); + } + return m_gradient; } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/graphics/Gradient.h b/sky/engine/platform/graphics/Gradient.h index e2f4b10ca2043..585580035c45a 100644 --- a/sky/engine/platform/graphics/Gradient.h +++ b/sky/engine/platform/graphics/Gradient.h @@ -44,104 +44,115 @@ class SkShader; namespace blink { class PLATFORM_EXPORT Gradient : public RefCounted { -public: - static PassRefPtr create(const FloatPoint& p0, const FloatPoint& p1) - { - return adoptRef(new Gradient(p0, p1)); - } - static PassRefPtr create(const FloatPoint& p0, float r0, const FloatPoint& p1, float r1, float aspectRatio = 1) - { - return adoptRef(new Gradient(p0, r0, p1, r1, aspectRatio)); - } - ~Gradient(); - - struct ColorStop { - float stop; - Color color; - - ColorStop(float s, const Color& c) : stop(s), color(c) { } - }; - void addColorStop(const ColorStop&); - void addColorStop(float value, const Color& color) { addColorStop(ColorStop(value, color)); } - - bool hasAlpha() const; - bool shaderChanged() const { return !m_gradient; } - - bool isRadial() const { return m_radial; } - bool isZeroSize() const { return m_p0.x() == m_p1.x() && m_p0.y() == m_p1.y() && (!m_radial || m_r0 == m_r1); } - - const FloatPoint& p0() const { return m_p0; } - const FloatPoint& p1() const { return m_p1; } - - void setP0(const FloatPoint& p) - { - if (m_p0 == p) - return; - - m_p0 = p; - } - - void setP1(const FloatPoint& p) - { - if (m_p1 == p) - return; - - m_p1 = p; - } - - float startRadius() const { return m_r0; } - float endRadius() const { return m_r1; } - - void setStartRadius(float r) - { - if (m_r0 == r) - return; - - m_r0 = r; - } - - void setEndRadius(float r) - { - if (m_r1 == r) - return; - - m_r1 = r; - } - - float aspectRatio() const { return m_aspectRatio; } - - sk_sp shader(); - - void setDrawsInPMColorSpace(bool drawInPMColorSpace); - - void setSpreadMethod(GradientSpreadMethod); - GradientSpreadMethod spreadMethod() { return m_spreadMethod; } - void setGradientSpaceTransform(const AffineTransform& gradientSpaceTransformation); - AffineTransform gradientSpaceTransform() { return m_gradientSpaceTransformation; } - -private: - Gradient(const FloatPoint& p0, const FloatPoint& p1); - Gradient(const FloatPoint& p0, float r0, const FloatPoint& p1, float r1, float aspectRatio); - - void destroyShader(); - - void sortStopsIfNecessary(); - - FloatPoint m_p0; - FloatPoint m_p1; - float m_r0; - float m_r1; - float m_aspectRatio; // For elliptical gradient, width / height. - Vector m_stops; - bool m_radial; - bool m_stopsSorted; - bool m_drawInPMColorSpace; - GradientSpreadMethod m_spreadMethod; - AffineTransform m_gradientSpaceTransformation; - - sk_sp m_gradient; + public: + static PassRefPtr create(const FloatPoint& p0, + const FloatPoint& p1) { + return adoptRef(new Gradient(p0, p1)); + } + static PassRefPtr create(const FloatPoint& p0, + float r0, + const FloatPoint& p1, + float r1, + float aspectRatio = 1) { + return adoptRef(new Gradient(p0, r0, p1, r1, aspectRatio)); + } + ~Gradient(); + + struct ColorStop { + float stop; + Color color; + + ColorStop(float s, const Color& c) : stop(s), color(c) {} + }; + void addColorStop(const ColorStop&); + void addColorStop(float value, const Color& color) { + addColorStop(ColorStop(value, color)); + } + + bool hasAlpha() const; + bool shaderChanged() const { return !m_gradient; } + + bool isRadial() const { return m_radial; } + bool isZeroSize() const { + return m_p0.x() == m_p1.x() && m_p0.y() == m_p1.y() && + (!m_radial || m_r0 == m_r1); + } + + const FloatPoint& p0() const { return m_p0; } + const FloatPoint& p1() const { return m_p1; } + + void setP0(const FloatPoint& p) { + if (m_p0 == p) + return; + + m_p0 = p; + } + + void setP1(const FloatPoint& p) { + if (m_p1 == p) + return; + + m_p1 = p; + } + + float startRadius() const { return m_r0; } + float endRadius() const { return m_r1; } + + void setStartRadius(float r) { + if (m_r0 == r) + return; + + m_r0 = r; + } + + void setEndRadius(float r) { + if (m_r1 == r) + return; + + m_r1 = r; + } + + float aspectRatio() const { return m_aspectRatio; } + + sk_sp shader(); + + void setDrawsInPMColorSpace(bool drawInPMColorSpace); + + void setSpreadMethod(GradientSpreadMethod); + GradientSpreadMethod spreadMethod() { return m_spreadMethod; } + void setGradientSpaceTransform( + const AffineTransform& gradientSpaceTransformation); + AffineTransform gradientSpaceTransform() { + return m_gradientSpaceTransformation; + } + + private: + Gradient(const FloatPoint& p0, const FloatPoint& p1); + Gradient(const FloatPoint& p0, + float r0, + const FloatPoint& p1, + float r1, + float aspectRatio); + + void destroyShader(); + + void sortStopsIfNecessary(); + + FloatPoint m_p0; + FloatPoint m_p1; + float m_r0; + float m_r1; + float m_aspectRatio; // For elliptical gradient, width / height. + Vector m_stops; + bool m_radial; + bool m_stopsSorted; + bool m_drawInPMColorSpace; + GradientSpreadMethod m_spreadMethod; + AffineTransform m_gradientSpaceTransformation; + + sk_sp m_gradient; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_GRAPHICS_GRADIENT_H_ diff --git a/sky/engine/platform/graphics/GradientGeneratedImage.cpp b/sky/engine/platform/graphics/GradientGeneratedImage.cpp index a335d8d5fe819..0db697a06695f 100644 --- a/sky/engine/platform/graphics/GradientGeneratedImage.cpp +++ b/sky/engine/platform/graphics/GradientGeneratedImage.cpp @@ -30,72 +30,87 @@ namespace blink { -void GradientGeneratedImage::draw(GraphicsContext* destContext, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator compositeOp, WebBlendMode blendMode) -{ - GraphicsContextStateSaver stateSaver(*destContext); - destContext->setCompositeOperation(compositeOp, blendMode); - destContext->clip(destRect); - destContext->translate(destRect.x(), destRect.y()); - if (destRect.size() != srcRect.size()) - destContext->scale(destRect.width() / srcRect.width(), destRect.height() / srcRect.height()); - destContext->translate(-srcRect.x(), -srcRect.y()); - destContext->setFillGradient(m_gradient); - destContext->fillRect(FloatRect(FloatPoint(), m_size)); +void GradientGeneratedImage::draw(GraphicsContext* destContext, + const FloatRect& destRect, + const FloatRect& srcRect, + CompositeOperator compositeOp, + WebBlendMode blendMode) { + GraphicsContextStateSaver stateSaver(*destContext); + destContext->setCompositeOperation(compositeOp, blendMode); + destContext->clip(destRect); + destContext->translate(destRect.x(), destRect.y()); + if (destRect.size() != srcRect.size()) + destContext->scale(destRect.width() / srcRect.width(), + destRect.height() / srcRect.height()); + destContext->translate(-srcRect.x(), -srcRect.y()); + destContext->setFillGradient(m_gradient); + destContext->fillRect(FloatRect(FloatPoint(), m_size)); } -void GradientGeneratedImage::drawPattern(GraphicsContext* destContext, const FloatRect& srcRect, const FloatSize& scale, - const FloatPoint& phase, CompositeOperator compositeOp, const FloatRect& destRect, WebBlendMode blendMode, const IntSize& repeatSpacing) -{ - float stepX = srcRect.width() + repeatSpacing.width(); - float stepY = srcRect.height() + repeatSpacing.height(); - int firstColumn = static_cast(floorf((((destRect.x() - phase.x()) / scale.width()) - srcRect.x()) / srcRect.width())); - int firstRow = static_cast(floorf((((destRect.y() - phase.y()) / scale.height()) - srcRect.y()) / srcRect.height())); - for (int i = firstColumn; ; ++i) { - float dstX = (srcRect.x() + i * stepX) * scale.width() + phase.x(); - // assert that first column encroaches left edge of dstRect. - ASSERT(i > firstColumn || dstX <= destRect.x()); - ASSERT(i == firstColumn || dstX > destRect.x()); +void GradientGeneratedImage::drawPattern(GraphicsContext* destContext, + const FloatRect& srcRect, + const FloatSize& scale, + const FloatPoint& phase, + CompositeOperator compositeOp, + const FloatRect& destRect, + WebBlendMode blendMode, + const IntSize& repeatSpacing) { + float stepX = srcRect.width() + repeatSpacing.width(); + float stepY = srcRect.height() + repeatSpacing.height(); + int firstColumn = static_cast( + floorf((((destRect.x() - phase.x()) / scale.width()) - srcRect.x()) / + srcRect.width())); + int firstRow = static_cast( + floorf((((destRect.y() - phase.y()) / scale.height()) - srcRect.y()) / + srcRect.height())); + for (int i = firstColumn;; ++i) { + float dstX = (srcRect.x() + i * stepX) * scale.width() + phase.x(); + // assert that first column encroaches left edge of dstRect. + ASSERT(i > firstColumn || dstX <= destRect.x()); + ASSERT(i == firstColumn || dstX > destRect.x()); - if (dstX >= destRect.maxX()) - break; - float dstMaxX = dstX + srcRect.width() * scale.width(); - if (dstX < destRect.x()) - dstX = destRect.x(); - if (dstMaxX > destRect.maxX()) - dstMaxX = destRect.maxX(); - if (dstX >= dstMaxX) - continue; + if (dstX >= destRect.maxX()) + break; + float dstMaxX = dstX + srcRect.width() * scale.width(); + if (dstX < destRect.x()) + dstX = destRect.x(); + if (dstMaxX > destRect.maxX()) + dstMaxX = destRect.maxX(); + if (dstX >= dstMaxX) + continue; - FloatRect visibleSrcRect; - FloatRect tileDstRect; - tileDstRect.setX(dstX); - tileDstRect.setWidth(dstMaxX - dstX); - visibleSrcRect.setX((tileDstRect.x() - phase.x()) / scale.width() - i * stepX); - visibleSrcRect.setWidth(tileDstRect.width() / scale.width()); + FloatRect visibleSrcRect; + FloatRect tileDstRect; + tileDstRect.setX(dstX); + tileDstRect.setWidth(dstMaxX - dstX); + visibleSrcRect.setX((tileDstRect.x() - phase.x()) / scale.width() - + i * stepX); + visibleSrcRect.setWidth(tileDstRect.width() / scale.width()); - for (int j = firstRow; ; j++) { - float dstY = (srcRect.y() + j * stepY) * scale.height() + phase.y(); - // assert that first row encroaches top edge of dstRect. - ASSERT(j > firstRow || dstY <= destRect.y()); - ASSERT(j == firstRow || dstY > destRect.y()); + for (int j = firstRow;; j++) { + float dstY = (srcRect.y() + j * stepY) * scale.height() + phase.y(); + // assert that first row encroaches top edge of dstRect. + ASSERT(j > firstRow || dstY <= destRect.y()); + ASSERT(j == firstRow || dstY > destRect.y()); - if (dstY >= destRect.maxY()) - break; - float dstMaxY = dstY + srcRect.height() * scale.height(); - if (dstY < destRect.y()) - dstY = destRect.y(); - if (dstMaxY > destRect.maxY()) - dstMaxY = destRect.maxY(); - if (dstY >= dstMaxY) - continue; + if (dstY >= destRect.maxY()) + break; + float dstMaxY = dstY + srcRect.height() * scale.height(); + if (dstY < destRect.y()) + dstY = destRect.y(); + if (dstMaxY > destRect.maxY()) + dstMaxY = destRect.maxY(); + if (dstY >= dstMaxY) + continue; - tileDstRect.setY(dstY); - tileDstRect.setHeight(dstMaxY - dstY); - visibleSrcRect.setY((tileDstRect.y() - phase.y()) / scale.height() - j * stepY); - visibleSrcRect.setHeight(tileDstRect.height() / scale.height()); - draw(destContext, tileDstRect, visibleSrcRect, compositeOp, blendMode); - } + tileDstRect.setY(dstY); + tileDstRect.setHeight(dstMaxY - dstY); + visibleSrcRect.setY((tileDstRect.y() - phase.y()) / scale.height() - + j * stepY); + visibleSrcRect.setHeight(tileDstRect.height() / scale.height()); + draw(destContext, tileDstRect, visibleSrcRect, compositeOp, blendMode); } + } } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/graphics/GradientGeneratedImage.h b/sky/engine/platform/graphics/GradientGeneratedImage.h index 4a6dc53d06526..7d9f8c30c3ae4 100644 --- a/sky/engine/platform/graphics/GradientGeneratedImage.h +++ b/sky/engine/platform/graphics/GradientGeneratedImage.h @@ -35,32 +35,38 @@ namespace blink { class PLATFORM_EXPORT GradientGeneratedImage : public GeneratedImage { -public: - static PassRefPtr create(PassRefPtr generator, const IntSize& size) - { - return adoptRef(new GradientGeneratedImage(generator, size)); - } + public: + static PassRefPtr create( + PassRefPtr generator, + const IntSize& size) { + return adoptRef(new GradientGeneratedImage(generator, size)); + } - virtual ~GradientGeneratedImage() - { - } + virtual ~GradientGeneratedImage() {} -protected: - virtual void draw(GraphicsContext*, const FloatRect&, const FloatRect&, - CompositeOperator, WebBlendMode) override; - virtual void drawPattern(GraphicsContext*, const FloatRect&, - const FloatSize&, const FloatPoint&, CompositeOperator, - const FloatRect&, WebBlendMode, const IntSize& repeatSpacing) override; + protected: + virtual void draw(GraphicsContext*, + const FloatRect&, + const FloatRect&, + CompositeOperator, + WebBlendMode) override; + virtual void drawPattern(GraphicsContext*, + const FloatRect&, + const FloatSize&, + const FloatPoint&, + CompositeOperator, + const FloatRect&, + WebBlendMode, + const IntSize& repeatSpacing) override; - GradientGeneratedImage(PassRefPtr generator, const IntSize& size) - : m_gradient(generator) - { - m_size = size; - } + GradientGeneratedImage(PassRefPtr generator, const IntSize& size) + : m_gradient(generator) { + m_size = size; + } - RefPtr m_gradient; + RefPtr m_gradient; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_GRAPHICS_GRADIENTGENERATEDIMAGE_H_ diff --git a/sky/engine/platform/graphics/GraphicsContext.cpp b/sky/engine/platform/graphics/GraphicsContext.cpp index 2f9e095054e38..09e3c9d7bcac2 100644 --- a/sky/engine/platform/graphics/GraphicsContext.cpp +++ b/sky/engine/platform/graphics/GraphicsContext.cpp @@ -51,1409 +51,1496 @@ namespace blink { struct GraphicsContext::CanvasSaveState { - CanvasSaveState(bool pendingSave, int count) - : m_pendingSave(pendingSave), m_restoreCount(count) { } + CanvasSaveState(bool pendingSave, int count) + : m_pendingSave(pendingSave), m_restoreCount(count) {} - bool m_pendingSave; - int m_restoreCount; + bool m_pendingSave; + int m_restoreCount; }; -GraphicsContext::GraphicsContext(SkCanvas* canvas, DisabledMode disableContextOrPainting) - : m_canvas(canvas) - , m_paintStateStack() - , m_paintStateIndex(0) - , m_pendingCanvasSave(false) +GraphicsContext::GraphicsContext(SkCanvas* canvas, + DisabledMode disableContextOrPainting) + : m_canvas(canvas), + m_paintStateStack(), + m_paintStateIndex(0), + m_pendingCanvasSave(false) #if ENABLE(ASSERT) - , m_layerCount(0) - , m_disableDestructionChecks(false) + , + m_layerCount(0), + m_disableDestructionChecks(false) #endif - , m_disabledState(disableContextOrPainting) - , m_deviceScaleFactor(1.0f) - , m_regionTrackingMode(RegionTrackingDisabled) - , m_trackTextRegion(false) - , m_accelerated(false) - , m_isCertainlyOpaque(true) - , m_antialiasHairlineImages(false) - , m_shouldSmoothFonts(true) -{ - ASSERT(canvas); - - // FIXME: Do some tests to determine how many states are typically used, and allocate - // several here. - m_paintStateStack.append(GraphicsContextState::create()); - m_paintState = m_paintStateStack.last().get(); -} - -GraphicsContext::~GraphicsContext() -{ + , + m_disabledState(disableContextOrPainting), + m_deviceScaleFactor(1.0f), + m_regionTrackingMode(RegionTrackingDisabled), + m_trackTextRegion(false), + m_accelerated(false), + m_isCertainlyOpaque(true), + m_antialiasHairlineImages(false), + m_shouldSmoothFonts(true) { + ASSERT(canvas); + + // FIXME: Do some tests to determine how many states are typically used, and + // allocate several here. + m_paintStateStack.append(GraphicsContextState::create()); + m_paintState = m_paintStateStack.last().get(); +} + +GraphicsContext::~GraphicsContext() { #if ENABLE(ASSERT) - if (!m_disableDestructionChecks) { - ASSERT(!m_paintStateIndex); - ASSERT(!m_paintState->saveCount()); - ASSERT(!m_layerCount); - ASSERT(m_canvasStateStack.isEmpty()); - } + if (!m_disableDestructionChecks) { + ASSERT(!m_paintStateIndex); + ASSERT(!m_paintState->saveCount()); + ASSERT(!m_layerCount); + ASSERT(m_canvasStateStack.isEmpty()); + } #endif } -void GraphicsContext::resetCanvas(SkCanvas* canvas) -{ - ASSERT(canvas); - m_canvas = canvas; - m_trackedRegion.reset(); +void GraphicsContext::resetCanvas(SkCanvas* canvas) { + ASSERT(canvas); + m_canvas = canvas; + m_trackedRegion.reset(); } -void GraphicsContext::setRegionTrackingMode(RegionTrackingMode mode) -{ - m_regionTrackingMode = mode; - if (mode == RegionTrackingOpaque) - m_trackedRegion.setTrackedRegionType(RegionTracker::Opaque); - else if (mode == RegionTrackingOverwrite) - m_trackedRegion.setTrackedRegionType(RegionTracker::Overwrite); +void GraphicsContext::setRegionTrackingMode(RegionTrackingMode mode) { + m_regionTrackingMode = mode; + if (mode == RegionTrackingOpaque) + m_trackedRegion.setTrackedRegionType(RegionTracker::Opaque); + else if (mode == RegionTrackingOverwrite) + m_trackedRegion.setTrackedRegionType(RegionTracker::Overwrite); } -void GraphicsContext::save() -{ - if (contextDisabled()) - return; +void GraphicsContext::save() { + if (contextDisabled()) + return; - m_paintState->incrementSaveCount(); + m_paintState->incrementSaveCount(); - m_canvasStateStack.append(CanvasSaveState(m_pendingCanvasSave, m_canvas->getSaveCount())); - m_pendingCanvasSave = true; + m_canvasStateStack.append( + CanvasSaveState(m_pendingCanvasSave, m_canvas->getSaveCount())); + m_pendingCanvasSave = true; } -void GraphicsContext::restore() -{ - if (contextDisabled()) - return; +void GraphicsContext::restore() { + if (contextDisabled()) + return; - if (!m_paintStateIndex && !m_paintState->saveCount()) { - WTF_LOG_ERROR("ERROR void GraphicsContext::restore() stack is empty"); - return; - } + if (!m_paintStateIndex && !m_paintState->saveCount()) { + WTF_LOG_ERROR("ERROR void GraphicsContext::restore() stack is empty"); + return; + } - if (m_paintState->saveCount()) { - m_paintState->decrementSaveCount(); - } else { - m_paintStateIndex--; - m_paintState = m_paintStateStack[m_paintStateIndex].get(); - } + if (m_paintState->saveCount()) { + m_paintState->decrementSaveCount(); + } else { + m_paintStateIndex--; + m_paintState = m_paintStateStack[m_paintStateIndex].get(); + } - CanvasSaveState savedState = m_canvasStateStack.last(); - m_canvasStateStack.removeLast(); - m_pendingCanvasSave = savedState.m_pendingSave; - m_canvas->restoreToCount(savedState.m_restoreCount); + CanvasSaveState savedState = m_canvasStateStack.last(); + m_canvasStateStack.removeLast(); + m_pendingCanvasSave = savedState.m_pendingSave; + m_canvas->restoreToCount(savedState.m_restoreCount); } -void GraphicsContext::saveLayer(const SkRect* bounds, const SkPaint* paint) -{ - if (contextDisabled()) - return; +void GraphicsContext::saveLayer(const SkRect* bounds, const SkPaint* paint) { + if (contextDisabled()) + return; - realizeCanvasSave(); + realizeCanvasSave(); - m_canvas->saveLayer(bounds, paint); - if (regionTrackingEnabled()) - m_trackedRegion.pushCanvasLayer(paint); + m_canvas->saveLayer(bounds, paint); + if (regionTrackingEnabled()) + m_trackedRegion.pushCanvasLayer(paint); } -void GraphicsContext::restoreLayer() -{ - if (contextDisabled()) - return; +void GraphicsContext::restoreLayer() { + if (contextDisabled()) + return; - m_canvas->restore(); - if (regionTrackingEnabled()) - m_trackedRegion.popCanvasLayer(this); + m_canvas->restore(); + if (regionTrackingEnabled()) + m_trackedRegion.popCanvasLayer(this); } -void GraphicsContext::setStrokePattern(PassRefPtr pattern) -{ - if (contextDisabled()) - return; +void GraphicsContext::setStrokePattern(PassRefPtr pattern) { + if (contextDisabled()) + return; - ASSERT(pattern); - if (!pattern) { - setStrokeColor(Color::black); - return; - } - mutableState()->setStrokePattern(pattern); + ASSERT(pattern); + if (!pattern) { + setStrokeColor(Color::black); + return; + } + mutableState()->setStrokePattern(pattern); } -void GraphicsContext::setStrokeGradient(PassRefPtr gradient) -{ - if (contextDisabled()) - return; +void GraphicsContext::setStrokeGradient(PassRefPtr gradient) { + if (contextDisabled()) + return; - ASSERT(gradient); - if (!gradient) { - setStrokeColor(Color::black); - return; - } - mutableState()->setStrokeGradient(gradient); + ASSERT(gradient); + if (!gradient) { + setStrokeColor(Color::black); + return; + } + mutableState()->setStrokeGradient(gradient); } -void GraphicsContext::setFillPattern(PassRefPtr pattern) -{ - if (contextDisabled()) - return; +void GraphicsContext::setFillPattern(PassRefPtr pattern) { + if (contextDisabled()) + return; - ASSERT(pattern); - if (!pattern) { - setFillColor(Color::black); - return; - } + ASSERT(pattern); + if (!pattern) { + setFillColor(Color::black); + return; + } - mutableState()->setFillPattern(pattern); + mutableState()->setFillPattern(pattern); } -void GraphicsContext::setFillGradient(PassRefPtr gradient) -{ - if (contextDisabled()) - return; +void GraphicsContext::setFillGradient(PassRefPtr gradient) { + if (contextDisabled()) + return; - ASSERT(gradient); - if (!gradient) { - setFillColor(Color::black); - return; - } + ASSERT(gradient); + if (!gradient) { + setFillColor(Color::black); + return; + } - mutableState()->setFillGradient(gradient); + mutableState()->setFillGradient(gradient); } -void GraphicsContext::setShadow(const FloatSize& offset, float blur, const Color& color, +void GraphicsContext::setShadow( + const FloatSize& offset, + float blur, + const Color& color, DrawLooperBuilder::ShadowTransformMode shadowTransformMode, - DrawLooperBuilder::ShadowAlphaMode shadowAlphaMode) -{ - if (contextDisabled()) - return; - - if (!color.alpha() || (!offset.width() && !offset.height() && !blur)) { - clearShadow(); - return; - } + DrawLooperBuilder::ShadowAlphaMode shadowAlphaMode) { + if (contextDisabled()) + return; - OwnPtr drawLooperBuilder = DrawLooperBuilder::create(); - drawLooperBuilder->addShadow(offset, blur, color, shadowTransformMode, shadowAlphaMode); - drawLooperBuilder->addUnmodifiedContent(); - setDrawLooper(drawLooperBuilder.release()); + if (!color.alpha() || (!offset.width() && !offset.height() && !blur)) { + clearShadow(); + return; + } + + OwnPtr drawLooperBuilder = DrawLooperBuilder::create(); + drawLooperBuilder->addShadow(offset, blur, color, shadowTransformMode, + shadowAlphaMode); + drawLooperBuilder->addUnmodifiedContent(); + setDrawLooper(drawLooperBuilder.release()); } -void GraphicsContext::setDrawLooper(PassOwnPtr drawLooperBuilder) -{ - if (contextDisabled()) - return; +void GraphicsContext::setDrawLooper( + PassOwnPtr drawLooperBuilder) { + if (contextDisabled()) + return; - mutableState()->setDrawLooper(drawLooperBuilder->detachDrawLooper()); + mutableState()->setDrawLooper(drawLooperBuilder->detachDrawLooper()); } -void GraphicsContext::clearDrawLooper() -{ - if (contextDisabled()) - return; +void GraphicsContext::clearDrawLooper() { + if (contextDisabled()) + return; - mutableState()->clearDrawLooper(); + mutableState()->clearDrawLooper(); } -bool GraphicsContext::hasShadow() const -{ - return !!immutableState()->drawLooper(); +bool GraphicsContext::hasShadow() const { + return !!immutableState()->drawLooper(); } -bool GraphicsContext::getTransformedClipBounds(FloatRect* bounds) const -{ - if (contextDisabled()) - return false; - SkIRect skIBounds; - if (!m_canvas->getDeviceClipBounds(&skIBounds)) - return false; - SkRect skBounds = SkRect::Make(skIBounds); - *bounds = FloatRect(skBounds); - return true; +bool GraphicsContext::getTransformedClipBounds(FloatRect* bounds) const { + if (contextDisabled()) + return false; + SkIRect skIBounds; + if (!m_canvas->getDeviceClipBounds(&skIBounds)) + return false; + SkRect skBounds = SkRect::Make(skIBounds); + *bounds = FloatRect(skBounds); + return true; } -SkMatrix GraphicsContext::getTotalMatrix() const -{ - if (contextDisabled()) - return SkMatrix::I(); +SkMatrix GraphicsContext::getTotalMatrix() const { + if (contextDisabled()) + return SkMatrix::I(); - return m_canvas->getTotalMatrix(); + return m_canvas->getTotalMatrix(); } -void GraphicsContext::adjustTextRenderMode(SkPaint* paint) -{ - if (contextDisabled()) - return; +void GraphicsContext::adjustTextRenderMode(SkPaint* paint) { + if (contextDisabled()) + return; - if (!paint->isLCDRenderText()) - return; + if (!paint->isLCDRenderText()) + return; - paint->setLCDRenderText(couldUseLCDRenderedText()); + paint->setLCDRenderText(couldUseLCDRenderedText()); } -void GraphicsContext::setCompositeOperation(CompositeOperator compositeOperation, WebBlendMode blendMode) -{ - if (contextDisabled()) - return; - mutableState()->setCompositeOperation(compositeOperation, blendMode); +void GraphicsContext::setCompositeOperation( + CompositeOperator compositeOperation, + WebBlendMode blendMode) { + if (contextDisabled()) + return; + mutableState()->setCompositeOperation(compositeOperation, blendMode); } -SkColorFilter* GraphicsContext::colorFilter() const -{ - return immutableState()->colorFilter(); +SkColorFilter* GraphicsContext::colorFilter() const { + return immutableState()->colorFilter(); } -void GraphicsContext::setColorFilter(ColorFilterObsolete colorFilter) -{ -} +void GraphicsContext::setColorFilter(ColorFilterObsolete colorFilter) {} -bool GraphicsContext::readPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, int x, int y) -{ - if (contextDisabled()) - return false; +bool GraphicsContext::readPixels(const SkImageInfo& info, + void* pixels, + size_t rowBytes, + int x, + int y) { + if (contextDisabled()) + return false; - return m_canvas->readPixels(info, pixels, rowBytes, x, y); + return m_canvas->readPixels(info, pixels, rowBytes, x, y); } -void GraphicsContext::setMatrix(const SkMatrix& matrix) -{ - if (contextDisabled()) - return; +void GraphicsContext::setMatrix(const SkMatrix& matrix) { + if (contextDisabled()) + return; - realizeCanvasSave(); + realizeCanvasSave(); - m_canvas->setMatrix(matrix); + m_canvas->setMatrix(matrix); } -void GraphicsContext::concat(const SkMatrix& matrix) -{ - if (contextDisabled()) - return; +void GraphicsContext::concat(const SkMatrix& matrix) { + if (contextDisabled()) + return; - if (matrix.isIdentity()) - return; + if (matrix.isIdentity()) + return; - realizeCanvasSave(); + realizeCanvasSave(); - m_canvas->concat(matrix); + m_canvas->concat(matrix); } -void GraphicsContext::beginTransparencyLayer(float opacity, const FloatRect* bounds) -{ - beginLayer(opacity, immutableState()->compositeOperator(), bounds); +void GraphicsContext::beginTransparencyLayer(float opacity, + const FloatRect* bounds) { + beginLayer(opacity, immutableState()->compositeOperator(), bounds); } -void GraphicsContext::beginLayer(float opacity, CompositeOperator op, const FloatRect* bounds, ColorFilterObsolete colorFilter, sk_sp imageFilter) -{ - if (contextDisabled()) - return; +void GraphicsContext::beginLayer(float opacity, + CompositeOperator op, + const FloatRect* bounds, + ColorFilterObsolete colorFilter, + sk_sp imageFilter) { + if (contextDisabled()) + return; - SkPaint layerPaint; - layerPaint.setAlpha(static_cast(opacity * 255)); - layerPaint.setBlendMode(WebCoreCompositeToSkiaComposite(op, m_paintState->blendMode())); - layerPaint.setImageFilter(imageFilter); + SkPaint layerPaint; + layerPaint.setAlpha(static_cast(opacity * 255)); + layerPaint.setBlendMode( + WebCoreCompositeToSkiaComposite(op, m_paintState->blendMode())); + layerPaint.setImageFilter(imageFilter); - if (bounds) { - SkRect skBounds = WebCoreFloatRectToSKRect(*bounds); - saveLayer(&skBounds, &layerPaint); - } else { - saveLayer(0, &layerPaint); - } + if (bounds) { + SkRect skBounds = WebCoreFloatRectToSKRect(*bounds); + saveLayer(&skBounds, &layerPaint); + } else { + saveLayer(0, &layerPaint); + } #if ENABLE(ASSERT) - ++m_layerCount; + ++m_layerCount; #endif } -void GraphicsContext::endLayer() -{ - if (contextDisabled()) - return; +void GraphicsContext::endLayer() { + if (contextDisabled()) + return; - restoreLayer(); + restoreLayer(); - ASSERT(m_layerCount > 0); + ASSERT(m_layerCount > 0); #if ENABLE(ASSERT) - --m_layerCount; + --m_layerCount; #endif } -void GraphicsContext::drawConvexPolygon(size_t numPoints, const FloatPoint* points, bool shouldAntialias) -{ - if (contextDisabled()) - return; +void GraphicsContext::drawConvexPolygon(size_t numPoints, + const FloatPoint* points, + bool shouldAntialias) { + if (contextDisabled()) + return; - if (numPoints <= 1) - return; + if (numPoints <= 1) + return; - SkPath path; - setPathFromConvexPoints(&path, numPoints, points); + SkPath path; + setPathFromConvexPoints(&path, numPoints, points); - SkPaint paint(immutableState()->fillPaint()); - paint.setAntiAlias(shouldAntialias); - drawPath(path, paint); + SkPaint paint(immutableState()->fillPaint()); + paint.setAntiAlias(shouldAntialias); + drawPath(path, paint); - if (strokeStyle() != NoStroke) - drawPath(path, immutableState()->strokePaint()); + if (strokeStyle() != NoStroke) + drawPath(path, immutableState()->strokePaint()); } -float GraphicsContext::prepareFocusRingPaint(SkPaint& paint, const Color& color, int width) const -{ - paint.setAntiAlias(true); - paint.setStyle(SkPaint::kStroke_Style); - paint.setColor(color.rgb()); - paint.setStrokeWidth(focusRingWidth(width)); - return 1; +float GraphicsContext::prepareFocusRingPaint(SkPaint& paint, + const Color& color, + int width) const { + paint.setAntiAlias(true); + paint.setStyle(SkPaint::kStroke_Style); + paint.setColor(color.rgb()); + paint.setStrokeWidth(focusRingWidth(width)); + return 1; } -void GraphicsContext::drawFocusRingPath(const SkPath& path, const Color& color, int width) -{ - SkPaint paint; - float cornerRadius = prepareFocusRingPaint(paint, color, width); +void GraphicsContext::drawFocusRingPath(const SkPath& path, + const Color& color, + int width) { + SkPaint paint; + float cornerRadius = prepareFocusRingPaint(paint, color, width); - paint.setPathEffect(SkCornerPathEffect::Make(SkFloatToScalar(cornerRadius))); + paint.setPathEffect(SkCornerPathEffect::Make(SkFloatToScalar(cornerRadius))); - // Outer path - drawPath(path, paint); + // Outer path + drawPath(path, paint); } -void GraphicsContext::drawFocusRingRect(const SkRect& rect, const Color& color, int width) -{ - SkPaint paint; - float cornerRadius = prepareFocusRingPaint(paint, color, width); +void GraphicsContext::drawFocusRingRect(const SkRect& rect, + const Color& color, + int width) { + SkPaint paint; + float cornerRadius = prepareFocusRingPaint(paint, color, width); - SkRRect rrect; - rrect.setRectXY(rect, SkFloatToScalar(cornerRadius), SkFloatToScalar(cornerRadius)); + SkRRect rrect; + rrect.setRectXY(rect, SkFloatToScalar(cornerRadius), + SkFloatToScalar(cornerRadius)); - // Outer rect - drawRRect(rrect, paint); + // Outer rect + drawRRect(rrect, paint); } -static inline IntRect areaCastingShadowInHole(const IntRect& holeRect, int shadowBlur, int shadowSpread, const IntSize& shadowOffset) -{ - IntRect bounds(holeRect); +static inline IntRect areaCastingShadowInHole(const IntRect& holeRect, + int shadowBlur, + int shadowSpread, + const IntSize& shadowOffset) { + IntRect bounds(holeRect); - bounds.inflate(shadowBlur); + bounds.inflate(shadowBlur); - if (shadowSpread < 0) - bounds.inflate(-shadowSpread); + if (shadowSpread < 0) + bounds.inflate(-shadowSpread); - IntRect offsetBounds = bounds; - offsetBounds.move(-shadowOffset); - return unionRect(bounds, offsetBounds); + IntRect offsetBounds = bounds; + offsetBounds.move(-shadowOffset); + return unionRect(bounds, offsetBounds); } -void GraphicsContext::drawInnerShadow(const RoundedRect& rect, const Color& shadowColor, const IntSize shadowOffset, int shadowBlur, int shadowSpread, Edges clippedEdges) -{ - if (contextDisabled()) - return; - - IntRect holeRect(rect.rect()); - holeRect.inflate(-shadowSpread); - - if (holeRect.isEmpty()) { - if (rect.isRounded()) - fillRoundedRect(rect, shadowColor); - else - fillRect(rect.rect(), shadowColor); - return; - } - - if (clippedEdges & LeftEdge) { - holeRect.move(-std::max(shadowOffset.width(), 0) - shadowBlur, 0); - holeRect.setWidth(holeRect.width() + std::max(shadowOffset.width(), 0) + shadowBlur); - } - if (clippedEdges & TopEdge) { - holeRect.move(0, -std::max(shadowOffset.height(), 0) - shadowBlur); - holeRect.setHeight(holeRect.height() + std::max(shadowOffset.height(), 0) + shadowBlur); - } - if (clippedEdges & RightEdge) - holeRect.setWidth(holeRect.width() - std::min(shadowOffset.width(), 0) + shadowBlur); - if (clippedEdges & BottomEdge) - holeRect.setHeight(holeRect.height() - std::min(shadowOffset.height(), 0) + shadowBlur); +void GraphicsContext::drawInnerShadow(const RoundedRect& rect, + const Color& shadowColor, + const IntSize shadowOffset, + int shadowBlur, + int shadowSpread, + Edges clippedEdges) { + if (contextDisabled()) + return; - Color fillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), 255); + IntRect holeRect(rect.rect()); + holeRect.inflate(-shadowSpread); - IntRect outerRect = areaCastingShadowInHole(rect.rect(), shadowBlur, shadowSpread, shadowOffset); - RoundedRect roundedHole(holeRect, rect.radii()); - - save(); - if (rect.isRounded()) { - Path path; - path.addRoundedRect(rect); - clipPath(path); - roundedHole.shrinkRadii(shadowSpread); + if (holeRect.isEmpty()) { + if (rect.isRounded()) + fillRoundedRect(rect, shadowColor); + else + fillRect(rect.rect(), shadowColor); + return; + } + + if (clippedEdges & LeftEdge) { + holeRect.move(-std::max(shadowOffset.width(), 0) - shadowBlur, 0); + holeRect.setWidth(holeRect.width() + std::max(shadowOffset.width(), 0) + + shadowBlur); + } + if (clippedEdges & TopEdge) { + holeRect.move(0, -std::max(shadowOffset.height(), 0) - shadowBlur); + holeRect.setHeight(holeRect.height() + std::max(shadowOffset.height(), 0) + + shadowBlur); + } + if (clippedEdges & RightEdge) + holeRect.setWidth(holeRect.width() - std::min(shadowOffset.width(), 0) + + shadowBlur); + if (clippedEdges & BottomEdge) + holeRect.setHeight(holeRect.height() - std::min(shadowOffset.height(), 0) + + shadowBlur); + + Color fillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), + 255); + + IntRect outerRect = areaCastingShadowInHole(rect.rect(), shadowBlur, + shadowSpread, shadowOffset); + RoundedRect roundedHole(holeRect, rect.radii()); + + save(); + if (rect.isRounded()) { + Path path; + path.addRoundedRect(rect); + clipPath(path); + roundedHole.shrinkRadii(shadowSpread); + } else { + clip(rect.rect()); + } + + OwnPtr drawLooperBuilder = DrawLooperBuilder::create(); + drawLooperBuilder->addShadow(shadowOffset, shadowBlur, shadowColor, + DrawLooperBuilder::ShadowRespectsTransforms, + DrawLooperBuilder::ShadowIgnoresAlpha); + setDrawLooper(drawLooperBuilder.release()); + fillRectWithRoundedHole(outerRect, roundedHole, fillColor); + restore(); + clearDrawLooper(); +} + +void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2) { + if (contextDisabled()) + return; + + StrokeStyle penStyle = strokeStyle(); + if (penStyle == NoStroke) + return; + + FloatPoint p1 = point1; + FloatPoint p2 = point2; + bool isVerticalLine = (p1.x() == p2.x()); + int width = roundf(strokeThickness()); + + // We know these are vertical or horizontal lines, so the length will just + // be the sum of the displacement component vectors give or take 1 - + // probably worth the speed up of no square root, which also won't be exact. + FloatSize disp = p2 - p1; + int length = SkScalarRoundToInt(disp.width() + disp.height()); + SkPaint paint(immutableState()->strokePaint(length)); + + if (strokeStyle() == DottedStroke || strokeStyle() == DashedStroke) { + // Do a rect fill of our endpoints. This ensures we always have the + // appearance of being a border. We then draw the actual dotted/dashed + // line. + SkRect r1, r2; + r1.set(p1.x(), p1.y(), p1.x() + width, p1.y() + width); + r2.set(p2.x(), p2.y(), p2.x() + width, p2.y() + width); + + if (isVerticalLine) { + r1.offset(-width / 2, 0); + r2.offset(-width / 2, -width); } else { - clip(rect.rect()); - } - - OwnPtr drawLooperBuilder = DrawLooperBuilder::create(); - drawLooperBuilder->addShadow(shadowOffset, shadowBlur, shadowColor, - DrawLooperBuilder::ShadowRespectsTransforms, DrawLooperBuilder::ShadowIgnoresAlpha); - setDrawLooper(drawLooperBuilder.release()); - fillRectWithRoundedHole(outerRect, roundedHole, fillColor); - restore(); - clearDrawLooper(); -} - -void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2) -{ - if (contextDisabled()) - return; - - StrokeStyle penStyle = strokeStyle(); - if (penStyle == NoStroke) - return; - - FloatPoint p1 = point1; - FloatPoint p2 = point2; - bool isVerticalLine = (p1.x() == p2.x()); - int width = roundf(strokeThickness()); - - // We know these are vertical or horizontal lines, so the length will just - // be the sum of the displacement component vectors give or take 1 - - // probably worth the speed up of no square root, which also won't be exact. - FloatSize disp = p2 - p1; - int length = SkScalarRoundToInt(disp.width() + disp.height()); - SkPaint paint(immutableState()->strokePaint(length)); - - if (strokeStyle() == DottedStroke || strokeStyle() == DashedStroke) { - // Do a rect fill of our endpoints. This ensures we always have the - // appearance of being a border. We then draw the actual dotted/dashed line. - SkRect r1, r2; - r1.set(p1.x(), p1.y(), p1.x() + width, p1.y() + width); - r2.set(p2.x(), p2.y(), p2.x() + width, p2.y() + width); - - if (isVerticalLine) { - r1.offset(-width / 2, 0); - r2.offset(-width / 2, -width); - } else { - r1.offset(0, -width / 2); - r2.offset(-width, -width / 2); - } - SkPaint fillPaint; - fillPaint.setColor(paint.getColor()); - drawRect(r1, fillPaint); - drawRect(r2, fillPaint); + r1.offset(0, -width / 2); + r2.offset(-width, -width / 2); } + SkPaint fillPaint; + fillPaint.setColor(paint.getColor()); + drawRect(r1, fillPaint); + drawRect(r2, fillPaint); + } + + adjustLineToPixelBoundaries(p1, p2, width, penStyle); + SkPoint pts[2] = {p1.data(), p2.data()}; + + m_canvas->drawPoints(SkCanvas::kLines_PointMode, 2, pts, paint); + + if (regionTrackingEnabled()) + m_trackedRegion.didDrawPoints(this, SkCanvas::kLines_PointMode, 2, pts, + paint); +} + +void GraphicsContext::drawLineForDocumentMarker(const FloatPoint& pt, + float width, + DocumentMarkerLineStyle style) { + if (contextDisabled()) + return; + + // Use 2x resources for a device scale factor of 1.5 or above. + int deviceScaleFactor = m_deviceScaleFactor > 1.5f ? 2 : 1; + + // Create the pattern we'll use to draw the underline. + int index = style == DocumentMarkerGrammarLineStyle ? 1 : 0; + static SkBitmap* misspellBitmap1x[2] = {0, 0}; + static SkBitmap* misspellBitmap2x[2] = {0, 0}; + SkBitmap** misspellBitmap = + deviceScaleFactor == 2 ? misspellBitmap2x : misspellBitmap1x; + if (!misspellBitmap[index]) { + // We use a 2-pixel-high misspelling indicator because that seems to be + // what WebKit is designed for, and how much room there is in a typical + // page for it. + const int rowPixels = + 32 * deviceScaleFactor; // Must be multiple of 4 for pattern below. + const int colPixels = 2 * deviceScaleFactor; + SkBitmap bitmap; + bitmap.allocN32Pixels(rowPixels, colPixels); + + bitmap.eraseARGB(0, 0, 0, 0); + if (deviceScaleFactor == 1) + draw1xMarker(&bitmap, index); + else if (deviceScaleFactor == 2) + draw2xMarker(&bitmap, index); + else + ASSERT_NOT_REACHED(); - adjustLineToPixelBoundaries(p1, p2, width, penStyle); - SkPoint pts[2] = { p1.data(), p2.data() }; - - m_canvas->drawPoints(SkCanvas::kLines_PointMode, 2, pts, paint); - - if (regionTrackingEnabled()) - m_trackedRegion.didDrawPoints(this, SkCanvas::kLines_PointMode, 2, pts, paint); -} - -void GraphicsContext::drawLineForDocumentMarker(const FloatPoint& pt, float width, DocumentMarkerLineStyle style) -{ - if (contextDisabled()) - return; - - // Use 2x resources for a device scale factor of 1.5 or above. - int deviceScaleFactor = m_deviceScaleFactor > 1.5f ? 2 : 1; - - // Create the pattern we'll use to draw the underline. - int index = style == DocumentMarkerGrammarLineStyle ? 1 : 0; - static SkBitmap* misspellBitmap1x[2] = { 0, 0 }; - static SkBitmap* misspellBitmap2x[2] = { 0, 0 }; - SkBitmap** misspellBitmap = deviceScaleFactor == 2 ? misspellBitmap2x : misspellBitmap1x; - if (!misspellBitmap[index]) { - // We use a 2-pixel-high misspelling indicator because that seems to be - // what WebKit is designed for, and how much room there is in a typical - // page for it. - const int rowPixels = 32 * deviceScaleFactor; // Must be multiple of 4 for pattern below. - const int colPixels = 2 * deviceScaleFactor; - SkBitmap bitmap; - bitmap.allocN32Pixels(rowPixels, colPixels); - - bitmap.eraseARGB(0, 0, 0, 0); - if (deviceScaleFactor == 1) - draw1xMarker(&bitmap, index); - else if (deviceScaleFactor == 2) - draw2xMarker(&bitmap, index); - else - ASSERT_NOT_REACHED(); - - misspellBitmap[index] = new SkBitmap(bitmap); - } + misspellBitmap[index] = new SkBitmap(bitmap); + } - SkScalar originX = WebCoreFloatToSkScalar(pt.x()); + SkScalar originX = WebCoreFloatToSkScalar(pt.x()); - // Offset it vertically by 1 so that there's some space under the text. - SkScalar originY = WebCoreFloatToSkScalar(pt.y()) + 1; - originX *= deviceScaleFactor; - originY *= deviceScaleFactor; + // Offset it vertically by 1 so that there's some space under the text. + SkScalar originY = WebCoreFloatToSkScalar(pt.y()) + 1; + originX *= deviceScaleFactor; + originY *= deviceScaleFactor; - SkMatrix localMatrix; - localMatrix.setTranslate(originX, originY); + SkMatrix localMatrix; + localMatrix.setTranslate(originX, originY); - SkPaint paint; - paint.setShader(SkShader::MakeBitmapShader( - *misspellBitmap[index], SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix)); + SkPaint paint; + paint.setShader(SkShader::MakeBitmapShader( + *misspellBitmap[index], SkShader::kRepeat_TileMode, + SkShader::kRepeat_TileMode, &localMatrix)); - SkRect rect; - rect.set(originX, originY, originX + WebCoreFloatToSkScalar(width) * deviceScaleFactor, originY + SkIntToScalar(misspellBitmap[index]->height())); + SkRect rect; + rect.set(originX, originY, + originX + WebCoreFloatToSkScalar(width) * deviceScaleFactor, + originY + SkIntToScalar(misspellBitmap[index]->height())); - if (deviceScaleFactor == 2) { - save(); - scale(0.5, 0.5); - } - drawRect(rect, paint); - if (deviceScaleFactor == 2) - restore(); + if (deviceScaleFactor == 2) { + save(); + scale(0.5, 0.5); + } + drawRect(rect, paint); + if (deviceScaleFactor == 2) + restore(); } -void GraphicsContext::drawLineForText(const FloatPoint& pt, float width) -{ - if (contextDisabled()) - return; +void GraphicsContext::drawLineForText(const FloatPoint& pt, float width) { + if (contextDisabled()) + return; - if (width <= 0) - return; + if (width <= 0) + return; - SkPaint paint; - switch (strokeStyle()) { + SkPaint paint; + switch (strokeStyle()) { case NoStroke: case SolidStroke: case DoubleStroke: case WavyStroke: { - int thickness = SkMax32(static_cast(strokeThickness()), 1); - SkRect r; - r.fLeft = WebCoreFloatToSkScalar(pt.x()); - // Avoid anti-aliasing lines. Currently, these are always horizontal. - // Round to nearest pixel to match text and other content. - r.fTop = WebCoreFloatToSkScalar(floorf(pt.y() + 0.5f)); - r.fRight = r.fLeft + WebCoreFloatToSkScalar(width); - r.fBottom = r.fTop + SkIntToScalar(thickness); - paint = immutableState()->fillPaint(); - // Text lines are drawn using the stroke color. - paint.setColor(effectiveStrokeColor()); - drawRect(r, paint); - return; + int thickness = SkMax32(static_cast(strokeThickness()), 1); + SkRect r; + r.fLeft = WebCoreFloatToSkScalar(pt.x()); + // Avoid anti-aliasing lines. Currently, these are always horizontal. + // Round to nearest pixel to match text and other content. + r.fTop = WebCoreFloatToSkScalar(floorf(pt.y() + 0.5f)); + r.fRight = r.fLeft + WebCoreFloatToSkScalar(width); + r.fBottom = r.fTop + SkIntToScalar(thickness); + paint = immutableState()->fillPaint(); + // Text lines are drawn using the stroke color. + paint.setColor(effectiveStrokeColor()); + drawRect(r, paint); + return; } case DottedStroke: case DashedStroke: { - int y = floorf(pt.y() + std::max(strokeThickness() / 2.0f, 0.5f)); - drawLine(IntPoint(pt.x(), y), IntPoint(pt.x() + width, y)); - return; - } + int y = floorf(pt.y() + std::max(strokeThickness() / 2.0f, 0.5f)); + drawLine(IntPoint(pt.x(), y), IntPoint(pt.x() + width, y)); + return; } + } - ASSERT_NOT_REACHED(); + ASSERT_NOT_REACHED(); } // Draws a filled rectangle with a stroked border. -void GraphicsContext::drawRect(const IntRect& rect) -{ - if (contextDisabled()) - return; - - ASSERT(!rect.isEmpty()); - if (rect.isEmpty()) - return; - - SkRect skRect = rect; - int fillcolorNotTransparent = immutableState()->fillColor().rgb() & 0xFF000000; - if (fillcolorNotTransparent) - drawRect(skRect, immutableState()->fillPaint()); - - if (immutableState()->strokeData().style() != NoStroke - && immutableState()->strokeData().color().alpha()) { - // Stroke a width: 1 inset border - SkPaint paint(immutableState()->fillPaint()); - paint.setColor(effectiveStrokeColor()); - paint.setStyle(SkPaint::kStroke_Style); - paint.setStrokeWidth(1); - - skRect.inset(0.5f, 0.5f); - drawRect(skRect, paint); - } -} - -void GraphicsContext::drawText(const Font& font, const TextRunPaintInfo& runInfo, const FloatPoint& point) -{ - if (contextDisabled()) - return; - - font.drawText(this, runInfo, point); -} - -void GraphicsContext::drawEmphasisMarks(const Font& font, const TextRunPaintInfo& runInfo, const AtomicString& mark, const FloatPoint& point) -{ - if (contextDisabled()) - return; - - font.drawEmphasisMarks(this, runInfo, mark, point); -} - -void GraphicsContext::drawBidiText(const Font& font, const TextRunPaintInfo& runInfo, const FloatPoint& point, Font::CustomFontNotReadyAction customFontNotReadyAction) -{ - if (contextDisabled()) - return; - - // sub-run painting is not supported for Bidi text. - const TextRun& run = runInfo.run; - ASSERT((runInfo.from == 0) && (runInfo.to == run.length())); - BidiResolver bidiResolver; - bidiResolver.setStatus(BidiStatus(run.direction(), run.directionalOverride())); - bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&run, 0)); - - // FIXME: This ownership should be reversed. We should pass BidiRunList - // to BidiResolver in createBidiRunsForLine. - BidiRunList& bidiRuns = bidiResolver.runs(); - bidiResolver.createBidiRunsForLine(TextRunIterator(&run, run.length())); - if (!bidiRuns.runCount()) - return; - - FloatPoint currPoint = point; - BidiCharacterRun* bidiRun = bidiRuns.firstRun(); - while (bidiRun) { - TextRun subrun = run.subRun(bidiRun->start(), bidiRun->stop() - bidiRun->start()); - bool isRTL = bidiRun->level() % 2; - subrun.setDirection(isRTL ? RTL : LTR); - subrun.setDirectionalOverride(bidiRun->dirOverride()); - - TextRunPaintInfo subrunInfo(subrun); - subrunInfo.bounds = runInfo.bounds; - float runWidth = font.drawUncachedText(this, subrunInfo, currPoint, customFontNotReadyAction); - - bidiRun = bidiRun->next(); - currPoint.move(runWidth, 0); - } - - bidiRuns.deleteRuns(); -} - -void GraphicsContext::drawHighlightForText(const Font& font, const TextRun& run, const FloatPoint& point, int h, const Color& backgroundColor, int from, int to) -{ - if (contextDisabled()) - return; - - fillRect(font.selectionRectForText(run, point, h, from, to), backgroundColor); -} - -void GraphicsContext::drawImage(Image* image, const IntPoint& p, CompositeOperator op, RespectImageOrientationEnum shouldRespectImageOrientation) -{ - if (!image) - return; - drawImage(image, FloatRect(IntRect(p, image->size())), FloatRect(FloatPoint(), FloatSize(image->size())), op, shouldRespectImageOrientation); -} - -void GraphicsContext::drawImage(Image* image, const IntRect& r, CompositeOperator op, RespectImageOrientationEnum shouldRespectImageOrientation) -{ - if (!image) - return; - drawImage(image, FloatRect(r), FloatRect(FloatPoint(), FloatSize(image->size())), op, shouldRespectImageOrientation); -} - -void GraphicsContext::drawImage(Image* image, const FloatRect& dest, const FloatRect& src, CompositeOperator op, RespectImageOrientationEnum shouldRespectImageOrientation) -{ - drawImage(image, dest, src, op, WebBlendModeNormal, shouldRespectImageOrientation); -} - -void GraphicsContext::drawImage(Image* image, const FloatRect& dest) -{ - if (!image) - return; - drawImage(image, dest, FloatRect(IntRect(IntPoint(), image->size()))); -} - -void GraphicsContext::drawImage(Image* image, const FloatRect& dest, const FloatRect& src, CompositeOperator op, WebBlendMode blendMode, RespectImageOrientationEnum shouldRespectImageOrientation) -{ - if (contextDisabled() || !image) - return; - image->draw(this, dest, src, op, blendMode, shouldRespectImageOrientation); +void GraphicsContext::drawRect(const IntRect& rect) { + if (contextDisabled()) + return; + + ASSERT(!rect.isEmpty()); + if (rect.isEmpty()) + return; + + SkRect skRect = rect; + int fillcolorNotTransparent = + immutableState()->fillColor().rgb() & 0xFF000000; + if (fillcolorNotTransparent) + drawRect(skRect, immutableState()->fillPaint()); + + if (immutableState()->strokeData().style() != NoStroke && + immutableState()->strokeData().color().alpha()) { + // Stroke a width: 1 inset border + SkPaint paint(immutableState()->fillPaint()); + paint.setColor(effectiveStrokeColor()); + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(1); + + skRect.inset(0.5f, 0.5f); + drawRect(skRect, paint); + } +} + +void GraphicsContext::drawText(const Font& font, + const TextRunPaintInfo& runInfo, + const FloatPoint& point) { + if (contextDisabled()) + return; + + font.drawText(this, runInfo, point); +} + +void GraphicsContext::drawEmphasisMarks(const Font& font, + const TextRunPaintInfo& runInfo, + const AtomicString& mark, + const FloatPoint& point) { + if (contextDisabled()) + return; + + font.drawEmphasisMarks(this, runInfo, mark, point); +} + +void GraphicsContext::drawBidiText( + const Font& font, + const TextRunPaintInfo& runInfo, + const FloatPoint& point, + Font::CustomFontNotReadyAction customFontNotReadyAction) { + if (contextDisabled()) + return; + + // sub-run painting is not supported for Bidi text. + const TextRun& run = runInfo.run; + ASSERT((runInfo.from == 0) && (runInfo.to == run.length())); + BidiResolver bidiResolver; + bidiResolver.setStatus( + BidiStatus(run.direction(), run.directionalOverride())); + bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&run, 0)); + + // FIXME: This ownership should be reversed. We should pass BidiRunList + // to BidiResolver in createBidiRunsForLine. + BidiRunList& bidiRuns = bidiResolver.runs(); + bidiResolver.createBidiRunsForLine(TextRunIterator(&run, run.length())); + if (!bidiRuns.runCount()) + return; + + FloatPoint currPoint = point; + BidiCharacterRun* bidiRun = bidiRuns.firstRun(); + while (bidiRun) { + TextRun subrun = + run.subRun(bidiRun->start(), bidiRun->stop() - bidiRun->start()); + bool isRTL = bidiRun->level() % 2; + subrun.setDirection(isRTL ? RTL : LTR); + subrun.setDirectionalOverride(bidiRun->dirOverride()); + + TextRunPaintInfo subrunInfo(subrun); + subrunInfo.bounds = runInfo.bounds; + float runWidth = font.drawUncachedText(this, subrunInfo, currPoint, + customFontNotReadyAction); + + bidiRun = bidiRun->next(); + currPoint.move(runWidth, 0); + } + + bidiRuns.deleteRuns(); +} + +void GraphicsContext::drawHighlightForText(const Font& font, + const TextRun& run, + const FloatPoint& point, + int h, + const Color& backgroundColor, + int from, + int to) { + if (contextDisabled()) + return; + + fillRect(font.selectionRectForText(run, point, h, from, to), backgroundColor); +} + +void GraphicsContext::drawImage( + Image* image, + const IntPoint& p, + CompositeOperator op, + RespectImageOrientationEnum shouldRespectImageOrientation) { + if (!image) + return; + drawImage(image, FloatRect(IntRect(p, image->size())), + FloatRect(FloatPoint(), FloatSize(image->size())), op, + shouldRespectImageOrientation); +} + +void GraphicsContext::drawImage( + Image* image, + const IntRect& r, + CompositeOperator op, + RespectImageOrientationEnum shouldRespectImageOrientation) { + if (!image) + return; + drawImage(image, FloatRect(r), + FloatRect(FloatPoint(), FloatSize(image->size())), op, + shouldRespectImageOrientation); +} + +void GraphicsContext::drawImage( + Image* image, + const FloatRect& dest, + const FloatRect& src, + CompositeOperator op, + RespectImageOrientationEnum shouldRespectImageOrientation) { + drawImage(image, dest, src, op, WebBlendModeNormal, + shouldRespectImageOrientation); +} + +void GraphicsContext::drawImage(Image* image, const FloatRect& dest) { + if (!image) + return; + drawImage(image, dest, FloatRect(IntRect(IntPoint(), image->size()))); +} + +void GraphicsContext::drawImage( + Image* image, + const FloatRect& dest, + const FloatRect& src, + CompositeOperator op, + WebBlendMode blendMode, + RespectImageOrientationEnum shouldRespectImageOrientation) { + if (contextDisabled() || !image) + return; + image->draw(this, dest, src, op, blendMode, shouldRespectImageOrientation); } -void GraphicsContext::drawTiledImage(Image* image, const IntRect& destRect, const IntPoint& srcPoint, const IntSize& tileSize, CompositeOperator op, WebBlendMode blendMode, const IntSize& repeatSpacing) -{ - if (contextDisabled() || !image) - return; - image->drawTiled(this, destRect, srcPoint, tileSize, op, blendMode, repeatSpacing); +void GraphicsContext::drawTiledImage(Image* image, + const IntRect& destRect, + const IntPoint& srcPoint, + const IntSize& tileSize, + CompositeOperator op, + WebBlendMode blendMode, + const IntSize& repeatSpacing) { + if (contextDisabled() || !image) + return; + image->drawTiled(this, destRect, srcPoint, tileSize, op, blendMode, + repeatSpacing); } -void GraphicsContext::drawTiledImage(Image* image, const IntRect& dest, const IntRect& srcRect, - const FloatSize& tileScaleFactor, Image::TileRule hRule, Image::TileRule vRule, CompositeOperator op) -{ - if (contextDisabled() || !image) - return; +void GraphicsContext::drawTiledImage(Image* image, + const IntRect& dest, + const IntRect& srcRect, + const FloatSize& tileScaleFactor, + Image::TileRule hRule, + Image::TileRule vRule, + CompositeOperator op) { + if (contextDisabled() || !image) + return; - if (hRule == Image::StretchTile && vRule == Image::StretchTile) { - // Just do a scale. - drawImage(image, dest, srcRect, op); - return; - } + if (hRule == Image::StretchTile && vRule == Image::StretchTile) { + // Just do a scale. + drawImage(image, dest, srcRect, op); + return; + } - image->drawTiled(this, dest, srcRect, tileScaleFactor, hRule, vRule, op); + image->drawTiled(this, dest, srcRect, tileScaleFactor, hRule, vRule, op); } -void GraphicsContext::drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top, const SkPaint* paint) -{ - if (contextDisabled()) - return; +void GraphicsContext::drawBitmap(const SkBitmap& bitmap, + SkScalar left, + SkScalar top, + const SkPaint* paint) { + if (contextDisabled()) + return; - m_canvas->drawBitmap(bitmap, left, top, paint); + m_canvas->drawBitmap(bitmap, left, top, paint); - if (regionTrackingEnabled()) { - SkRect rect = SkRect::MakeXYWH(left, top, bitmap.width(), bitmap.height()); - m_trackedRegion.didDrawRect(this, rect, *paint, &bitmap); - } + if (regionTrackingEnabled()) { + SkRect rect = SkRect::MakeXYWH(left, top, bitmap.width(), bitmap.height()); + m_trackedRegion.didDrawRect(this, rect, *paint, &bitmap); + } } -void GraphicsContext::drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, - const SkRect& dst, const SkPaint* paint) -{ - if (contextDisabled()) - return; +void GraphicsContext::drawBitmapRect(const SkBitmap& bitmap, + const SkRect* src, + const SkRect& dst, + const SkPaint* paint) { + if (contextDisabled()) + return; - SkCanvas::SrcRectConstraint flags = - immutableState()->shouldClampToSourceRect() ? SkCanvas::kStrict_SrcRectConstraint : SkCanvas::kFast_SrcRectConstraint; + SkCanvas::SrcRectConstraint flags = + immutableState()->shouldClampToSourceRect() + ? SkCanvas::kStrict_SrcRectConstraint + : SkCanvas::kFast_SrcRectConstraint; - m_canvas->drawBitmapRect(bitmap, *src, dst, paint, flags); + m_canvas->drawBitmapRect(bitmap, *src, dst, paint, flags); - if (regionTrackingEnabled()) - m_trackedRegion.didDrawRect(this, dst, *paint, &bitmap); + if (regionTrackingEnabled()) + m_trackedRegion.didDrawRect(this, dst, *paint, &bitmap); } -void GraphicsContext::drawOval(const SkRect& oval, const SkPaint& paint) -{ - if (contextDisabled()) - return; +void GraphicsContext::drawOval(const SkRect& oval, const SkPaint& paint) { + if (contextDisabled()) + return; - m_canvas->drawOval(oval, paint); + m_canvas->drawOval(oval, paint); - if (regionTrackingEnabled()) - m_trackedRegion.didDrawBounded(this, oval, paint); + if (regionTrackingEnabled()) + m_trackedRegion.didDrawBounded(this, oval, paint); } -void GraphicsContext::drawPath(const SkPath& path, const SkPaint& paint) -{ - if (contextDisabled()) - return; +void GraphicsContext::drawPath(const SkPath& path, const SkPaint& paint) { + if (contextDisabled()) + return; - m_canvas->drawPath(path, paint); + m_canvas->drawPath(path, paint); - if (regionTrackingEnabled()) - m_trackedRegion.didDrawPath(this, path, paint); + if (regionTrackingEnabled()) + m_trackedRegion.didDrawPath(this, path, paint); } -void GraphicsContext::drawRect(const SkRect& rect, const SkPaint& paint) -{ - if (contextDisabled()) - return; +void GraphicsContext::drawRect(const SkRect& rect, const SkPaint& paint) { + if (contextDisabled()) + return; - m_canvas->drawRect(rect, paint); + m_canvas->drawRect(rect, paint); - if (regionTrackingEnabled()) - m_trackedRegion.didDrawRect(this, rect, paint, 0); + if (regionTrackingEnabled()) + m_trackedRegion.didDrawRect(this, rect, paint, 0); } -void GraphicsContext::drawRRect(const SkRRect& rrect, const SkPaint& paint) -{ - if (contextDisabled()) - return; +void GraphicsContext::drawRRect(const SkRRect& rrect, const SkPaint& paint) { + if (contextDisabled()) + return; - m_canvas->drawRRect(rrect, paint); + m_canvas->drawRRect(rrect, paint); - if (regionTrackingEnabled()) - m_trackedRegion.didDrawBounded(this, rrect.rect(), paint); + if (regionTrackingEnabled()) + m_trackedRegion.didDrawBounded(this, rrect.rect(), paint); } -void GraphicsContext::didDrawRect(const SkRect& rect, const SkPaint& paint, const SkBitmap* bitmap) -{ - if (contextDisabled()) - return; +void GraphicsContext::didDrawRect(const SkRect& rect, + const SkPaint& paint, + const SkBitmap* bitmap) { + if (contextDisabled()) + return; - if (regionTrackingEnabled()) - m_trackedRegion.didDrawRect(this, rect, paint, bitmap); + if (regionTrackingEnabled()) + m_trackedRegion.didDrawRect(this, rect, paint, bitmap); } -void GraphicsContext::drawPosText(const void* text, size_t byteLength, - const SkPoint pos[], const SkRect& textRect, const SkPaint& paint) -{ - if (contextDisabled()) - return; +void GraphicsContext::drawPosText(const void* text, + size_t byteLength, + const SkPoint pos[], + const SkRect& textRect, + const SkPaint& paint) { + if (contextDisabled()) + return; - m_canvas->drawPosText(text, byteLength, pos, paint); - didDrawTextInRect(textRect); + m_canvas->drawPosText(text, byteLength, pos, paint); + didDrawTextInRect(textRect); - // FIXME: compute bounds for positioned text. - if (regionTrackingEnabled()) - m_trackedRegion.didDrawUnbounded(this, paint, RegionTracker::FillOrStroke); + // FIXME: compute bounds for positioned text. + if (regionTrackingEnabled()) + m_trackedRegion.didDrawUnbounded(this, paint, RegionTracker::FillOrStroke); } -void GraphicsContext::drawPosTextH(const void* text, size_t byteLength, - const SkScalar xpos[], SkScalar constY, const SkRect& textRect, const SkPaint& paint) -{ - if (contextDisabled()) - return; +void GraphicsContext::drawPosTextH(const void* text, + size_t byteLength, + const SkScalar xpos[], + SkScalar constY, + const SkRect& textRect, + const SkPaint& paint) { + if (contextDisabled()) + return; - m_canvas->drawPosTextH(text, byteLength, xpos, constY, paint); - didDrawTextInRect(textRect); + m_canvas->drawPosTextH(text, byteLength, xpos, constY, paint); + didDrawTextInRect(textRect); - // FIXME: compute bounds for positioned text. - if (regionTrackingEnabled()) - m_trackedRegion.didDrawUnbounded(this, paint, RegionTracker::FillOrStroke); + // FIXME: compute bounds for positioned text. + if (regionTrackingEnabled()) + m_trackedRegion.didDrawUnbounded(this, paint, RegionTracker::FillOrStroke); } -void GraphicsContext::drawTextBlob(const SkTextBlob* blob, const SkPoint& origin, const SkPaint& paint) -{ - if (contextDisabled()) - return; +void GraphicsContext::drawTextBlob(const SkTextBlob* blob, + const SkPoint& origin, + const SkPaint& paint) { + if (contextDisabled()) + return; - m_canvas->drawTextBlob(blob, origin.x(), origin.y(), paint); + m_canvas->drawTextBlob(blob, origin.x(), origin.y(), paint); - SkRect bounds = blob->bounds(); - bounds.offset(origin); - didDrawTextInRect(bounds); + SkRect bounds = blob->bounds(); + bounds.offset(origin); + didDrawTextInRect(bounds); - // FIXME: use bounds here if it helps performance. - if (regionTrackingEnabled()) - m_trackedRegion.didDrawUnbounded(this, paint, RegionTracker::FillOrStroke); + // FIXME: use bounds here if it helps performance. + if (regionTrackingEnabled()) + m_trackedRegion.didDrawUnbounded(this, paint, RegionTracker::FillOrStroke); } -void GraphicsContext::fillPath(const Path& pathToFill) -{ - if (contextDisabled() || pathToFill.isEmpty()) - return; +void GraphicsContext::fillPath(const Path& pathToFill) { + if (contextDisabled() || pathToFill.isEmpty()) + return; - // Use const_cast and temporarily modify the fill type instead of copying the path. - SkPath& path = const_cast(pathToFill.skPath()); - SkPath::FillType previousFillType = path.getFillType(); + // Use const_cast and temporarily modify the fill type instead of copying the + // path. + SkPath& path = const_cast(pathToFill.skPath()); + SkPath::FillType previousFillType = path.getFillType(); - SkPath::FillType temporaryFillType = WebCoreWindRuleToSkFillType(immutableState()->fillRule()); - path.setFillType(temporaryFillType); + SkPath::FillType temporaryFillType = + WebCoreWindRuleToSkFillType(immutableState()->fillRule()); + path.setFillType(temporaryFillType); - drawPath(path, immutableState()->fillPaint()); + drawPath(path, immutableState()->fillPaint()); - path.setFillType(previousFillType); + path.setFillType(previousFillType); } -void GraphicsContext::fillRect(const FloatRect& rect) -{ - if (contextDisabled()) - return; +void GraphicsContext::fillRect(const FloatRect& rect) { + if (contextDisabled()) + return; - SkRect r = rect; + SkRect r = rect; - drawRect(r, immutableState()->fillPaint()); + drawRect(r, immutableState()->fillPaint()); } -void GraphicsContext::fillRect(const FloatRect& rect, const Color& color) -{ - if (contextDisabled()) - return; +void GraphicsContext::fillRect(const FloatRect& rect, const Color& color) { + if (contextDisabled()) + return; - SkRect r = rect; - SkPaint paint = immutableState()->fillPaint(); - paint.setColor(color.rgb()); - drawRect(r, paint); + SkRect r = rect; + SkPaint paint = immutableState()->fillPaint(); + paint.setColor(color.rgb()); + drawRect(r, paint); } -void GraphicsContext::fillBetweenRoundedRects(const IntRect& outer, const IntSize& outerTopLeft, const IntSize& outerTopRight, const IntSize& outerBottomLeft, const IntSize& outerBottomRight, - const IntRect& inner, const IntSize& innerTopLeft, const IntSize& innerTopRight, const IntSize& innerBottomLeft, const IntSize& innerBottomRight, const Color& color) { - if (contextDisabled()) - return; - - SkVector outerRadii[4]; - SkVector innerRadii[4]; - setRadii(outerRadii, outerTopLeft, outerTopRight, outerBottomRight, outerBottomLeft); - setRadii(innerRadii, innerTopLeft, innerTopRight, innerBottomRight, innerBottomLeft); +void GraphicsContext::fillBetweenRoundedRects(const IntRect& outer, + const IntSize& outerTopLeft, + const IntSize& outerTopRight, + const IntSize& outerBottomLeft, + const IntSize& outerBottomRight, + const IntRect& inner, + const IntSize& innerTopLeft, + const IntSize& innerTopRight, + const IntSize& innerBottomLeft, + const IntSize& innerBottomRight, + const Color& color) { + if (contextDisabled()) + return; + + SkVector outerRadii[4]; + SkVector innerRadii[4]; + setRadii(outerRadii, outerTopLeft, outerTopRight, outerBottomRight, + outerBottomLeft); + setRadii(innerRadii, innerTopLeft, innerTopRight, innerBottomRight, + innerBottomLeft); - SkRRect rrOuter; - SkRRect rrInner; - rrOuter.setRectRadii(outer, outerRadii); - rrInner.setRectRadii(inner, innerRadii); + SkRRect rrOuter; + SkRRect rrInner; + rrOuter.setRectRadii(outer, outerRadii); + rrInner.setRectRadii(inner, innerRadii); - SkPaint paint(immutableState()->fillPaint()); - paint.setColor(color.rgb()); + SkPaint paint(immutableState()->fillPaint()); + paint.setColor(color.rgb()); - m_canvas->drawDRRect(rrOuter, rrInner, paint); + m_canvas->drawDRRect(rrOuter, rrInner, paint); - if (regionTrackingEnabled()) - m_trackedRegion.didDrawBounded(this, rrOuter.getBounds(), paint); + if (regionTrackingEnabled()) + m_trackedRegion.didDrawBounded(this, rrOuter.getBounds(), paint); } -void GraphicsContext::fillBetweenRoundedRects(const RoundedRect& outer, const RoundedRect& inner, const Color& color) -{ - fillBetweenRoundedRects(outer.rect(), outer.radii().topLeft(), outer.radii().topRight(), outer.radii().bottomLeft(), outer.radii().bottomRight(), - inner.rect(), inner.radii().topLeft(), inner.radii().topRight(), inner.radii().bottomLeft(), inner.radii().bottomRight(), color); +void GraphicsContext::fillBetweenRoundedRects(const RoundedRect& outer, + const RoundedRect& inner, + const Color& color) { + fillBetweenRoundedRects( + outer.rect(), outer.radii().topLeft(), outer.radii().topRight(), + outer.radii().bottomLeft(), outer.radii().bottomRight(), inner.rect(), + inner.radii().topLeft(), inner.radii().topRight(), + inner.radii().bottomLeft(), inner.radii().bottomRight(), color); } -void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight, - const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color) -{ - if (contextDisabled()) - return; +void GraphicsContext::fillRoundedRect(const IntRect& rect, + const IntSize& topLeft, + const IntSize& topRight, + const IntSize& bottomLeft, + const IntSize& bottomRight, + const Color& color) { + if (contextDisabled()) + return; - if (topLeft.width() + topRight.width() > rect.width() - || bottomLeft.width() + bottomRight.width() > rect.width() - || topLeft.height() + bottomLeft.height() > rect.height() - || topRight.height() + bottomRight.height() > rect.height()) { - // Not all the radii fit, return a rect. This matches the behavior of - // Path::createRoundedRectangle. Without this we attempt to draw a round - // shadow for a square box. - fillRect(rect, color); - return; - } + if (topLeft.width() + topRight.width() > rect.width() || + bottomLeft.width() + bottomRight.width() > rect.width() || + topLeft.height() + bottomLeft.height() > rect.height() || + topRight.height() + bottomRight.height() > rect.height()) { + // Not all the radii fit, return a rect. This matches the behavior of + // Path::createRoundedRectangle. Without this we attempt to draw a round + // shadow for a square box. + fillRect(rect, color); + return; + } - SkVector radii[4]; - setRadii(radii, topLeft, topRight, bottomRight, bottomLeft); + SkVector radii[4]; + setRadii(radii, topLeft, topRight, bottomRight, bottomLeft); - SkRRect rr; - rr.setRectRadii(rect, radii); + SkRRect rr; + rr.setRectRadii(rect, radii); - SkPaint paint(immutableState()->fillPaint()); - paint.setColor(color.rgb()); + SkPaint paint(immutableState()->fillPaint()); + paint.setColor(color.rgb()); - m_canvas->drawRRect(rr, paint); + m_canvas->drawRRect(rr, paint); - if (regionTrackingEnabled()) - m_trackedRegion.didDrawBounded(this, rr.getBounds(), paint); + if (regionTrackingEnabled()) + m_trackedRegion.didDrawBounded(this, rr.getBounds(), paint); } -void GraphicsContext::fillEllipse(const FloatRect& ellipse) -{ - if (contextDisabled()) - return; +void GraphicsContext::fillEllipse(const FloatRect& ellipse) { + if (contextDisabled()) + return; - SkRect rect = ellipse; - drawOval(rect, immutableState()->fillPaint()); + SkRect rect = ellipse; + drawOval(rect, immutableState()->fillPaint()); } -void GraphicsContext::strokePath(const Path& pathToStroke) -{ - if (contextDisabled() || pathToStroke.isEmpty()) - return; +void GraphicsContext::strokePath(const Path& pathToStroke) { + if (contextDisabled() || pathToStroke.isEmpty()) + return; - const SkPath& path = pathToStroke.skPath(); - drawPath(path, immutableState()->strokePaint()); + const SkPath& path = pathToStroke.skPath(); + drawPath(path, immutableState()->strokePaint()); } -void GraphicsContext::strokeRect(const FloatRect& rect) -{ - strokeRect(rect, strokeThickness()); -} - -void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth) -{ - if (contextDisabled()) - return; - - SkPaint paint(immutableState()->strokePaint()); - paint.setStrokeWidth(WebCoreFloatToSkScalar(lineWidth)); - // Reset the dash effect to account for the width - immutableState()->strokeData().setupPaintDashPathEffect(&paint, 0); - // strokerect has special rules for CSS when the rect is degenerate: - // if width==0 && height==0, do nothing - // if width==0 || height==0, then just draw line for the other dimension - SkRect r(rect); - bool validW = r.width() > 0; - bool validH = r.height() > 0; - if (validW && validH) { - drawRect(r, paint); - } else if (validW || validH) { - // we are expected to respect the lineJoin, so we can't just call - // drawLine -- we have to create a path that doubles back on itself. - SkPath path; - path.moveTo(r.fLeft, r.fTop); - path.lineTo(r.fRight, r.fBottom); - path.close(); - drawPath(path, paint); - } +void GraphicsContext::strokeRect(const FloatRect& rect) { + strokeRect(rect, strokeThickness()); +} + +void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth) { + if (contextDisabled()) + return; + + SkPaint paint(immutableState()->strokePaint()); + paint.setStrokeWidth(WebCoreFloatToSkScalar(lineWidth)); + // Reset the dash effect to account for the width + immutableState()->strokeData().setupPaintDashPathEffect(&paint, 0); + // strokerect has special rules for CSS when the rect is degenerate: + // if width==0 && height==0, do nothing + // if width==0 || height==0, then just draw line for the other dimension + SkRect r(rect); + bool validW = r.width() > 0; + bool validH = r.height() > 0; + if (validW && validH) { + drawRect(r, paint); + } else if (validW || validH) { + // we are expected to respect the lineJoin, so we can't just call + // drawLine -- we have to create a path that doubles back on itself. + SkPath path; + path.moveTo(r.fLeft, r.fTop); + path.lineTo(r.fRight, r.fBottom); + path.close(); + drawPath(path, paint); + } } -void GraphicsContext::strokeEllipse(const FloatRect& ellipse) -{ - if (contextDisabled()) - return; +void GraphicsContext::strokeEllipse(const FloatRect& ellipse) { + if (contextDisabled()) + return; - drawOval(ellipse, immutableState()->strokePaint()); + drawOval(ellipse, immutableState()->strokePaint()); } -void GraphicsContext::clipRoundedRect(const RoundedRect& rect, SkClipOp clipOp) -{ - if (contextDisabled()) - return; +void GraphicsContext::clipRoundedRect(const RoundedRect& rect, + SkClipOp clipOp) { + if (contextDisabled()) + return; - if (!rect.isRounded()) { - clipRect(rect.rect(), NotAntiAliased, clipOp); - return; - } + if (!rect.isRounded()) { + clipRect(rect.rect(), NotAntiAliased, clipOp); + return; + } - SkVector radii[4]; - RoundedRect::Radii wkRadii = rect.radii(); - setRadii(radii, wkRadii.topLeft(), wkRadii.topRight(), wkRadii.bottomRight(), wkRadii.bottomLeft()); + SkVector radii[4]; + RoundedRect::Radii wkRadii = rect.radii(); + setRadii(radii, wkRadii.topLeft(), wkRadii.topRight(), wkRadii.bottomRight(), + wkRadii.bottomLeft()); - SkRRect r; - r.setRectRadii(rect.rect(), radii); + SkRRect r; + r.setRectRadii(rect.rect(), radii); - clipRRect(r, AntiAliased, clipOp); + clipRRect(r, AntiAliased, clipOp); } -void GraphicsContext::clipOut(const Path& pathToClip) -{ - if (contextDisabled()) - return; +void GraphicsContext::clipOut(const Path& pathToClip) { + if (contextDisabled()) + return; - // Use const_cast and temporarily toggle the inverse fill type instead of copying the path. - SkPath& path = const_cast(pathToClip.skPath()); - path.toggleInverseFillType(); - clipPath(path, AntiAliased); - path.toggleInverseFillType(); + // Use const_cast and temporarily toggle the inverse fill type instead of + // copying the path. + SkPath& path = const_cast(pathToClip.skPath()); + path.toggleInverseFillType(); + clipPath(path, AntiAliased); + path.toggleInverseFillType(); } -void GraphicsContext::clipPath(const Path& pathToClip, WindRule clipRule) -{ - if (contextDisabled() || pathToClip.isEmpty()) - return; +void GraphicsContext::clipPath(const Path& pathToClip, WindRule clipRule) { + if (contextDisabled() || pathToClip.isEmpty()) + return; - // Use const_cast and temporarily modify the fill type instead of copying the path. - SkPath& path = const_cast(pathToClip.skPath()); - SkPath::FillType previousFillType = path.getFillType(); + // Use const_cast and temporarily modify the fill type instead of copying the + // path. + SkPath& path = const_cast(pathToClip.skPath()); + SkPath::FillType previousFillType = path.getFillType(); - SkPath::FillType temporaryFillType = WebCoreWindRuleToSkFillType(clipRule); - path.setFillType(temporaryFillType); - clipPath(path, AntiAliased); + SkPath::FillType temporaryFillType = WebCoreWindRuleToSkFillType(clipRule); + path.setFillType(temporaryFillType); + clipPath(path, AntiAliased); - path.setFillType(previousFillType); + path.setFillType(previousFillType); } -void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint* points, bool antialiased) -{ - if (contextDisabled()) - return; +void GraphicsContext::clipConvexPolygon(size_t numPoints, + const FloatPoint* points, + bool antialiased) { + if (contextDisabled()) + return; - if (numPoints <= 1) - return; + if (numPoints <= 1) + return; - SkPath path; - setPathFromConvexPoints(&path, numPoints, points); - clipPath(path, antialiased ? AntiAliased : NotAntiAliased); + SkPath path; + setPathFromConvexPoints(&path, numPoints, points); + clipPath(path, antialiased ? AntiAliased : NotAntiAliased); } -void GraphicsContext::clipOutRoundedRect(const RoundedRect& rect) -{ - if (contextDisabled()) - return; +void GraphicsContext::clipOutRoundedRect(const RoundedRect& rect) { + if (contextDisabled()) + return; - clipRoundedRect(rect, SkClipOp::kDifference); + clipRoundedRect(rect, SkClipOp::kDifference); } -void GraphicsContext::canvasClip(const Path& pathToClip, WindRule clipRule) -{ - if (contextDisabled()) - return; +void GraphicsContext::canvasClip(const Path& pathToClip, WindRule clipRule) { + if (contextDisabled()) + return; - // Use const_cast and temporarily modify the fill type instead of copying the path. - SkPath& path = const_cast(pathToClip.skPath()); - SkPath::FillType previousFillType = path.getFillType(); + // Use const_cast and temporarily modify the fill type instead of copying the + // path. + SkPath& path = const_cast(pathToClip.skPath()); + SkPath::FillType previousFillType = path.getFillType(); - SkPath::FillType temporaryFillType = WebCoreWindRuleToSkFillType(clipRule); - path.setFillType(temporaryFillType); - clipPath(path); + SkPath::FillType temporaryFillType = WebCoreWindRuleToSkFillType(clipRule); + path.setFillType(temporaryFillType); + clipPath(path); - path.setFillType(previousFillType); + path.setFillType(previousFillType); } -void GraphicsContext::clipRect(const SkRect& rect, AntiAliasingMode aa, SkClipOp op) -{ - if (contextDisabled()) - return; +void GraphicsContext::clipRect(const SkRect& rect, + AntiAliasingMode aa, + SkClipOp op) { + if (contextDisabled()) + return; - realizeCanvasSave(); + realizeCanvasSave(); - m_canvas->clipRect(rect, op, aa == AntiAliased); + m_canvas->clipRect(rect, op, aa == AntiAliased); } -void GraphicsContext::clipPath(const SkPath& path, AntiAliasingMode aa, SkClipOp op) -{ - if (contextDisabled()) - return; +void GraphicsContext::clipPath(const SkPath& path, + AntiAliasingMode aa, + SkClipOp op) { + if (contextDisabled()) + return; - realizeCanvasSave(); + realizeCanvasSave(); - m_canvas->clipPath(path, op, aa == AntiAliased); + m_canvas->clipPath(path, op, aa == AntiAliased); } -void GraphicsContext::clipRRect(const SkRRect& rect, AntiAliasingMode aa, SkClipOp op) -{ - if (contextDisabled()) - return; +void GraphicsContext::clipRRect(const SkRRect& rect, + AntiAliasingMode aa, + SkClipOp op) { + if (contextDisabled()) + return; - realizeCanvasSave(); + realizeCanvasSave(); - m_canvas->clipRRect(rect, op, aa == AntiAliased); + m_canvas->clipRRect(rect, op, aa == AntiAliased); } -void GraphicsContext::rotate(float angleInRadians) -{ - if (contextDisabled()) - return; +void GraphicsContext::rotate(float angleInRadians) { + if (contextDisabled()) + return; - realizeCanvasSave(); + realizeCanvasSave(); - m_canvas->rotate(WebCoreFloatToSkScalar(angleInRadians * (180.0f / 3.14159265f))); + m_canvas->rotate( + WebCoreFloatToSkScalar(angleInRadians * (180.0f / 3.14159265f))); } -void GraphicsContext::translate(float x, float y) -{ - if (contextDisabled()) - return; +void GraphicsContext::translate(float x, float y) { + if (contextDisabled()) + return; - if (!x && !y) - return; + if (!x && !y) + return; - realizeCanvasSave(); + realizeCanvasSave(); - m_canvas->translate(WebCoreFloatToSkScalar(x), WebCoreFloatToSkScalar(y)); + m_canvas->translate(WebCoreFloatToSkScalar(x), WebCoreFloatToSkScalar(y)); } -void GraphicsContext::scale(float x, float y) -{ - if (contextDisabled()) - return; +void GraphicsContext::scale(float x, float y) { + if (contextDisabled()) + return; - if (x == 1.0f && y == 1.0f) - return; + if (x == 1.0f && y == 1.0f) + return; - realizeCanvasSave(); + realizeCanvasSave(); - m_canvas->scale(WebCoreFloatToSkScalar(x), WebCoreFloatToSkScalar(y)); + m_canvas->scale(WebCoreFloatToSkScalar(x), WebCoreFloatToSkScalar(y)); } -AffineTransform GraphicsContext::getCTM() const -{ - if (contextDisabled()) - return AffineTransform(); +AffineTransform GraphicsContext::getCTM() const { + if (contextDisabled()) + return AffineTransform(); - SkMatrix m = getTotalMatrix(); - return AffineTransform(SkScalarToDouble(m.getScaleX()), - SkScalarToDouble(m.getSkewY()), - SkScalarToDouble(m.getSkewX()), - SkScalarToDouble(m.getScaleY()), - SkScalarToDouble(m.getTranslateX()), - SkScalarToDouble(m.getTranslateY())); + SkMatrix m = getTotalMatrix(); + return AffineTransform( + SkScalarToDouble(m.getScaleX()), SkScalarToDouble(m.getSkewY()), + SkScalarToDouble(m.getSkewX()), SkScalarToDouble(m.getScaleY()), + SkScalarToDouble(m.getTranslateX()), SkScalarToDouble(m.getTranslateY())); } -void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, CompositeOperator op) -{ - if (contextDisabled()) - return; +void GraphicsContext::fillRect(const FloatRect& rect, + const Color& color, + CompositeOperator op) { + if (contextDisabled()) + return; - CompositeOperator previousOperator = compositeOperation(); - setCompositeOperation(op); - fillRect(rect, color); - setCompositeOperation(previousOperator); + CompositeOperator previousOperator = compositeOperation(); + setCompositeOperation(op); + fillRect(rect, color); + setCompositeOperation(previousOperator); } -void GraphicsContext::fillRoundedRect(const RoundedRect& rect, const Color& color) -{ - if (contextDisabled()) - return; +void GraphicsContext::fillRoundedRect(const RoundedRect& rect, + const Color& color) { + if (contextDisabled()) + return; - if (rect.isRounded()) - fillRoundedRect(rect.rect(), rect.radii().topLeft(), rect.radii().topRight(), rect.radii().bottomLeft(), rect.radii().bottomRight(), color); - else - fillRect(rect.rect(), color); + if (rect.isRounded()) + fillRoundedRect(rect.rect(), rect.radii().topLeft(), + rect.radii().topRight(), rect.radii().bottomLeft(), + rect.radii().bottomRight(), color); + else + fillRect(rect.rect(), color); } -void GraphicsContext::fillRectWithRoundedHole(const IntRect& rect, const RoundedRect& roundedHoleRect, const Color& color) -{ - if (contextDisabled()) - return; +void GraphicsContext::fillRectWithRoundedHole( + const IntRect& rect, + const RoundedRect& roundedHoleRect, + const Color& color) { + if (contextDisabled()) + return; - Path path; - path.addRect(rect); + Path path; + path.addRect(rect); - if (!roundedHoleRect.radii().isZero()) - path.addRoundedRect(roundedHoleRect); - else - path.addRect(roundedHoleRect.rect()); + if (!roundedHoleRect.radii().isZero()) + path.addRoundedRect(roundedHoleRect); + else + path.addRect(roundedHoleRect.rect()); - WindRule oldFillRule = fillRule(); - Color oldFillColor = fillColor(); + WindRule oldFillRule = fillRule(); + Color oldFillColor = fillColor(); - setFillRule(RULE_EVENODD); - setFillColor(color); + setFillRule(RULE_EVENODD); + setFillColor(color); - fillPath(path); + fillPath(path); - setFillRule(oldFillRule); - setFillColor(oldFillColor); + setFillRule(oldFillRule); + setFillColor(oldFillColor); } -void GraphicsContext::clearRect(const FloatRect& rect) -{ - if (contextDisabled()) - return; +void GraphicsContext::clearRect(const FloatRect& rect) { + if (contextDisabled()) + return; - SkRect r = rect; - SkPaint paint(immutableState()->fillPaint()); - paint.setBlendMode(SkBlendMode::kClear); - drawRect(r, paint); + SkRect r = rect; + SkPaint paint(immutableState()->fillPaint()); + paint.setBlendMode(SkBlendMode::kClear); + drawRect(r, paint); } -void GraphicsContext::adjustLineToPixelBoundaries(FloatPoint& p1, FloatPoint& p2, float strokeWidth, StrokeStyle penStyle) -{ - // For odd widths, we add in 0.5 to the appropriate x/y so that the float arithmetic - // works out. For example, with a border width of 3, WebKit will pass us (y1+y2)/2, e.g., - // (50+53)/2 = 103/2 = 51 when we want 51.5. It is always true that an even width gave - // us a perfect position, but an odd width gave us a position that is off by exactly 0.5. - if (penStyle == DottedStroke || penStyle == DashedStroke) { - if (p1.x() == p2.x()) { - p1.setY(p1.y() + strokeWidth); - p2.setY(p2.y() - strokeWidth); - } else { - p1.setX(p1.x() + strokeWidth); - p2.setX(p2.x() - strokeWidth); - } - } - - if (static_cast(strokeWidth) % 2) { //odd - if (p1.x() == p2.x()) { - // We're a vertical line. Adjust our x. - p1.setX(p1.x() + 0.5f); - p2.setX(p2.x() + 0.5f); - } else { - // We're a horizontal line. Adjust our y. - p1.setY(p1.y() + 0.5f); - p2.setY(p2.y() + 0.5f); - } +void GraphicsContext::adjustLineToPixelBoundaries(FloatPoint& p1, + FloatPoint& p2, + float strokeWidth, + StrokeStyle penStyle) { + // For odd widths, we add in 0.5 to the appropriate x/y so that the float + // arithmetic works out. For example, with a border width of 3, WebKit will + // pass us (y1+y2)/2, e.g., (50+53)/2 = 103/2 = 51 when we want 51.5. It is + // always true that an even width gave us a perfect position, but an odd width + // gave us a position that is off by exactly 0.5. + if (penStyle == DottedStroke || penStyle == DashedStroke) { + if (p1.x() == p2.x()) { + p1.setY(p1.y() + strokeWidth); + p2.setY(p2.y() - strokeWidth); + } else { + p1.setX(p1.x() + strokeWidth); + p2.setX(p2.x() - strokeWidth); } -} + } -void GraphicsContext::setPathFromConvexPoints(SkPath* path, size_t numPoints, const FloatPoint* points) -{ - path->incReserve(numPoints); - path->moveTo(WebCoreFloatToSkScalar(points[0].x()), - WebCoreFloatToSkScalar(points[0].y())); - for (size_t i = 1; i < numPoints; ++i) { - path->lineTo(WebCoreFloatToSkScalar(points[i].x()), - WebCoreFloatToSkScalar(points[i].y())); + if (static_cast(strokeWidth) % 2) { // odd + if (p1.x() == p2.x()) { + // We're a vertical line. Adjust our x. + p1.setX(p1.x() + 0.5f); + p2.setX(p2.x() + 0.5f); + } else { + // We're a horizontal line. Adjust our y. + p1.setY(p1.y() + 0.5f); + p2.setY(p2.y() + 0.5f); } - - /* The code used to just blindly call this - path->setIsConvex(true); - But webkit can sometimes send us non-convex 4-point values, so we mark the path's - convexity as unknown, so it will get computed by skia at draw time. - See crbug.com 108605 - */ - SkPath::Convexity convexity = SkPath::kConvex_Convexity; - if (numPoints == 4) - convexity = SkPath::kUnknown_Convexity; - path->setConvexity(convexity); -} - -void GraphicsContext::setRadii(SkVector* radii, IntSize topLeft, IntSize topRight, IntSize bottomRight, IntSize bottomLeft) -{ - radii[SkRRect::kUpperLeft_Corner].set(SkIntToScalar(topLeft.width()), - SkIntToScalar(topLeft.height())); - radii[SkRRect::kUpperRight_Corner].set(SkIntToScalar(topRight.width()), - SkIntToScalar(topRight.height())); - radii[SkRRect::kLowerRight_Corner].set(SkIntToScalar(bottomRight.width()), - SkIntToScalar(bottomRight.height())); - radii[SkRRect::kLowerLeft_Corner].set(SkIntToScalar(bottomLeft.width()), - SkIntToScalar(bottomLeft.height())); -} - -void GraphicsContext::draw2xMarker(SkBitmap* bitmap, int index) -{ - const SkPMColor lineColor = lineColors(index); - const SkPMColor antiColor1 = antiColors1(index); - const SkPMColor antiColor2 = antiColors2(index); - - uint32_t* row1 = bitmap->getAddr32(0, 0); - uint32_t* row2 = bitmap->getAddr32(0, 1); - uint32_t* row3 = bitmap->getAddr32(0, 2); - uint32_t* row4 = bitmap->getAddr32(0, 3); - - // Pattern: X0o o0X0o o0 - // XX0o o0XXX0o o0X - // o0XXX0o o0XXX0o - // o0X0o o0X0o - const SkPMColor row1Color[] = { lineColor, antiColor1, antiColor2, 0, 0, 0, antiColor2, antiColor1 }; - const SkPMColor row2Color[] = { lineColor, lineColor, antiColor1, antiColor2, 0, antiColor2, antiColor1, lineColor }; - const SkPMColor row3Color[] = { 0, antiColor2, antiColor1, lineColor, lineColor, lineColor, antiColor1, antiColor2 }; - const SkPMColor row4Color[] = { 0, 0, antiColor2, antiColor1, lineColor, antiColor1, antiColor2, 0 }; - - for (int x = 0; x < bitmap->width() + 8; x += 8) { - int count = std::min(bitmap->width() - x, 8); - if (count > 0) { - memcpy(row1 + x, row1Color, count * sizeof(SkPMColor)); - memcpy(row2 + x, row2Color, count * sizeof(SkPMColor)); - memcpy(row3 + x, row3Color, count * sizeof(SkPMColor)); - memcpy(row4 + x, row4Color, count * sizeof(SkPMColor)); - } + } +} + +void GraphicsContext::setPathFromConvexPoints(SkPath* path, + size_t numPoints, + const FloatPoint* points) { + path->incReserve(numPoints); + path->moveTo(WebCoreFloatToSkScalar(points[0].x()), + WebCoreFloatToSkScalar(points[0].y())); + for (size_t i = 1; i < numPoints; ++i) { + path->lineTo(WebCoreFloatToSkScalar(points[i].x()), + WebCoreFloatToSkScalar(points[i].y())); + } + + /* The code used to just blindly call this + path->setIsConvex(true); + But webkit can sometimes send us non-convex 4-point values, so we mark the + path's convexity as unknown, so it will get computed by skia at draw time. + See crbug.com 108605 + */ + SkPath::Convexity convexity = SkPath::kConvex_Convexity; + if (numPoints == 4) + convexity = SkPath::kUnknown_Convexity; + path->setConvexity(convexity); +} + +void GraphicsContext::setRadii(SkVector* radii, + IntSize topLeft, + IntSize topRight, + IntSize bottomRight, + IntSize bottomLeft) { + radii[SkRRect::kUpperLeft_Corner].set(SkIntToScalar(topLeft.width()), + SkIntToScalar(topLeft.height())); + radii[SkRRect::kUpperRight_Corner].set(SkIntToScalar(topRight.width()), + SkIntToScalar(topRight.height())); + radii[SkRRect::kLowerRight_Corner].set(SkIntToScalar(bottomRight.width()), + SkIntToScalar(bottomRight.height())); + radii[SkRRect::kLowerLeft_Corner].set(SkIntToScalar(bottomLeft.width()), + SkIntToScalar(bottomLeft.height())); +} + +void GraphicsContext::draw2xMarker(SkBitmap* bitmap, int index) { + const SkPMColor lineColor = lineColors(index); + const SkPMColor antiColor1 = antiColors1(index); + const SkPMColor antiColor2 = antiColors2(index); + + uint32_t* row1 = bitmap->getAddr32(0, 0); + uint32_t* row2 = bitmap->getAddr32(0, 1); + uint32_t* row3 = bitmap->getAddr32(0, 2); + uint32_t* row4 = bitmap->getAddr32(0, 3); + + // Pattern: X0o o0X0o o0 + // XX0o o0XXX0o o0X + // o0XXX0o o0XXX0o + // o0X0o o0X0o + const SkPMColor row1Color[] = {lineColor, antiColor1, antiColor2, 0, + 0, 0, antiColor2, antiColor1}; + const SkPMColor row2Color[] = {lineColor, lineColor, antiColor1, antiColor2, + 0, antiColor2, antiColor1, lineColor}; + const SkPMColor row3Color[] = {0, antiColor2, antiColor1, lineColor, + lineColor, lineColor, antiColor1, antiColor2}; + const SkPMColor row4Color[] = {0, 0, antiColor2, antiColor1, + lineColor, antiColor1, antiColor2, 0}; + + for (int x = 0; x < bitmap->width() + 8; x += 8) { + int count = std::min(bitmap->width() - x, 8); + if (count > 0) { + memcpy(row1 + x, row1Color, count * sizeof(SkPMColor)); + memcpy(row2 + x, row2Color, count * sizeof(SkPMColor)); + memcpy(row3 + x, row3Color, count * sizeof(SkPMColor)); + memcpy(row4 + x, row4Color, count * sizeof(SkPMColor)); } -} - -void GraphicsContext::draw1xMarker(SkBitmap* bitmap, int index) -{ - const uint32_t lineColor = lineColors(index); - const uint32_t antiColor = antiColors2(index); - - // Pattern: X o o X o o X - // o X o o X o - uint32_t* row1 = bitmap->getAddr32(0, 0); - uint32_t* row2 = bitmap->getAddr32(0, 1); - for (int x = 0; x < bitmap->width(); x++) { - switch (x % 4) { - case 0: - row1[x] = lineColor; - break; - case 1: - row1[x] = antiColor; - row2[x] = antiColor; - break; - case 2: - row2[x] = lineColor; - break; - case 3: - row1[x] = antiColor; - row2[x] = antiColor; - break; - } + } +} + +void GraphicsContext::draw1xMarker(SkBitmap* bitmap, int index) { + const uint32_t lineColor = lineColors(index); + const uint32_t antiColor = antiColors2(index); + + // Pattern: X o o X o o X + // o X o o X o + uint32_t* row1 = bitmap->getAddr32(0, 0); + uint32_t* row2 = bitmap->getAddr32(0, 1); + for (int x = 0; x < bitmap->width(); x++) { + switch (x % 4) { + case 0: + row1[x] = lineColor; + break; + case 1: + row1[x] = antiColor; + row2[x] = antiColor; + break; + case 2: + row2[x] = lineColor; + break; + case 3: + row1[x] = antiColor; + row2[x] = antiColor; + break; } + } } -SkPMColor GraphicsContext::lineColors(int index) -{ - static const SkPMColor colors[] = { - SkPreMultiplyARGB(0xFF, 0xFF, 0x00, 0x00), // Opaque red. - SkPreMultiplyARGB(0xFF, 0xC0, 0xC0, 0xC0) // Opaque gray. - }; +SkPMColor GraphicsContext::lineColors(int index) { + static const SkPMColor colors[] = { + SkPreMultiplyARGB(0xFF, 0xFF, 0x00, 0x00), // Opaque red. + SkPreMultiplyARGB(0xFF, 0xC0, 0xC0, 0xC0) // Opaque gray. + }; - return colors[index]; + return colors[index]; } -SkPMColor GraphicsContext::antiColors1(int index) -{ - static const SkPMColor colors[] = { - SkPreMultiplyARGB(0xB0, 0xFF, 0x00, 0x00), // Semitransparent red. - SkPreMultiplyARGB(0xB0, 0xC0, 0xC0, 0xC0) // Semitransparent gray. - }; +SkPMColor GraphicsContext::antiColors1(int index) { + static const SkPMColor colors[] = { + SkPreMultiplyARGB(0xB0, 0xFF, 0x00, 0x00), // Semitransparent red. + SkPreMultiplyARGB(0xB0, 0xC0, 0xC0, 0xC0) // Semitransparent gray. + }; - return colors[index]; + return colors[index]; } -SkPMColor GraphicsContext::antiColors2(int index) -{ - static const SkPMColor colors[] = { - SkPreMultiplyARGB(0x60, 0xFF, 0x00, 0x00), // More transparent red - SkPreMultiplyARGB(0x60, 0xC0, 0xC0, 0xC0) // More transparent gray - }; +SkPMColor GraphicsContext::antiColors2(int index) { + static const SkPMColor colors[] = { + SkPreMultiplyARGB(0x60, 0xFF, 0x00, 0x00), // More transparent red + SkPreMultiplyARGB(0x60, 0xC0, 0xC0, 0xC0) // More transparent gray + }; - return colors[index]; + return colors[index]; } -void GraphicsContext::didDrawTextInRect(const SkRect& textRect) -{ - if (m_trackTextRegion) - m_textRegion.join(textRect); +void GraphicsContext::didDrawTextInRect(const SkRect& textRect) { + if (m_trackTextRegion) + m_textRegion.join(textRect); } void GraphicsContext::preparePaintForDrawRectToRect( @@ -1463,40 +1550,42 @@ void GraphicsContext::preparePaintForDrawRectToRect( CompositeOperator compositeOp, WebBlendMode blendMode, bool isLazyDecoded, - bool isDataComplete) const -{ - paint->setBlendMode(WebCoreCompositeToSkiaComposite(compositeOp, blendMode)); - paint->setColorFilter(sk_ref_sp(this->colorFilter())); - paint->setAlpha(this->getNormalizedAlpha()); - paint->setLooper(this->drawLooper()); - paint->setAntiAlias(shouldDrawAntiAliased(this, destRect)); - - InterpolationQuality resampling; - if (this->isAccelerated()) { - resampling = InterpolationLow; - } else if (isLazyDecoded) { - resampling = InterpolationHigh; - } else { - // Take into account scale applied to the canvas when computing sampling mode (e.g. CSS scale or page scale). - SkRect destRectTarget = destRect; - SkMatrix totalMatrix = this->getTotalMatrix(); - if (!(totalMatrix.getType() & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask))) - totalMatrix.mapRect(&destRectTarget, destRect); - - resampling = computeInterpolationQuality(totalMatrix, - SkScalarToFloat(srcRect.width()), SkScalarToFloat(srcRect.height()), - SkScalarToFloat(destRectTarget.width()), SkScalarToFloat(destRectTarget.height()), - isDataComplete); - } - - if (resampling == InterpolationNone) { - // FIXME: This is to not break tests (it results in the filter bitmap flag - // being set to true). We need to decide if we respect InterpolationNone - // being returned from computeInterpolationQuality. - resampling = InterpolationLow; - } - resampling = limitInterpolationQuality(this, resampling); - paint->setFilterQuality(static_cast(resampling)); -} - -} // namespace blink + bool isDataComplete) const { + paint->setBlendMode(WebCoreCompositeToSkiaComposite(compositeOp, blendMode)); + paint->setColorFilter(sk_ref_sp(this->colorFilter())); + paint->setAlpha(this->getNormalizedAlpha()); + paint->setLooper(this->drawLooper()); + paint->setAntiAlias(shouldDrawAntiAliased(this, destRect)); + + InterpolationQuality resampling; + if (this->isAccelerated()) { + resampling = InterpolationLow; + } else if (isLazyDecoded) { + resampling = InterpolationHigh; + } else { + // Take into account scale applied to the canvas when computing sampling + // mode (e.g. CSS scale or page scale). + SkRect destRectTarget = destRect; + SkMatrix totalMatrix = this->getTotalMatrix(); + if (!(totalMatrix.getType() & + (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask))) + totalMatrix.mapRect(&destRectTarget, destRect); + + resampling = computeInterpolationQuality( + totalMatrix, SkScalarToFloat(srcRect.width()), + SkScalarToFloat(srcRect.height()), + SkScalarToFloat(destRectTarget.width()), + SkScalarToFloat(destRectTarget.height()), isDataComplete); + } + + if (resampling == InterpolationNone) { + // FIXME: This is to not break tests (it results in the filter bitmap flag + // being set to true). We need to decide if we respect InterpolationNone + // being returned from computeInterpolationQuality. + resampling = InterpolationLow; + } + resampling = limitInterpolationQuality(this, resampling); + paint->setFilterQuality(static_cast(resampling)); +} + +} // namespace blink diff --git a/sky/engine/platform/graphics/GraphicsContext.h b/sky/engine/platform/graphics/GraphicsContext.h index dcf995f016291..dd7350d737abd 100644 --- a/sky/engine/platform/graphics/GraphicsContext.h +++ b/sky/engine/platform/graphics/GraphicsContext.h @@ -55,428 +55,579 @@ namespace blink { class KURL; class PLATFORM_EXPORT GraphicsContext { - WTF_MAKE_NONCOPYABLE(GraphicsContext); WTF_MAKE_FAST_ALLOCATED; -public: - enum AntiAliasingMode { - NotAntiAliased, - AntiAliased - }; - enum AccessMode { - ReadOnly, - ReadWrite - }; - - enum DisabledMode { - NothingDisabled = 0, // Run as normal. - FullyDisabled = 1 // Do absolutely minimal work to remove the cost of the context from performance tests. - }; - - explicit GraphicsContext(SkCanvas*, DisabledMode = NothingDisabled); - ~GraphicsContext(); - - // Returns the canvas used for painting. Must not be called if painting is disabled. - // Accessing the backing canvas this way flushes all queued save ops, - // so it should be avoided. Use the corresponding draw/matrix/clip methods instead. - SkCanvas* canvas() - { - // Flush any pending saves. - realizeCanvasSave(); - - return m_canvas; - } - const SkCanvas* canvas() const - { - return m_canvas; - } - - void resetCanvas(SkCanvas*); - - bool contextDisabled() const { return m_disabledState; } - - // ---------- State management methods ----------------- - void save(); - void restore(); - unsigned saveCount() { return m_canvasStateStack.size(); } + WTF_MAKE_NONCOPYABLE(GraphicsContext); + WTF_MAKE_FAST_ALLOCATED; + + public: + enum AntiAliasingMode { NotAntiAliased, AntiAliased }; + enum AccessMode { ReadOnly, ReadWrite }; + + enum DisabledMode { + NothingDisabled = 0, // Run as normal. + FullyDisabled = 1 // Do absolutely minimal work to remove the cost of the + // context from performance tests. + }; + + explicit GraphicsContext(SkCanvas*, DisabledMode = NothingDisabled); + ~GraphicsContext(); + + // Returns the canvas used for painting. Must not be called if painting is + // disabled. Accessing the backing canvas this way flushes all queued save + // ops, so it should be avoided. Use the corresponding draw/matrix/clip + // methods instead. + SkCanvas* canvas() { + // Flush any pending saves. + realizeCanvasSave(); + + return m_canvas; + } + const SkCanvas* canvas() const { return m_canvas; } + + void resetCanvas(SkCanvas*); + + bool contextDisabled() const { return m_disabledState; } + + // ---------- State management methods ----------------- + void save(); + void restore(); + unsigned saveCount() { return m_canvasStateStack.size(); } #if ENABLE(ASSERT) - void disableDestructionChecks() { m_disableDestructionChecks = true; } + void disableDestructionChecks() { m_disableDestructionChecks = true; } #endif - void saveLayer(const SkRect* bounds, const SkPaint*); - void restoreLayer(); - - bool hasStroke() const { return strokeStyle() != NoStroke && strokeThickness() > 0; } - - float strokeThickness() const { return immutableState()->strokeData().thickness(); } - void setStrokeThickness(float thickness) { mutableState()->setStrokeThickness(thickness); } - - StrokeStyle strokeStyle() const { return immutableState()->strokeData().style(); } - void setStrokeStyle(StrokeStyle style) { mutableState()->setStrokeStyle(style); } - - Color strokeColor() const { return immutableState()->strokeData().color(); } - void setStrokeColor(const Color& color) { mutableState()->setStrokeColor(color); } - SkColor effectiveStrokeColor() const { return immutableState()->effectiveStrokeColor(); } - - Pattern* strokePattern() const { return immutableState()->strokeData().pattern(); } - void setStrokePattern(PassRefPtr); - - Gradient* strokeGradient() const { return immutableState()->strokeData().gradient(); } - void setStrokeGradient(PassRefPtr); - - void setLineCap(LineCap cap) { mutableState()->setLineCap(cap); } - void setLineDash(const DashArray& dashes, float dashOffset) { mutableState()->setLineDash(dashes, dashOffset); } - void setLineJoin(LineJoin join) { mutableState()->setLineJoin(join); } - void setMiterLimit(float limit) { mutableState()->setMiterLimit(limit); } - - WindRule fillRule() const { return immutableState()->fillRule(); } - void setFillRule(WindRule fillRule) { mutableState()->setFillRule(fillRule); } - - Color fillColor() const { return immutableState()->fillColor(); } - void setFillColor(const Color& color) { mutableState()->setFillColor(color); } - SkColor effectiveFillColor() const { return immutableState()->effectiveFillColor(); } - - void setFillPattern(PassRefPtr); - Pattern* fillPattern() const { return immutableState()->fillPattern(); } - - void setFillGradient(PassRefPtr); - Gradient* fillGradient() const { return immutableState()->fillGradient(); } - - const sk_sp& drawLooper() const { return immutableState()->drawLooper(); } - - bool getTransformedClipBounds(FloatRect* bounds) const; - SkMatrix getTotalMatrix() const; - - void setShouldAntialias(bool antialias) { mutableState()->setShouldAntialias(antialias); } - bool shouldAntialias() const { return immutableState()->shouldAntialias(); } - - // Disable the anti-aliasing optimization for scales/multiple-of-90-degrees - // rotations of thin ("hairline") images. - // Note: This will only be reliable when the device pixel scale/ratio is - // fixed (e.g. when drawing to context backed by an ImageBuffer). - void disableAntialiasingOptimizationForHairlineImages() { m_antialiasHairlineImages = true; } - bool shouldAntialiasHairlineImages() const { return m_antialiasHairlineImages; } - - void setShouldClampToSourceRect(bool clampToSourceRect) { mutableState()->setShouldClampToSourceRect(clampToSourceRect); } - bool shouldClampToSourceRect() const { return immutableState()->shouldClampToSourceRect(); } - - // FIXME: the setter is only used once, at construction time; convert to a constructor param, - // and possibly consolidate with other flags (paintDisabled, isPrinting, ...) - void setShouldSmoothFonts(bool smoothFonts) { m_shouldSmoothFonts = smoothFonts; } - - // Turn off LCD text for the paint if not supported on this context. - void adjustTextRenderMode(SkPaint*); - bool couldUseLCDRenderedText() const { return m_isCertainlyOpaque && m_shouldSmoothFonts; } - - void setTextDrawingMode(TextDrawingModeFlags mode) { mutableState()->setTextDrawingMode(mode); } - TextDrawingModeFlags textDrawingMode() const { return immutableState()->textDrawingMode(); } - - void setAlphaAsFloat(float alpha) { mutableState()->setAlphaAsFloat(alpha);} - int getNormalizedAlpha() const - { - int alpha = immutableState()->alpha(); - return alpha > 255 ? 255 : alpha; - } - - void setImageInterpolationQuality(InterpolationQuality quality) { mutableState()->setInterpolationQuality(quality); } - InterpolationQuality imageInterpolationQuality() const { return immutableState()->interpolationQuality(); } - - void setCompositeOperation(CompositeOperator, WebBlendMode = WebBlendModeNormal); - CompositeOperator compositeOperation() const { return immutableState()->compositeOperator(); } - WebBlendMode blendModeOperation() const { return immutableState()->blendMode(); } - - // Speicy the device scale factor which may change the way document markers - // and fonts are rendered. - void setDeviceScaleFactor(float factor) { m_deviceScaleFactor = factor; } - float deviceScaleFactor() const { return m_deviceScaleFactor; } - - // If true we are (most likely) rendering to a web page and the - // canvas has been prepared with an opaque background. If false, - // the canvas may have transparency (as is the case when rendering - // to a canvas object). - void setCertainlyOpaque(bool isOpaque) { m_isCertainlyOpaque = isOpaque; } - - bool isAccelerated() const { return m_accelerated; } - void setAccelerated(bool accelerated) { m_accelerated = accelerated; } - - // The opaque region is empty until tracking is turned on. - // It is never clerared by the context. - enum RegionTrackingMode { - RegionTrackingDisabled = 0, - RegionTrackingOpaque, - RegionTrackingOverwrite - }; - void setRegionTrackingMode(RegionTrackingMode); - bool regionTrackingEnabled() { return m_regionTrackingMode != RegionTrackingDisabled; } - const RegionTracker& opaqueRegion() const { return m_trackedRegion; } - - // The text region is empty until tracking is turned on. - // It is never clerared by the context. - void setTrackTextRegion(bool track) { m_trackTextRegion = track; } - const SkRect& textRegion() const { return m_textRegion; } - - SkColorFilter* colorFilter() const; - void setColorFilter(ColorFilterObsolete); - // ---------- End state management methods ----------------- - - // Get the contents of the image buffer - bool readPixels(const SkImageInfo&, void* pixels, size_t rowBytes, int x, int y); - - // Get the current fill style. - const SkPaint& fillPaint() const { return immutableState()->fillPaint(); } - - // Get the current stroke style. - const SkPaint& strokePaint() const { return immutableState()->strokePaint(); } - - // These draw methods will do both stroking and filling. - // FIXME: ...except drawRect(), which fills properly but always strokes - // using a 1-pixel stroke inset from the rect borders (of the correct - // stroke color). - void drawRect(const IntRect&); - void drawLine(const IntPoint&, const IntPoint&); - void drawConvexPolygon(size_t numPoints, const FloatPoint*, bool shouldAntialias = false); - - void fillPath(const Path&); - void strokePath(const Path&); - - void fillEllipse(const FloatRect&); - void strokeEllipse(const FloatRect&); - - void fillRect(const FloatRect&); - void fillRect(const FloatRect&, const Color&); - void fillRect(const FloatRect&, const Color&, CompositeOperator); - void fillRoundedRect(const IntRect&, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, const Color&); - void fillRoundedRect(const RoundedRect&, const Color&); - - void clearRect(const FloatRect&); - - void strokeRect(const FloatRect&); - void strokeRect(const FloatRect&, float lineWidth); - - void fillBetweenRoundedRects(const IntRect&, const IntSize& outerTopLeft, const IntSize& outerTopRight, const IntSize& outerBottomLeft, const IntSize& outerBottomRight, - const IntRect&, const IntSize& innerTopLeft, const IntSize& innerTopRight, const IntSize& innerBottomLeft, const IntSize& innerBottomRight, const Color&); - void fillBetweenRoundedRects(const RoundedRect&, const RoundedRect&, const Color&); - - void drawImage(Image*, const IntPoint&, CompositeOperator = CompositeSourceOver, RespectImageOrientationEnum = DoNotRespectImageOrientation); - void drawImage(Image*, const IntRect&, CompositeOperator = CompositeSourceOver, RespectImageOrientationEnum = DoNotRespectImageOrientation); - void drawImage(Image*, const FloatRect& destRect); - void drawImage(Image*, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator = CompositeSourceOver, RespectImageOrientationEnum = DoNotRespectImageOrientation); - void drawImage(Image*, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator, WebBlendMode, RespectImageOrientationEnum = DoNotRespectImageOrientation); - - void drawTiledImage(Image*, const IntRect& destRect, const IntPoint& srcPoint, const IntSize& tileSize, - CompositeOperator = CompositeSourceOver, WebBlendMode = WebBlendModeNormal, const IntSize& repeatSpacing = IntSize()); - void drawTiledImage(Image*, const IntRect& destRect, const IntRect& srcRect, - const FloatSize& tileScaleFactor, Image::TileRule hRule = Image::StretchTile, Image::TileRule vRule = Image::StretchTile, - CompositeOperator = CompositeSourceOver); - - void drawBitmap(const SkBitmap&, SkScalar, SkScalar, const SkPaint* = 0); - void drawBitmapRect(const SkBitmap&, const SkRect*, const SkRect&, const SkPaint* = 0); - void drawOval(const SkRect&, const SkPaint&); - void drawPath(const SkPath&, const SkPaint&); - // After drawing directly to the context's canvas, use this function to notify the context so - // it can track the opaque region. - // FIXME: this is still needed only because ImageSkia::paintSkBitmap() may need to notify for a - // smaller rect than the one drawn to, due to its clipping logic. - void didDrawRect(const SkRect&, const SkPaint&, const SkBitmap* = 0); - void drawRect(const SkRect&, const SkPaint&); - void drawPosText(const void* text, size_t byteLength, const SkPoint pos[], const SkRect& textRect, const SkPaint&); - void drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[], SkScalar constY, const SkRect& textRect, const SkPaint&); - void drawTextBlob(const SkTextBlob*, const SkPoint& origin, const SkPaint&); - - void clip(const IntRect& rect) { clipRect(rect); } - void clip(const FloatRect& rect) { clipRect(rect); } - void clipRoundedRect(const RoundedRect&, SkClipOp = SkClipOp::kIntersect); - void clipOut(const IntRect& rect) { clipRect(rect, NotAntiAliased, SkClipOp::kDifference); } - void clipOutRoundedRect(const RoundedRect&); - void clipPath(const Path&, WindRule = RULE_EVENODD); - void clipConvexPolygon(size_t numPoints, const FloatPoint*, bool antialias = true); - void clipRect(const SkRect&, AntiAliasingMode = NotAntiAliased, SkClipOp = SkClipOp::kIntersect); - - void drawText(const Font&, const TextRunPaintInfo&, const FloatPoint&); - void drawEmphasisMarks(const Font&, const TextRunPaintInfo&, const AtomicString& mark, const FloatPoint&); - void drawBidiText(const Font&, const TextRunPaintInfo&, const FloatPoint&, Font::CustomFontNotReadyAction = Font::DoNotPaintIfFontNotReady); - void drawHighlightForText(const Font&, const TextRun&, const FloatPoint&, int h, const Color& backgroundColor, int from = 0, int to = -1); - - void drawLineForText(const FloatPoint&, float width); - enum DocumentMarkerLineStyle { - DocumentMarkerSpellingLineStyle, - DocumentMarkerGrammarLineStyle - }; - void drawLineForDocumentMarker(const FloatPoint&, float width, DocumentMarkerLineStyle); - - void beginTransparencyLayer(float opacity, const FloatRect* = 0); - void beginLayer(float opacity, CompositeOperator, const FloatRect* = 0, ColorFilterObsolete = ColorFilterNone, sk_sp = 0); - void endLayer(); - - bool hasShadow() const; - void setShadow(const FloatSize& offset, float blur, const Color&, - DrawLooperBuilder::ShadowTransformMode = DrawLooperBuilder::ShadowRespectsTransforms, - DrawLooperBuilder::ShadowAlphaMode = DrawLooperBuilder::ShadowRespectsAlpha); - void clearShadow() { clearDrawLooper(); } - - // It is assumed that this draw looper is used only for shadows - // (i.e. a draw looper is set if and only if there is a shadow). - // The builder passed into this method will be destroyed. - void setDrawLooper(PassOwnPtr); - void clearDrawLooper(); - - enum Edge { - NoEdge = 0, - TopEdge = 1 << 1, - RightEdge = 1 << 2, - BottomEdge = 1 << 3, - LeftEdge = 1 << 4 - }; - typedef unsigned Edges; - void drawInnerShadow(const RoundedRect&, const Color& shadowColor, const IntSize shadowOffset, int shadowBlur, int shadowSpread, Edges clippedEdges = NoEdge); - - // This clip function is used only by code. It allows - // implementations to handle clipping on the canvas differently since - // the discipline is different. - void canvasClip(const Path&, WindRule = RULE_EVENODD); - void clipOut(const Path&); - - // ---------- Transformation methods ----------------- - // Note that the getCTM method returns only the current transform from Blink's perspective, - // which is not the final transform used to place content on screen. It cannot be relied upon - // for testing where a point will appear on screen or how large it will be. - AffineTransform getCTM() const; - void concatCTM(const AffineTransform& affine) { concat(affineTransformToSkMatrix(affine)); } - void setCTM(const AffineTransform& affine) { setMatrix(affineTransformToSkMatrix(affine)); } - void setMatrix(const SkMatrix&); - - void scale(float x, float y); - void rotate(float angleInRadians); - void translate(float x, float y); - - // This function applies the device scale factor to the context, making the context capable of - // acting as a base-level context for a HiDPI environment. - void applyDeviceScaleFactor(float deviceScaleFactor) { scale(deviceScaleFactor, deviceScaleFactor); } - // ---------- End transformation methods ----------------- - - static void adjustLineToPixelBoundaries(FloatPoint& p1, FloatPoint& p2, float strokeWidth, StrokeStyle); - - void preparePaintForDrawRectToRect( - SkPaint*, - const SkRect& srcRect, - const SkRect& destRect, - CompositeOperator, - WebBlendMode, - bool isLazyDecoded = false, - bool isDataComplete = true) const; - - static int focusRingOutsetExtent(int offset, int width) - { - return focusRingOutset(offset) + (focusRingWidth(width) + 1) / 2; - } - -private: - const GraphicsContextState* immutableState() const { return m_paintState; } - - GraphicsContextState* mutableState() - { - realizePaintSave(); - return m_paintState; + void saveLayer(const SkRect* bounds, const SkPaint*); + void restoreLayer(); + + bool hasStroke() const { + return strokeStyle() != NoStroke && strokeThickness() > 0; + } + + float strokeThickness() const { + return immutableState()->strokeData().thickness(); + } + void setStrokeThickness(float thickness) { + mutableState()->setStrokeThickness(thickness); + } + + StrokeStyle strokeStyle() const { + return immutableState()->strokeData().style(); + } + void setStrokeStyle(StrokeStyle style) { + mutableState()->setStrokeStyle(style); + } + + Color strokeColor() const { return immutableState()->strokeData().color(); } + void setStrokeColor(const Color& color) { + mutableState()->setStrokeColor(color); + } + SkColor effectiveStrokeColor() const { + return immutableState()->effectiveStrokeColor(); + } + + Pattern* strokePattern() const { + return immutableState()->strokeData().pattern(); + } + void setStrokePattern(PassRefPtr); + + Gradient* strokeGradient() const { + return immutableState()->strokeData().gradient(); + } + void setStrokeGradient(PassRefPtr); + + void setLineCap(LineCap cap) { mutableState()->setLineCap(cap); } + void setLineDash(const DashArray& dashes, float dashOffset) { + mutableState()->setLineDash(dashes, dashOffset); + } + void setLineJoin(LineJoin join) { mutableState()->setLineJoin(join); } + void setMiterLimit(float limit) { mutableState()->setMiterLimit(limit); } + + WindRule fillRule() const { return immutableState()->fillRule(); } + void setFillRule(WindRule fillRule) { mutableState()->setFillRule(fillRule); } + + Color fillColor() const { return immutableState()->fillColor(); } + void setFillColor(const Color& color) { mutableState()->setFillColor(color); } + SkColor effectiveFillColor() const { + return immutableState()->effectiveFillColor(); + } + + void setFillPattern(PassRefPtr); + Pattern* fillPattern() const { return immutableState()->fillPattern(); } + + void setFillGradient(PassRefPtr); + Gradient* fillGradient() const { return immutableState()->fillGradient(); } + + const sk_sp& drawLooper() const { + return immutableState()->drawLooper(); + } + + bool getTransformedClipBounds(FloatRect* bounds) const; + SkMatrix getTotalMatrix() const; + + void setShouldAntialias(bool antialias) { + mutableState()->setShouldAntialias(antialias); + } + bool shouldAntialias() const { return immutableState()->shouldAntialias(); } + + // Disable the anti-aliasing optimization for scales/multiple-of-90-degrees + // rotations of thin ("hairline") images. + // Note: This will only be reliable when the device pixel scale/ratio is + // fixed (e.g. when drawing to context backed by an ImageBuffer). + void disableAntialiasingOptimizationForHairlineImages() { + m_antialiasHairlineImages = true; + } + bool shouldAntialiasHairlineImages() const { + return m_antialiasHairlineImages; + } + + void setShouldClampToSourceRect(bool clampToSourceRect) { + mutableState()->setShouldClampToSourceRect(clampToSourceRect); + } + bool shouldClampToSourceRect() const { + return immutableState()->shouldClampToSourceRect(); + } + + // FIXME: the setter is only used once, at construction time; convert to a + // constructor param, and possibly consolidate with other flags + // (paintDisabled, isPrinting, ...) + void setShouldSmoothFonts(bool smoothFonts) { + m_shouldSmoothFonts = smoothFonts; + } + + // Turn off LCD text for the paint if not supported on this context. + void adjustTextRenderMode(SkPaint*); + bool couldUseLCDRenderedText() const { + return m_isCertainlyOpaque && m_shouldSmoothFonts; + } + + void setTextDrawingMode(TextDrawingModeFlags mode) { + mutableState()->setTextDrawingMode(mode); + } + TextDrawingModeFlags textDrawingMode() const { + return immutableState()->textDrawingMode(); + } + + void setAlphaAsFloat(float alpha) { mutableState()->setAlphaAsFloat(alpha); } + int getNormalizedAlpha() const { + int alpha = immutableState()->alpha(); + return alpha > 255 ? 255 : alpha; + } + + void setImageInterpolationQuality(InterpolationQuality quality) { + mutableState()->setInterpolationQuality(quality); + } + InterpolationQuality imageInterpolationQuality() const { + return immutableState()->interpolationQuality(); + } + + void setCompositeOperation(CompositeOperator, + WebBlendMode = WebBlendModeNormal); + CompositeOperator compositeOperation() const { + return immutableState()->compositeOperator(); + } + WebBlendMode blendModeOperation() const { + return immutableState()->blendMode(); + } + + // Speicy the device scale factor which may change the way document markers + // and fonts are rendered. + void setDeviceScaleFactor(float factor) { m_deviceScaleFactor = factor; } + float deviceScaleFactor() const { return m_deviceScaleFactor; } + + // If true we are (most likely) rendering to a web page and the + // canvas has been prepared with an opaque background. If false, + // the canvas may have transparency (as is the case when rendering + // to a canvas object). + void setCertainlyOpaque(bool isOpaque) { m_isCertainlyOpaque = isOpaque; } + + bool isAccelerated() const { return m_accelerated; } + void setAccelerated(bool accelerated) { m_accelerated = accelerated; } + + // The opaque region is empty until tracking is turned on. + // It is never clerared by the context. + enum RegionTrackingMode { + RegionTrackingDisabled = 0, + RegionTrackingOpaque, + RegionTrackingOverwrite + }; + void setRegionTrackingMode(RegionTrackingMode); + bool regionTrackingEnabled() { + return m_regionTrackingMode != RegionTrackingDisabled; + } + const RegionTracker& opaqueRegion() const { return m_trackedRegion; } + + // The text region is empty until tracking is turned on. + // It is never clerared by the context. + void setTrackTextRegion(bool track) { m_trackTextRegion = track; } + const SkRect& textRegion() const { return m_textRegion; } + + SkColorFilter* colorFilter() const; + void setColorFilter(ColorFilterObsolete); + // ---------- End state management methods ----------------- + + // Get the contents of the image buffer + bool readPixels(const SkImageInfo&, + void* pixels, + size_t rowBytes, + int x, + int y); + + // Get the current fill style. + const SkPaint& fillPaint() const { return immutableState()->fillPaint(); } + + // Get the current stroke style. + const SkPaint& strokePaint() const { return immutableState()->strokePaint(); } + + // These draw methods will do both stroking and filling. + // FIXME: ...except drawRect(), which fills properly but always strokes + // using a 1-pixel stroke inset from the rect borders (of the correct + // stroke color). + void drawRect(const IntRect&); + void drawLine(const IntPoint&, const IntPoint&); + void drawConvexPolygon(size_t numPoints, + const FloatPoint*, + bool shouldAntialias = false); + + void fillPath(const Path&); + void strokePath(const Path&); + + void fillEllipse(const FloatRect&); + void strokeEllipse(const FloatRect&); + + void fillRect(const FloatRect&); + void fillRect(const FloatRect&, const Color&); + void fillRect(const FloatRect&, const Color&, CompositeOperator); + void fillRoundedRect(const IntRect&, + const IntSize& topLeft, + const IntSize& topRight, + const IntSize& bottomLeft, + const IntSize& bottomRight, + const Color&); + void fillRoundedRect(const RoundedRect&, const Color&); + + void clearRect(const FloatRect&); + + void strokeRect(const FloatRect&); + void strokeRect(const FloatRect&, float lineWidth); + + void fillBetweenRoundedRects(const IntRect&, + const IntSize& outerTopLeft, + const IntSize& outerTopRight, + const IntSize& outerBottomLeft, + const IntSize& outerBottomRight, + const IntRect&, + const IntSize& innerTopLeft, + const IntSize& innerTopRight, + const IntSize& innerBottomLeft, + const IntSize& innerBottomRight, + const Color&); + void fillBetweenRoundedRects(const RoundedRect&, + const RoundedRect&, + const Color&); + + void drawImage(Image*, + const IntPoint&, + CompositeOperator = CompositeSourceOver, + RespectImageOrientationEnum = DoNotRespectImageOrientation); + void drawImage(Image*, + const IntRect&, + CompositeOperator = CompositeSourceOver, + RespectImageOrientationEnum = DoNotRespectImageOrientation); + void drawImage(Image*, const FloatRect& destRect); + void drawImage(Image*, + const FloatRect& destRect, + const FloatRect& srcRect, + CompositeOperator = CompositeSourceOver, + RespectImageOrientationEnum = DoNotRespectImageOrientation); + void drawImage(Image*, + const FloatRect& destRect, + const FloatRect& srcRect, + CompositeOperator, + WebBlendMode, + RespectImageOrientationEnum = DoNotRespectImageOrientation); + + void drawTiledImage(Image*, + const IntRect& destRect, + const IntPoint& srcPoint, + const IntSize& tileSize, + CompositeOperator = CompositeSourceOver, + WebBlendMode = WebBlendModeNormal, + const IntSize& repeatSpacing = IntSize()); + void drawTiledImage(Image*, + const IntRect& destRect, + const IntRect& srcRect, + const FloatSize& tileScaleFactor, + Image::TileRule hRule = Image::StretchTile, + Image::TileRule vRule = Image::StretchTile, + CompositeOperator = CompositeSourceOver); + + void drawBitmap(const SkBitmap&, SkScalar, SkScalar, const SkPaint* = 0); + void drawBitmapRect(const SkBitmap&, + const SkRect*, + const SkRect&, + const SkPaint* = 0); + void drawOval(const SkRect&, const SkPaint&); + void drawPath(const SkPath&, const SkPaint&); + // After drawing directly to the context's canvas, use this function to notify + // the context so it can track the opaque region. + // FIXME: this is still needed only because ImageSkia::paintSkBitmap() may + // need to notify for a + // smaller rect than the one drawn to, due to its clipping logic. + void didDrawRect(const SkRect&, const SkPaint&, const SkBitmap* = 0); + void drawRect(const SkRect&, const SkPaint&); + void drawPosText(const void* text, + size_t byteLength, + const SkPoint pos[], + const SkRect& textRect, + const SkPaint&); + void drawPosTextH(const void* text, + size_t byteLength, + const SkScalar xpos[], + SkScalar constY, + const SkRect& textRect, + const SkPaint&); + void drawTextBlob(const SkTextBlob*, const SkPoint& origin, const SkPaint&); + + void clip(const IntRect& rect) { clipRect(rect); } + void clip(const FloatRect& rect) { clipRect(rect); } + void clipRoundedRect(const RoundedRect&, SkClipOp = SkClipOp::kIntersect); + void clipOut(const IntRect& rect) { + clipRect(rect, NotAntiAliased, SkClipOp::kDifference); + } + void clipOutRoundedRect(const RoundedRect&); + void clipPath(const Path&, WindRule = RULE_EVENODD); + void clipConvexPolygon(size_t numPoints, + const FloatPoint*, + bool antialias = true); + void clipRect(const SkRect&, + AntiAliasingMode = NotAntiAliased, + SkClipOp = SkClipOp::kIntersect); + + void drawText(const Font&, const TextRunPaintInfo&, const FloatPoint&); + void drawEmphasisMarks(const Font&, + const TextRunPaintInfo&, + const AtomicString& mark, + const FloatPoint&); + void drawBidiText( + const Font&, + const TextRunPaintInfo&, + const FloatPoint&, + Font::CustomFontNotReadyAction = Font::DoNotPaintIfFontNotReady); + void drawHighlightForText(const Font&, + const TextRun&, + const FloatPoint&, + int h, + const Color& backgroundColor, + int from = 0, + int to = -1); + + void drawLineForText(const FloatPoint&, float width); + enum DocumentMarkerLineStyle { + DocumentMarkerSpellingLineStyle, + DocumentMarkerGrammarLineStyle + }; + void drawLineForDocumentMarker(const FloatPoint&, + float width, + DocumentMarkerLineStyle); + + void beginTransparencyLayer(float opacity, const FloatRect* = 0); + void beginLayer(float opacity, + CompositeOperator, + const FloatRect* = 0, + ColorFilterObsolete = ColorFilterNone, + sk_sp = 0); + void endLayer(); + + bool hasShadow() const; + void setShadow(const FloatSize& offset, + float blur, + const Color&, + DrawLooperBuilder::ShadowTransformMode = + DrawLooperBuilder::ShadowRespectsTransforms, + DrawLooperBuilder::ShadowAlphaMode = + DrawLooperBuilder::ShadowRespectsAlpha); + void clearShadow() { clearDrawLooper(); } + + // It is assumed that this draw looper is used only for shadows + // (i.e. a draw looper is set if and only if there is a shadow). + // The builder passed into this method will be destroyed. + void setDrawLooper(PassOwnPtr); + void clearDrawLooper(); + + enum Edge { + NoEdge = 0, + TopEdge = 1 << 1, + RightEdge = 1 << 2, + BottomEdge = 1 << 3, + LeftEdge = 1 << 4 + }; + typedef unsigned Edges; + void drawInnerShadow(const RoundedRect&, + const Color& shadowColor, + const IntSize shadowOffset, + int shadowBlur, + int shadowSpread, + Edges clippedEdges = NoEdge); + + // This clip function is used only by code. It allows + // implementations to handle clipping on the canvas differently since + // the discipline is different. + void canvasClip(const Path&, WindRule = RULE_EVENODD); + void clipOut(const Path&); + + // ---------- Transformation methods ----------------- + // Note that the getCTM method returns only the current transform from Blink's + // perspective, which is not the final transform used to place content on + // screen. It cannot be relied upon for testing where a point will appear on + // screen or how large it will be. + AffineTransform getCTM() const; + void concatCTM(const AffineTransform& affine) { + concat(affineTransformToSkMatrix(affine)); + } + void setCTM(const AffineTransform& affine) { + setMatrix(affineTransformToSkMatrix(affine)); + } + void setMatrix(const SkMatrix&); + + void scale(float x, float y); + void rotate(float angleInRadians); + void translate(float x, float y); + + // This function applies the device scale factor to the context, making the + // context capable of acting as a base-level context for a HiDPI environment. + void applyDeviceScaleFactor(float deviceScaleFactor) { + scale(deviceScaleFactor, deviceScaleFactor); + } + // ---------- End transformation methods ----------------- + + static void adjustLineToPixelBoundaries(FloatPoint& p1, + FloatPoint& p2, + float strokeWidth, + StrokeStyle); + + void preparePaintForDrawRectToRect(SkPaint*, + const SkRect& srcRect, + const SkRect& destRect, + CompositeOperator, + WebBlendMode, + bool isLazyDecoded = false, + bool isDataComplete = true) const; + + static int focusRingOutsetExtent(int offset, int width) { + return focusRingOutset(offset) + (focusRingWidth(width) + 1) / 2; + } + + private: + const GraphicsContextState* immutableState() const { return m_paintState; } + + GraphicsContextState* mutableState() { + realizePaintSave(); + return m_paintState; + } + + static void setPathFromConvexPoints(SkPath*, size_t, const FloatPoint*); + static void setRadii(SkVector*, IntSize, IntSize, IntSize, IntSize); + + static inline int focusRingOutset(int offset) { return 0; } + static inline int focusRingWidth(int width) { return 1; } + static SkPMColor lineColors(int); + static SkPMColor antiColors1(int); + static SkPMColor antiColors2(int); + static void draw1xMarker(SkBitmap*, int); + static void draw2xMarker(SkBitmap*, int); + + // Helpers for drawing a focus ring (drawFocusRing) + float prepareFocusRingPaint(SkPaint&, const Color&, int width) const; + void drawFocusRingPath(const SkPath&, const Color&, int width); + void drawFocusRingRect(const SkRect&, const Color&, int width); + + // SkCanvas wrappers. + void clipPath(const SkPath&, + AntiAliasingMode = NotAntiAliased, + SkClipOp = SkClipOp::kIntersect); + void clipRRect(const SkRRect&, + AntiAliasingMode = NotAntiAliased, + SkClipOp = SkClipOp::kIntersect); + void concat(const SkMatrix&); + void drawRRect(const SkRRect&, const SkPaint&); + + // Apply deferred paint state saves + void realizePaintSave() { + if (contextDisabled()) + return; + + if (m_paintState->saveCount()) { + m_paintState->decrementSaveCount(); + ++m_paintStateIndex; + if (m_paintStateStack.size() == m_paintStateIndex) { + m_paintStateStack.append( + GraphicsContextState::createAndCopy(*m_paintState)); + m_paintState = m_paintStateStack[m_paintStateIndex].get(); + } else { + GraphicsContextState* priorPaintState = m_paintState; + m_paintState = m_paintStateStack[m_paintStateIndex].get(); + m_paintState->copy(*priorPaintState); + } } - - static void setPathFromConvexPoints(SkPath*, size_t, const FloatPoint*); - static void setRadii(SkVector*, IntSize, IntSize, IntSize, IntSize); - - static inline int focusRingOutset(int offset) { return 0; } - static inline int focusRingWidth(int width) { return 1; } - static SkPMColor lineColors(int); - static SkPMColor antiColors1(int); - static SkPMColor antiColors2(int); - static void draw1xMarker(SkBitmap*, int); - static void draw2xMarker(SkBitmap*, int); - - // Helpers for drawing a focus ring (drawFocusRing) - float prepareFocusRingPaint(SkPaint&, const Color&, int width) const; - void drawFocusRingPath(const SkPath&, const Color&, int width); - void drawFocusRingRect(const SkRect&, const Color&, int width); - - - // SkCanvas wrappers. - void clipPath(const SkPath&, AntiAliasingMode = NotAntiAliased, SkClipOp = SkClipOp::kIntersect); - void clipRRect(const SkRRect&, AntiAliasingMode = NotAntiAliased, SkClipOp = SkClipOp::kIntersect); - void concat(const SkMatrix&); - void drawRRect(const SkRRect&, const SkPaint&); - - // Apply deferred paint state saves - void realizePaintSave() - { - if (contextDisabled()) - return; - - if (m_paintState->saveCount()) { - m_paintState->decrementSaveCount(); - ++m_paintStateIndex; - if (m_paintStateStack.size() == m_paintStateIndex) { - m_paintStateStack.append(GraphicsContextState::createAndCopy(*m_paintState)); - m_paintState = m_paintStateStack[m_paintStateIndex].get(); - } else { - GraphicsContextState* priorPaintState = m_paintState; - m_paintState = m_paintStateStack[m_paintStateIndex].get(); - m_paintState->copy(*priorPaintState); - } - } - } - - // Apply deferred canvas state saves - void realizeCanvasSave() - { - if (!m_pendingCanvasSave || contextDisabled()) - return; - - m_canvas->save(); - m_pendingCanvasSave = false; - } - - void didDrawTextInRect(const SkRect& textRect); - - void fillRectWithRoundedHole(const IntRect&, const RoundedRect& roundedHoleRect, const Color&); - - // null indicates painting is contextDisabled. Never delete this object. - SkCanvas* m_canvas; - - // Paint states stack. Enables local drawing state change with save()/restore() calls. - // This state controls the appearance of drawn content. - // We do not delete from this stack to avoid memory churn. - Vector > m_paintStateStack; - // Current index on the stack. May not be the last thing on the stack. - unsigned m_paintStateIndex; - // Raw pointer to the current state. - GraphicsContextState* m_paintState; - - // Currently pending save flags for Skia Canvas state. - // Canvas state includes the canavs, it's matrix and clips. Think of it as _where_ - // the draw operations will happen. - struct CanvasSaveState; - Vector m_canvasStateStack; - bool m_pendingCanvasSave; + } + + // Apply deferred canvas state saves + void realizeCanvasSave() { + if (!m_pendingCanvasSave || contextDisabled()) + return; + + m_canvas->save(); + m_pendingCanvasSave = false; + } + + void didDrawTextInRect(const SkRect& textRect); + + void fillRectWithRoundedHole(const IntRect&, + const RoundedRect& roundedHoleRect, + const Color&); + + // null indicates painting is contextDisabled. Never delete this object. + SkCanvas* m_canvas; + + // Paint states stack. Enables local drawing state change with + // save()/restore() calls. This state controls the appearance of drawn + // content. We do not delete from this stack to avoid memory churn. + Vector> m_paintStateStack; + // Current index on the stack. May not be the last thing on the stack. + unsigned m_paintStateIndex; + // Raw pointer to the current state. + GraphicsContextState* m_paintState; + + // Currently pending save flags for Skia Canvas state. + // Canvas state includes the canavs, it's matrix and clips. Think of it as + // _where_ the draw operations will happen. + struct CanvasSaveState; + Vector m_canvasStateStack; + bool m_pendingCanvasSave; #if ENABLE(ASSERT) - unsigned m_layerCount; - bool m_disableDestructionChecks; + unsigned m_layerCount; + bool m_disableDestructionChecks; #endif - // Tracks the region painted opaque via the GraphicsContext. - RegionTracker m_trackedRegion; + // Tracks the region painted opaque via the GraphicsContext. + RegionTracker m_trackedRegion; - // Tracks the region where text is painted via the GraphicsContext. - SkRect m_textRegion; + // Tracks the region where text is painted via the GraphicsContext. + SkRect m_textRegion; - unsigned m_disabledState; + unsigned m_disabledState; - float m_deviceScaleFactor; + float m_deviceScaleFactor; - // Activation for the above region tracking features - unsigned m_regionTrackingMode : 2; - bool m_trackTextRegion : 1; + // Activation for the above region tracking features + unsigned m_regionTrackingMode : 2; + bool m_trackTextRegion : 1; - bool m_accelerated : 1; - bool m_isCertainlyOpaque : 1; - bool m_antialiasHairlineImages : 1; - bool m_shouldSmoothFonts : 1; + bool m_accelerated : 1; + bool m_isCertainlyOpaque : 1; + bool m_antialiasHairlineImages : 1; + bool m_shouldSmoothFonts : 1; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_GRAPHICS_GRAPHICSCONTEXT_H_ diff --git a/sky/engine/platform/graphics/GraphicsContextState.cpp b/sky/engine/platform/graphics/GraphicsContextState.cpp index f6828b4a78403..66e42d6244390 100644 --- a/sky/engine/platform/graphics/GraphicsContextState.cpp +++ b/sky/engine/platform/graphics/GraphicsContextState.cpp @@ -9,243 +9,226 @@ namespace blink { GraphicsContextState::GraphicsContextState() - : m_fillColor(Color::black) - , m_fillRule(RULE_NONZERO) - , m_textDrawingMode(TextModeFill) - , m_alpha(256) - , m_compositeOperator(CompositeSourceOver) - , m_blendMode(WebBlendModeNormal) - , m_interpolationQuality(InterpolationDefault) - , m_saveCount(0) - , m_shouldAntialias(true) - , m_shouldClampToSourceRect(true) -{ - m_strokePaint.setStyle(SkPaint::kStroke_Style); - m_strokePaint.setStrokeWidth(SkFloatToScalar(m_strokeData.thickness())); - m_strokePaint.setColor(applyAlpha(m_strokeData.color().rgb())); - m_strokePaint.setStrokeCap(SkPaint::kDefault_Cap); - m_strokePaint.setStrokeJoin(SkPaint::kDefault_Join); - m_strokePaint.setStrokeMiter(SkFloatToScalar(m_strokeData.miterLimit())); - m_strokePaint.setFilterQuality(WebCoreInterpolationQualityToSkFilterQuality(m_interpolationQuality)); - m_strokePaint.setAntiAlias(m_shouldAntialias); - m_fillPaint.setColor(applyAlpha(m_fillColor.rgb())); - m_fillPaint.setFilterQuality(WebCoreInterpolationQualityToSkFilterQuality(m_interpolationQuality)); - m_fillPaint.setAntiAlias(m_shouldAntialias); + : m_fillColor(Color::black), + m_fillRule(RULE_NONZERO), + m_textDrawingMode(TextModeFill), + m_alpha(256), + m_compositeOperator(CompositeSourceOver), + m_blendMode(WebBlendModeNormal), + m_interpolationQuality(InterpolationDefault), + m_saveCount(0), + m_shouldAntialias(true), + m_shouldClampToSourceRect(true) { + m_strokePaint.setStyle(SkPaint::kStroke_Style); + m_strokePaint.setStrokeWidth(SkFloatToScalar(m_strokeData.thickness())); + m_strokePaint.setColor(applyAlpha(m_strokeData.color().rgb())); + m_strokePaint.setStrokeCap(SkPaint::kDefault_Cap); + m_strokePaint.setStrokeJoin(SkPaint::kDefault_Join); + m_strokePaint.setStrokeMiter(SkFloatToScalar(m_strokeData.miterLimit())); + m_strokePaint.setFilterQuality( + WebCoreInterpolationQualityToSkFilterQuality(m_interpolationQuality)); + m_strokePaint.setAntiAlias(m_shouldAntialias); + m_fillPaint.setColor(applyAlpha(m_fillColor.rgb())); + m_fillPaint.setFilterQuality( + WebCoreInterpolationQualityToSkFilterQuality(m_interpolationQuality)); + m_fillPaint.setAntiAlias(m_shouldAntialias); } GraphicsContextState::GraphicsContextState(const GraphicsContextState& other) - : m_strokePaint(other.m_strokePaint) - , m_fillPaint(other.m_fillPaint) - , m_strokeData(other.m_strokeData) - , m_fillColor(other.m_fillColor) - , m_fillRule(other.m_fillRule) - , m_fillGradient(other.m_fillGradient) - , m_fillPattern(other.m_fillPattern) - , m_looper(other.m_looper) - , m_textDrawingMode(other.m_textDrawingMode) - , m_alpha(other.m_alpha) - , m_colorFilter(other.m_colorFilter) - , m_compositeOperator(other.m_compositeOperator) - , m_blendMode(other.m_blendMode) - , m_interpolationQuality(other.m_interpolationQuality) - , m_saveCount(0) - , m_shouldAntialias(other.m_shouldAntialias) - , m_shouldClampToSourceRect(other.m_shouldClampToSourceRect) { } - -void GraphicsContextState::copy(const GraphicsContextState& source) -{ - this->~GraphicsContextState(); - new (this) GraphicsContextState(source); -} - -const SkPaint& GraphicsContextState::strokePaint(int strokedPathLength) const -{ - if (m_strokeData.gradient() && m_strokeData.gradient()->shaderChanged()) - m_strokePaint.setShader(m_strokeData.gradient()->shader()); - m_strokeData.setupPaintDashPathEffect(&m_strokePaint, strokedPathLength); - return m_strokePaint; -} - -const SkPaint& GraphicsContextState::fillPaint() const -{ - if (m_fillGradient && m_fillGradient->shaderChanged()) - m_fillPaint.setShader(m_fillGradient->shader()); - return m_fillPaint; -} - -void GraphicsContextState::setStrokeStyle(StrokeStyle style) -{ - m_strokeData.setStyle(style); -} - -void GraphicsContextState::setStrokeThickness(float thickness) -{ - m_strokeData.setThickness(thickness); - m_strokePaint.setStrokeWidth(SkFloatToScalar(thickness)); -} - -void GraphicsContextState::setStrokeColor(const Color& color) -{ - m_strokeData.clearGradient(); - m_strokeData.clearPattern(); - m_strokeData.setColor(color); - m_strokePaint.setColor(applyAlpha(color.rgb())); - m_strokePaint.setShader(0); -} - -void GraphicsContextState::setStrokeGradient(const PassRefPtr gradient) -{ - m_strokeData.setColor(Color::black); - m_strokeData.clearPattern(); - m_strokeData.setGradient(gradient); - m_strokePaint.setColor(applyAlpha(SK_ColorBLACK)); + : m_strokePaint(other.m_strokePaint), + m_fillPaint(other.m_fillPaint), + m_strokeData(other.m_strokeData), + m_fillColor(other.m_fillColor), + m_fillRule(other.m_fillRule), + m_fillGradient(other.m_fillGradient), + m_fillPattern(other.m_fillPattern), + m_looper(other.m_looper), + m_textDrawingMode(other.m_textDrawingMode), + m_alpha(other.m_alpha), + m_colorFilter(other.m_colorFilter), + m_compositeOperator(other.m_compositeOperator), + m_blendMode(other.m_blendMode), + m_interpolationQuality(other.m_interpolationQuality), + m_saveCount(0), + m_shouldAntialias(other.m_shouldAntialias), + m_shouldClampToSourceRect(other.m_shouldClampToSourceRect) {} + +void GraphicsContextState::copy(const GraphicsContextState& source) { + this->~GraphicsContextState(); + new (this) GraphicsContextState(source); +} + +const SkPaint& GraphicsContextState::strokePaint(int strokedPathLength) const { + if (m_strokeData.gradient() && m_strokeData.gradient()->shaderChanged()) m_strokePaint.setShader(m_strokeData.gradient()->shader()); + m_strokeData.setupPaintDashPathEffect(&m_strokePaint, strokedPathLength); + return m_strokePaint; } -void GraphicsContextState::clearStrokeGradient() -{ - m_strokeData.clearGradient(); - ASSERT(!m_strokeData.pattern()); - m_strokePaint.setColor(applyAlpha(m_strokeData.color().rgb())); +const SkPaint& GraphicsContextState::fillPaint() const { + if (m_fillGradient && m_fillGradient->shaderChanged()) + m_fillPaint.setShader(m_fillGradient->shader()); + return m_fillPaint; } -void GraphicsContextState::setStrokePattern(const PassRefPtr pattern) -{ - m_strokeData.setColor(Color::black); - m_strokeData.clearGradient(); - m_strokeData.setPattern(pattern); - m_strokePaint.setColor(applyAlpha(SK_ColorBLACK)); - m_strokePaint.setShader(sk_ref_sp(m_strokeData.pattern()->shader())); +void GraphicsContextState::setStrokeStyle(StrokeStyle style) { + m_strokeData.setStyle(style); } -void GraphicsContextState::clearStrokePattern() -{ - m_strokeData.clearPattern(); - ASSERT(!m_strokeData.gradient()); - m_strokePaint.setColor(applyAlpha(m_strokeData.color().rgb())); +void GraphicsContextState::setStrokeThickness(float thickness) { + m_strokeData.setThickness(thickness); + m_strokePaint.setStrokeWidth(SkFloatToScalar(thickness)); } -void GraphicsContextState::setLineCap(LineCap cap) -{ - m_strokeData.setLineCap(cap); - m_strokePaint.setStrokeCap((SkPaint::Cap)cap); +void GraphicsContextState::setStrokeColor(const Color& color) { + m_strokeData.clearGradient(); + m_strokeData.clearPattern(); + m_strokeData.setColor(color); + m_strokePaint.setColor(applyAlpha(color.rgb())); + m_strokePaint.setShader(0); } -void GraphicsContextState::setLineJoin(LineJoin join) -{ - m_strokeData.setLineJoin(join); - m_strokePaint.setStrokeJoin((SkPaint::Join)join); +void GraphicsContextState::setStrokeGradient( + const PassRefPtr gradient) { + m_strokeData.setColor(Color::black); + m_strokeData.clearPattern(); + m_strokeData.setGradient(gradient); + m_strokePaint.setColor(applyAlpha(SK_ColorBLACK)); + m_strokePaint.setShader(m_strokeData.gradient()->shader()); } -void GraphicsContextState::setMiterLimit(float miterLimit) -{ - m_strokeData.setMiterLimit(miterLimit); - m_strokePaint.setStrokeMiter(SkFloatToScalar(miterLimit)); +void GraphicsContextState::clearStrokeGradient() { + m_strokeData.clearGradient(); + ASSERT(!m_strokeData.pattern()); + m_strokePaint.setColor(applyAlpha(m_strokeData.color().rgb())); } -void GraphicsContextState::setFillColor(const Color& color) -{ - m_fillColor = color; - m_fillGradient.clear(); - m_fillPattern.clear(); - m_fillPaint.setColor(applyAlpha(color.rgb())); - m_fillPaint.setShader(0); +void GraphicsContextState::setStrokePattern(const PassRefPtr pattern) { + m_strokeData.setColor(Color::black); + m_strokeData.clearGradient(); + m_strokeData.setPattern(pattern); + m_strokePaint.setColor(applyAlpha(SK_ColorBLACK)); + m_strokePaint.setShader(sk_ref_sp(m_strokeData.pattern()->shader())); } -void GraphicsContextState::setFillGradient(const PassRefPtr gradient) -{ - m_fillColor = Color::black; - m_fillPattern.clear(); - m_fillGradient = gradient; - m_fillPaint.setColor(applyAlpha(SK_ColorBLACK)); - m_fillPaint.setShader(m_fillGradient->shader()); +void GraphicsContextState::clearStrokePattern() { + m_strokeData.clearPattern(); + ASSERT(!m_strokeData.gradient()); + m_strokePaint.setColor(applyAlpha(m_strokeData.color().rgb())); } -void GraphicsContextState::clearFillGradient() -{ - m_fillGradient.clear(); - ASSERT(!m_fillPattern); - m_fillPaint.setColor(applyAlpha(m_fillColor.rgb())); +void GraphicsContextState::setLineCap(LineCap cap) { + m_strokeData.setLineCap(cap); + m_strokePaint.setStrokeCap((SkPaint::Cap)cap); } -void GraphicsContextState::setFillPattern(const PassRefPtr pattern) -{ - m_fillColor = Color::black; - m_fillGradient.clear(); - m_fillPattern = pattern; - m_fillPaint.setColor(applyAlpha(SK_ColorBLACK)); - m_fillPaint.setShader(sk_ref_sp(m_fillPattern->shader())); +void GraphicsContextState::setLineJoin(LineJoin join) { + m_strokeData.setLineJoin(join); + m_strokePaint.setStrokeJoin((SkPaint::Join)join); } -void GraphicsContextState::clearFillPattern() -{ - m_fillPattern.clear(); - ASSERT(!m_fillGradient); - m_fillPaint.setColor(applyAlpha(m_fillColor.rgb())); +void GraphicsContextState::setMiterLimit(float miterLimit) { + m_strokeData.setMiterLimit(miterLimit); + m_strokePaint.setStrokeMiter(SkFloatToScalar(miterLimit)); } -// Shadow. (This will need tweaking if we use draw loopers for other things.) -void GraphicsContextState::setDrawLooper(sk_sp drawLooper) -{ - m_looper = drawLooper; - m_strokePaint.setLooper(m_looper); - m_fillPaint.setLooper(m_looper); +void GraphicsContextState::setFillColor(const Color& color) { + m_fillColor = color; + m_fillGradient.clear(); + m_fillPattern.clear(); + m_fillPaint.setColor(applyAlpha(color.rgb())); + m_fillPaint.setShader(0); } -void GraphicsContextState::clearDrawLooper() -{ - m_looper.reset(); - m_strokePaint.setLooper(nullptr); - m_fillPaint.setLooper(nullptr); +void GraphicsContextState::setFillGradient( + const PassRefPtr gradient) { + m_fillColor = Color::black; + m_fillPattern.clear(); + m_fillGradient = gradient; + m_fillPaint.setColor(applyAlpha(SK_ColorBLACK)); + m_fillPaint.setShader(m_fillGradient->shader()); } -void GraphicsContextState::setAlphaAsFloat(float alpha) -{ - if (alpha < 0) { - m_alpha = 0; - } else { - m_alpha = roundf(alpha * 256); - if (m_alpha > 256) - m_alpha = 256; - } - m_strokePaint.setColor(applyAlpha(m_strokeData.color().rgb())); - m_fillPaint.setColor(applyAlpha(m_fillColor.rgb())); +void GraphicsContextState::clearFillGradient() { + m_fillGradient.clear(); + ASSERT(!m_fillPattern); + m_fillPaint.setColor(applyAlpha(m_fillColor.rgb())); } -void GraphicsContextState::setLineDash(const DashArray& dashes, float dashOffset) -{ - m_strokeData.setLineDash(dashes, dashOffset); +void GraphicsContextState::setFillPattern(const PassRefPtr pattern) { + m_fillColor = Color::black; + m_fillGradient.clear(); + m_fillPattern = pattern; + m_fillPaint.setColor(applyAlpha(SK_ColorBLACK)); + m_fillPaint.setShader(sk_ref_sp(m_fillPattern->shader())); } -void GraphicsContextState::setColorFilter(sk_sp colorFilter) -{ - m_colorFilter = colorFilter; - m_strokePaint.setColorFilter(m_colorFilter); - m_fillPaint.setColorFilter(m_colorFilter); +void GraphicsContextState::clearFillPattern() { + m_fillPattern.clear(); + ASSERT(!m_fillGradient); + m_fillPaint.setColor(applyAlpha(m_fillColor.rgb())); } -void GraphicsContextState::setCompositeOperation(CompositeOperator compositeOperation, WebBlendMode blendMode) -{ - m_compositeOperator = compositeOperation; - m_blendMode = blendMode; - SkBlendMode skiaBlendMode = WebCoreCompositeToSkiaComposite(compositeOperation, blendMode); - m_strokePaint.setBlendMode(skiaBlendMode); - m_fillPaint.setBlendMode(skiaBlendMode); +// Shadow. (This will need tweaking if we use draw loopers for other things.) +void GraphicsContextState::setDrawLooper(sk_sp drawLooper) { + m_looper = drawLooper; + m_strokePaint.setLooper(m_looper); + m_fillPaint.setLooper(m_looper); } -void GraphicsContextState::setInterpolationQuality(InterpolationQuality quality) -{ - m_interpolationQuality = quality; - m_strokePaint.setFilterQuality(WebCoreInterpolationQualityToSkFilterQuality(quality)); - m_fillPaint.setFilterQuality(WebCoreInterpolationQualityToSkFilterQuality(quality)); +void GraphicsContextState::clearDrawLooper() { + m_looper.reset(); + m_strokePaint.setLooper(nullptr); + m_fillPaint.setLooper(nullptr); } -void GraphicsContextState::setShouldAntialias(bool shouldAntialias) -{ - m_shouldAntialias = shouldAntialias; - m_strokePaint.setAntiAlias(shouldAntialias); - m_fillPaint.setAntiAlias(shouldAntialias); +void GraphicsContextState::setAlphaAsFloat(float alpha) { + if (alpha < 0) { + m_alpha = 0; + } else { + m_alpha = roundf(alpha * 256); + if (m_alpha > 256) + m_alpha = 256; + } + m_strokePaint.setColor(applyAlpha(m_strokeData.color().rgb())); + m_fillPaint.setColor(applyAlpha(m_fillColor.rgb())); } +void GraphicsContextState::setLineDash(const DashArray& dashes, + float dashOffset) { + m_strokeData.setLineDash(dashes, dashOffset); +} + +void GraphicsContextState::setColorFilter(sk_sp colorFilter) { + m_colorFilter = colorFilter; + m_strokePaint.setColorFilter(m_colorFilter); + m_fillPaint.setColorFilter(m_colorFilter); +} + +void GraphicsContextState::setCompositeOperation( + CompositeOperator compositeOperation, + WebBlendMode blendMode) { + m_compositeOperator = compositeOperation; + m_blendMode = blendMode; + SkBlendMode skiaBlendMode = + WebCoreCompositeToSkiaComposite(compositeOperation, blendMode); + m_strokePaint.setBlendMode(skiaBlendMode); + m_fillPaint.setBlendMode(skiaBlendMode); +} + +void GraphicsContextState::setInterpolationQuality( + InterpolationQuality quality) { + m_interpolationQuality = quality; + m_strokePaint.setFilterQuality( + WebCoreInterpolationQualityToSkFilterQuality(quality)); + m_fillPaint.setFilterQuality( + WebCoreInterpolationQualityToSkFilterQuality(quality)); +} + +void GraphicsContextState::setShouldAntialias(bool shouldAntialias) { + m_shouldAntialias = shouldAntialias; + m_strokePaint.setAntiAlias(shouldAntialias); + m_fillPaint.setAntiAlias(shouldAntialias); +} -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/graphics/GraphicsContextState.h b/sky/engine/platform/graphics/GraphicsContextState.h index 570bc4ed6f1d2..4e19e2bfbdd47 100644 --- a/sky/engine/platform/graphics/GraphicsContextState.h +++ b/sky/engine/platform/graphics/GraphicsContextState.h @@ -45,142 +45,149 @@ namespace blink { // Encapsulates the state information we store for each pushed graphics state. // Only GraphicsContext can use this class. class PLATFORM_EXPORT GraphicsContextState final { -public: - static PassOwnPtr create() - { - return adoptPtr(new GraphicsContextState()); - } + public: + static PassOwnPtr create() { + return adoptPtr(new GraphicsContextState()); + } - static PassOwnPtr createAndCopy(const GraphicsContextState& other) - { - return adoptPtr(new GraphicsContextState(other)); - } + static PassOwnPtr createAndCopy( + const GraphicsContextState& other) { + return adoptPtr(new GraphicsContextState(other)); + } - void copy(const GraphicsContextState&); + void copy(const GraphicsContextState&); - // SkPaint objects that reflect the current state. If the length of the - // path to be stroked is known, pass it in for correct dash or dot placement. - const SkPaint& strokePaint(int strokedPathLength = 0) const; - const SkPaint& fillPaint() const; + // SkPaint objects that reflect the current state. If the length of the + // path to be stroked is known, pass it in for correct dash or dot placement. + const SkPaint& strokePaint(int strokedPathLength = 0) const; + const SkPaint& fillPaint() const; - uint16_t saveCount() const { return m_saveCount; } - void incrementSaveCount() { ++m_saveCount; } - void decrementSaveCount() { --m_saveCount; } + uint16_t saveCount() const { return m_saveCount; } + void incrementSaveCount() { ++m_saveCount; } + void decrementSaveCount() { --m_saveCount; } - // Stroke data - const StrokeData& strokeData() const { return m_strokeData; } + // Stroke data + const StrokeData& strokeData() const { return m_strokeData; } - void setStrokeStyle(StrokeStyle); + void setStrokeStyle(StrokeStyle); - void setStrokeThickness(float); + void setStrokeThickness(float); - SkColor effectiveStrokeColor() const { return applyAlpha(m_strokeData.color().rgb()); } - void setStrokeColor(const Color&); + SkColor effectiveStrokeColor() const { + return applyAlpha(m_strokeData.color().rgb()); + } + void setStrokeColor(const Color&); - void setStrokeGradient(const PassRefPtr); - void clearStrokeGradient(); + void setStrokeGradient(const PassRefPtr); + void clearStrokeGradient(); - void setStrokePattern(const PassRefPtr); - void clearStrokePattern(); + void setStrokePattern(const PassRefPtr); + void clearStrokePattern(); - void setLineCap(LineCap); + void setLineCap(LineCap); - void setLineJoin(LineJoin); + void setLineJoin(LineJoin); - void setMiterLimit(float); + void setMiterLimit(float); - void setLineDash(const DashArray&, float); + void setLineDash(const DashArray&, float); - // Fill data - Color fillColor() const { return m_fillColor; } - SkColor effectiveFillColor() const { return applyAlpha(m_fillColor.rgb()); } - void setFillColor(const Color&); + // Fill data + Color fillColor() const { return m_fillColor; } + SkColor effectiveFillColor() const { return applyAlpha(m_fillColor.rgb()); } + void setFillColor(const Color&); - Gradient* fillGradient() const { return m_fillGradient.get(); } - void setFillGradient(const PassRefPtr); - void clearFillGradient(); + Gradient* fillGradient() const { return m_fillGradient.get(); } + void setFillGradient(const PassRefPtr); + void clearFillGradient(); - Pattern* fillPattern() const { return m_fillPattern.get(); } - void setFillPattern(const PassRefPtr); - void clearFillPattern(); + Pattern* fillPattern() const { return m_fillPattern.get(); } + void setFillPattern(const PassRefPtr); + void clearFillPattern(); - // Path fill rule - WindRule fillRule() const { return m_fillRule; } - void setFillRule(WindRule rule) { m_fillRule = rule; } + // Path fill rule + WindRule fillRule() const { return m_fillRule; } + void setFillRule(WindRule rule) { m_fillRule = rule; } - // Shadow. (This will need tweaking if we use draw loopers for other things.) - const sk_sp& drawLooper() const { return m_looper; } - void setDrawLooper(sk_sp); - void clearDrawLooper(); + // Shadow. (This will need tweaking if we use draw loopers for other things.) + const sk_sp& drawLooper() const { return m_looper; } + void setDrawLooper(sk_sp); + void clearDrawLooper(); - // Text. (See TextModeFill & friends.) - TextDrawingModeFlags textDrawingMode() const { return m_textDrawingMode; } - void setTextDrawingMode(TextDrawingModeFlags mode) { m_textDrawingMode = mode; } + // Text. (See TextModeFill & friends.) + TextDrawingModeFlags textDrawingMode() const { return m_textDrawingMode; } + void setTextDrawingMode(TextDrawingModeFlags mode) { + m_textDrawingMode = mode; + } - // Common shader state. - int alpha() const { return m_alpha; } - void setAlphaAsFloat(float); + // Common shader state. + int alpha() const { return m_alpha; } + void setAlphaAsFloat(float); - SkColorFilter* colorFilter() const { return m_colorFilter.get(); } - void setColorFilter(sk_sp); + SkColorFilter* colorFilter() const { return m_colorFilter.get(); } + void setColorFilter(sk_sp); - // Compositing control, for the CSS and Canvas compositing spec. - void setCompositeOperation(CompositeOperator, WebBlendMode); - CompositeOperator compositeOperator() const { return m_compositeOperator; } - WebBlendMode blendMode() const { return m_blendMode; } + // Compositing control, for the CSS and Canvas compositing spec. + void setCompositeOperation(CompositeOperator, WebBlendMode); + CompositeOperator compositeOperator() const { return m_compositeOperator; } + WebBlendMode blendMode() const { return m_blendMode; } - // Image interpolation control. - InterpolationQuality interpolationQuality() const { return m_interpolationQuality; } - void setInterpolationQuality(InterpolationQuality); + // Image interpolation control. + InterpolationQuality interpolationQuality() const { + return m_interpolationQuality; + } + void setInterpolationQuality(InterpolationQuality); - bool shouldAntialias() const { return m_shouldAntialias; } - void setShouldAntialias(bool); + bool shouldAntialias() const { return m_shouldAntialias; } + void setShouldAntialias(bool); - bool shouldClampToSourceRect() const { return m_shouldClampToSourceRect; } - void setShouldClampToSourceRect(bool shouldClampToSourceRect) { m_shouldClampToSourceRect = shouldClampToSourceRect; } + bool shouldClampToSourceRect() const { return m_shouldClampToSourceRect; } + void setShouldClampToSourceRect(bool shouldClampToSourceRect) { + m_shouldClampToSourceRect = shouldClampToSourceRect; + } -private: - GraphicsContextState(); - explicit GraphicsContextState(const GraphicsContextState&); - GraphicsContextState& operator=(const GraphicsContextState&); + private: + GraphicsContextState(); + explicit GraphicsContextState(const GraphicsContextState&); + GraphicsContextState& operator=(const GraphicsContextState&); - // Helper function for applying the state's alpha value to the given input - // color to produce a new output color. - SkColor applyAlpha(SkColor c) const - { - int a = SkAlphaMul(SkColorGetA(c), m_alpha); - return (c & 0x00FFFFFF) | (a << 24); - } + // Helper function for applying the state's alpha value to the given input + // color to produce a new output color. + SkColor applyAlpha(SkColor c) const { + int a = SkAlphaMul(SkColorGetA(c), m_alpha); + return (c & 0x00FFFFFF) | (a << 24); + } - // These are mutbale to enable gradient updates when the paints are fetched for use. - mutable SkPaint m_strokePaint; - mutable SkPaint m_fillPaint; + // These are mutbale to enable gradient updates when the paints are fetched + // for use. + mutable SkPaint m_strokePaint; + mutable SkPaint m_fillPaint; - StrokeData m_strokeData; + StrokeData m_strokeData; - Color m_fillColor; - WindRule m_fillRule; - RefPtr m_fillGradient; - RefPtr m_fillPattern; + Color m_fillColor; + WindRule m_fillRule; + RefPtr m_fillGradient; + RefPtr m_fillPattern; - sk_sp m_looper; + sk_sp m_looper; - TextDrawingModeFlags m_textDrawingMode; + TextDrawingModeFlags m_textDrawingMode; - int m_alpha; - sk_sp m_colorFilter; + int m_alpha; + sk_sp m_colorFilter; - CompositeOperator m_compositeOperator; - WebBlendMode m_blendMode; + CompositeOperator m_compositeOperator; + WebBlendMode m_blendMode; - InterpolationQuality m_interpolationQuality; + InterpolationQuality m_interpolationQuality; - uint16_t m_saveCount; + uint16_t m_saveCount; - bool m_shouldAntialias : 1; - bool m_shouldClampToSourceRect : 1; + bool m_shouldAntialias : 1; + bool m_shouldClampToSourceRect : 1; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_GRAPHICS_GRAPHICSCONTEXTSTATE_H_ diff --git a/sky/engine/platform/graphics/GraphicsContextStateSaver.h b/sky/engine/platform/graphics/GraphicsContextStateSaver.h index 6703363acb0dc..cc1358cc82430 100644 --- a/sky/engine/platform/graphics/GraphicsContextStateSaver.h +++ b/sky/engine/platform/graphics/GraphicsContextStateSaver.h @@ -35,44 +35,41 @@ namespace blink { class PLATFORM_EXPORT GraphicsContextStateSaver { - WTF_MAKE_FAST_ALLOCATED; -public: - GraphicsContextStateSaver(GraphicsContext& context, bool saveAndRestore = true) - : m_context(context) - , m_saveAndRestore(saveAndRestore) - { - if (m_saveAndRestore) - m_context.save(); - } + WTF_MAKE_FAST_ALLOCATED; - ~GraphicsContextStateSaver() - { - if (m_saveAndRestore) - m_context.restore(); - } + public: + GraphicsContextStateSaver(GraphicsContext& context, + bool saveAndRestore = true) + : m_context(context), m_saveAndRestore(saveAndRestore) { + if (m_saveAndRestore) + m_context.save(); + } - void save() - { - ASSERT(!m_saveAndRestore); - m_context.save(); - m_saveAndRestore = true; - } + ~GraphicsContextStateSaver() { + if (m_saveAndRestore) + m_context.restore(); + } - void restore() - { - ASSERT(m_saveAndRestore); - m_context.restore(); - m_saveAndRestore = false; - } + void save() { + ASSERT(!m_saveAndRestore); + m_context.save(); + m_saveAndRestore = true; + } - GraphicsContext* context() const { return &m_context; } - bool saved() const { return m_saveAndRestore; } + void restore() { + ASSERT(m_saveAndRestore); + m_context.restore(); + m_saveAndRestore = false; + } -private: - GraphicsContext& m_context; - bool m_saveAndRestore; + GraphicsContext* context() const { return &m_context; } + bool saved() const { return m_saveAndRestore; } + + private: + GraphicsContext& m_context; + bool m_saveAndRestore; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_GRAPHICS_GRAPHICSCONTEXTSTATESAVER_H_ diff --git a/sky/engine/platform/graphics/GraphicsTypes.cpp b/sky/engine/platform/graphics/GraphicsTypes.cpp index eec7efeb74a19..9717a0d882682 100644 --- a/sky/engine/platform/graphics/GraphicsTypes.cpp +++ b/sky/engine/platform/graphics/GraphicsTypes.cpp @@ -31,195 +31,174 @@ namespace blink { -static const char* const compositeOperatorNames[] = { - "clear", - "copy", - "source-over", - "source-in", - "source-out", - "source-atop", - "destination-over", - "destination-in", - "destination-out", - "destination-atop", - "xor", - "darker", - "lighter" -}; +static const char* const compositeOperatorNames[] = {"clear", + "copy", + "source-over", + "source-in", + "source-out", + "source-atop", + "destination-over", + "destination-in", + "destination-out", + "destination-atop", + "xor", + "darker", + "lighter"}; static const char* const blendOperatorNames[] = { - "multiply", - "screen", - "overlay", - "darken", - "lighten", - "color-dodge", - "color-burn", - "hard-light", - "soft-light", - "difference", - "exclusion", - "hue", - "saturation", - "color", - "luminosity" -}; + "multiply", "screen", "overlay", "darken", "lighten", + "color-dodge", "color-burn", "hard-light", "soft-light", "difference", + "exclusion", "hue", "saturation", "color", "luminosity"}; const int numCompositeOperatorNames = WTF_ARRAY_LENGTH(compositeOperatorNames); const int numBlendOperatorNames = WTF_ARRAY_LENGTH(blendOperatorNames); -bool parseCompositeAndBlendOperator(const String& s, CompositeOperator& op, WebBlendMode& blendOp) -{ - for (int i = 0; i < numCompositeOperatorNames; i++) { - if (s == compositeOperatorNames[i]) { - op = static_cast(i); - blendOp = WebBlendModeNormal; - return true; - } +bool parseCompositeAndBlendOperator(const String& s, + CompositeOperator& op, + WebBlendMode& blendOp) { + for (int i = 0; i < numCompositeOperatorNames; i++) { + if (s == compositeOperatorNames[i]) { + op = static_cast(i); + blendOp = WebBlendModeNormal; + return true; } + } - for (int i = 0; i < numBlendOperatorNames; i++) { - if (s == blendOperatorNames[i]) { - blendOp = static_cast(i+1); - // For now, blending will always assume source-over. This will be fixed in the future - op = CompositeSourceOver; - return true; - } + for (int i = 0; i < numBlendOperatorNames; i++) { + if (s == blendOperatorNames[i]) { + blendOp = static_cast(i + 1); + // For now, blending will always assume source-over. This will be fixed in + // the future + op = CompositeSourceOver; + return true; } + } - return false; + return false; } -// FIXME: when we support blend modes in combination with compositing other than source-over -// this routine needs to be updated. -String compositeOperatorName(CompositeOperator op, WebBlendMode blendOp) -{ - ASSERT(op >= 0); - ASSERT(op < numCompositeOperatorNames); - ASSERT(blendOp >= 0); - ASSERT(blendOp <= numBlendOperatorNames); - if (blendOp != WebBlendModeNormal) - return blendOperatorNames[blendOp-1]; - return compositeOperatorNames[op]; +// FIXME: when we support blend modes in combination with compositing other than +// source-over this routine needs to be updated. +String compositeOperatorName(CompositeOperator op, WebBlendMode blendOp) { + ASSERT(op >= 0); + ASSERT(op < numCompositeOperatorNames); + ASSERT(blendOp >= 0); + ASSERT(blendOp <= numBlendOperatorNames); + if (blendOp != WebBlendModeNormal) + return blendOperatorNames[blendOp - 1]; + return compositeOperatorNames[op]; } -bool parseLineCap(const String& s, LineCap& cap) -{ - if (s == "butt") { - cap = ButtCap; - return true; - } - if (s == "round") { - cap = RoundCap; - return true; - } - if (s == "square") { - cap = SquareCap; - return true; - } - return false; +bool parseLineCap(const String& s, LineCap& cap) { + if (s == "butt") { + cap = ButtCap; + return true; + } + if (s == "round") { + cap = RoundCap; + return true; + } + if (s == "square") { + cap = SquareCap; + return true; + } + return false; } -String lineCapName(LineCap cap) -{ - ASSERT(cap >= 0); - ASSERT(cap < 3); - const char* const names[3] = { "butt", "round", "square" }; - return names[cap]; +String lineCapName(LineCap cap) { + ASSERT(cap >= 0); + ASSERT(cap < 3); + const char* const names[3] = {"butt", "round", "square"}; + return names[cap]; } -bool parseLineJoin(const String& s, LineJoin& join) -{ - if (s == "miter") { - join = MiterJoin; - return true; - } - if (s == "round") { - join = RoundJoin; - return true; - } - if (s == "bevel") { - join = BevelJoin; - return true; - } - return false; +bool parseLineJoin(const String& s, LineJoin& join) { + if (s == "miter") { + join = MiterJoin; + return true; + } + if (s == "round") { + join = RoundJoin; + return true; + } + if (s == "bevel") { + join = BevelJoin; + return true; + } + return false; } -String lineJoinName(LineJoin join) -{ - ASSERT(join >= 0); - ASSERT(join < 3); - const char* const names[3] = { "miter", "round", "bevel" }; - return names[join]; +String lineJoinName(LineJoin join) { + ASSERT(join >= 0); + ASSERT(join < 3); + const char* const names[3] = {"miter", "round", "bevel"}; + return names[join]; } -String textAlignName(TextAlign align) -{ - ASSERT(align >= 0); - ASSERT(align < 5); - const char* const names[5] = { "start", "end", "left", "center", "right" }; - return names[align]; +String textAlignName(TextAlign align) { + ASSERT(align >= 0); + ASSERT(align < 5); + const char* const names[5] = {"start", "end", "left", "center", "right"}; + return names[align]; } -bool parseTextAlign(const String& s, TextAlign& align) -{ - if (s == "start") { - align = StartTextAlign; - return true; - } - if (s == "end") { - align = EndTextAlign; - return true; - } - if (s == "left") { - align = LeftTextAlign; - return true; - } - if (s == "center") { - align = CenterTextAlign; - return true; - } - if (s == "right") { - align = RightTextAlign; - return true; - } - return false; +bool parseTextAlign(const String& s, TextAlign& align) { + if (s == "start") { + align = StartTextAlign; + return true; + } + if (s == "end") { + align = EndTextAlign; + return true; + } + if (s == "left") { + align = LeftTextAlign; + return true; + } + if (s == "center") { + align = CenterTextAlign; + return true; + } + if (s == "right") { + align = RightTextAlign; + return true; + } + return false; } -String textBaselineName(TextBaseline baseline) -{ - ASSERT(baseline >= 0); - ASSERT(baseline < 6); - const char* const names[6] = { "alphabetic", "top", "middle", "bottom", "ideographic", "hanging" }; - return names[baseline]; +String textBaselineName(TextBaseline baseline) { + ASSERT(baseline >= 0); + ASSERT(baseline < 6); + const char* const names[6] = {"alphabetic", "top", "middle", + "bottom", "ideographic", "hanging"}; + return names[baseline]; } -bool parseTextBaseline(const String& s, TextBaseline& baseline) -{ - if (s == "alphabetic") { - baseline = AlphabeticTextBaseline; - return true; - } - if (s == "top") { - baseline = TopTextBaseline; - return true; - } - if (s == "middle") { - baseline = MiddleTextBaseline; - return true; - } - if (s == "bottom") { - baseline = BottomTextBaseline; - return true; - } - if (s == "ideographic") { - baseline = IdeographicTextBaseline; - return true; - } - if (s == "hanging") { - baseline = HangingTextBaseline; - return true; - } - return false; +bool parseTextBaseline(const String& s, TextBaseline& baseline) { + if (s == "alphabetic") { + baseline = AlphabeticTextBaseline; + return true; + } + if (s == "top") { + baseline = TopTextBaseline; + return true; + } + if (s == "middle") { + baseline = MiddleTextBaseline; + return true; + } + if (s == "bottom") { + baseline = BottomTextBaseline; + return true; + } + if (s == "ideographic") { + baseline = IdeographicTextBaseline; + return true; + } + if (s == "hanging") { + baseline = HangingTextBaseline; + return true; + } + return false; } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/graphics/GraphicsTypes.h b/sky/engine/platform/graphics/GraphicsTypes.h index f42da597be910..f42851b50c6ed 100644 --- a/sky/engine/platform/graphics/GraphicsTypes.h +++ b/sky/engine/platform/graphics/GraphicsTypes.h @@ -36,85 +36,100 @@ namespace blink { enum StrokeStyle { - NoStroke, - SolidStroke, - DottedStroke, - DashedStroke, - DoubleStroke, - WavyStroke, + NoStroke, + SolidStroke, + DottedStroke, + DashedStroke, + DoubleStroke, + WavyStroke, }; enum InterpolationQuality { - InterpolationNone = kNone_SkFilterQuality, - InterpolationLow = kLow_SkFilterQuality, - InterpolationMedium = kMedium_SkFilterQuality, - InterpolationHigh = kHigh_SkFilterQuality, + InterpolationNone = kNone_SkFilterQuality, + InterpolationLow = kLow_SkFilterQuality, + InterpolationMedium = kMedium_SkFilterQuality, + InterpolationHigh = kHigh_SkFilterQuality, #if USE(LOW_QUALITY_IMAGE_INTERPOLATION) - InterpolationDefault = InterpolationLow, + InterpolationDefault = InterpolationLow, #else - InterpolationDefault = InterpolationHigh, + InterpolationDefault = InterpolationHigh, #endif }; enum CompositeOperator { - CompositeClear, - CompositeCopy, - CompositeSourceOver, - CompositeSourceIn, - CompositeSourceOut, - CompositeSourceAtop, - CompositeDestinationOver, - CompositeDestinationIn, - CompositeDestinationOut, - CompositeDestinationAtop, - CompositeXOR, - CompositePlusDarker, - CompositePlusLighter, - CompositeDifference + CompositeClear, + CompositeCopy, + CompositeSourceOver, + CompositeSourceIn, + CompositeSourceOut, + CompositeSourceAtop, + CompositeDestinationOver, + CompositeDestinationIn, + CompositeDestinationOut, + CompositeDestinationAtop, + CompositeXOR, + CompositePlusDarker, + CompositePlusLighter, + CompositeDifference }; enum GradientSpreadMethod { - SpreadMethodPad, - SpreadMethodReflect, - SpreadMethodRepeat + SpreadMethodPad, + SpreadMethodReflect, + SpreadMethodRepeat }; enum LineCap { - ButtCap = SkPaint::kButt_Cap, - RoundCap = SkPaint::kRound_Cap, - SquareCap = SkPaint::kSquare_Cap + ButtCap = SkPaint::kButt_Cap, + RoundCap = SkPaint::kRound_Cap, + SquareCap = SkPaint::kSquare_Cap }; enum LineJoin { - MiterJoin = SkPaint::kMiter_Join, - RoundJoin = SkPaint::kRound_Join, - BevelJoin = SkPaint::kBevel_Join + MiterJoin = SkPaint::kMiter_Join, + RoundJoin = SkPaint::kRound_Join, + BevelJoin = SkPaint::kBevel_Join }; enum HorizontalAlignment { AlignLeft, AlignRight, AlignHCenter }; -enum TextBaseline { AlphabeticTextBaseline, TopTextBaseline, MiddleTextBaseline, BottomTextBaseline, IdeographicTextBaseline, HangingTextBaseline }; +enum TextBaseline { + AlphabeticTextBaseline, + TopTextBaseline, + MiddleTextBaseline, + BottomTextBaseline, + IdeographicTextBaseline, + HangingTextBaseline +}; -enum TextAlign { StartTextAlign, EndTextAlign, LeftTextAlign, CenterTextAlign, RightTextAlign }; +enum TextAlign { + StartTextAlign, + EndTextAlign, + LeftTextAlign, + CenterTextAlign, + RightTextAlign +}; enum TextDrawingMode { - TextModeFill = 1 << 0, - TextModeStroke = 1 << 1, + TextModeFill = 1 << 0, + TextModeStroke = 1 << 1, }; typedef unsigned TextDrawingModeFlags; // FIXME(sky): Remove this enum. enum ColorFilterObsolete { - ColorFilterNone, + ColorFilterNone, }; enum WindRule { - RULE_NONZERO = SkPath::kWinding_FillType, - RULE_EVENODD = SkPath::kEvenOdd_FillType + RULE_NONZERO = SkPath::kWinding_FillType, + RULE_EVENODD = SkPath::kEvenOdd_FillType }; PLATFORM_EXPORT String compositeOperatorName(CompositeOperator, WebBlendMode); -PLATFORM_EXPORT bool parseCompositeAndBlendOperator(const String&, CompositeOperator&, WebBlendMode&); +PLATFORM_EXPORT bool parseCompositeAndBlendOperator(const String&, + CompositeOperator&, + WebBlendMode&); PLATFORM_EXPORT String lineCapName(LineCap); PLATFORM_EXPORT bool parseLineCap(const String&, LineCap&); @@ -128,6 +143,6 @@ PLATFORM_EXPORT bool parseTextAlign(const String&, TextAlign&); PLATFORM_EXPORT String textBaselineName(TextBaseline); PLATFORM_EXPORT bool parseTextBaseline(const String&, TextBaseline&); -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_GRAPHICS_GRAPHICSTYPES_H_ diff --git a/sky/engine/platform/graphics/Image.cpp b/sky/engine/platform/graphics/Image.cpp index 0c401d326f437..f1c2c7f80a5a5 100644 --- a/sky/engine/platform/graphics/Image.cpp +++ b/sky/engine/platform/graphics/Image.cpp @@ -41,80 +41,90 @@ namespace blink { -Image::Image(ImageObserver* observer) - : m_imageObserver(observer) -{ -} - -Image::~Image() -{ -} +Image::Image(ImageObserver* observer) : m_imageObserver(observer) {} -bool Image::setData(PassRefPtr data, bool allDataReceived) -{ - m_encodedImageData = data; - if (!m_encodedImageData.get()) - return true; +Image::~Image() {} - int length = m_encodedImageData->size(); - if (!length) - return true; +bool Image::setData(PassRefPtr data, bool allDataReceived) { + m_encodedImageData = data; + if (!m_encodedImageData.get()) + return true; - return dataChanged(allDataReceived); -} - -void Image::fillWithSolidColor(GraphicsContext* ctxt, const FloatRect& dstRect, const Color& color, CompositeOperator op) -{ - if (!color.alpha()) - return; - - CompositeOperator previousOperator = ctxt->compositeOperation(); - ctxt->setCompositeOperation(!color.hasAlpha() && op == CompositeSourceOver ? CompositeCopy : op); - ctxt->fillRect(dstRect, color); - ctxt->setCompositeOperation(previousOperator); -} - -FloatRect Image::adjustForNegativeSize(const FloatRect& rect) -{ - FloatRect norm = rect; - if (norm.width() < 0) { - norm.setX(norm.x() + norm.width()); - norm.setWidth(-norm.width()); - } - if (norm.height() < 0) { - norm.setY(norm.y() + norm.height()); - norm.setHeight(-norm.height()); - } - return norm; -} - -void Image::draw(GraphicsContext* ctx, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator op, WebBlendMode blendMode, RespectImageOrientationEnum) -{ -} - -void Image::drawTiled(GraphicsContext* ctxt, const FloatRect& destRect, const FloatPoint& srcPoint, const FloatSize& scaledTileSize, CompositeOperator op, WebBlendMode blendMode, const IntSize& repeatSpacing) -{ -} + int length = m_encodedImageData->size(); + if (!length) + return true; -// FIXME: Merge with the other drawTiled eventually, since we need a combination of both for some things. -void Image::drawTiled(GraphicsContext* ctxt, const FloatRect& dstRect, const FloatRect& srcRect, - const FloatSize& providedTileScaleFactor, TileRule hRule, TileRule vRule, CompositeOperator op) -{ + return dataChanged(allDataReceived); } -void Image::drawPattern(GraphicsContext* context, const FloatRect& floatSrcRect, const FloatSize& scale, - const FloatPoint& phase, CompositeOperator compositeOp, const FloatRect& destRect, WebBlendMode blendMode, const IntSize& repeatSpacing) -{ +void Image::fillWithSolidColor(GraphicsContext* ctxt, + const FloatRect& dstRect, + const Color& color, + CompositeOperator op) { + if (!color.alpha()) + return; + + CompositeOperator previousOperator = ctxt->compositeOperation(); + ctxt->setCompositeOperation( + !color.hasAlpha() && op == CompositeSourceOver ? CompositeCopy : op); + ctxt->fillRect(dstRect, color); + ctxt->setCompositeOperation(previousOperator); } -void Image::computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio) -{ +FloatRect Image::adjustForNegativeSize(const FloatRect& rect) { + FloatRect norm = rect; + if (norm.width() < 0) { + norm.setX(norm.x() + norm.width()); + norm.setWidth(-norm.width()); + } + if (norm.height() < 0) { + norm.setY(norm.y() + norm.height()); + norm.setHeight(-norm.height()); + } + return norm; } -PassRefPtr Image::imageForDefaultFrame() -{ - RefPtr image(this); - return image.release(); +void Image::draw(GraphicsContext* ctx, + const FloatRect& dstRect, + const FloatRect& srcRect, + CompositeOperator op, + WebBlendMode blendMode, + RespectImageOrientationEnum) {} + +void Image::drawTiled(GraphicsContext* ctxt, + const FloatRect& destRect, + const FloatPoint& srcPoint, + const FloatSize& scaledTileSize, + CompositeOperator op, + WebBlendMode blendMode, + const IntSize& repeatSpacing) {} + +// FIXME: Merge with the other drawTiled eventually, since we need a combination +// of both for some things. +void Image::drawTiled(GraphicsContext* ctxt, + const FloatRect& dstRect, + const FloatRect& srcRect, + const FloatSize& providedTileScaleFactor, + TileRule hRule, + TileRule vRule, + CompositeOperator op) {} + +void Image::drawPattern(GraphicsContext* context, + const FloatRect& floatSrcRect, + const FloatSize& scale, + const FloatPoint& phase, + CompositeOperator compositeOp, + const FloatRect& destRect, + WebBlendMode blendMode, + const IntSize& repeatSpacing) {} + +void Image::computeIntrinsicDimensions(Length& intrinsicWidth, + Length& intrinsicHeight, + FloatSize& intrinsicRatio) {} + +PassRefPtr Image::imageForDefaultFrame() { + RefPtr image(this); + return image.release(); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/graphics/Image.h b/sky/engine/platform/graphics/Image.h index a44a567947f44..e17b1b775e366 100644 --- a/sky/engine/platform/graphics/Image.h +++ b/sky/engine/platform/graphics/Image.h @@ -47,92 +47,131 @@ class GraphicsContext; class Length; class SharedBuffer; -// This class gets notified when an image creates or destroys decoded frames and when it advances animation frames. +// This class gets notified when an image creates or destroys decoded frames and +// when it advances animation frames. class ImageObserver; class PLATFORM_EXPORT Image : public RefCounted { - friend class GeneratedImage; - friend class GradientGeneratedImage; - friend class GraphicsContext; + friend class GeneratedImage; + friend class GradientGeneratedImage; + friend class GraphicsContext; -public: - virtual ~Image(); + public: + virtual ~Image(); - virtual bool currentFrameKnownToBeOpaque() = 0; + virtual bool currentFrameKnownToBeOpaque() = 0; - bool isNull() const { return size().isEmpty(); } + bool isNull() const { return size().isEmpty(); } - virtual void setContainerSize(const IntSize&) { } - virtual bool usesContainerSize() const { return false; } - virtual bool hasRelativeWidth() const { return false; } - virtual bool hasRelativeHeight() const { return false; } - virtual void computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio); + virtual void setContainerSize(const IntSize&) {} + virtual bool usesContainerSize() const { return false; } + virtual bool hasRelativeWidth() const { return false; } + virtual bool hasRelativeHeight() const { return false; } + virtual void computeIntrinsicDimensions(Length& intrinsicWidth, + Length& intrinsicHeight, + FloatSize& intrinsicRatio); - virtual IntSize size() const = 0; - IntRect rect() const { return IntRect(IntPoint(), size()); } - int width() const { return size().width(); } - int height() const { return size().height(); } - virtual bool getHotSpot(IntPoint&) const { return false; } + virtual IntSize size() const = 0; + IntRect rect() const { return IntRect(IntPoint(), size()); } + int width() const { return size().width(); } + int height() const { return size().height(); } + virtual bool getHotSpot(IntPoint&) const { return false; } - bool setData(PassRefPtr data, bool allDataReceived); - virtual bool dataChanged(bool /*allDataReceived*/) { return false; } + bool setData(PassRefPtr data, bool allDataReceived); + virtual bool dataChanged(bool /*allDataReceived*/) { return false; } - virtual String filenameExtension() const { return String(); } // null string if unknown + virtual String filenameExtension() const { + return String(); + } // null string if unknown - virtual void destroyDecodedData(bool destroyAll) = 0; + virtual void destroyDecodedData(bool destroyAll) = 0; - SharedBuffer* data() { return m_encodedImageData.get(); } + SharedBuffer* data() { return m_encodedImageData.get(); } - // Animation begins whenever someone draws the image, so startAnimation() is not normally called. - // It will automatically pause once all observers no longer want to render the image anywhere. - enum CatchUpAnimation { DoNotCatchUp, CatchUp }; - virtual void startAnimation(CatchUpAnimation = CatchUp) { } - virtual void stopAnimation() {} - virtual void resetAnimation() {} + // Animation begins whenever someone draws the image, so startAnimation() is + // not normally called. It will automatically pause once all observers no + // longer want to render the image anywhere. + enum CatchUpAnimation { DoNotCatchUp, CatchUp }; + virtual void startAnimation(CatchUpAnimation = CatchUp) {} + virtual void stopAnimation() {} + virtual void resetAnimation() {} - // True if this image can potentially animate. - virtual bool maybeAnimated() { return false; } + // True if this image can potentially animate. + virtual bool maybeAnimated() { return false; } - // Typically the ImageResource that owns us. - ImageObserver* imageObserver() const { return m_imageObserver; } - void setImageObserver(ImageObserver* observer) { m_imageObserver = observer; } + // Typically the ImageResource that owns us. + ImageObserver* imageObserver() const { return m_imageObserver; } + void setImageObserver(ImageObserver* observer) { m_imageObserver = observer; } - enum TileRule { StretchTile, RoundTile, SpaceTile, RepeatTile }; + enum TileRule { StretchTile, RoundTile, SpaceTile, RepeatTile }; - virtual PassRefPtr imageForDefaultFrame(); + virtual PassRefPtr imageForDefaultFrame(); - virtual void drawPattern(GraphicsContext*, const FloatRect&, - const FloatSize&, const FloatPoint& phase, CompositeOperator, - const FloatRect&, WebBlendMode = WebBlendModeNormal, const IntSize& repeatSpacing = IntSize()); + virtual void drawPattern(GraphicsContext*, + const FloatRect&, + const FloatSize&, + const FloatPoint& phase, + CompositeOperator, + const FloatRect&, + WebBlendMode = WebBlendModeNormal, + const IntSize& repeatSpacing = IntSize()); #if ENABLE(ASSERT) - virtual bool notSolidColor() { return true; } + virtual bool notSolidColor() { return true; } #endif -protected: - Image(ImageObserver* = 0); - - static void fillWithSolidColor(GraphicsContext*, const FloatRect& dstRect, const Color&, CompositeOperator); - static FloatRect adjustForNegativeSize(const FloatRect&); // A helper method for translating negative width and height values. - - virtual void draw(GraphicsContext*, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator, WebBlendMode) = 0; - virtual void draw(GraphicsContext*, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator, WebBlendMode, RespectImageOrientationEnum); - void drawTiled(GraphicsContext*, const FloatRect& dstRect, const FloatPoint& srcPoint, const FloatSize& tileSize, - CompositeOperator, WebBlendMode, const IntSize& repeatSpacing); - void drawTiled(GraphicsContext*, const FloatRect& dstRect, const FloatRect& srcRect, const FloatSize& tileScaleFactor, TileRule hRule, TileRule vRule, CompositeOperator); - - // Supporting tiled drawing - virtual bool mayFillWithSolidColor() { return false; } - virtual Color solidColor() const { return Color(); } - -private: - RefPtr m_encodedImageData; - ImageObserver* m_imageObserver; + protected: + Image(ImageObserver* = 0); + + static void fillWithSolidColor(GraphicsContext*, + const FloatRect& dstRect, + const Color&, + CompositeOperator); + static FloatRect adjustForNegativeSize(const FloatRect&); // A helper method + // for translating + // negative width + // and height + // values. + + virtual void draw(GraphicsContext*, + const FloatRect& dstRect, + const FloatRect& srcRect, + CompositeOperator, + WebBlendMode) = 0; + virtual void draw(GraphicsContext*, + const FloatRect& dstRect, + const FloatRect& srcRect, + CompositeOperator, + WebBlendMode, + RespectImageOrientationEnum); + void drawTiled(GraphicsContext*, + const FloatRect& dstRect, + const FloatPoint& srcPoint, + const FloatSize& tileSize, + CompositeOperator, + WebBlendMode, + const IntSize& repeatSpacing); + void drawTiled(GraphicsContext*, + const FloatRect& dstRect, + const FloatRect& srcRect, + const FloatSize& tileScaleFactor, + TileRule hRule, + TileRule vRule, + CompositeOperator); + + // Supporting tiled drawing + virtual bool mayFillWithSolidColor() { return false; } + virtual Color solidColor() const { return Color(); } + + private: + RefPtr m_encodedImageData; + ImageObserver* m_imageObserver; }; -#define DEFINE_IMAGE_TYPE_CASTS(typeName) \ - DEFINE_TYPE_CASTS(typeName, Image, image, image->is##typeName(), image.is##typeName()) +#define DEFINE_IMAGE_TYPE_CASTS(typeName) \ + DEFINE_TYPE_CASTS(typeName, Image, image, image->is##typeName(), \ + image.is##typeName()) -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_GRAPHICS_IMAGE_H_ diff --git a/sky/engine/platform/graphics/ImageFilter.cpp b/sky/engine/platform/graphics/ImageFilter.cpp index 03d1d8ad25c7f..486b0a206a652 100644 --- a/sky/engine/platform/graphics/ImageFilter.cpp +++ b/sky/engine/platform/graphics/ImageFilter.cpp @@ -8,9 +8,8 @@ namespace blink { -FloatRect mapImageFilterRect(SkImageFilter* filter, const FloatRect& rect) -{ - return filter->computeFastBounds(rect); +FloatRect mapImageFilterRect(SkImageFilter* filter, const FloatRect& rect) { + return filter->computeFastBounds(rect); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/graphics/ImageFilter.h b/sky/engine/platform/graphics/ImageFilter.h index f3ddd6bf1f624..ddc144dd2ffb7 100644 --- a/sky/engine/platform/graphics/ImageFilter.h +++ b/sky/engine/platform/graphics/ImageFilter.h @@ -13,6 +13,6 @@ namespace blink { PLATFORM_EXPORT FloatRect mapImageFilterRect(SkImageFilter*, const FloatRect&); -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_GRAPHICS_IMAGEFILTER_H_ diff --git a/sky/engine/platform/graphics/ImageObserver.cpp b/sky/engine/platform/graphics/ImageObserver.cpp index 4955145ea93f1..0d4ba408c7fdb 100644 --- a/sky/engine/platform/graphics/ImageObserver.cpp +++ b/sky/engine/platform/graphics/ImageObserver.cpp @@ -27,13 +27,10 @@ * */ - #include "flutter/sky/engine/platform/graphics/ImageObserver.h" namespace blink { -ImageObserver::~ImageObserver() -{ -} +ImageObserver::~ImageObserver() {} -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/graphics/ImageObserver.h b/sky/engine/platform/graphics/ImageObserver.h index 85585751a2c7d..05d9ab7f64941 100644 --- a/sky/engine/platform/graphics/ImageObserver.h +++ b/sky/engine/platform/graphics/ImageObserver.h @@ -36,18 +36,19 @@ class IntRect; // Interface for notification about changes to an image, including decoding, // drawing, and animating. class PLATFORM_EXPORT ImageObserver { -protected: - virtual ~ImageObserver(); -public: - virtual void decodedSizeChanged(const Image*, int delta) = 0; - virtual void didDraw(const Image*) = 0; + protected: + virtual ~ImageObserver(); - virtual bool shouldPauseAnimation(const Image*) = 0; - virtual void animationAdvanced(const Image*) = 0; + public: + virtual void decodedSizeChanged(const Image*, int delta) = 0; + virtual void didDraw(const Image*) = 0; - virtual void changedInRect(const Image*, const IntRect&) = 0; + virtual bool shouldPauseAnimation(const Image*) = 0; + virtual void animationAdvanced(const Image*) = 0; + + virtual void changedInRect(const Image*, const IntRect&) = 0; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_GRAPHICS_IMAGEOBSERVER_H_ diff --git a/sky/engine/platform/graphics/ImageOrientation.cpp b/sky/engine/platform/graphics/ImageOrientation.cpp index c4b7f7ed3a767..ebfdaa31ecca3 100644 --- a/sky/engine/platform/graphics/ImageOrientation.cpp +++ b/sky/engine/platform/graphics/ImageOrientation.cpp @@ -30,32 +30,32 @@ namespace blink { -AffineTransform ImageOrientation::transformFromDefault(const FloatSize& drawnSize) const -{ - float w = drawnSize.width(); - float h = drawnSize.height(); +AffineTransform ImageOrientation::transformFromDefault( + const FloatSize& drawnSize) const { + float w = drawnSize.width(); + float h = drawnSize.height(); - switch (m_orientation) { + switch (m_orientation) { case OriginTopLeft: - return AffineTransform(); + return AffineTransform(); case OriginTopRight: - return AffineTransform(-1, 0, 0, 1, w, 0); + return AffineTransform(-1, 0, 0, 1, w, 0); case OriginBottomRight: - return AffineTransform(-1, 0, 0, -1, w, h); + return AffineTransform(-1, 0, 0, -1, w, h); case OriginBottomLeft: - return AffineTransform( 1, 0, 0, -1, 0, h); + return AffineTransform(1, 0, 0, -1, 0, h); case OriginLeftTop: - return AffineTransform( 0, 1, 1, 0, 0, 0); + return AffineTransform(0, 1, 1, 0, 0, 0); case OriginRightTop: - return AffineTransform( 0, 1, -1, 0, w, 0); + return AffineTransform(0, 1, -1, 0, w, 0); case OriginRightBottom: - return AffineTransform( 0, -1, -1, 0, w, h); + return AffineTransform(0, -1, -1, 0, w, h); case OriginLeftBottom: - return AffineTransform( 0, -1, 1, 0, 0, h); - } + return AffineTransform(0, -1, 1, 0, 0, h); + } - ASSERT_NOT_REACHED(); - return AffineTransform(); + ASSERT_NOT_REACHED(); + return AffineTransform(); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/graphics/ImageOrientation.h b/sky/engine/platform/graphics/ImageOrientation.h index 2012bea12a237..4301ecca25563 100644 --- a/sky/engine/platform/graphics/ImageOrientation.h +++ b/sky/engine/platform/graphics/ImageOrientation.h @@ -36,59 +36,61 @@ class FloatSize; // This enum intentionally matches the orientation values from the EXIF spec. // See JEITA CP-3451, page 18. http://www.exif.org/Exif2-2.PDF enum ImageOrientationEnum { - // "TopLeft" means that the 0 row starts at the Top, the 0 column starts at the Left. - OriginTopLeft = 1, // default - OriginTopRight = 2, // mirror along y-axis - OriginBottomRight = 3, // 180 degree rotation - OriginBottomLeft = 4, // mirror along the x-axis - OriginLeftTop = 5, // mirror along x-axis + 270 degree CW rotation - OriginRightTop = 6, // 90 degree CW rotation - OriginRightBottom = 7, // mirror along x-axis + 90 degree CW rotation - OriginLeftBottom = 8, // 270 degree CW rotation - // All other values are "reserved" as of EXIF 2.2 - DefaultImageOrientation = OriginTopLeft, + // "TopLeft" means that the 0 row starts at the Top, the 0 column starts at + // the Left. + OriginTopLeft = 1, // default + OriginTopRight = 2, // mirror along y-axis + OriginBottomRight = 3, // 180 degree rotation + OriginBottomLeft = 4, // mirror along the x-axis + OriginLeftTop = 5, // mirror along x-axis + 270 degree CW rotation + OriginRightTop = 6, // 90 degree CW rotation + OriginRightBottom = 7, // mirror along x-axis + 90 degree CW rotation + OriginLeftBottom = 8, // 270 degree CW rotation + // All other values are "reserved" as of EXIF 2.2 + DefaultImageOrientation = OriginTopLeft, }; enum RespectImageOrientationEnum { - DoNotRespectImageOrientation = 0, - RespectImageOrientation = 1 + DoNotRespectImageOrientation = 0, + RespectImageOrientation = 1 }; class PLATFORM_EXPORT ImageOrientation { -public: - ImageOrientation(ImageOrientationEnum orientation = DefaultImageOrientation) - : m_orientation(orientation) - { - } + public: + ImageOrientation(ImageOrientationEnum orientation = DefaultImageOrientation) + : m_orientation(orientation) {} - bool usesWidthAsHeight() const - { - // Values 5 through 8 all flip the width/height. - return m_orientation >= OriginLeftTop; - } + bool usesWidthAsHeight() const { + // Values 5 through 8 all flip the width/height. + return m_orientation >= OriginLeftTop; + } - // ImageOrientationEnum currently matches EXIF values, however code outside - // this function should never assume that. - static ImageOrientation fromEXIFValue(int exifValue) - { - // Values direct from images may be invalid, in which case we use the default. - if (exifValue < OriginTopLeft || exifValue > OriginLeftBottom) - return DefaultImageOrientation; - return static_cast(exifValue); - } + // ImageOrientationEnum currently matches EXIF values, however code outside + // this function should never assume that. + static ImageOrientation fromEXIFValue(int exifValue) { + // Values direct from images may be invalid, in which case we use the + // default. + if (exifValue < OriginTopLeft || exifValue > OriginLeftBottom) + return DefaultImageOrientation; + return static_cast(exifValue); + } - // This transform can be used for drawing an image according to the orientation. - // It should be used in a right-handed coordinate system. - AffineTransform transformFromDefault(const FloatSize& drawnSize) const; + // This transform can be used for drawing an image according to the + // orientation. It should be used in a right-handed coordinate system. + AffineTransform transformFromDefault(const FloatSize& drawnSize) const; - inline bool operator==(const ImageOrientation& other) const { return other.m_orientation == m_orientation; } - inline bool operator!=(const ImageOrientation& other) const { return !(*this == other); } + inline bool operator==(const ImageOrientation& other) const { + return other.m_orientation == m_orientation; + } + inline bool operator!=(const ImageOrientation& other) const { + return !(*this == other); + } -private: - // FIXME: This only needs to be one byte. - ImageOrientationEnum m_orientation; + private: + // FIXME: This only needs to be one byte. + ImageOrientationEnum m_orientation; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_GRAPHICS_IMAGEORIENTATION_H_ diff --git a/sky/engine/platform/graphics/Path.cpp b/sky/engine/platform/graphics/Path.cpp index bf413f7d52fcd..c104dc12060aa 100644 --- a/sky/engine/platform/graphics/Path.cpp +++ b/sky/engine/platform/graphics/Path.cpp @@ -40,466 +40,487 @@ namespace blink { -Path::Path() - : m_path() -{ -} +Path::Path() : m_path() {} -Path::Path(const Path& other) -{ - m_path = SkPath(other.m_path); +Path::Path(const Path& other) { + m_path = SkPath(other.m_path); } -Path::~Path() -{ -} +Path::~Path() {} -Path& Path::operator=(const Path& other) -{ - m_path = SkPath(other.m_path); - return *this; +Path& Path::operator=(const Path& other) { + m_path = SkPath(other.m_path); + return *this; } -bool Path::operator==(const Path& other) const -{ - return m_path == other.m_path; +bool Path::operator==(const Path& other) const { + return m_path == other.m_path; } -bool Path::contains(const FloatPoint& point, WindRule rule) const -{ - return SkPathContainsPoint(m_path, point, static_cast(rule)); +bool Path::contains(const FloatPoint& point, WindRule rule) const { + return SkPathContainsPoint(m_path, point, + static_cast(rule)); } -bool Path::strokeContains(const FloatPoint& point, const StrokeData& strokeData) const -{ - SkPaint paint; - strokeData.setupPaint(&paint); - SkPath strokePath; - paint.getFillPath(m_path, &strokePath); +bool Path::strokeContains(const FloatPoint& point, + const StrokeData& strokeData) const { + SkPaint paint; + strokeData.setupPaint(&paint); + SkPath strokePath; + paint.getFillPath(m_path, &strokePath); - return SkPathContainsPoint(strokePath, point, SkPath::kWinding_FillType); + return SkPathContainsPoint(strokePath, point, SkPath::kWinding_FillType); } -FloatRect Path::boundingRect() const -{ - return m_path.getBounds(); +FloatRect Path::boundingRect() const { + return m_path.getBounds(); } -FloatRect Path::strokeBoundingRect(const StrokeData& strokeData) const -{ - SkPaint paint; - strokeData.setupPaint(&paint); - SkPath boundingPath; - paint.getFillPath(m_path, &boundingPath); +FloatRect Path::strokeBoundingRect(const StrokeData& strokeData) const { + SkPaint paint; + strokeData.setupPaint(&paint); + SkPath boundingPath; + paint.getFillPath(m_path, &boundingPath); - return boundingPath.getBounds(); + return boundingPath.getBounds(); } -static FloatPoint* convertPathPoints(FloatPoint dst[], const SkPoint src[], int count) -{ - for (int i = 0; i < count; i++) { - dst[i].setX(SkScalarToFloat(src[i].fX)); - dst[i].setY(SkScalarToFloat(src[i].fY)); - } - return dst; -} - -void Path::apply(void* info, PathApplierFunction function) const -{ - SkPath::RawIter iter(m_path); - SkPoint pts[4]; - PathElement pathElement; - FloatPoint pathPoints[3]; - - for (;;) { - switch (iter.next(pts)) { - case SkPath::kMove_Verb: - pathElement.type = PathElementMoveToPoint; - pathElement.points = convertPathPoints(pathPoints, &pts[0], 1); - break; - case SkPath::kLine_Verb: - pathElement.type = PathElementAddLineToPoint; - pathElement.points = convertPathPoints(pathPoints, &pts[1], 1); - break; - case SkPath::kQuad_Verb: - pathElement.type = PathElementAddQuadCurveToPoint; - pathElement.points = convertPathPoints(pathPoints, &pts[1], 2); - break; - case SkPath::kCubic_Verb: - pathElement.type = PathElementAddCurveToPoint; - pathElement.points = convertPathPoints(pathPoints, &pts[1], 3); - break; - case SkPath::kClose_Verb: - pathElement.type = PathElementCloseSubpath; - pathElement.points = convertPathPoints(pathPoints, 0, 0); - break; - case SkPath::kDone_Verb: - return; - default: // place-holder for kConic_Verb, when that lands from skia - break; - } - function(info, &pathElement); - } +static FloatPoint* convertPathPoints(FloatPoint dst[], + const SkPoint src[], + int count) { + for (int i = 0; i < count; i++) { + dst[i].setX(SkScalarToFloat(src[i].fX)); + dst[i].setY(SkScalarToFloat(src[i].fY)); + } + return dst; } -void Path::transform(const AffineTransform& xform) -{ - m_path.transform(affineTransformToSkMatrix(xform)); +void Path::apply(void* info, PathApplierFunction function) const { + SkPath::RawIter iter(m_path); + SkPoint pts[4]; + PathElement pathElement; + FloatPoint pathPoints[3]; + + for (;;) { + switch (iter.next(pts)) { + case SkPath::kMove_Verb: + pathElement.type = PathElementMoveToPoint; + pathElement.points = convertPathPoints(pathPoints, &pts[0], 1); + break; + case SkPath::kLine_Verb: + pathElement.type = PathElementAddLineToPoint; + pathElement.points = convertPathPoints(pathPoints, &pts[1], 1); + break; + case SkPath::kQuad_Verb: + pathElement.type = PathElementAddQuadCurveToPoint; + pathElement.points = convertPathPoints(pathPoints, &pts[1], 2); + break; + case SkPath::kCubic_Verb: + pathElement.type = PathElementAddCurveToPoint; + pathElement.points = convertPathPoints(pathPoints, &pts[1], 3); + break; + case SkPath::kClose_Verb: + pathElement.type = PathElementCloseSubpath; + pathElement.points = convertPathPoints(pathPoints, 0, 0); + break; + case SkPath::kDone_Verb: + return; + default: // place-holder for kConic_Verb, when that lands from skia + break; + } + function(info, &pathElement); + } } -float Path::length() const -{ - SkScalar length = 0; - SkPathMeasure measure(m_path, false); +void Path::transform(const AffineTransform& xform) { + m_path.transform(affineTransformToSkMatrix(xform)); +} - do { - length += measure.getLength(); - } while (measure.nextContour()); +float Path::length() const { + SkScalar length = 0; + SkPathMeasure measure(m_path, false); - return SkScalarToFloat(length); -} + do { + length += measure.getLength(); + } while (measure.nextContour()); -FloatPoint Path::pointAtLength(float length, bool& ok) const -{ - FloatPoint point; - float normal; - ok = pointAndNormalAtLength(length, point, normal); - return point; + return SkScalarToFloat(length); } -float Path::normalAngleAtLength(float length, bool& ok) const -{ - FloatPoint point; - float normal; - ok = pointAndNormalAtLength(length, point, normal); - return normal; +FloatPoint Path::pointAtLength(float length, bool& ok) const { + FloatPoint point; + float normal; + ok = pointAndNormalAtLength(length, point, normal); + return point; } -static bool calculatePointAndNormalOnPath(SkPathMeasure& measure, SkScalar length, FloatPoint& point, float& normalAngle, SkScalar* accumulatedLength = 0) -{ - do { - SkScalar contourLength = measure.getLength(); - if (length <= contourLength) { - SkVector tangent; - SkPoint position; - - if (measure.getPosTan(length, &position, &tangent)) { - normalAngle = rad2deg(SkScalarToFloat(SkScalarATan2(tangent.fY, tangent.fX))); - point = FloatPoint(SkScalarToFloat(position.fX), SkScalarToFloat(position.fY)); - return true; - } - } - length -= contourLength; - if (accumulatedLength) - *accumulatedLength += contourLength; - } while (measure.nextContour()); - return false; +float Path::normalAngleAtLength(float length, bool& ok) const { + FloatPoint point; + float normal; + ok = pointAndNormalAtLength(length, point, normal); + return normal; } -bool Path::pointAndNormalAtLength(float length, FloatPoint& point, float& normal) const -{ - SkPathMeasure measure(m_path, false); +static bool calculatePointAndNormalOnPath(SkPathMeasure& measure, + SkScalar length, + FloatPoint& point, + float& normalAngle, + SkScalar* accumulatedLength = 0) { + do { + SkScalar contourLength = measure.getLength(); + if (length <= contourLength) { + SkVector tangent; + SkPoint position; - if (calculatePointAndNormalOnPath(measure, WebCoreFloatToSkScalar(length), point, normal)) + if (measure.getPosTan(length, &position, &tangent)) { + normalAngle = + rad2deg(SkScalarToFloat(SkScalarATan2(tangent.fY, tangent.fX))); + point = FloatPoint(SkScalarToFloat(position.fX), + SkScalarToFloat(position.fY)); return true; - - normal = 0; - point = FloatPoint(0, 0); - return false; -} - -Path::PositionCalculator::PositionCalculator(const Path& path) - : m_path(path.skPath()) - , m_pathMeasure(path.skPath(), false) - , m_accumulatedLength(0) -{ -} - -bool Path::PositionCalculator::pointAndNormalAtLength(float length, FloatPoint& point, float& normalAngle) -{ - SkScalar skLength = WebCoreFloatToSkScalar(length); - if (skLength >= 0) { - if (skLength < m_accumulatedLength) { - // Reset path measurer to rewind (and restart from 0). - m_pathMeasure.setPath(&m_path, false); - m_accumulatedLength = 0; - } else { - skLength -= m_accumulatedLength; - } - - if (calculatePointAndNormalOnPath(m_pathMeasure, skLength, point, normalAngle, &m_accumulatedLength)) - return true; + } } - - normalAngle = 0; - point = FloatPoint(0, 0); - return false; + length -= contourLength; + if (accumulatedLength) + *accumulatedLength += contourLength; + } while (measure.nextContour()); + return false; } -void Path::clear() -{ - m_path.reset(); -} +bool Path::pointAndNormalAtLength(float length, + FloatPoint& point, + float& normal) const { + SkPathMeasure measure(m_path, false); -bool Path::isEmpty() const -{ - return m_path.isEmpty(); -} + if (calculatePointAndNormalOnPath(measure, WebCoreFloatToSkScalar(length), + point, normal)) + return true; -bool Path::hasCurrentPoint() const -{ - return m_path.getPoints(0, 0); + normal = 0; + point = FloatPoint(0, 0); + return false; } -FloatPoint Path::currentPoint() const -{ - if (m_path.countPoints() > 0) { - SkPoint skResult; - m_path.getLastPt(&skResult); - FloatPoint result; - result.setX(SkScalarToFloat(skResult.fX)); - result.setY(SkScalarToFloat(skResult.fY)); - return result; +Path::PositionCalculator::PositionCalculator(const Path& path) + : m_path(path.skPath()), + m_pathMeasure(path.skPath(), false), + m_accumulatedLength(0) {} + +bool Path::PositionCalculator::pointAndNormalAtLength(float length, + FloatPoint& point, + float& normalAngle) { + SkScalar skLength = WebCoreFloatToSkScalar(length); + if (skLength >= 0) { + if (skLength < m_accumulatedLength) { + // Reset path measurer to rewind (and restart from 0). + m_pathMeasure.setPath(&m_path, false); + m_accumulatedLength = 0; + } else { + skLength -= m_accumulatedLength; } - // FIXME: Why does this return quietNaN? Other ports return 0,0. - float quietNaN = std::numeric_limits::quiet_NaN(); - return FloatPoint(quietNaN, quietNaN); -} + if (calculatePointAndNormalOnPath(m_pathMeasure, skLength, point, + normalAngle, &m_accumulatedLength)) + return true; + } -WindRule Path::windRule() const -{ - return m_path.getFillType() == SkPath::kEvenOdd_FillType - ? RULE_EVENODD - : RULE_NONZERO; + normalAngle = 0; + point = FloatPoint(0, 0); + return false; } -void Path::setWindRule(const WindRule rule) -{ - m_path.setFillType(WebCoreWindRuleToSkFillType(rule)); +void Path::clear() { + m_path.reset(); } -void Path::moveTo(const FloatPoint& point) -{ - m_path.moveTo(point.data()); +bool Path::isEmpty() const { + return m_path.isEmpty(); } -void Path::addLineTo(const FloatPoint& point) -{ - m_path.lineTo(point.data()); +bool Path::hasCurrentPoint() const { + return m_path.getPoints(0, 0); } -void Path::addQuadCurveTo(const FloatPoint& cp, const FloatPoint& ep) -{ - m_path.quadTo(cp.data(), ep.data()); -} +FloatPoint Path::currentPoint() const { + if (m_path.countPoints() > 0) { + SkPoint skResult; + m_path.getLastPt(&skResult); + FloatPoint result; + result.setX(SkScalarToFloat(skResult.fX)); + result.setY(SkScalarToFloat(skResult.fY)); + return result; + } -void Path::addBezierCurveTo(const FloatPoint& p1, const FloatPoint& p2, const FloatPoint& ep) -{ - m_path.cubicTo(p1.data(), p2.data(), ep.data()); + // FIXME: Why does this return quietNaN? Other ports return 0,0. + float quietNaN = std::numeric_limits::quiet_NaN(); + return FloatPoint(quietNaN, quietNaN); } -void Path::addArcTo(const FloatPoint& p1, const FloatPoint& p2, float radius) -{ - m_path.arcTo(p1.data(), p2.data(), WebCoreFloatToSkScalar(radius)); +WindRule Path::windRule() const { + return m_path.getFillType() == SkPath::kEvenOdd_FillType ? RULE_EVENODD + : RULE_NONZERO; } -void Path::closeSubpath() -{ - m_path.close(); +void Path::setWindRule(const WindRule rule) { + m_path.setFillType(WebCoreWindRuleToSkFillType(rule)); } -void Path::addEllipse(const FloatPoint& p, float radiusX, float radiusY, float startAngle, float endAngle, bool anticlockwise) -{ - ASSERT(ellipseIsRenderable(startAngle, endAngle)); - ASSERT(startAngle >= 0 && startAngle < twoPiFloat); - ASSERT((anticlockwise && (startAngle - endAngle) >= 0) || (!anticlockwise && (endAngle - startAngle) >= 0)); - - SkScalar cx = WebCoreFloatToSkScalar(p.x()); - SkScalar cy = WebCoreFloatToSkScalar(p.y()); - SkScalar radiusXScalar = WebCoreFloatToSkScalar(radiusX); - SkScalar radiusYScalar = WebCoreFloatToSkScalar(radiusY); - - SkRect oval; - oval.set(cx - radiusXScalar, cy - radiusYScalar, cx + radiusXScalar, cy + radiusYScalar); - - float sweep = endAngle - startAngle; - SkScalar startDegrees = WebCoreFloatToSkScalar(startAngle * 180 / piFloat); - SkScalar sweepDegrees = WebCoreFloatToSkScalar(sweep * 180 / piFloat); - SkScalar s360 = SkIntToScalar(360); - - // We can't use SkPath::addOval(), because addOval() makes new sub-path. addOval() calls moveTo() and close() internally. - - // Use s180, not s360, because SkPath::arcTo(oval, angle, s360, false) draws nothing. - SkScalar s180 = SkIntToScalar(180); - if (SkScalarNearlyEqual(sweepDegrees, s360)) { - // SkPath::arcTo can't handle the sweepAngle that is equal to or greater than 2Pi. - m_path.arcTo(oval, startDegrees, s180, false); - m_path.arcTo(oval, startDegrees + s180, s180, false); - return; - } - if (SkScalarNearlyEqual(sweepDegrees, -s360)) { - m_path.arcTo(oval, startDegrees, -s180, false); - m_path.arcTo(oval, startDegrees - s180, -s180, false); - return; - } - - m_path.arcTo(oval, startDegrees, sweepDegrees, false); +void Path::moveTo(const FloatPoint& point) { + m_path.moveTo(point.data()); } -void Path::addArc(const FloatPoint& p, float radius, float startAngle, float endAngle, bool anticlockwise) -{ - addEllipse(p, radius, radius, startAngle, endAngle, anticlockwise); +void Path::addLineTo(const FloatPoint& point) { + m_path.lineTo(point.data()); } -void Path::addRect(const FloatRect& rect) -{ - m_path.addRect(rect); +void Path::addQuadCurveTo(const FloatPoint& cp, const FloatPoint& ep) { + m_path.quadTo(cp.data(), ep.data()); } -void Path::addEllipse(const FloatPoint& p, float radiusX, float radiusY, float rotation, float startAngle, float endAngle, bool anticlockwise) -{ - ASSERT(ellipseIsRenderable(startAngle, endAngle)); - ASSERT(startAngle >= 0 && startAngle < twoPiFloat); - ASSERT((anticlockwise && (startAngle - endAngle) >= 0) || (!anticlockwise && (endAngle - startAngle) >= 0)); - - if (!rotation) { - addEllipse(FloatPoint(p.x(), p.y()), radiusX, radiusY, startAngle, endAngle, anticlockwise); - return; - } - - // Add an arc after the relevant transform. - AffineTransform ellipseTransform = AffineTransform::translation(p.x(), p.y()).rotateRadians(rotation); - ASSERT(ellipseTransform.isInvertible()); - AffineTransform inverseEllipseTransform = ellipseTransform.inverse(); - transform(inverseEllipseTransform); - addEllipse(FloatPoint::zero(), radiusX, radiusY, startAngle, endAngle, anticlockwise); - transform(ellipseTransform); +void Path::addBezierCurveTo(const FloatPoint& p1, + const FloatPoint& p2, + const FloatPoint& ep) { + m_path.cubicTo(p1.data(), p2.data(), ep.data()); } -void Path::addEllipse(const FloatRect& rect) -{ - m_path.addOval(rect); +void Path::addArcTo(const FloatPoint& p1, const FloatPoint& p2, float radius) { + m_path.arcTo(p1.data(), p2.data(), WebCoreFloatToSkScalar(radius)); } -void Path::addRoundedRect(const RoundedRect& r) -{ - addRoundedRect(r.rect(), r.radii().topLeft(), r.radii().topRight(), r.radii().bottomLeft(), r.radii().bottomRight()); +void Path::closeSubpath() { + m_path.close(); } -void Path::addRoundedRect(const FloatRect& rect, const FloatSize& roundingRadii) -{ - if (rect.isEmpty()) - return; - - FloatSize radius(roundingRadii); - FloatSize halfSize(rect.width() / 2, rect.height() / 2); +void Path::addEllipse(const FloatPoint& p, + float radiusX, + float radiusY, + float startAngle, + float endAngle, + bool anticlockwise) { + ASSERT(ellipseIsRenderable(startAngle, endAngle)); + ASSERT(startAngle >= 0 && startAngle < twoPiFloat); + ASSERT((anticlockwise && (startAngle - endAngle) >= 0) || + (!anticlockwise && (endAngle - startAngle) >= 0)); - // Apply the SVG corner radius constraints, per the rect section of the SVG shapes spec: if - // one of rx,ry is negative, then the other corner radius value is used. If both values are - // negative then rx = ry = 0. If rx is greater than half of the width of the rectangle - // then set rx to half of the width; ry is handled similarly. + SkScalar cx = WebCoreFloatToSkScalar(p.x()); + SkScalar cy = WebCoreFloatToSkScalar(p.y()); + SkScalar radiusXScalar = WebCoreFloatToSkScalar(radiusX); + SkScalar radiusYScalar = WebCoreFloatToSkScalar(radiusY); - if (radius.width() < 0) - radius.setWidth((radius.height() < 0) ? 0 : radius.height()); + SkRect oval; + oval.set(cx - radiusXScalar, cy - radiusYScalar, cx + radiusXScalar, + cy + radiusYScalar); - if (radius.height() < 0) - radius.setHeight(radius.width()); + float sweep = endAngle - startAngle; + SkScalar startDegrees = WebCoreFloatToSkScalar(startAngle * 180 / piFloat); + SkScalar sweepDegrees = WebCoreFloatToSkScalar(sweep * 180 / piFloat); + SkScalar s360 = SkIntToScalar(360); - if (radius.width() > halfSize.width()) - radius.setWidth(halfSize.width()); + // We can't use SkPath::addOval(), because addOval() makes new sub-path. + // addOval() calls moveTo() and close() internally. - if (radius.height() > halfSize.height()) - radius.setHeight(halfSize.height()); + // Use s180, not s360, because SkPath::arcTo(oval, angle, s360, false) draws + // nothing. + SkScalar s180 = SkIntToScalar(180); + if (SkScalarNearlyEqual(sweepDegrees, s360)) { + // SkPath::arcTo can't handle the sweepAngle that is equal to or greater + // than 2Pi. + m_path.arcTo(oval, startDegrees, s180, false); + m_path.arcTo(oval, startDegrees + s180, s180, false); + return; + } + if (SkScalarNearlyEqual(sweepDegrees, -s360)) { + m_path.arcTo(oval, startDegrees, -s180, false); + m_path.arcTo(oval, startDegrees - s180, -s180, false); + return; + } - addPathForRoundedRect(rect, radius, radius, radius, radius); + m_path.arcTo(oval, startDegrees, sweepDegrees, false); } -void Path::addRoundedRect(const FloatRect& rect, const FloatSize& topLeftRadius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius) -{ - if (rect.isEmpty()) - return; - - if (rect.width() < topLeftRadius.width() + topRightRadius.width() - || rect.width() < bottomLeftRadius.width() + bottomRightRadius.width() - || rect.height() < topLeftRadius.height() + bottomLeftRadius.height() - || rect.height() < topRightRadius.height() + bottomRightRadius.height()) { - // If all the radii cannot be accommodated, return a rect. - addRect(rect); - return; - } - - addPathForRoundedRect(rect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius); +void Path::addArc(const FloatPoint& p, + float radius, + float startAngle, + float endAngle, + bool anticlockwise) { + addEllipse(p, radius, radius, startAngle, endAngle, anticlockwise); } -void Path::addPathForRoundedRect(const FloatRect& rect, const FloatSize& topLeftRadius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius) -{ - addBeziersForRoundedRect(rect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius); +void Path::addRect(const FloatRect& rect) { + m_path.addRect(rect); } -// Approximation of control point positions on a bezier to simulate a quarter of a circle. -// This is 1-kappa, where kappa = 4 * (sqrt(2) - 1) / 3 -static const float gCircleControlPoint = 0.447715f; - -void Path::addBeziersForRoundedRect(const FloatRect& rect, const FloatSize& topLeftRadius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius) -{ - moveTo(FloatPoint(rect.x() + topLeftRadius.width(), rect.y())); - - addLineTo(FloatPoint(rect.maxX() - topRightRadius.width(), rect.y())); - if (topRightRadius.width() > 0 || topRightRadius.height() > 0) - addBezierCurveTo(FloatPoint(rect.maxX() - topRightRadius.width() * gCircleControlPoint, rect.y()), - FloatPoint(rect.maxX(), rect.y() + topRightRadius.height() * gCircleControlPoint), - FloatPoint(rect.maxX(), rect.y() + topRightRadius.height())); - addLineTo(FloatPoint(rect.maxX(), rect.maxY() - bottomRightRadius.height())); - if (bottomRightRadius.width() > 0 || bottomRightRadius.height() > 0) - addBezierCurveTo(FloatPoint(rect.maxX(), rect.maxY() - bottomRightRadius.height() * gCircleControlPoint), - FloatPoint(rect.maxX() - bottomRightRadius.width() * gCircleControlPoint, rect.maxY()), - FloatPoint(rect.maxX() - bottomRightRadius.width(), rect.maxY())); - addLineTo(FloatPoint(rect.x() + bottomLeftRadius.width(), rect.maxY())); - if (bottomLeftRadius.width() > 0 || bottomLeftRadius.height() > 0) - addBezierCurveTo(FloatPoint(rect.x() + bottomLeftRadius.width() * gCircleControlPoint, rect.maxY()), - FloatPoint(rect.x(), rect.maxY() - bottomLeftRadius.height() * gCircleControlPoint), - FloatPoint(rect.x(), rect.maxY() - bottomLeftRadius.height())); - addLineTo(FloatPoint(rect.x(), rect.y() + topLeftRadius.height())); - if (topLeftRadius.width() > 0 || topLeftRadius.height() > 0) - addBezierCurveTo(FloatPoint(rect.x(), rect.y() + topLeftRadius.height() * gCircleControlPoint), - FloatPoint(rect.x() + topLeftRadius.width() * gCircleControlPoint, rect.y()), - FloatPoint(rect.x() + topLeftRadius.width(), rect.y())); +void Path::addEllipse(const FloatPoint& p, + float radiusX, + float radiusY, + float rotation, + float startAngle, + float endAngle, + bool anticlockwise) { + ASSERT(ellipseIsRenderable(startAngle, endAngle)); + ASSERT(startAngle >= 0 && startAngle < twoPiFloat); + ASSERT((anticlockwise && (startAngle - endAngle) >= 0) || + (!anticlockwise && (endAngle - startAngle) >= 0)); - closeSubpath(); -} + if (!rotation) { + addEllipse(FloatPoint(p.x(), p.y()), radiusX, radiusY, startAngle, endAngle, + anticlockwise); + return; + } -void Path::addPath(const Path& src, const AffineTransform& transform) -{ - m_path.addPath(src.skPath(), affineTransformToSkMatrix(transform)); -} + // Add an arc after the relevant transform. + AffineTransform ellipseTransform = + AffineTransform::translation(p.x(), p.y()).rotateRadians(rotation); + ASSERT(ellipseTransform.isInvertible()); + AffineTransform inverseEllipseTransform = ellipseTransform.inverse(); + transform(inverseEllipseTransform); + addEllipse(FloatPoint::zero(), radiusX, radiusY, startAngle, endAngle, + anticlockwise); + transform(ellipseTransform); +} + +void Path::addEllipse(const FloatRect& rect) { + m_path.addOval(rect); +} + +void Path::addRoundedRect(const RoundedRect& r) { + addRoundedRect(r.rect(), r.radii().topLeft(), r.radii().topRight(), + r.radii().bottomLeft(), r.radii().bottomRight()); +} + +void Path::addRoundedRect(const FloatRect& rect, + const FloatSize& roundingRadii) { + if (rect.isEmpty()) + return; + + FloatSize radius(roundingRadii); + FloatSize halfSize(rect.width() / 2, rect.height() / 2); + + // Apply the SVG corner radius constraints, per the rect section of the SVG + // shapes spec: if one of rx,ry is negative, then the other corner radius + // value is used. If both values are negative then rx = ry = 0. If rx is + // greater than half of the width of the rectangle then set rx to half of the + // width; ry is handled similarly. + + if (radius.width() < 0) + radius.setWidth((radius.height() < 0) ? 0 : radius.height()); + + if (radius.height() < 0) + radius.setHeight(radius.width()); + + if (radius.width() > halfSize.width()) + radius.setWidth(halfSize.width()); + + if (radius.height() > halfSize.height()) + radius.setHeight(halfSize.height()); + + addPathForRoundedRect(rect, radius, radius, radius, radius); +} + +void Path::addRoundedRect(const FloatRect& rect, + const FloatSize& topLeftRadius, + const FloatSize& topRightRadius, + const FloatSize& bottomLeftRadius, + const FloatSize& bottomRightRadius) { + if (rect.isEmpty()) + return; + + if (rect.width() < topLeftRadius.width() + topRightRadius.width() || + rect.width() < bottomLeftRadius.width() + bottomRightRadius.width() || + rect.height() < topLeftRadius.height() + bottomLeftRadius.height() || + rect.height() < topRightRadius.height() + bottomRightRadius.height()) { + // If all the radii cannot be accommodated, return a rect. + addRect(rect); + return; + } -void Path::translate(const FloatSize& size) -{ - m_path.offset(WebCoreFloatToSkScalar(size.width()), WebCoreFloatToSkScalar(size.height())); + addPathForRoundedRect(rect, topLeftRadius, topRightRadius, bottomLeftRadius, + bottomRightRadius); } -bool Path::subtractPath(const Path& other) -{ - return Op(m_path, other.m_path, kDifference_SkPathOp, &m_path); +void Path::addPathForRoundedRect(const FloatRect& rect, + const FloatSize& topLeftRadius, + const FloatSize& topRightRadius, + const FloatSize& bottomLeftRadius, + const FloatSize& bottomRightRadius) { + addBeziersForRoundedRect(rect, topLeftRadius, topRightRadius, + bottomLeftRadius, bottomRightRadius); } -bool Path::intersectPath(const Path& other) -{ - return Op(m_path, other.m_path, kIntersect_SkPathOp, &m_path); -} +// Approximation of control point positions on a bezier to simulate a quarter of +// a circle. This is 1-kappa, where kappa = 4 * (sqrt(2) - 1) / 3 +static const float gCircleControlPoint = 0.447715f; -bool Path::unionPath(const Path& other) -{ - return Op(m_path, other.m_path, kUnion_SkPathOp, &m_path); +void Path::addBeziersForRoundedRect(const FloatRect& rect, + const FloatSize& topLeftRadius, + const FloatSize& topRightRadius, + const FloatSize& bottomLeftRadius, + const FloatSize& bottomRightRadius) { + moveTo(FloatPoint(rect.x() + topLeftRadius.width(), rect.y())); + + addLineTo(FloatPoint(rect.maxX() - topRightRadius.width(), rect.y())); + if (topRightRadius.width() > 0 || topRightRadius.height() > 0) + addBezierCurveTo( + FloatPoint(rect.maxX() - topRightRadius.width() * gCircleControlPoint, + rect.y()), + FloatPoint(rect.maxX(), + rect.y() + topRightRadius.height() * gCircleControlPoint), + FloatPoint(rect.maxX(), rect.y() + topRightRadius.height())); + addLineTo(FloatPoint(rect.maxX(), rect.maxY() - bottomRightRadius.height())); + if (bottomRightRadius.width() > 0 || bottomRightRadius.height() > 0) + addBezierCurveTo( + FloatPoint(rect.maxX(), rect.maxY() - bottomRightRadius.height() * + gCircleControlPoint), + FloatPoint( + rect.maxX() - bottomRightRadius.width() * gCircleControlPoint, + rect.maxY()), + FloatPoint(rect.maxX() - bottomRightRadius.width(), rect.maxY())); + addLineTo(FloatPoint(rect.x() + bottomLeftRadius.width(), rect.maxY())); + if (bottomLeftRadius.width() > 0 || bottomLeftRadius.height() > 0) + addBezierCurveTo( + FloatPoint(rect.x() + bottomLeftRadius.width() * gCircleControlPoint, + rect.maxY()), + FloatPoint(rect.x(), rect.maxY() - bottomLeftRadius.height() * + gCircleControlPoint), + FloatPoint(rect.x(), rect.maxY() - bottomLeftRadius.height())); + addLineTo(FloatPoint(rect.x(), rect.y() + topLeftRadius.height())); + if (topLeftRadius.width() > 0 || topLeftRadius.height() > 0) + addBezierCurveTo( + FloatPoint(rect.x(), + rect.y() + topLeftRadius.height() * gCircleControlPoint), + FloatPoint(rect.x() + topLeftRadius.width() * gCircleControlPoint, + rect.y()), + FloatPoint(rect.x() + topLeftRadius.width(), rect.y())); + + closeSubpath(); +} + +void Path::addPath(const Path& src, const AffineTransform& transform) { + m_path.addPath(src.skPath(), affineTransformToSkMatrix(transform)); +} + +void Path::translate(const FloatSize& size) { + m_path.offset(WebCoreFloatToSkScalar(size.width()), + WebCoreFloatToSkScalar(size.height())); +} + +bool Path::subtractPath(const Path& other) { + return Op(m_path, other.m_path, kDifference_SkPathOp, &m_path); +} + +bool Path::intersectPath(const Path& other) { + return Op(m_path, other.m_path, kIntersect_SkPathOp, &m_path); +} + +bool Path::unionPath(const Path& other) { + return Op(m_path, other.m_path, kUnion_SkPathOp, &m_path); } #if ENABLE(ASSERT) -bool ellipseIsRenderable(float startAngle, float endAngle) -{ - return (std::abs(endAngle - startAngle) < twoPiFloat) - || WebCoreFloatNearlyEqual(std::abs(endAngle - startAngle), twoPiFloat); +bool ellipseIsRenderable(float startAngle, float endAngle) { + return (std::abs(endAngle - startAngle) < twoPiFloat) || + WebCoreFloatNearlyEqual(std::abs(endAngle - startAngle), twoPiFloat); } #endif -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/graphics/Path.h b/sky/engine/platform/graphics/Path.h index f07cc6f87764a..53016e8dc99d4 100644 --- a/sky/engine/platform/graphics/Path.h +++ b/sky/engine/platform/graphics/Path.h @@ -48,116 +48,149 @@ class FloatSize; class StrokeData; enum PathElementType { - PathElementMoveToPoint, // The points member will contain 1 value. - PathElementAddLineToPoint, // The points member will contain 1 value. - PathElementAddQuadCurveToPoint, // The points member will contain 2 values. - PathElementAddCurveToPoint, // The points member will contain 3 values. - PathElementCloseSubpath // The points member will contain no values. + PathElementMoveToPoint, // The points member will contain 1 value. + PathElementAddLineToPoint, // The points member will contain 1 value. + PathElementAddQuadCurveToPoint, // The points member will contain 2 values. + PathElementAddCurveToPoint, // The points member will contain 3 values. + PathElementCloseSubpath // The points member will contain no values. }; // The points in the structure are the same as those that would be used with the -// add... method. For example, a line returns the endpoint, while a cubic returns -// two tangent points and the endpoint. +// add... method. For example, a line returns the endpoint, while a cubic +// returns two tangent points and the endpoint. struct PathElement { - PathElementType type; - FloatPoint* points; + PathElementType type; + FloatPoint* points; }; typedef void (*PathApplierFunction)(void* info, const PathElement*); class PLATFORM_EXPORT Path { - WTF_MAKE_FAST_ALLOCATED; -public: - Path(); - ~Path(); - - Path(const Path&); - Path& operator=(const Path&); - bool operator==(const Path&) const; - - bool contains(const FloatPoint&, WindRule = RULE_NONZERO) const; - bool strokeContains(const FloatPoint&, const StrokeData&) const; - FloatRect boundingRect() const; - FloatRect strokeBoundingRect(const StrokeData&) const; - - float length() const; - FloatPoint pointAtLength(float length, bool& ok) const; - float normalAngleAtLength(float length, bool& ok) const; - bool pointAndNormalAtLength(float length, FloatPoint&, float&) const; - - // Helper for computing a sequence of positions and normals (normal angles) on a path. - // The best possible access pattern will be one where the |length| value is - // strictly increasing. - // For other access patterns, performance will vary depending on curvature - // and number of segments, but should never be worse than that of the - // state-less method on Path. - class PLATFORM_EXPORT PositionCalculator { - WTF_MAKE_NONCOPYABLE(PositionCalculator); - public: - explicit PositionCalculator(const Path&); - - bool pointAndNormalAtLength(float length, FloatPoint&, float&); - - private: - SkPath m_path; - SkPathMeasure m_pathMeasure; - SkScalar m_accumulatedLength; - }; - - void clear(); - bool isEmpty() const; - // Gets the current point of the current path, which is conceptually the final point reached by the path so far. - // Note the Path can be empty (isEmpty() == true) and still have a current point. - bool hasCurrentPoint() const; - FloatPoint currentPoint() const; - - WindRule windRule() const; - void setWindRule(const WindRule); - - void moveTo(const FloatPoint&); - void addLineTo(const FloatPoint&); - void addQuadCurveTo(const FloatPoint& controlPoint, const FloatPoint& endPoint); - void addBezierCurveTo(const FloatPoint& controlPoint1, const FloatPoint& controlPoint2, const FloatPoint& endPoint); - void addArcTo(const FloatPoint&, const FloatPoint&, float radius); - void closeSubpath(); - - void addArc(const FloatPoint&, float radius, float startAngle, float endAngle, bool anticlockwise); - void addRect(const FloatRect&); - void addEllipse(const FloatPoint&, float radiusX, float radiusY, float rotation, float startAngle, float endAngle, bool anticlockwise); - void addEllipse(const FloatRect&); - - void addRoundedRect(const FloatRect&, const FloatSize& roundingRadii); - void addRoundedRect(const FloatRect&, const FloatSize& topLeftRadius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius); - void addRoundedRect(const RoundedRect&); - - void addPath(const Path&, const AffineTransform&); - - void translate(const FloatSize&); - - const SkPath& skPath() const { return m_path; } - - void apply(void* info, PathApplierFunction) const; - void transform(const AffineTransform&); - - void addPathForRoundedRect(const FloatRect&, const FloatSize& topLeftRadius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius); - void addBeziersForRoundedRect(const FloatRect&, const FloatSize& topLeftRadius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius); - - bool subtractPath(const Path&); - bool intersectPath(const Path&); - - // Updates the path to the union (inclusive-or) of itself with the given argument. - bool unionPath(const Path& other); - -private: - void addEllipse(const FloatPoint&, float radiusX, float radiusY, float startAngle, float endAngle, bool anticlockwise); + WTF_MAKE_FAST_ALLOCATED; + public: + Path(); + ~Path(); + + Path(const Path&); + Path& operator=(const Path&); + bool operator==(const Path&) const; + + bool contains(const FloatPoint&, WindRule = RULE_NONZERO) const; + bool strokeContains(const FloatPoint&, const StrokeData&) const; + FloatRect boundingRect() const; + FloatRect strokeBoundingRect(const StrokeData&) const; + + float length() const; + FloatPoint pointAtLength(float length, bool& ok) const; + float normalAngleAtLength(float length, bool& ok) const; + bool pointAndNormalAtLength(float length, FloatPoint&, float&) const; + + // Helper for computing a sequence of positions and normals (normal angles) on + // a path. The best possible access pattern will be one where the |length| + // value is strictly increasing. For other access patterns, performance will + // vary depending on curvature and number of segments, but should never be + // worse than that of the state-less method on Path. + class PLATFORM_EXPORT PositionCalculator { + WTF_MAKE_NONCOPYABLE(PositionCalculator); + + public: + explicit PositionCalculator(const Path&); + + bool pointAndNormalAtLength(float length, FloatPoint&, float&); + + private: SkPath m_path; + SkPathMeasure m_pathMeasure; + SkScalar m_accumulatedLength; + }; + + void clear(); + bool isEmpty() const; + // Gets the current point of the current path, which is conceptually the final + // point reached by the path so far. Note the Path can be empty (isEmpty() == + // true) and still have a current point. + bool hasCurrentPoint() const; + FloatPoint currentPoint() const; + + WindRule windRule() const; + void setWindRule(const WindRule); + + void moveTo(const FloatPoint&); + void addLineTo(const FloatPoint&); + void addQuadCurveTo(const FloatPoint& controlPoint, + const FloatPoint& endPoint); + void addBezierCurveTo(const FloatPoint& controlPoint1, + const FloatPoint& controlPoint2, + const FloatPoint& endPoint); + void addArcTo(const FloatPoint&, const FloatPoint&, float radius); + void closeSubpath(); + + void addArc(const FloatPoint&, + float radius, + float startAngle, + float endAngle, + bool anticlockwise); + void addRect(const FloatRect&); + void addEllipse(const FloatPoint&, + float radiusX, + float radiusY, + float rotation, + float startAngle, + float endAngle, + bool anticlockwise); + void addEllipse(const FloatRect&); + + void addRoundedRect(const FloatRect&, const FloatSize& roundingRadii); + void addRoundedRect(const FloatRect&, + const FloatSize& topLeftRadius, + const FloatSize& topRightRadius, + const FloatSize& bottomLeftRadius, + const FloatSize& bottomRightRadius); + void addRoundedRect(const RoundedRect&); + + void addPath(const Path&, const AffineTransform&); + + void translate(const FloatSize&); + + const SkPath& skPath() const { return m_path; } + + void apply(void* info, PathApplierFunction) const; + void transform(const AffineTransform&); + + void addPathForRoundedRect(const FloatRect&, + const FloatSize& topLeftRadius, + const FloatSize& topRightRadius, + const FloatSize& bottomLeftRadius, + const FloatSize& bottomRightRadius); + void addBeziersForRoundedRect(const FloatRect&, + const FloatSize& topLeftRadius, + const FloatSize& topRightRadius, + const FloatSize& bottomLeftRadius, + const FloatSize& bottomRightRadius); + + bool subtractPath(const Path&); + bool intersectPath(const Path&); + + // Updates the path to the union (inclusive-or) of itself with the given + // argument. + bool unionPath(const Path& other); + + private: + void addEllipse(const FloatPoint&, + float radiusX, + float radiusY, + float startAngle, + float endAngle, + bool anticlockwise); + + SkPath m_path; }; #if ENABLE(ASSERT) PLATFORM_EXPORT bool ellipseIsRenderable(float startAngle, float endAngle); #endif -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_GRAPHICS_PATH_H_ diff --git a/sky/engine/platform/graphics/PathTraversalState.cpp b/sky/engine/platform/graphics/PathTraversalState.cpp index 0573a7c60618b..9ea151656c609 100644 --- a/sky/engine/platform/graphics/PathTraversalState.cpp +++ b/sky/engine/platform/graphics/PathTraversalState.cpp @@ -24,210 +24,211 @@ namespace blink { -static inline FloatPoint midPoint(const FloatPoint& first, const FloatPoint& second) -{ - return FloatPoint((first.x() + second.x()) / 2.0f, (first.y() + second.y()) / 2.0f); +static inline FloatPoint midPoint(const FloatPoint& first, + const FloatPoint& second) { + return FloatPoint((first.x() + second.x()) / 2.0f, + (first.y() + second.y()) / 2.0f); } -static inline float distanceLine(const FloatPoint& start, const FloatPoint& end) -{ - return sqrtf((end.x() - start.x()) * (end.x() - start.x()) + (end.y() - start.y()) * (end.y() - start.y())); +static inline float distanceLine(const FloatPoint& start, + const FloatPoint& end) { + return sqrtf((end.x() - start.x()) * (end.x() - start.x()) + + (end.y() - start.y()) * (end.y() - start.y())); } struct QuadraticBezier { - QuadraticBezier() { } - QuadraticBezier(const FloatPoint& s, const FloatPoint& c, const FloatPoint& e) - : start(s) - , control(c) - , end(e) - , splitDepth(0) - { - } - - double magnitudeSquared() const - { - return ((double)(start.dot(start)) + (double)(control.dot(control)) + (double)(end.dot(end))) / 9.0; - } - - float approximateDistance() const - { - return distanceLine(start, control) + distanceLine(control, end); - } - - void split(QuadraticBezier& left, QuadraticBezier& right) const - { - left.control = midPoint(start, control); - right.control = midPoint(control, end); - - FloatPoint leftControlToRightControl = midPoint(left.control, right.control); - left.end = leftControlToRightControl; - right.start = leftControlToRightControl; - - left.start = start; - right.end = end; - - left.splitDepth = right.splitDepth = splitDepth + 1; - } - - FloatPoint start; - FloatPoint control; - FloatPoint end; - unsigned short splitDepth; + QuadraticBezier() {} + QuadraticBezier(const FloatPoint& s, const FloatPoint& c, const FloatPoint& e) + : start(s), control(c), end(e), splitDepth(0) {} + + double magnitudeSquared() const { + return ((double)(start.dot(start)) + (double)(control.dot(control)) + + (double)(end.dot(end))) / + 9.0; + } + + float approximateDistance() const { + return distanceLine(start, control) + distanceLine(control, end); + } + + void split(QuadraticBezier& left, QuadraticBezier& right) const { + left.control = midPoint(start, control); + right.control = midPoint(control, end); + + FloatPoint leftControlToRightControl = + midPoint(left.control, right.control); + left.end = leftControlToRightControl; + right.start = leftControlToRightControl; + + left.start = start; + right.end = end; + + left.splitDepth = right.splitDepth = splitDepth + 1; + } + + FloatPoint start; + FloatPoint control; + FloatPoint end; + unsigned short splitDepth; }; struct CubicBezier { - CubicBezier() { } - CubicBezier(const FloatPoint& s, const FloatPoint& c1, const FloatPoint& c2, const FloatPoint& e) - : start(s) - , control1(c1) - , control2(c2) - , end(e) - , splitDepth(0) - { - } - - double magnitudeSquared() const - { - return ((double)(start.dot(start)) + (double)(control1.dot(control1)) + (double)(control2.dot(control2)) + (double)(end.dot(end))) / 16.0; - } - - float approximateDistance() const - { - return distanceLine(start, control1) + distanceLine(control1, control2) + distanceLine(control2, end); - } - - void split(CubicBezier& left, CubicBezier& right) const - { - FloatPoint startToControl1 = midPoint(control1, control2); - - left.start = start; - left.control1 = midPoint(start, control1); - left.control2 = midPoint(left.control1, startToControl1); + CubicBezier() {} + CubicBezier(const FloatPoint& s, + const FloatPoint& c1, + const FloatPoint& c2, + const FloatPoint& e) + : start(s), control1(c1), control2(c2), end(e), splitDepth(0) {} + + double magnitudeSquared() const { + return ((double)(start.dot(start)) + (double)(control1.dot(control1)) + + (double)(control2.dot(control2)) + (double)(end.dot(end))) / + 16.0; + } + + float approximateDistance() const { + return distanceLine(start, control1) + distanceLine(control1, control2) + + distanceLine(control2, end); + } + + void split(CubicBezier& left, CubicBezier& right) const { + FloatPoint startToControl1 = midPoint(control1, control2); + + left.start = start; + left.control1 = midPoint(start, control1); + left.control2 = midPoint(left.control1, startToControl1); + + right.control2 = midPoint(control2, end); + right.control1 = midPoint(right.control2, startToControl1); + right.end = end; + + FloatPoint leftControl2ToRightControl1 = + midPoint(left.control2, right.control1); + left.end = leftControl2ToRightControl1; + right.start = leftControl2ToRightControl1; + + left.splitDepth = right.splitDepth = splitDepth + 1; + } + + FloatPoint start; + FloatPoint control1; + FloatPoint control2; + FloatPoint end; + unsigned short splitDepth; +}; - right.control2 = midPoint(control2, end); - right.control1 = midPoint(right.control2, startToControl1); - right.end = end; +template +static float curveLength(PathTraversalState& traversalState, CurveType curve) { + static const unsigned short curveSplitDepthLimit = 20; + static const double pathSegmentLengthToleranceSquared = 1.e-16; - FloatPoint leftControl2ToRightControl1 = midPoint(left.control2, right.control1); - left.end = leftControl2ToRightControl1; - right.start = leftControl2ToRightControl1; + double curveScaleForToleranceSquared = curve.magnitudeSquared(); + if (curveScaleForToleranceSquared < pathSegmentLengthToleranceSquared) + return 0; - left.splitDepth = right.splitDepth = splitDepth + 1; + Vector curveStack; + curveStack.append(curve); + + float totalLength = 0; + do { + float length = curve.approximateDistance(); + double lengthDiscrepancy = length - distanceLine(curve.start, curve.end); + if ((lengthDiscrepancy * lengthDiscrepancy) / + curveScaleForToleranceSquared > + pathSegmentLengthToleranceSquared && + curve.splitDepth < curveSplitDepthLimit) { + CurveType leftCurve; + CurveType rightCurve; + curve.split(leftCurve, rightCurve); + curve = leftCurve; + curveStack.append(rightCurve); + } else { + totalLength += length; + if (traversalState.m_action == + PathTraversalState::TraversalPointAtLength || + traversalState.m_action == + PathTraversalState::TraversalNormalAngleAtLength) { + traversalState.m_previous = curve.start; + traversalState.m_current = curve.end; + if (traversalState.m_totalLength + totalLength > + traversalState.m_desiredLength) + return totalLength; + } + curve = curveStack.last(); + curveStack.removeLast(); } + } while (!curveStack.isEmpty()); - FloatPoint start; - FloatPoint control1; - FloatPoint control2; - FloatPoint end; - unsigned short splitDepth; -}; - -template -static float curveLength(PathTraversalState& traversalState, CurveType curve) -{ - static const unsigned short curveSplitDepthLimit = 20; - static const double pathSegmentLengthToleranceSquared = 1.e-16; - - double curveScaleForToleranceSquared = curve.magnitudeSquared(); - if (curveScaleForToleranceSquared < pathSegmentLengthToleranceSquared) - return 0; - - Vector curveStack; - curveStack.append(curve); - - float totalLength = 0; - do { - float length = curve.approximateDistance(); - double lengthDiscrepancy = length - distanceLine(curve.start, curve.end); - if ((lengthDiscrepancy * lengthDiscrepancy) / curveScaleForToleranceSquared > pathSegmentLengthToleranceSquared && curve.splitDepth < curveSplitDepthLimit) { - CurveType leftCurve; - CurveType rightCurve; - curve.split(leftCurve, rightCurve); - curve = leftCurve; - curveStack.append(rightCurve); - } else { - totalLength += length; - if (traversalState.m_action == PathTraversalState::TraversalPointAtLength || traversalState.m_action == PathTraversalState::TraversalNormalAngleAtLength) { - traversalState.m_previous = curve.start; - traversalState.m_current = curve.end; - if (traversalState.m_totalLength + totalLength > traversalState.m_desiredLength) - return totalLength; - } - curve = curveStack.last(); - curveStack.removeLast(); - } - } while (!curveStack.isEmpty()); - - return totalLength; + return totalLength; } PathTraversalState::PathTraversalState(PathTraversalAction action) - : m_action(action) - , m_success(false) - , m_totalLength(0) - , m_segmentIndex(0) - , m_desiredLength(0) - , m_normalAngle(0) -{ -} - -float PathTraversalState::closeSubpath() -{ - float distance = distanceLine(m_current, m_start); - m_current = m_start; - return distance; + : m_action(action), + m_success(false), + m_totalLength(0), + m_segmentIndex(0), + m_desiredLength(0), + m_normalAngle(0) {} + +float PathTraversalState::closeSubpath() { + float distance = distanceLine(m_current, m_start); + m_current = m_start; + return distance; } -float PathTraversalState::moveTo(const FloatPoint& point) -{ - m_current = m_start = point; - return 0; +float PathTraversalState::moveTo(const FloatPoint& point) { + m_current = m_start = point; + return 0; } -float PathTraversalState::lineTo(const FloatPoint& point) -{ - float distance = distanceLine(m_current, point); - m_current = point; - return distance; +float PathTraversalState::lineTo(const FloatPoint& point) { + float distance = distanceLine(m_current, point); + m_current = point; + return distance; } -float PathTraversalState::quadraticBezierTo(const FloatPoint& newControl, const FloatPoint& newEnd) -{ - float distance = curveLength(*this, QuadraticBezier(m_current, newControl, newEnd)); +float PathTraversalState::quadraticBezierTo(const FloatPoint& newControl, + const FloatPoint& newEnd) { + float distance = curveLength( + *this, QuadraticBezier(m_current, newControl, newEnd)); - if (m_action != TraversalPointAtLength && m_action != TraversalNormalAngleAtLength) - m_current = newEnd; + if (m_action != TraversalPointAtLength && + m_action != TraversalNormalAngleAtLength) + m_current = newEnd; - return distance; + return distance; } -float PathTraversalState::cubicBezierTo(const FloatPoint& newControl1, const FloatPoint& newControl2, const FloatPoint& newEnd) -{ - float distance = curveLength(*this, CubicBezier(m_current, newControl1, newControl2, newEnd)); +float PathTraversalState::cubicBezierTo(const FloatPoint& newControl1, + const FloatPoint& newControl2, + const FloatPoint& newEnd) { + float distance = curveLength( + *this, CubicBezier(m_current, newControl1, newControl2, newEnd)); - if (m_action != TraversalPointAtLength && m_action != TraversalNormalAngleAtLength) - m_current = newEnd; + if (m_action != TraversalPointAtLength && + m_action != TraversalNormalAngleAtLength) + m_current = newEnd; - return distance; + return distance; } -void PathTraversalState::processSegment() -{ - if (m_action == TraversalSegmentAtLength && m_totalLength >= m_desiredLength) - m_success = true; - - if ((m_action == TraversalPointAtLength || m_action == TraversalNormalAngleAtLength) && m_totalLength >= m_desiredLength) { - float slope = FloatPoint(m_current - m_previous).slopeAngleRadians(); - if (m_action == TraversalPointAtLength) { - float offset = m_desiredLength - m_totalLength; - m_current.move(offset * cosf(slope), offset * sinf(slope)); - } else { - m_normalAngle = rad2deg(slope); - } - m_success = true; +void PathTraversalState::processSegment() { + if (m_action == TraversalSegmentAtLength && m_totalLength >= m_desiredLength) + m_success = true; + + if ((m_action == TraversalPointAtLength || + m_action == TraversalNormalAngleAtLength) && + m_totalLength >= m_desiredLength) { + float slope = FloatPoint(m_current - m_previous).slopeAngleRadians(); + if (m_action == TraversalPointAtLength) { + float offset = m_desiredLength - m_totalLength; + m_current.move(offset * cosf(slope), offset * sinf(slope)); + } else { + m_normalAngle = rad2deg(slope); } - m_previous = m_current; + m_success = true; + } + m_previous = m_current; } -} // namespace blink - +} // namespace blink diff --git a/sky/engine/platform/graphics/PathTraversalState.h b/sky/engine/platform/graphics/PathTraversalState.h index 8a9aed7130530..65d84411c8b1a 100644 --- a/sky/engine/platform/graphics/PathTraversalState.h +++ b/sky/engine/platform/graphics/PathTraversalState.h @@ -32,40 +32,43 @@ namespace blink { class PLATFORM_EXPORT PathTraversalState { -public: - enum PathTraversalAction { - TraversalTotalLength, - TraversalPointAtLength, - TraversalSegmentAtLength, - TraversalNormalAngleAtLength - }; + public: + enum PathTraversalAction { + TraversalTotalLength, + TraversalPointAtLength, + TraversalSegmentAtLength, + TraversalNormalAngleAtLength + }; - PathTraversalState(PathTraversalAction); + PathTraversalState(PathTraversalAction); - float closeSubpath(); - float moveTo(const FloatPoint&); - float lineTo(const FloatPoint&); - float quadraticBezierTo(const FloatPoint& newControl, const FloatPoint& newEnd); - float cubicBezierTo(const FloatPoint& newControl1, const FloatPoint& newControl2, const FloatPoint& newEnd); + float closeSubpath(); + float moveTo(const FloatPoint&); + float lineTo(const FloatPoint&); + float quadraticBezierTo(const FloatPoint& newControl, + const FloatPoint& newEnd); + float cubicBezierTo(const FloatPoint& newControl1, + const FloatPoint& newControl2, + const FloatPoint& newEnd); - void processSegment(); + void processSegment(); -public: - PathTraversalAction m_action; - bool m_success; + public: + PathTraversalAction m_action; + bool m_success; - FloatPoint m_current; - FloatPoint m_start; + FloatPoint m_current; + FloatPoint m_start; - float m_totalLength; - unsigned m_segmentIndex; - float m_desiredLength; + float m_totalLength; + unsigned m_segmentIndex; + float m_desiredLength; - // For normal calculations - FloatPoint m_previous; - float m_normalAngle; // degrees + // For normal calculations + FloatPoint m_previous; + float m_normalAngle; // degrees }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_GRAPHICS_PATHTRAVERSALSTATE_H_ diff --git a/sky/engine/platform/graphics/Pattern.cpp b/sky/engine/platform/graphics/Pattern.cpp index 8632af55a653f..74157d8ea8766 100644 --- a/sky/engine/platform/graphics/Pattern.cpp +++ b/sky/engine/platform/graphics/Pattern.cpp @@ -32,31 +32,26 @@ namespace blink { -PassRefPtr Pattern::createBitmapPattern(PassRefPtr tileImage, RepeatMode repeatMode) -{ - return adoptRef(new Pattern(tileImage, repeatMode)); +PassRefPtr Pattern::createBitmapPattern(PassRefPtr tileImage, + RepeatMode repeatMode) { + return adoptRef(new Pattern(tileImage, repeatMode)); } -Pattern::Pattern(PassRefPtr image, RepeatMode repeatMode) -{ -} +Pattern::Pattern(PassRefPtr image, RepeatMode repeatMode) {} -Pattern::~Pattern() -{ -} +Pattern::~Pattern() {} -SkShader* Pattern::shader() -{ - return m_pattern.get(); +SkShader* Pattern::shader() { + return m_pattern.get(); } -void Pattern::setPatternSpaceTransform(const AffineTransform& patternSpaceTransformation) -{ - if (patternSpaceTransformation == m_patternSpaceTransformation) - return; +void Pattern::setPatternSpaceTransform( + const AffineTransform& patternSpaceTransformation) { + if (patternSpaceTransformation == m_patternSpaceTransformation) + return; - m_patternSpaceTransformation = patternSpaceTransformation; - m_pattern.reset(); + m_patternSpaceTransformation = patternSpaceTransformation; + m_pattern.reset(); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/graphics/Pattern.h b/sky/engine/platform/graphics/Pattern.h index eb573197d47dd..9238e1b78a209 100644 --- a/sky/engine/platform/graphics/Pattern.h +++ b/sky/engine/platform/graphics/Pattern.h @@ -29,8 +29,8 @@ #ifndef SKY_ENGINE_PLATFORM_GRAPHICS_PATTERN_H_ #define SKY_ENGINE_PLATFORM_GRAPHICS_PATTERN_H_ -#include "flutter/sky/engine/platform/graphics/Image.h" #include "flutter/sky/engine/platform/PlatformExport.h" +#include "flutter/sky/engine/platform/graphics/Image.h" #include "flutter/sky/engine/platform/transforms/AffineTransform.h" #include "flutter/sky/engine/wtf/PassRefPtr.h" #include "flutter/sky/engine/wtf/RefCounted.h" @@ -40,29 +40,30 @@ namespace blink { class PLATFORM_EXPORT Pattern : public RefCounted { -public: - enum RepeatMode { - RepeatModeX = 1 << 0, - RepeatModeY = 1 << 1, + public: + enum RepeatMode { + RepeatModeX = 1 << 0, + RepeatModeY = 1 << 1, - RepeatModeNone = 0, - RepeatModeXY = RepeatModeX | RepeatModeY - }; + RepeatModeNone = 0, + RepeatModeXY = RepeatModeX | RepeatModeY + }; - static PassRefPtr createBitmapPattern(PassRefPtr tileImage, - RepeatMode = RepeatModeXY); - ~Pattern(); + static PassRefPtr createBitmapPattern(PassRefPtr tileImage, + RepeatMode = RepeatModeXY); + ~Pattern(); - SkShader* shader(); + SkShader* shader(); - void setPatternSpaceTransform(const AffineTransform& patternSpaceTransformation); + void setPatternSpaceTransform( + const AffineTransform& patternSpaceTransformation); -private: - Pattern(PassRefPtr, RepeatMode); - AffineTransform m_patternSpaceTransformation; - sk_sp m_pattern; + private: + Pattern(PassRefPtr, RepeatMode); + AffineTransform m_patternSpaceTransformation; + sk_sp m_pattern; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_GRAPHICS_PATTERN_H_ diff --git a/sky/engine/platform/graphics/RegionTracker.cpp b/sky/engine/platform/graphics/RegionTracker.cpp index 0d429a99800fb..659d368b6f8e4 100644 --- a/sky/engine/platform/graphics/RegionTracker.cpp +++ b/sky/engine/platform/graphics/RegionTracker.cpp @@ -28,7 +28,6 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #include "flutter/sky/engine/platform/graphics/RegionTracker.h" #include "flutter/sky/engine/platform/graphics/GraphicsContext.h" @@ -38,412 +37,434 @@ namespace blink { RegionTracker::RegionTracker() - : m_opaqueRect(SkRect::MakeEmpty()) - , m_trackedRegionType(Opaque) -{ -} + : m_opaqueRect(SkRect::MakeEmpty()), m_trackedRegionType(Opaque) {} -void RegionTracker::reset() -{ - ASSERT(m_canvasLayerStack.isEmpty()); - m_opaqueRect = SkRect::MakeEmpty(); +void RegionTracker::reset() { + ASSERT(m_canvasLayerStack.isEmpty()); + m_opaqueRect = SkRect::MakeEmpty(); } -IntRect RegionTracker::asRect() const -{ - // Returns the largest enclosed rect. - // TODO: actually, this logic looks like its returning the smallest. - // to return largest, shouldn't we take floor of left/top - // and the ceil of right/bottom? - int left = SkScalarCeilToInt(m_opaqueRect.fLeft); - int top = SkScalarCeilToInt(m_opaqueRect.fTop); - int right = SkScalarFloorToInt(m_opaqueRect.fRight); - int bottom = SkScalarFloorToInt(m_opaqueRect.fBottom); - return IntRect(left, top, right-left, bottom-top); +IntRect RegionTracker::asRect() const { + // Returns the largest enclosed rect. + // TODO: actually, this logic looks like its returning the smallest. + // to return largest, shouldn't we take floor of left/top + // and the ceil of right/bottom? + int left = SkScalarCeilToInt(m_opaqueRect.fLeft); + int top = SkScalarCeilToInt(m_opaqueRect.fTop); + int right = SkScalarFloorToInt(m_opaqueRect.fRight); + int bottom = SkScalarFloorToInt(m_opaqueRect.fBottom); + return IntRect(left, top, right - left, bottom - top); } -// Returns true if the xfermode will force the dst to be opaque, regardless of the current dst. -static inline bool xfermodeIsOpaque(const SkPaint& paint, bool srcIsOpaque) -{ - if (!srcIsOpaque) - return false; - - switch (paint.getBlendMode()) { - case SkBlendMode::kSrc: // source - case SkBlendMode::kSrcOver: // source + dest - source*dest - case SkBlendMode::kDstOver: // source + dest - source*dest - case SkBlendMode::kDstATop: // source - case SkBlendMode::kPlus: // source+dest - default: // the rest are all source + dest - source*dest - return true; - case SkBlendMode::kClear: // 0 - case SkBlendMode::kDst: // dest - case SkBlendMode::kSrcIn: // source * dest - case SkBlendMode::kDstIn: // dest * source - case SkBlendMode::kSrcOut: // source * (1-dest) - case SkBlendMode::kDstOut: // dest * (1-source) - case SkBlendMode::kSrcATop: // dest - case SkBlendMode::kXor: // source + dest - 2*(source*dest) - return false; - } +// Returns true if the xfermode will force the dst to be opaque, regardless of +// the current dst. +static inline bool xfermodeIsOpaque(const SkPaint& paint, bool srcIsOpaque) { + if (!srcIsOpaque) + return false; + + switch (paint.getBlendMode()) { + case SkBlendMode::kSrc: // source + case SkBlendMode::kSrcOver: // source + dest - source*dest + case SkBlendMode::kDstOver: // source + dest - source*dest + case SkBlendMode::kDstATop: // source + case SkBlendMode::kPlus: // source+dest + default: // the rest are all source + dest - source*dest + return true; + case SkBlendMode::kClear: // 0 + case SkBlendMode::kDst: // dest + case SkBlendMode::kSrcIn: // source * dest + case SkBlendMode::kDstIn: // dest * source + case SkBlendMode::kSrcOut: // source * (1-dest) + case SkBlendMode::kDstOut: // dest * (1-source) + case SkBlendMode::kSrcATop: // dest + case SkBlendMode::kXor: // source + dest - 2*(source*dest) + return false; + } } -static inline bool xfermodeIsOverwrite(const SkPaint& paint) -{ - switch (paint.getBlendMode()) { +static inline bool xfermodeIsOverwrite(const SkPaint& paint) { + switch (paint.getBlendMode()) { case SkBlendMode::kSrc: case SkBlendMode::kClear: - return true; + return true; default: - return false; - } + return false; + } } -// Returns true if the xfermode will keep the dst opaque, assuming the dst is already opaque. -static inline bool xfermodePreservesOpaque(const SkPaint& paint, bool srcIsOpaque) -{ - switch (paint.getBlendMode()) { - case SkBlendMode::kDst: // dest - case SkBlendMode::kSrcOver: // source + dest - source*dest - case SkBlendMode::kDstOver: // source + dest - source*dest - case SkBlendMode::kSrcATop: // dest - case SkBlendMode::kPlus: // source+dest - default: // the rest are all source + dest - source*dest - return true; - case SkBlendMode::kClear: // 0 - case SkBlendMode::kSrcOut: // source * (1-dest) - case SkBlendMode::kDstOut: // dest * (1-source) - case SkBlendMode::kXor: // source + dest - 2*(source*dest) - return false; - case SkBlendMode::kSrc: // source - case SkBlendMode::kSrcIn: // source * dest - case SkBlendMode::kDstIn: // dest * source - case SkBlendMode::kDstATop: // source - return srcIsOpaque; - } +// Returns true if the xfermode will keep the dst opaque, assuming the dst is +// already opaque. +static inline bool xfermodePreservesOpaque(const SkPaint& paint, + bool srcIsOpaque) { + switch (paint.getBlendMode()) { + case SkBlendMode::kDst: // dest + case SkBlendMode::kSrcOver: // source + dest - source*dest + case SkBlendMode::kDstOver: // source + dest - source*dest + case SkBlendMode::kSrcATop: // dest + case SkBlendMode::kPlus: // source+dest + default: // the rest are all source + dest - source*dest + return true; + case SkBlendMode::kClear: // 0 + case SkBlendMode::kSrcOut: // source * (1-dest) + case SkBlendMode::kDstOut: // dest * (1-source) + case SkBlendMode::kXor: // source + dest - 2*(source*dest) + return false; + case SkBlendMode::kSrc: // source + case SkBlendMode::kSrcIn: // source * dest + case SkBlendMode::kDstIn: // dest * source + case SkBlendMode::kDstATop: // source + return srcIsOpaque; + } } // Returns true if all pixels painted will be opaque. -static inline bool paintIsOpaque(const SkPaint& paint, RegionTracker::DrawType drawType, const SkBitmap* bitmap) -{ - if (paint.getAlpha() < 0xFF) - return false; - bool checkFillOnly = drawType != RegionTracker::FillOrStroke; - if (!checkFillOnly && paint.getStyle() != SkPaint::kFill_Style && paint.isAntiAlias()) - return false; - SkShader* shader = paint.getShader(); - if (shader && !shader->isOpaque()) - return false; - if (bitmap && !bitmap->isOpaque()) - return false; - if (paint.getLooper()) - return false; - if (paint.getImageFilter()) - return false; - if (paint.getMaskFilter()) - return false; - SkColorFilter* colorFilter = paint.getColorFilter(); - if (colorFilter && !(colorFilter->getFlags() & SkColorFilter::kAlphaUnchanged_Flag)) - return false; - return true; +static inline bool paintIsOpaque(const SkPaint& paint, + RegionTracker::DrawType drawType, + const SkBitmap* bitmap) { + if (paint.getAlpha() < 0xFF) + return false; + bool checkFillOnly = drawType != RegionTracker::FillOrStroke; + if (!checkFillOnly && paint.getStyle() != SkPaint::kFill_Style && + paint.isAntiAlias()) + return false; + SkShader* shader = paint.getShader(); + if (shader && !shader->isOpaque()) + return false; + if (bitmap && !bitmap->isOpaque()) + return false; + if (paint.getLooper()) + return false; + if (paint.getImageFilter()) + return false; + if (paint.getMaskFilter()) + return false; + SkColorFilter* colorFilter = paint.getColorFilter(); + if (colorFilter && + !(colorFilter->getFlags() & SkColorFilter::kAlphaUnchanged_Flag)) + return false; + return true; } -// Returns true if there is a rectangular clip, with the result in |deviceClipRect|. -static inline bool getDeviceClipAsRect(const GraphicsContext* context, SkRect& deviceClipRect) -{ - // Get the current clip in device coordinate space. - if (!context->canvas()->isClipRect()) { - deviceClipRect.setEmpty(); - return false; - } - - SkIRect deviceClipIRect; - if (context->canvas()->getDeviceClipBounds(&deviceClipIRect)) - deviceClipRect.set(deviceClipIRect); - else - deviceClipRect.setEmpty(); - - return true; +// Returns true if there is a rectangular clip, with the result in +// |deviceClipRect|. +static inline bool getDeviceClipAsRect(const GraphicsContext* context, + SkRect& deviceClipRect) { + // Get the current clip in device coordinate space. + if (!context->canvas()->isClipRect()) { + deviceClipRect.setEmpty(); + return false; + } + + SkIRect deviceClipIRect; + if (context->canvas()->getDeviceClipBounds(&deviceClipIRect)) + deviceClipRect.set(deviceClipIRect); + else + deviceClipRect.setEmpty(); + + return true; } -void RegionTracker::pushCanvasLayer(const SkPaint* paint) -{ - CanvasLayerState state; - if (paint) - state.paint = *paint; - m_canvasLayerStack.append(state); +void RegionTracker::pushCanvasLayer(const SkPaint* paint) { + CanvasLayerState state; + if (paint) + state.paint = *paint; + m_canvasLayerStack.append(state); } -void RegionTracker::popCanvasLayer(const GraphicsContext* context) -{ - ASSERT(!m_canvasLayerStack.isEmpty()); - if (m_canvasLayerStack.isEmpty()) - return; - - const CanvasLayerState& canvasLayer = m_canvasLayerStack.last(); - SkRect layerOpaqueRect = canvasLayer.opaqueRect; - SkPaint layerPaint = canvasLayer.paint; +void RegionTracker::popCanvasLayer(const GraphicsContext* context) { + ASSERT(!m_canvasLayerStack.isEmpty()); + if (m_canvasLayerStack.isEmpty()) + return; - // Apply the image mask. - if (canvasLayer.hasImageMask && !layerOpaqueRect.intersect(canvasLayer.imageOpaqueRect)) - layerOpaqueRect.setEmpty(); + const CanvasLayerState& canvasLayer = m_canvasLayerStack.last(); + SkRect layerOpaqueRect = canvasLayer.opaqueRect; + SkPaint layerPaint = canvasLayer.paint; - m_canvasLayerStack.removeLast(); + // Apply the image mask. + if (canvasLayer.hasImageMask && + !layerOpaqueRect.intersect(canvasLayer.imageOpaqueRect)) + layerOpaqueRect.setEmpty(); - applyOpaqueRegionFromLayer(context, layerOpaqueRect, layerPaint); -} + m_canvasLayerStack.removeLast(); -void RegionTracker::setImageMask(const SkRect& imageOpaqueRect) -{ - ASSERT(!m_canvasLayerStack.isEmpty()); - m_canvasLayerStack.last().hasImageMask = true; - m_canvasLayerStack.last().imageOpaqueRect = imageOpaqueRect; + applyOpaqueRegionFromLayer(context, layerOpaqueRect, layerPaint); } -void RegionTracker::didDrawRect(const GraphicsContext* context, const SkRect& fillRect, const SkPaint& paint, const SkBitmap* sourceBitmap) -{ - // Any stroking may put alpha in pixels even if the filling part does not. - if (paint.getStyle() != SkPaint::kFill_Style) { - bool fillsBounds = false; - - if (!paint.canComputeFastBounds()) { - didDrawUnbounded(context, paint, FillOrStroke); - } else { - SkRect strokeRect; - strokeRect = paint.computeFastBounds(fillRect, &strokeRect); - didDraw(context, strokeRect, paint, sourceBitmap, fillsBounds, FillOrStroke); - } - } - - bool fillsBounds = paint.getStyle() != SkPaint::kStroke_Style; - didDraw(context, fillRect, paint, sourceBitmap, fillsBounds, FillOnly); +void RegionTracker::setImageMask(const SkRect& imageOpaqueRect) { + ASSERT(!m_canvasLayerStack.isEmpty()); + m_canvasLayerStack.last().hasImageMask = true; + m_canvasLayerStack.last().imageOpaqueRect = imageOpaqueRect; } -void RegionTracker::didDrawPath(const GraphicsContext* context, const SkPath& path, const SkPaint& paint) -{ - SkRect rect; - if (path.isRect(&rect)) { - didDrawRect(context, rect, paint, 0); - return; - } - +void RegionTracker::didDrawRect(const GraphicsContext* context, + const SkRect& fillRect, + const SkPaint& paint, + const SkBitmap* sourceBitmap) { + // Any stroking may put alpha in pixels even if the filling part does not. + if (paint.getStyle() != SkPaint::kFill_Style) { bool fillsBounds = false; if (!paint.canComputeFastBounds()) { - didDrawUnbounded(context, paint, FillOrStroke); + didDrawUnbounded(context, paint, FillOrStroke); } else { - rect = paint.computeFastBounds(path.getBounds(), &rect); - didDraw(context, rect, paint, 0, fillsBounds, FillOrStroke); + SkRect strokeRect; + strokeRect = paint.computeFastBounds(fillRect, &strokeRect); + didDraw(context, strokeRect, paint, sourceBitmap, fillsBounds, + FillOrStroke); } -} - -void RegionTracker::didDrawPoints(const GraphicsContext* context, SkCanvas::PointMode mode, int numPoints, const SkPoint points[], const SkPaint& paint) -{ - if (!numPoints) - return; + } - SkRect rect; - rect.fLeft = points[0].fX; - rect.fRight = points[0].fX + 1; - rect.fTop = points[0].fY; - rect.fBottom = points[0].fY + 1; - - for (int i = 1; i < numPoints; ++i) { - rect.fLeft = std::min(rect.fLeft, points[i].fX); - rect.fRight = std::max(rect.fRight, points[i].fX + 1); - rect.fTop = std::min(rect.fTop, points[i].fY); - rect.fBottom = std::max(rect.fBottom, points[i].fY + 1); - } - - bool fillsBounds = false; - - if (!paint.canComputeFastBounds()) { - didDrawUnbounded(context, paint, FillOrStroke); - } else { - rect = paint.computeFastBounds(rect, &rect); - didDraw(context, rect, paint, 0, fillsBounds, FillOrStroke); - } + bool fillsBounds = paint.getStyle() != SkPaint::kStroke_Style; + didDraw(context, fillRect, paint, sourceBitmap, fillsBounds, FillOnly); } -void RegionTracker::didDrawBounded(const GraphicsContext* context, const SkRect& bounds, const SkPaint& paint) -{ - bool fillsBounds = false; +void RegionTracker::didDrawPath(const GraphicsContext* context, + const SkPath& path, + const SkPaint& paint) { + SkRect rect; + if (path.isRect(&rect)) { + didDrawRect(context, rect, paint, 0); + return; + } + + bool fillsBounds = false; + + if (!paint.canComputeFastBounds()) { + didDrawUnbounded(context, paint, FillOrStroke); + } else { + rect = paint.computeFastBounds(path.getBounds(), &rect); + didDraw(context, rect, paint, 0, fillsBounds, FillOrStroke); + } +} - if (!paint.canComputeFastBounds()) { - didDrawUnbounded(context, paint, FillOrStroke); - } else { - SkRect rect; - rect = paint.computeFastBounds(bounds, &rect); - didDraw(context, rect, paint, 0, fillsBounds, FillOrStroke); - } +void RegionTracker::didDrawPoints(const GraphicsContext* context, + SkCanvas::PointMode mode, + int numPoints, + const SkPoint points[], + const SkPaint& paint) { + if (!numPoints) + return; + + SkRect rect; + rect.fLeft = points[0].fX; + rect.fRight = points[0].fX + 1; + rect.fTop = points[0].fY; + rect.fBottom = points[0].fY + 1; + + for (int i = 1; i < numPoints; ++i) { + rect.fLeft = std::min(rect.fLeft, points[i].fX); + rect.fRight = std::max(rect.fRight, points[i].fX + 1); + rect.fTop = std::min(rect.fTop, points[i].fY); + rect.fBottom = std::max(rect.fBottom, points[i].fY + 1); + } + + bool fillsBounds = false; + + if (!paint.canComputeFastBounds()) { + didDrawUnbounded(context, paint, FillOrStroke); + } else { + rect = paint.computeFastBounds(rect, &rect); + didDraw(context, rect, paint, 0, fillsBounds, FillOrStroke); + } } -void RegionTracker::didDraw(const GraphicsContext* context, const SkRect& rect, const SkPaint& paint, const SkBitmap* sourceBitmap, bool fillsBounds, DrawType drawType) -{ - SkRect targetRect = rect; - - // Apply the transform to device coordinate space. - SkMatrix canvasTransform = context->canvas()->getTotalMatrix(); - if (!canvasTransform.mapRect(&targetRect)) - fillsBounds = false; - - // Apply the current clip. - SkRect deviceClipRect; - if (!getDeviceClipAsRect(context, deviceClipRect)) - fillsBounds = false; - else if (!targetRect.intersect(deviceClipRect)) - return; - - if (m_trackedRegionType == Overwrite && fillsBounds && xfermodeIsOverwrite(paint)) { - markRectAsOpaque(targetRect); - return; - } +void RegionTracker::didDrawBounded(const GraphicsContext* context, + const SkRect& bounds, + const SkPaint& paint) { + bool fillsBounds = false; - bool drawsOpaque = paintIsOpaque(paint, drawType, sourceBitmap); - bool xfersOpaque = xfermodeIsOpaque(paint, drawsOpaque); + if (!paint.canComputeFastBounds()) { + didDrawUnbounded(context, paint, FillOrStroke); + } else { + SkRect rect; + rect = paint.computeFastBounds(bounds, &rect); + didDraw(context, rect, paint, 0, fillsBounds, FillOrStroke); + } +} - if (fillsBounds && xfersOpaque) { - markRectAsOpaque(targetRect); - } else if (m_trackedRegionType == Opaque && !xfermodePreservesOpaque(paint, drawsOpaque)) { - markRectAsNonOpaque(targetRect); - } +void RegionTracker::didDraw(const GraphicsContext* context, + const SkRect& rect, + const SkPaint& paint, + const SkBitmap* sourceBitmap, + bool fillsBounds, + DrawType drawType) { + SkRect targetRect = rect; + + // Apply the transform to device coordinate space. + SkMatrix canvasTransform = context->canvas()->getTotalMatrix(); + if (!canvasTransform.mapRect(&targetRect)) + fillsBounds = false; + + // Apply the current clip. + SkRect deviceClipRect; + if (!getDeviceClipAsRect(context, deviceClipRect)) + fillsBounds = false; + else if (!targetRect.intersect(deviceClipRect)) + return; + + if (m_trackedRegionType == Overwrite && fillsBounds && + xfermodeIsOverwrite(paint)) { + markRectAsOpaque(targetRect); + return; + } + + bool drawsOpaque = paintIsOpaque(paint, drawType, sourceBitmap); + bool xfersOpaque = xfermodeIsOpaque(paint, drawsOpaque); + + if (fillsBounds && xfersOpaque) { + markRectAsOpaque(targetRect); + } else if (m_trackedRegionType == Opaque && + !xfermodePreservesOpaque(paint, drawsOpaque)) { + markRectAsNonOpaque(targetRect); + } } -void RegionTracker::didDrawUnbounded(const GraphicsContext* context, const SkPaint& paint, DrawType drawType) -{ - bool drawsOpaque = paintIsOpaque(paint, drawType, 0); - bool preservesOpaque = xfermodePreservesOpaque(paint, drawsOpaque); +void RegionTracker::didDrawUnbounded(const GraphicsContext* context, + const SkPaint& paint, + DrawType drawType) { + bool drawsOpaque = paintIsOpaque(paint, drawType, 0); + bool preservesOpaque = xfermodePreservesOpaque(paint, drawsOpaque); - if (preservesOpaque) - return; + if (preservesOpaque) + return; - SkRect deviceClipRect; - getDeviceClipAsRect(context, deviceClipRect); - markRectAsNonOpaque(deviceClipRect); + SkRect deviceClipRect; + getDeviceClipAsRect(context, deviceClipRect); + markRectAsNonOpaque(deviceClipRect); } -void RegionTracker::applyOpaqueRegionFromLayer(const GraphicsContext* context, const SkRect& layerOpaqueRect, const SkPaint& paint) -{ - SkRect deviceClipRect; - bool deviceClipIsARect = getDeviceClipAsRect(context, deviceClipRect); - - if (deviceClipIsARect && deviceClipRect.isEmpty()) - return; - - SkRect sourceOpaqueRect = layerOpaqueRect; - // Save the opaque area in the destination, so we can preserve the parts of it under the source opaque area if possible. - SkRect destinationOpaqueRect = currentTrackingOpaqueRect(); - - bool outsideSourceOpaqueRectPreservesOpaque = xfermodePreservesOpaque(paint, false); - if (!outsideSourceOpaqueRectPreservesOpaque) { - if (!deviceClipIsARect) { - markAllAsNonOpaque(); - return; - } - markRectAsNonOpaque(deviceClipRect); +void RegionTracker::applyOpaqueRegionFromLayer(const GraphicsContext* context, + const SkRect& layerOpaqueRect, + const SkPaint& paint) { + SkRect deviceClipRect; + bool deviceClipIsARect = getDeviceClipAsRect(context, deviceClipRect); + + if (deviceClipIsARect && deviceClipRect.isEmpty()) + return; + + SkRect sourceOpaqueRect = layerOpaqueRect; + // Save the opaque area in the destination, so we can preserve the parts of it + // under the source opaque area if possible. + SkRect destinationOpaqueRect = currentTrackingOpaqueRect(); + + bool outsideSourceOpaqueRectPreservesOpaque = + xfermodePreservesOpaque(paint, false); + if (!outsideSourceOpaqueRectPreservesOpaque) { + if (!deviceClipIsARect) { + markAllAsNonOpaque(); + return; } - - if (!deviceClipIsARect) - return; - if (!sourceOpaqueRect.intersect(deviceClipRect)) - return; - - bool sourceOpaqueRectDrawsOpaque = paintIsOpaque(paint, FillOnly, 0); - bool sourceOpaqueRectXfersOpaque = xfermodeIsOpaque(paint, sourceOpaqueRectDrawsOpaque); - bool sourceOpaqueRectPreservesOpaque = xfermodePreservesOpaque(paint, sourceOpaqueRectDrawsOpaque); - - // If the layer's opaque area is being drawn opaque in the layer below, then mark it opaque. Otherwise, - // if it preserves opaque then keep the intersection of the two. - if (sourceOpaqueRectXfersOpaque) - markRectAsOpaque(sourceOpaqueRect); - else if (sourceOpaqueRectPreservesOpaque && sourceOpaqueRect.intersect(destinationOpaqueRect)) - markRectAsOpaque(sourceOpaqueRect); + markRectAsNonOpaque(deviceClipRect); + } + + if (!deviceClipIsARect) + return; + if (!sourceOpaqueRect.intersect(deviceClipRect)) + return; + + bool sourceOpaqueRectDrawsOpaque = paintIsOpaque(paint, FillOnly, 0); + bool sourceOpaqueRectXfersOpaque = + xfermodeIsOpaque(paint, sourceOpaqueRectDrawsOpaque); + bool sourceOpaqueRectPreservesOpaque = + xfermodePreservesOpaque(paint, sourceOpaqueRectDrawsOpaque); + + // If the layer's opaque area is being drawn opaque in the layer below, then + // mark it opaque. Otherwise, if it preserves opaque then keep the + // intersection of the two. + if (sourceOpaqueRectXfersOpaque) + markRectAsOpaque(sourceOpaqueRect); + else if (sourceOpaqueRectPreservesOpaque && + sourceOpaqueRect.intersect(destinationOpaqueRect)) + markRectAsOpaque(sourceOpaqueRect); } -void RegionTracker::markRectAsOpaque(const SkRect& rect) -{ - // We want to keep track of an opaque region but bound its complexity at a constant size. - // We keep track of the largest rectangle seen by area. If we can add the new rect to this - // rectangle then we do that, as that is the cheapest way to increase the area returned - // without increasing the complexity. - - SkRect& opaqueRect = currentTrackingOpaqueRect(); - - if (rect.isEmpty()) - return; - if (opaqueRect.contains(rect)) - return; - if (rect.contains(opaqueRect)) { - opaqueRect = rect; - return; - } - - if (rect.fTop <= opaqueRect.fTop && rect.fBottom >= opaqueRect.fBottom) { - if (rect.fLeft < opaqueRect.fLeft && rect.fRight >= opaqueRect.fLeft) - opaqueRect.fLeft = rect.fLeft; - if (rect.fRight > opaqueRect.fRight && rect.fLeft <= opaqueRect.fRight) - opaqueRect.fRight = rect.fRight; - } else if (rect.fLeft <= opaqueRect.fLeft && rect.fRight >= opaqueRect.fRight) { - if (rect.fTop < opaqueRect.fTop && rect.fBottom >= opaqueRect.fTop) - opaqueRect.fTop = rect.fTop; - if (rect.fBottom > opaqueRect.fBottom && rect.fTop <= opaqueRect.fBottom) - opaqueRect.fBottom = rect.fBottom; - } - - long opaqueArea = (long)opaqueRect.width() * (long)opaqueRect.height(); - long area = (long)rect.width() * (long)rect.height(); - if (area > opaqueArea) - opaqueRect = rect; +void RegionTracker::markRectAsOpaque(const SkRect& rect) { + // We want to keep track of an opaque region but bound its complexity at a + // constant size. We keep track of the largest rectangle seen by area. If we + // can add the new rect to this rectangle then we do that, as that is the + // cheapest way to increase the area returned without increasing the + // complexity. + + SkRect& opaqueRect = currentTrackingOpaqueRect(); + + if (rect.isEmpty()) + return; + if (opaqueRect.contains(rect)) + return; + if (rect.contains(opaqueRect)) { + opaqueRect = rect; + return; + } + + if (rect.fTop <= opaqueRect.fTop && rect.fBottom >= opaqueRect.fBottom) { + if (rect.fLeft < opaqueRect.fLeft && rect.fRight >= opaqueRect.fLeft) + opaqueRect.fLeft = rect.fLeft; + if (rect.fRight > opaqueRect.fRight && rect.fLeft <= opaqueRect.fRight) + opaqueRect.fRight = rect.fRight; + } else if (rect.fLeft <= opaqueRect.fLeft && + rect.fRight >= opaqueRect.fRight) { + if (rect.fTop < opaqueRect.fTop && rect.fBottom >= opaqueRect.fTop) + opaqueRect.fTop = rect.fTop; + if (rect.fBottom > opaqueRect.fBottom && rect.fTop <= opaqueRect.fBottom) + opaqueRect.fBottom = rect.fBottom; + } + + long opaqueArea = (long)opaqueRect.width() * (long)opaqueRect.height(); + long area = (long)rect.width() * (long)rect.height(); + if (area > opaqueArea) + opaqueRect = rect; } -void RegionTracker::markRectAsNonOpaque(const SkRect& rect) -{ - // We want to keep as much of the current opaque rectangle as we can, so find the one largest - // rectangle inside m_opaqueRect that does not intersect with |rect|. - - SkRect& opaqueRect = currentTrackingOpaqueRect(); - - if (!SkRect::Intersects(rect, opaqueRect)) - return; - if (rect.contains(opaqueRect)) { - markAllAsNonOpaque(); - return; - } - - int deltaLeft = rect.fLeft - opaqueRect.fLeft; - int deltaRight = opaqueRect.fRight - rect.fRight; - int deltaTop = rect.fTop - opaqueRect.fTop; - int deltaBottom = opaqueRect.fBottom - rect.fBottom; - - // horizontal is the larger of the two rectangles to the left or to the right of |rect| and inside opaqueRect. - // vertical is the larger of the two rectangles above or below |rect| and inside opaqueRect. - SkRect horizontal = opaqueRect; - if (deltaTop > deltaBottom) - horizontal.fBottom = rect.fTop; - else - horizontal.fTop = rect.fBottom; - SkRect vertical = opaqueRect; - if (deltaLeft > deltaRight) - vertical.fRight = rect.fLeft; - else - vertical.fLeft = rect.fRight; - - if ((long)horizontal.width() * (long)horizontal.height() > (long)vertical.width() * (long)vertical.height()) - opaqueRect = horizontal; - else - opaqueRect = vertical; +void RegionTracker::markRectAsNonOpaque(const SkRect& rect) { + // We want to keep as much of the current opaque rectangle as we can, so find + // the one largest rectangle inside m_opaqueRect that does not intersect with + // |rect|. + + SkRect& opaqueRect = currentTrackingOpaqueRect(); + + if (!SkRect::Intersects(rect, opaqueRect)) + return; + if (rect.contains(opaqueRect)) { + markAllAsNonOpaque(); + return; + } + + int deltaLeft = rect.fLeft - opaqueRect.fLeft; + int deltaRight = opaqueRect.fRight - rect.fRight; + int deltaTop = rect.fTop - opaqueRect.fTop; + int deltaBottom = opaqueRect.fBottom - rect.fBottom; + + // horizontal is the larger of the two rectangles to the left or to the right + // of |rect| and inside opaqueRect. vertical is the larger of the two + // rectangles above or below |rect| and inside opaqueRect. + SkRect horizontal = opaqueRect; + if (deltaTop > deltaBottom) + horizontal.fBottom = rect.fTop; + else + horizontal.fTop = rect.fBottom; + SkRect vertical = opaqueRect; + if (deltaLeft > deltaRight) + vertical.fRight = rect.fLeft; + else + vertical.fLeft = rect.fRight; + + if ((long)horizontal.width() * (long)horizontal.height() > + (long)vertical.width() * (long)vertical.height()) + opaqueRect = horizontal; + else + opaqueRect = vertical; } -void RegionTracker::markAllAsNonOpaque() -{ - SkRect& opaqueRect = currentTrackingOpaqueRect(); - opaqueRect.setEmpty(); +void RegionTracker::markAllAsNonOpaque() { + SkRect& opaqueRect = currentTrackingOpaqueRect(); + opaqueRect.setEmpty(); } -SkRect& RegionTracker::currentTrackingOpaqueRect() -{ - // If we are drawing into a canvas layer, then track the opaque rect in that layer. - return m_canvasLayerStack.isEmpty() ? m_opaqueRect : m_canvasLayerStack.last().opaqueRect; +SkRect& RegionTracker::currentTrackingOpaqueRect() { + // If we are drawing into a canvas layer, then track the opaque rect in that + // layer. + return m_canvasLayerStack.isEmpty() ? m_opaqueRect + : m_canvasLayerStack.last().opaqueRect; } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/graphics/RegionTracker.h b/sky/engine/platform/graphics/RegionTracker.h index 40b66bcc196b7..c73e34262311b 100644 --- a/sky/engine/platform/graphics/RegionTracker.h +++ b/sky/engine/platform/graphics/RegionTracker.h @@ -42,77 +42,81 @@ namespace blink { class GraphicsContext; -enum RegionTrackingMode { - TrackOpaqueRegion, - TRackOverwriteRegion -}; +enum RegionTrackingMode { TrackOpaqueRegion, TRackOverwriteRegion }; -// This class is an encapsulation of functionality for GraphicsContext, and its methods are mirrored -// there for the outside world. It tracks paints and computes what area will be opaque. +// This class is an encapsulation of functionality for GraphicsContext, and its +// methods are mirrored there for the outside world. It tracks paints and +// computes what area will be opaque. class PLATFORM_EXPORT RegionTracker final { -public: - RegionTracker(); + public: + RegionTracker(); - // The resulting opaque region as a single rect. - IntRect asRect() const; + // The resulting opaque region as a single rect. + IntRect asRect() const; - void pushCanvasLayer(const SkPaint*); - void popCanvasLayer(const GraphicsContext*); + void pushCanvasLayer(const SkPaint*); + void popCanvasLayer(const GraphicsContext*); - void setImageMask(const SkRect& imageOpaqueRect); + void setImageMask(const SkRect& imageOpaqueRect); - enum RegionType { - Opaque, - Overwrite - }; + enum RegionType { Opaque, Overwrite }; - // Set this to true to track regions that occlude the destination instead of only regions that produce opaque pixels. - void setTrackedRegionType(RegionType type) { m_trackedRegionType = type; } + // Set this to true to track regions that occlude the destination instead of + // only regions that produce opaque pixels. + void setTrackedRegionType(RegionType type) { m_trackedRegionType = type; } - enum DrawType { - FillOnly, - FillOrStroke - }; + enum DrawType { FillOnly, FillOrStroke }; - void didDrawRect(const GraphicsContext*, const SkRect&, const SkPaint&, const SkBitmap* sourceBitmap); - void didDrawPath(const GraphicsContext*, const SkPath&, const SkPaint&); - void didDrawPoints(const GraphicsContext*, SkCanvas::PointMode, int numPoints, const SkPoint[], const SkPaint&); - void didDrawBounded(const GraphicsContext*, const SkRect&, const SkPaint&); - void didDrawUnbounded(const GraphicsContext*, const SkPaint&, DrawType); + void didDrawRect(const GraphicsContext*, + const SkRect&, + const SkPaint&, + const SkBitmap* sourceBitmap); + void didDrawPath(const GraphicsContext*, const SkPath&, const SkPaint&); + void didDrawPoints(const GraphicsContext*, + SkCanvas::PointMode, + int numPoints, + const SkPoint[], + const SkPaint&); + void didDrawBounded(const GraphicsContext*, const SkRect&, const SkPaint&); + void didDrawUnbounded(const GraphicsContext*, const SkPaint&, DrawType); - struct CanvasLayerState { - CanvasLayerState() - : hasImageMask(false) - , opaqueRect(SkRect::MakeEmpty()) - { } + struct CanvasLayerState { + CanvasLayerState() : hasImageMask(false), opaqueRect(SkRect::MakeEmpty()) {} - SkPaint paint; + SkPaint paint; - // An image mask is being applied to the layer. - bool hasImageMask; - // The opaque area in the image mask. - SkRect imageOpaqueRect; + // An image mask is being applied to the layer. + bool hasImageMask; + // The opaque area in the image mask. + SkRect imageOpaqueRect; - SkRect opaqueRect; - }; + SkRect opaqueRect; + }; - void reset(); + void reset(); -private: - void didDraw(const GraphicsContext*, const SkRect&, const SkPaint&, const SkBitmap* sourceBitmap, bool fillsBounds, DrawType); - void applyOpaqueRegionFromLayer(const GraphicsContext*, const SkRect& layerOpaqueRect, const SkPaint&); - void markRectAsOpaque(const SkRect&); - void markRectAsNonOpaque(const SkRect&); - void markAllAsNonOpaque(); + private: + void didDraw(const GraphicsContext*, + const SkRect&, + const SkPaint&, + const SkBitmap* sourceBitmap, + bool fillsBounds, + DrawType); + void applyOpaqueRegionFromLayer(const GraphicsContext*, + const SkRect& layerOpaqueRect, + const SkPaint&); + void markRectAsOpaque(const SkRect&); + void markRectAsNonOpaque(const SkRect&); + void markAllAsNonOpaque(); - SkRect& currentTrackingOpaqueRect(); + SkRect& currentTrackingOpaqueRect(); - SkRect m_opaqueRect; - RegionType m_trackedRegionType; + SkRect m_opaqueRect; + RegionType m_trackedRegionType; - Vector m_canvasLayerStack; + Vector m_canvasLayerStack; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_GRAPHICS_REGIONTRACKER_H_ diff --git a/sky/engine/platform/graphics/StrokeData.cpp b/sky/engine/platform/graphics/StrokeData.cpp index dee135abea497..6c40d7c483bdb 100644 --- a/sky/engine/platform/graphics/StrokeData.cpp +++ b/sky/engine/platform/graphics/StrokeData.cpp @@ -34,81 +34,80 @@ namespace blink { -static const int dashRatio = 3; // Ratio of the length of a dash to its width. +static const int dashRatio = 3; // Ratio of the length of a dash to its width. -void StrokeData::setLineDash(const DashArray& dashes, float dashOffset) -{ - // FIXME: This is lifted directly off SkiaSupport, lines 49-74 - // so it is not guaranteed to work correctly. - size_t dashLength = dashes.size(); - if (!dashLength) { - // If no dash is set, revert to solid stroke - // FIXME: do we need to set NoStroke in some cases? - m_style = SolidStroke; - m_dash.reset(); - return; - } +void StrokeData::setLineDash(const DashArray& dashes, float dashOffset) { + // FIXME: This is lifted directly off SkiaSupport, lines 49-74 + // so it is not guaranteed to work correctly. + size_t dashLength = dashes.size(); + if (!dashLength) { + // If no dash is set, revert to solid stroke + // FIXME: do we need to set NoStroke in some cases? + m_style = SolidStroke; + m_dash.reset(); + return; + } - size_t count = !(dashLength % 2) ? dashLength : dashLength * 2; - OwnPtr intervals = adoptArrayPtr(new SkScalar[count]); + size_t count = !(dashLength % 2) ? dashLength : dashLength * 2; + OwnPtr intervals = adoptArrayPtr(new SkScalar[count]); - for (unsigned i = 0; i < count; i++) - intervals[i] = dashes[i % dashLength]; + for (unsigned i = 0; i < count; i++) + intervals[i] = dashes[i % dashLength]; - m_dash = SkDashPathEffect::Make(intervals.get(), count, dashOffset); + m_dash = SkDashPathEffect::Make(intervals.get(), count, dashOffset); } -void StrokeData::setupPaint(SkPaint* paint, int length) const -{ - paint->setStyle(SkPaint::kStroke_Style); - paint->setStrokeWidth(SkFloatToScalar(m_thickness)); - paint->setStrokeCap(m_lineCap); - paint->setStrokeJoin(m_lineJoin); - paint->setStrokeMiter(SkFloatToScalar(m_miterLimit)); +void StrokeData::setupPaint(SkPaint* paint, int length) const { + paint->setStyle(SkPaint::kStroke_Style); + paint->setStrokeWidth(SkFloatToScalar(m_thickness)); + paint->setStrokeCap(m_lineCap); + paint->setStrokeJoin(m_lineJoin); + paint->setStrokeMiter(SkFloatToScalar(m_miterLimit)); - setupPaintDashPathEffect(paint, length); + setupPaintDashPathEffect(paint, length); } -void StrokeData::setupPaintDashPathEffect(SkPaint* paint, int length) const -{ - float width = m_thickness; - if (m_dash) { - paint->setPathEffect(m_dash); - } else { - switch (m_style) { - case NoStroke: - case SolidStroke: - case DoubleStroke: - case WavyStroke: // FIXME: https://code.google.com/p/chromium/issues/detail?id=229574 - paint->setPathEffect(0); - return; - case DashedStroke: - width = dashRatio * width; - // Fall through. - case DottedStroke: - // Truncate the width, since we don't want fuzzy dots or dashes. - int dashLength = static_cast(width); - // Subtract off the endcaps, since they're rendered separately. - int distance = length - 2 * static_cast(m_thickness); - int phase = 1; - if (dashLength > 1) { - // Determine how many dashes or dots we should have. - int numDashes = distance / dashLength; - int remainder = distance % dashLength; - // Adjust the phase to center the dashes within the line. - if (numDashes % 2) { - // Odd: shift right a full dash, minus half the remainder. - phase = dashLength - remainder / 2; - } else { - // Even: shift right half a dash, minus half the remainder. - phase = (dashLength - remainder) / 2; - } - } - SkScalar dashLengthSk = SkIntToScalar(dashLength); - SkScalar intervals[2] = { dashLengthSk, dashLengthSk }; - paint->setPathEffect(SkDashPathEffect::Make(intervals, 2, SkIntToScalar(phase))); +void StrokeData::setupPaintDashPathEffect(SkPaint* paint, int length) const { + float width = m_thickness; + if (m_dash) { + paint->setPathEffect(m_dash); + } else { + switch (m_style) { + case NoStroke: + case SolidStroke: + case DoubleStroke: + case WavyStroke: // FIXME: + // https://code.google.com/p/chromium/issues/detail?id=229574 + paint->setPathEffect(0); + return; + case DashedStroke: + width = dashRatio * width; + // Fall through. + case DottedStroke: + // Truncate the width, since we don't want fuzzy dots or dashes. + int dashLength = static_cast(width); + // Subtract off the endcaps, since they're rendered separately. + int distance = length - 2 * static_cast(m_thickness); + int phase = 1; + if (dashLength > 1) { + // Determine how many dashes or dots we should have. + int numDashes = distance / dashLength; + int remainder = distance % dashLength; + // Adjust the phase to center the dashes within the line. + if (numDashes % 2) { + // Odd: shift right a full dash, minus half the remainder. + phase = dashLength - remainder / 2; + } else { + // Even: shift right half a dash, minus half the remainder. + phase = (dashLength - remainder) / 2; + } } + SkScalar dashLengthSk = SkIntToScalar(dashLength); + SkScalar intervals[2] = {dashLengthSk, dashLengthSk}; + paint->setPathEffect( + SkDashPathEffect::Make(intervals, 2, SkIntToScalar(phase))); } + } } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/graphics/StrokeData.h b/sky/engine/platform/graphics/StrokeData.h index 71bb9eed0c126..775ded88f4796 100644 --- a/sky/engine/platform/graphics/StrokeData.h +++ b/sky/engine/platform/graphics/StrokeData.h @@ -44,68 +44,68 @@ namespace blink { // Encapsulates stroke painting information. // It is pulled out of GraphicsContextState to enable other methods to use it. class PLATFORM_EXPORT StrokeData { -public: - StrokeData() - : m_style(SolidStroke) - , m_thickness(0) - , m_color(Color::black) - , m_lineCap(SkPaint::kDefault_Cap) - , m_lineJoin(SkPaint::kDefault_Join) - , m_miterLimit(4) - { - } - - StrokeStyle style() const { return m_style; } - void setStyle(StrokeStyle style) { m_style = style; } - - float thickness() const { return m_thickness; } - void setThickness(float thickness) { m_thickness = thickness; } - - Color color() const { return m_color; } - void setColor(const Color& color) { m_color = color; } - - Gradient* gradient() const { return m_gradient.get(); } - void setGradient(const PassRefPtr gradient) { m_gradient = gradient; } - void clearGradient() { m_gradient.clear(); } - - Pattern* pattern() const { return m_pattern.get(); } - void setPattern(const PassRefPtr pattern) { m_pattern = pattern; } - void clearPattern() { m_pattern.clear(); } - - LineCap lineCap() const { return (LineCap)m_lineCap; } - void setLineCap(LineCap cap) { m_lineCap = (SkPaint::Cap)cap; } - - LineJoin lineJoin() const { return (LineJoin)m_lineJoin; } - void setLineJoin(LineJoin join) { m_lineJoin = (SkPaint::Join)join; } - - float miterLimit() const { return m_miterLimit; } - void setMiterLimit(float miterLimit) { m_miterLimit = miterLimit; } - - void setLineDash(const DashArray&, float); - - // Sets everything on the paint except the pattern, gradient and color. - // If a non-zero length is provided, the number of dashes/dots on a - // dashed/dotted line will be adjusted to start and end that length with a - // dash/dot. - void setupPaint(SkPaint*, int length = 0) const; - - // Setup any DashPathEffect on the paint. If a non-zero length is provided, - // and no line dash has been set, the number of dashes/dots on a dashed/dotted - // line will be adjusted to start and end that length with a dash/dot. - void setupPaintDashPathEffect(SkPaint*, int) const; - -private: - StrokeStyle m_style; - float m_thickness; - Color m_color; - RefPtr m_gradient; - RefPtr m_pattern; - SkPaint::Cap m_lineCap; - SkPaint::Join m_lineJoin; - float m_miterLimit; - sk_sp m_dash; + public: + StrokeData() + : m_style(SolidStroke), + m_thickness(0), + m_color(Color::black), + m_lineCap(SkPaint::kDefault_Cap), + m_lineJoin(SkPaint::kDefault_Join), + m_miterLimit(4) {} + + StrokeStyle style() const { return m_style; } + void setStyle(StrokeStyle style) { m_style = style; } + + float thickness() const { return m_thickness; } + void setThickness(float thickness) { m_thickness = thickness; } + + Color color() const { return m_color; } + void setColor(const Color& color) { m_color = color; } + + Gradient* gradient() const { return m_gradient.get(); } + void setGradient(const PassRefPtr gradient) { + m_gradient = gradient; + } + void clearGradient() { m_gradient.clear(); } + + Pattern* pattern() const { return m_pattern.get(); } + void setPattern(const PassRefPtr pattern) { m_pattern = pattern; } + void clearPattern() { m_pattern.clear(); } + + LineCap lineCap() const { return (LineCap)m_lineCap; } + void setLineCap(LineCap cap) { m_lineCap = (SkPaint::Cap)cap; } + + LineJoin lineJoin() const { return (LineJoin)m_lineJoin; } + void setLineJoin(LineJoin join) { m_lineJoin = (SkPaint::Join)join; } + + float miterLimit() const { return m_miterLimit; } + void setMiterLimit(float miterLimit) { m_miterLimit = miterLimit; } + + void setLineDash(const DashArray&, float); + + // Sets everything on the paint except the pattern, gradient and color. + // If a non-zero length is provided, the number of dashes/dots on a + // dashed/dotted line will be adjusted to start and end that length with a + // dash/dot. + void setupPaint(SkPaint*, int length = 0) const; + + // Setup any DashPathEffect on the paint. If a non-zero length is provided, + // and no line dash has been set, the number of dashes/dots on a dashed/dotted + // line will be adjusted to start and end that length with a dash/dot. + void setupPaintDashPathEffect(SkPaint*, int) const; + + private: + StrokeStyle m_style; + float m_thickness; + Color m_color; + RefPtr m_gradient; + RefPtr m_pattern; + SkPaint::Cap m_lineCap; + SkPaint::Join m_lineJoin; + float m_miterLimit; + sk_sp m_dash; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_GRAPHICS_STROKEDATA_H_ diff --git a/sky/engine/platform/graphics/skia/SkSizeHash.h b/sky/engine/platform/graphics/skia/SkSizeHash.h index adf2d7ae1e4e0..be296df121aee 100644 --- a/sky/engine/platform/graphics/skia/SkSizeHash.h +++ b/sky/engine/platform/graphics/skia/SkSizeHash.h @@ -32,54 +32,60 @@ namespace WTF { -template<> struct IntHash { - static unsigned hash(const SkSize& key) { return pairIntHash(key.width(), key.height()); } - static bool equal(const SkSize& a, const SkSize& b) { return a == b; } - static const bool safeToCompareToEmptyOrDeleted = true; +template <> +struct IntHash { + static unsigned hash(const SkSize& key) { + return pairIntHash(key.width(), key.height()); + } + static bool equal(const SkSize& a, const SkSize& b) { return a == b; } + static const bool safeToCompareToEmptyOrDeleted = true; }; -template<> struct DefaultHash { - typedef IntHash Hash; +template <> +struct DefaultHash { + typedef IntHash Hash; }; -template<> struct HashTraits : GenericHashTraits { - static const bool emptyValueIsZero = true; - static const bool needsDestruction = false; - static SkSize emptyValue() { return SkSize::Make(0, 0); } - static void constructDeletedValue(SkSize& slot, bool) - { - slot = SkSize::Make(-1, -1); - } - static bool isDeletedValue(const SkSize& value) - { - return value.width() == -1 && value.height() == -1; - } +template <> +struct HashTraits : GenericHashTraits { + static const bool emptyValueIsZero = true; + static const bool needsDestruction = false; + static SkSize emptyValue() { return SkSize::Make(0, 0); } + static void constructDeletedValue(SkSize& slot, bool) { + slot = SkSize::Make(-1, -1); + } + static bool isDeletedValue(const SkSize& value) { + return value.width() == -1 && value.height() == -1; + } }; -template<> struct IntHash { - static unsigned hash(const SkISize& key) { return pairIntHash(key.width(), key.height()); } - static bool equal(const SkISize& a, const SkISize& b) { return a == b; } - static const bool safeToCompareToEmptyOrDeleted = true; +template <> +struct IntHash { + static unsigned hash(const SkISize& key) { + return pairIntHash(key.width(), key.height()); + } + static bool equal(const SkISize& a, const SkISize& b) { return a == b; } + static const bool safeToCompareToEmptyOrDeleted = true; }; -template<> struct DefaultHash { - typedef IntHash Hash; +template <> +struct DefaultHash { + typedef IntHash Hash; }; -template<> struct HashTraits : GenericHashTraits { - static const bool emptyValueIsZero = true; - static const bool needsDestruction = false; - static SkISize emptyValue() { return SkISize::Make(0, 0); } - static void constructDeletedValue(SkISize& slot, bool) - { - slot = SkISize::Make(-1, -1); - } - static bool isDeletedValue(const SkISize& value) - { - return value.width() == -1 && value.height() == -1; - } +template <> +struct HashTraits : GenericHashTraits { + static const bool emptyValueIsZero = true; + static const bool needsDestruction = false; + static SkISize emptyValue() { return SkISize::Make(0, 0); } + static void constructDeletedValue(SkISize& slot, bool) { + slot = SkISize::Make(-1, -1); + } + static bool isDeletedValue(const SkISize& value) { + return value.width() == -1 && value.height() == -1; + } }; -} // namespace WTF +} // namespace WTF #endif // SKY_ENGINE_PLATFORM_GRAPHICS_SKIA_SKSIZEHASH_H_ diff --git a/sky/engine/platform/graphics/skia/SkiaUtils.cpp b/sky/engine/platform/graphics/skia/SkiaUtils.cpp index 66a34c3896350..fc0b1f58befae 100644 --- a/sky/engine/platform/graphics/skia/SkiaUtils.cpp +++ b/sky/engine/platform/graphics/skia/SkiaUtils.cpp @@ -28,7 +28,6 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #include "flutter/sky/engine/platform/graphics/skia/SkiaUtils.h" #include "flutter/sky/engine/platform/graphics/GraphicsContext.h" @@ -38,278 +37,293 @@ namespace blink { static const struct CompositOpToXfermodeMode { - CompositeOperator mCompositOp; - SkBlendMode m_xfermodeMode; + CompositeOperator mCompositOp; + SkBlendMode m_xfermodeMode; } gMapCompositOpsToXfermodeModes[] = { - { CompositeClear, SkBlendMode::kClear }, - { CompositeCopy, SkBlendMode::kSrc }, - { CompositeSourceOver, SkBlendMode::kSrcOver }, - { CompositeSourceIn, SkBlendMode::kSrcIn }, - { CompositeSourceOut, SkBlendMode::kSrcOut }, - { CompositeSourceAtop, SkBlendMode::kSrcATop }, - { CompositeDestinationOver, SkBlendMode::kDstOver }, - { CompositeDestinationIn, SkBlendMode::kDstIn }, - { CompositeDestinationOut, SkBlendMode::kDstOut }, - { CompositeDestinationAtop, SkBlendMode::kDstATop }, - { CompositeXOR, SkBlendMode::kXor }, - { CompositePlusDarker, SkBlendMode::kDarken }, - { CompositePlusLighter, SkBlendMode::kPlus } -}; - -// keep this array in sync with WebBlendMode enum in public/platform/WebBlendMode.h + {CompositeClear, SkBlendMode::kClear}, + {CompositeCopy, SkBlendMode::kSrc}, + {CompositeSourceOver, SkBlendMode::kSrcOver}, + {CompositeSourceIn, SkBlendMode::kSrcIn}, + {CompositeSourceOut, SkBlendMode::kSrcOut}, + {CompositeSourceAtop, SkBlendMode::kSrcATop}, + {CompositeDestinationOver, SkBlendMode::kDstOver}, + {CompositeDestinationIn, SkBlendMode::kDstIn}, + {CompositeDestinationOut, SkBlendMode::kDstOut}, + {CompositeDestinationAtop, SkBlendMode::kDstATop}, + {CompositeXOR, SkBlendMode::kXor}, + {CompositePlusDarker, SkBlendMode::kDarken}, + {CompositePlusLighter, SkBlendMode::kPlus}}; + +// keep this array in sync with WebBlendMode enum in +// public/platform/WebBlendMode.h static const SkBlendMode gMapBlendOpsToXfermodeModes[] = { - SkBlendMode::kClear, // WebBlendModeNormal - SkBlendMode::kMultiply, // WebBlendModeMultiply - SkBlendMode::kScreen, // WebBlendModeScreen - SkBlendMode::kOverlay, // WebBlendModeOverlay - SkBlendMode::kDarken, // WebBlendModeDarken - SkBlendMode::kLighten, // WebBlendModeLighten - SkBlendMode::kColorDodge, // WebBlendModeColorDodge - SkBlendMode::kColorBurn, // WebBlendModeColorBurn - SkBlendMode::kHardLight, // WebBlendModeHardLight - SkBlendMode::kSoftLight, // WebBlendModeSoftLight - SkBlendMode::kDifference, // WebBlendModeDifference - SkBlendMode::kExclusion, // WebBlendModeExclusion - SkBlendMode::kHue, // WebBlendModeHue - SkBlendMode::kSaturation, // WebBlendModeSaturation - SkBlendMode::kColor, // WebBlendModeColor - SkBlendMode::kLuminosity // WebBlendModeLuminosity + SkBlendMode::kClear, // WebBlendModeNormal + SkBlendMode::kMultiply, // WebBlendModeMultiply + SkBlendMode::kScreen, // WebBlendModeScreen + SkBlendMode::kOverlay, // WebBlendModeOverlay + SkBlendMode::kDarken, // WebBlendModeDarken + SkBlendMode::kLighten, // WebBlendModeLighten + SkBlendMode::kColorDodge, // WebBlendModeColorDodge + SkBlendMode::kColorBurn, // WebBlendModeColorBurn + SkBlendMode::kHardLight, // WebBlendModeHardLight + SkBlendMode::kSoftLight, // WebBlendModeSoftLight + SkBlendMode::kDifference, // WebBlendModeDifference + SkBlendMode::kExclusion, // WebBlendModeExclusion + SkBlendMode::kHue, // WebBlendModeHue + SkBlendMode::kSaturation, // WebBlendModeSaturation + SkBlendMode::kColor, // WebBlendModeColor + SkBlendMode::kLuminosity // WebBlendModeLuminosity }; -SkBlendMode WebCoreCompositeToSkiaComposite(CompositeOperator op, WebBlendMode blendMode) -{ - if (blendMode != WebBlendModeNormal) { - if (static_cast(blendMode) >= SK_ARRAY_COUNT(gMapBlendOpsToXfermodeModes)) { - SkDEBUGF(("GraphicsContext::setPlatformCompositeOperation unknown WebBlendMode %d\n", blendMode)); - return SkBlendMode::kSrcOver; - } - return gMapBlendOpsToXfermodeModes[static_cast(blendMode)]; - } - - const CompositOpToXfermodeMode* table = gMapCompositOpsToXfermodeModes; - if (static_cast(op) >= SK_ARRAY_COUNT(gMapCompositOpsToXfermodeModes)) { - SkDEBUGF(("GraphicsContext::setPlatformCompositeOperation unknown CompositeOperator %d\n", op)); - return SkBlendMode::kSrcOver; +SkBlendMode WebCoreCompositeToSkiaComposite(CompositeOperator op, + WebBlendMode blendMode) { + if (blendMode != WebBlendModeNormal) { + if (static_cast(blendMode) >= + SK_ARRAY_COUNT(gMapBlendOpsToXfermodeModes)) { + SkDEBUGF( + ("GraphicsContext::setPlatformCompositeOperation unknown " + "WebBlendMode %d\n", + blendMode)); + return SkBlendMode::kSrcOver; } - SkASSERT(table[static_cast(op)].mCompositOp == op); - return table[static_cast(op)].m_xfermodeMode; + return gMapBlendOpsToXfermodeModes[static_cast(blendMode)]; + } + + const CompositOpToXfermodeMode* table = gMapCompositOpsToXfermodeModes; + if (static_cast(op) >= + SK_ARRAY_COUNT(gMapCompositOpsToXfermodeModes)) { + SkDEBUGF( + ("GraphicsContext::setPlatformCompositeOperation unknown " + "CompositeOperator %d\n", + op)); + return SkBlendMode::kSrcOver; + } + SkASSERT(table[static_cast(op)].mCompositOp == op); + return table[static_cast(op)].m_xfermodeMode; } -static U8CPU InvScaleByte(U8CPU component, uint32_t scale) -{ - SkASSERT(component == (uint8_t)component); - return (component * scale + 0x8000) >> 16; +static U8CPU InvScaleByte(U8CPU component, uint32_t scale) { + SkASSERT(component == (uint8_t)component); + return (component * scale + 0x8000) >> 16; } -SkColor SkPMColorToColor(SkPMColor pm) -{ - if (!pm) - return 0; - unsigned a = SkGetPackedA32(pm); - if (!a) { - // A zero alpha value when there are non-zero R, G, or B channels is an - // invalid premultiplied color (since all channels should have been - // multiplied by 0 if a=0). - SkASSERT(false); - // In production, return 0 to protect against division by zero. - return 0; - } - - uint32_t scale = (255 << 16) / a; - - return SkColorSetARGB(a, - InvScaleByte(SkGetPackedR32(pm), scale), - InvScaleByte(SkGetPackedG32(pm), scale), - InvScaleByte(SkGetPackedB32(pm), scale)); +SkColor SkPMColorToColor(SkPMColor pm) { + if (!pm) + return 0; + unsigned a = SkGetPackedA32(pm); + if (!a) { + // A zero alpha value when there are non-zero R, G, or B channels is an + // invalid premultiplied color (since all channels should have been + // multiplied by 0 if a=0). + SkASSERT(false); + // In production, return 0 to protect against division by zero. + return 0; + } + + uint32_t scale = (255 << 16) / a; + + return SkColorSetARGB(a, InvScaleByte(SkGetPackedR32(pm), scale), + InvScaleByte(SkGetPackedG32(pm), scale), + InvScaleByte(SkGetPackedB32(pm), scale)); } -bool SkPathContainsPoint(const SkPath& originalPath, const FloatPoint& point, SkPath::FillType ft) -{ - SkRect bounds = originalPath.getBounds(); - - // We can immediately return false if the point is outside the bounding - // rect. We don't use bounds.contains() here, since it would exclude - // points on the right and bottom edges of the bounding rect, and we want - // to include them. - SkScalar fX = SkFloatToScalar(point.x()); - SkScalar fY = SkFloatToScalar(point.y()); - if (fX < bounds.fLeft || fX > bounds.fRight || fY < bounds.fTop || fY > bounds.fBottom) - return false; - - // Scale the path to a large size before hit testing for two reasons: - // 1) Skia has trouble with coordinates close to the max signed 16-bit values, so we scale larger paths down. - // TODO: when Skia is patched to work properly with large values, this will not be necessary. - // 2) Skia does not support analytic hit testing, so we scale paths up to do raster hit testing with subpixel accuracy. - SkScalar biggestCoord = std::max(std::max(std::max(bounds.fRight, bounds.fBottom), -bounds.fLeft), -bounds.fTop); - if (SkScalarNearlyZero(biggestCoord)) - return false; - biggestCoord = std::max(std::max(biggestCoord, fX + 1), fY + 1); - - const SkScalar kMaxCoordinate = SkIntToScalar(1 << 15); - SkScalar scale = kMaxCoordinate / biggestCoord; - - SkRegion rgn; - SkRegion clip; - SkMatrix m; - SkPath scaledPath(originalPath); - - scaledPath.setFillType(ft); - m.setScale(scale, scale); - scaledPath.transform(m, 0); - - int x = static_cast(floorf(0.5f + point.x() * scale)); - int y = static_cast(floorf(0.5f + point.y() * scale)); - clip.setRect(x - 1, y - 1, x + 1, y + 1); - - return rgn.setPath(scaledPath, clip); +bool SkPathContainsPoint(const SkPath& originalPath, + const FloatPoint& point, + SkPath::FillType ft) { + SkRect bounds = originalPath.getBounds(); + + // We can immediately return false if the point is outside the bounding + // rect. We don't use bounds.contains() here, since it would exclude + // points on the right and bottom edges of the bounding rect, and we want + // to include them. + SkScalar fX = SkFloatToScalar(point.x()); + SkScalar fY = SkFloatToScalar(point.y()); + if (fX < bounds.fLeft || fX > bounds.fRight || fY < bounds.fTop || + fY > bounds.fBottom) + return false; + + // Scale the path to a large size before hit testing for two reasons: + // 1) Skia has trouble with coordinates close to the max signed 16-bit values, + // so we scale larger paths down. + // TODO: when Skia is patched to work properly with large values, this will + // not be necessary. + // 2) Skia does not support analytic hit testing, so we scale paths up to do + // raster hit testing with subpixel accuracy. + SkScalar biggestCoord = + std::max(std::max(std::max(bounds.fRight, bounds.fBottom), -bounds.fLeft), + -bounds.fTop); + if (SkScalarNearlyZero(biggestCoord)) + return false; + biggestCoord = std::max(std::max(biggestCoord, fX + 1), fY + 1); + + const SkScalar kMaxCoordinate = SkIntToScalar(1 << 15); + SkScalar scale = kMaxCoordinate / biggestCoord; + + SkRegion rgn; + SkRegion clip; + SkMatrix m; + SkPath scaledPath(originalPath); + + scaledPath.setFillType(ft); + m.setScale(scale, scale); + scaledPath.transform(m, 0); + + int x = static_cast(floorf(0.5f + point.x() * scale)); + int y = static_cast(floorf(0.5f + point.y() * scale)); + clip.setRect(x - 1, y - 1, x + 1, y + 1); + + return rgn.setPath(scaledPath, clip); } -SkMatrix affineTransformToSkMatrix(const AffineTransform& source) -{ - SkMatrix result; +SkMatrix affineTransformToSkMatrix(const AffineTransform& source) { + SkMatrix result; - result.setScaleX(WebCoreDoubleToSkScalar(source.a())); - result.setSkewX(WebCoreDoubleToSkScalar(source.c())); - result.setTranslateX(WebCoreDoubleToSkScalar(source.e())); + result.setScaleX(WebCoreDoubleToSkScalar(source.a())); + result.setSkewX(WebCoreDoubleToSkScalar(source.c())); + result.setTranslateX(WebCoreDoubleToSkScalar(source.e())); - result.setScaleY(WebCoreDoubleToSkScalar(source.d())); - result.setSkewY(WebCoreDoubleToSkScalar(source.b())); - result.setTranslateY(WebCoreDoubleToSkScalar(source.f())); + result.setScaleY(WebCoreDoubleToSkScalar(source.d())); + result.setSkewY(WebCoreDoubleToSkScalar(source.b())); + result.setTranslateY(WebCoreDoubleToSkScalar(source.f())); - // FIXME: Set perspective properly. - result.setPerspX(0); - result.setPerspY(0); - result.set(SkMatrix::kMPersp2, SK_Scalar1); + // FIXME: Set perspective properly. + result.setPerspX(0); + result.setPerspY(0); + result.set(SkMatrix::kMPersp2, SK_Scalar1); - return result; + return result; } -bool nearlyIntegral(float value) -{ - return fabs(value - floorf(value)) < std::numeric_limits::epsilon(); +bool nearlyIntegral(float value) { + return fabs(value - floorf(value)) < std::numeric_limits::epsilon(); } -InterpolationQuality limitInterpolationQuality(const GraphicsContext* context, InterpolationQuality resampling) -{ - return std::min(resampling, context->imageInterpolationQuality()); +InterpolationQuality limitInterpolationQuality( + const GraphicsContext* context, + InterpolationQuality resampling) { + return std::min(resampling, context->imageInterpolationQuality()); } -InterpolationQuality computeInterpolationQuality( - const SkMatrix& matrix, - float srcWidth, - float srcHeight, - float destWidth, - float destHeight, - bool isDataComplete) -{ - // The percent change below which we will not resample. This usually means - // an off-by-one error on the web page, and just doing nearest neighbor - // sampling is usually good enough. - const float kFractionalChangeThreshold = 0.025f; - - // Images smaller than this in either direction are considered "small" and - // are not resampled ever (see below). - const int kSmallImageSizeThreshold = 8; - - // The amount an image can be stretched in a single direction before we - // say that it is being stretched so much that it must be a line or - // background that doesn't need resampling. - const float kLargeStretch = 3.0f; - - // Figure out if we should resample this image. We try to prune out some - // common cases where resampling won't give us anything, since it is much - // slower than drawing stretched. - float diffWidth = fabs(destWidth - srcWidth); - float diffHeight = fabs(destHeight - srcHeight); - bool widthNearlyEqual = diffWidth < std::numeric_limits::epsilon(); - bool heightNearlyEqual = diffHeight < std::numeric_limits::epsilon(); - // We don't need to resample if the source and destination are the same. - if (widthNearlyEqual && heightNearlyEqual) - return InterpolationNone; - - if (srcWidth <= kSmallImageSizeThreshold - || srcHeight <= kSmallImageSizeThreshold - || destWidth <= kSmallImageSizeThreshold - || destHeight <= kSmallImageSizeThreshold) { - // Small image detected. - - // Resample in the case where the new size would be non-integral. - // This can cause noticeable breaks in repeating patterns, except - // when the source image is only one pixel wide in that dimension. - if ((!nearlyIntegral(destWidth) && srcWidth > 1 + std::numeric_limits::epsilon()) - || (!nearlyIntegral(destHeight) && srcHeight > 1 + std::numeric_limits::epsilon())) - return InterpolationLow; - - // Otherwise, don't resample small images. These are often used for - // borders and rules (think 1x1 images used to make lines). - return InterpolationNone; - } - - if (srcHeight * kLargeStretch <= destHeight || srcWidth * kLargeStretch <= destWidth) { - // Large image detected. - - // Don't resample if it is being stretched a lot in only one direction. - // This is trying to catch cases where somebody has created a border - // (which might be large) and then is stretching it to fill some part - // of the page. - if (widthNearlyEqual || heightNearlyEqual) - return InterpolationNone; - - // The image is growing a lot and in more than one direction. Resampling - // is slow and doesn't give us very much when growing a lot. - return InterpolationLow; - } - - if ((diffWidth / srcWidth < kFractionalChangeThreshold) - && (diffHeight / srcHeight < kFractionalChangeThreshold)) { - // It is disappointingly common on the web for image sizes to be off by - // one or two pixels. We don't bother resampling if the size difference - // is a small fraction of the original size. - return InterpolationNone; - } - - // When the image is not yet done loading, use linear. We don't cache the - // partially resampled images, and as they come in incrementally, it causes - // us to have to resample the whole thing every time. - if (!isDataComplete) - return InterpolationLow; +InterpolationQuality computeInterpolationQuality(const SkMatrix& matrix, + float srcWidth, + float srcHeight, + float destWidth, + float destHeight, + bool isDataComplete) { + // The percent change below which we will not resample. This usually means + // an off-by-one error on the web page, and just doing nearest neighbor + // sampling is usually good enough. + const float kFractionalChangeThreshold = 0.025f; + + // Images smaller than this in either direction are considered "small" and + // are not resampled ever (see below). + const int kSmallImageSizeThreshold = 8; + + // The amount an image can be stretched in a single direction before we + // say that it is being stretched so much that it must be a line or + // background that doesn't need resampling. + const float kLargeStretch = 3.0f; + + // Figure out if we should resample this image. We try to prune out some + // common cases where resampling won't give us anything, since it is much + // slower than drawing stretched. + float diffWidth = fabs(destWidth - srcWidth); + float diffHeight = fabs(destHeight - srcHeight); + bool widthNearlyEqual = diffWidth < std::numeric_limits::epsilon(); + bool heightNearlyEqual = diffHeight < std::numeric_limits::epsilon(); + // We don't need to resample if the source and destination are the same. + if (widthNearlyEqual && heightNearlyEqual) + return InterpolationNone; + + if (srcWidth <= kSmallImageSizeThreshold || + srcHeight <= kSmallImageSizeThreshold || + destWidth <= kSmallImageSizeThreshold || + destHeight <= kSmallImageSizeThreshold) { + // Small image detected. + + // Resample in the case where the new size would be non-integral. + // This can cause noticeable breaks in repeating patterns, except + // when the source image is only one pixel wide in that dimension. + if ((!nearlyIntegral(destWidth) && + srcWidth > 1 + std::numeric_limits::epsilon()) || + (!nearlyIntegral(destHeight) && + srcHeight > 1 + std::numeric_limits::epsilon())) + return InterpolationLow; + + // Otherwise, don't resample small images. These are often used for + // borders and rules (think 1x1 images used to make lines). + return InterpolationNone; + } + + if (srcHeight * kLargeStretch <= destHeight || + srcWidth * kLargeStretch <= destWidth) { + // Large image detected. + + // Don't resample if it is being stretched a lot in only one direction. + // This is trying to catch cases where somebody has created a border + // (which might be large) and then is stretching it to fill some part + // of the page. + if (widthNearlyEqual || heightNearlyEqual) + return InterpolationNone; + + // The image is growing a lot and in more than one direction. Resampling + // is slow and doesn't give us very much when growing a lot. + return InterpolationLow; + } + + if ((diffWidth / srcWidth < kFractionalChangeThreshold) && + (diffHeight / srcHeight < kFractionalChangeThreshold)) { + // It is disappointingly common on the web for image sizes to be off by + // one or two pixels. We don't bother resampling if the size difference + // is a small fraction of the original size. + return InterpolationNone; + } + + // When the image is not yet done loading, use linear. We don't cache the + // partially resampled images, and as they come in incrementally, it causes + // us to have to resample the whole thing every time. + if (!isDataComplete) + return InterpolationLow; - // Everything else gets resampled. - // High quality interpolation only enabled for scaling and translation. - if (!(matrix.getType() & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask))) - return InterpolationHigh; + // Everything else gets resampled. + // High quality interpolation only enabled for scaling and translation. + if (!(matrix.getType() & + (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask))) + return InterpolationHigh; - return InterpolationLow; + return InterpolationLow; } - -bool shouldDrawAntiAliased(const GraphicsContext* context, const SkRect& destRect) -{ - if (!context->shouldAntialias()) - return false; - const SkMatrix totalMatrix = context->getTotalMatrix(); - // Don't disable anti-aliasing if we're rotated or skewed. - if (!totalMatrix.rectStaysRect()) - return true; - // Disable anti-aliasing for scales or n*90 degree rotations. - // Allow to opt out of the optimization though for "hairline" geometry - // images - using the shouldAntialiasHairlineImages() GraphicsContext flag. - if (!context->shouldAntialiasHairlineImages()) - return false; - // Check if the dimensions of the destination are "small" (less than one - // device pixel). To prevent sudden drop-outs. Since we know that - // kRectStaysRect_Mask is set, the matrix either has scale and no skew or - // vice versa. We can query the kAffine_Mask flag to determine which case - // it is. - // FIXME: This queries the CTM while drawing, which is generally - // discouraged. Always drawing with AA can negatively impact performance - // though - that's why it's not always on. - SkScalar widthExpansion, heightExpansion; - if (totalMatrix.getType() & SkMatrix::kAffine_Mask) - widthExpansion = totalMatrix[SkMatrix::kMSkewY], heightExpansion = totalMatrix[SkMatrix::kMSkewX]; - else - widthExpansion = totalMatrix[SkMatrix::kMScaleX], heightExpansion = totalMatrix[SkMatrix::kMScaleY]; - return destRect.width() * fabs(widthExpansion) < 1 || destRect.height() * fabs(heightExpansion) < 1; +bool shouldDrawAntiAliased(const GraphicsContext* context, + const SkRect& destRect) { + if (!context->shouldAntialias()) + return false; + const SkMatrix totalMatrix = context->getTotalMatrix(); + // Don't disable anti-aliasing if we're rotated or skewed. + if (!totalMatrix.rectStaysRect()) + return true; + // Disable anti-aliasing for scales or n*90 degree rotations. + // Allow to opt out of the optimization though for "hairline" geometry + // images - using the shouldAntialiasHairlineImages() GraphicsContext flag. + if (!context->shouldAntialiasHairlineImages()) + return false; + // Check if the dimensions of the destination are "small" (less than one + // device pixel). To prevent sudden drop-outs. Since we know that + // kRectStaysRect_Mask is set, the matrix either has scale and no skew or + // vice versa. We can query the kAffine_Mask flag to determine which case + // it is. + // FIXME: This queries the CTM while drawing, which is generally + // discouraged. Always drawing with AA can negatively impact performance + // though - that's why it's not always on. + SkScalar widthExpansion, heightExpansion; + if (totalMatrix.getType() & SkMatrix::kAffine_Mask) + widthExpansion = totalMatrix[SkMatrix::kMSkewY], + heightExpansion = totalMatrix[SkMatrix::kMSkewX]; + else + widthExpansion = totalMatrix[SkMatrix::kMScaleX], + heightExpansion = totalMatrix[SkMatrix::kMScaleY]; + return destRect.width() * fabs(widthExpansion) < 1 || + destRect.height() * fabs(heightExpansion) < 1; } } // namespace blink diff --git a/sky/engine/platform/graphics/skia/SkiaUtils.h b/sky/engine/platform/graphics/skia/SkiaUtils.h index ea4f27e60c44e..91cf887b08dd1 100644 --- a/sky/engine/platform/graphics/skia/SkiaUtils.h +++ b/sky/engine/platform/graphics/skia/SkiaUtils.h @@ -28,7 +28,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -// All of the functions in this file should move to new homes and this file should be deleted. +// All of the functions in this file should move to new homes and this file +// should be deleted. #ifndef SKY_ENGINE_PLATFORM_GRAPHICS_SKIA_SKIAUTILS_H_ #define SKY_ENGINE_PLATFORM_GRAPHICS_SKIA_SKIAUTILS_H_ @@ -48,64 +49,66 @@ namespace blink { class GraphicsContext; -SkBlendMode PLATFORM_EXPORT WebCoreCompositeToSkiaComposite(CompositeOperator, WebBlendMode = WebBlendModeNormal); +SkBlendMode PLATFORM_EXPORT + WebCoreCompositeToSkiaComposite(CompositeOperator, + WebBlendMode = WebBlendModeNormal); // move this guy into SkColor.h SkColor SkPMColorToColor(SkPMColor); -inline SkFilterQuality WebCoreInterpolationQualityToSkFilterQuality(InterpolationQuality quality) -{ - // FIXME: this reflects existing client mappings, but should probably - // be expanded to map higher level interpolations more accurately. - return quality != InterpolationNone ? kLow_SkFilterQuality : kNone_SkFilterQuality; +inline SkFilterQuality WebCoreInterpolationQualityToSkFilterQuality( + InterpolationQuality quality) { + // FIXME: this reflects existing client mappings, but should probably + // be expanded to map higher level interpolations more accurately. + return quality != InterpolationNone ? kLow_SkFilterQuality + : kNone_SkFilterQuality; } // Skia has problems when passed infinite, etc floats, filter them to 0. -inline SkScalar WebCoreFloatToSkScalar(float f) -{ - return SkFloatToScalar(std::isfinite(f) ? f : 0); +inline SkScalar WebCoreFloatToSkScalar(float f) { + return SkFloatToScalar(std::isfinite(f) ? f : 0); } -inline SkScalar WebCoreDoubleToSkScalar(double d) -{ - return SkDoubleToScalar(std::isfinite(d) ? d : 0); +inline SkScalar WebCoreDoubleToSkScalar(double d) { + return SkDoubleToScalar(std::isfinite(d) ? d : 0); } -inline SkRect WebCoreFloatRectToSKRect(const FloatRect& rect) -{ - return SkRect::MakeLTRB(SkFloatToScalar(rect.x()), SkFloatToScalar(rect.y()), - SkFloatToScalar(rect.maxX()), SkFloatToScalar(rect.maxY())); +inline SkRect WebCoreFloatRectToSKRect(const FloatRect& rect) { + return SkRect::MakeLTRB(SkFloatToScalar(rect.x()), SkFloatToScalar(rect.y()), + SkFloatToScalar(rect.maxX()), + SkFloatToScalar(rect.maxY())); } -inline bool WebCoreFloatNearlyEqual(float a, float b) -{ - return SkScalarNearlyEqual(WebCoreFloatToSkScalar(a), WebCoreFloatToSkScalar(b)); +inline bool WebCoreFloatNearlyEqual(float a, float b) { + return SkScalarNearlyEqual(WebCoreFloatToSkScalar(a), + WebCoreFloatToSkScalar(b)); } -inline SkPath::FillType WebCoreWindRuleToSkFillType(WindRule rule) -{ - return static_cast(rule); +inline SkPath::FillType WebCoreWindRuleToSkFillType(WindRule rule) { + return static_cast(rule); } // Determine if a given WebKit point is contained in a path -bool PLATFORM_EXPORT SkPathContainsPoint(const SkPath&, const FloatPoint&, SkPath::FillType); +bool PLATFORM_EXPORT SkPathContainsPoint(const SkPath&, + const FloatPoint&, + SkPath::FillType); SkMatrix PLATFORM_EXPORT affineTransformToSkMatrix(const AffineTransform&); bool nearlyIntegral(float value); -InterpolationQuality limitInterpolationQuality(const GraphicsContext*, InterpolationQuality resampling); +InterpolationQuality limitInterpolationQuality(const GraphicsContext*, + InterpolationQuality resampling); -InterpolationQuality computeInterpolationQuality( - const SkMatrix&, - float srcWidth, - float srcHeight, - float destWidth, - float destHeight, - bool isDataComplete = true); +InterpolationQuality computeInterpolationQuality(const SkMatrix&, + float srcWidth, + float srcHeight, + float destWidth, + float destHeight, + bool isDataComplete = true); bool shouldDrawAntiAliased(const GraphicsContext*, const SkRect& destRect); -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_GRAPHICS_SKIA_SKIAUTILS_H_ diff --git a/sky/engine/platform/heap/Handle.h b/sky/engine/platform/heap/Handle.h index d746b61448018..6af362eabc027 100644 --- a/sky/engine/platform/heap/Handle.h +++ b/sky/engine/platform/heap/Handle.h @@ -43,22 +43,23 @@ // This disallows general allocation of this object but allows to put // the object as a value object in collections. // -#define DISALLOW_ALLOCATION() \ - private: \ - void* operator new(size_t) = delete; \ - void* operator new(size_t, NotNullTag, void*) = delete; \ - void* operator new(size_t, void*) = delete; +#define DISALLOW_ALLOCATION() \ + private: \ + void* operator new(size_t) = delete; \ + void* operator new(size_t, NotNullTag, void*) = delete; \ + void* operator new(size_t, void*) = delete; -#define ALLOW_ONLY_INLINE_ALLOCATION() \ - public: \ - void* operator new(size_t, NotNullTag, void* location) { return location; } \ - void* operator new(size_t, void* location) { return location; } \ - private: \ - void* operator new(size_t) = delete; +#define ALLOW_ONLY_INLINE_ALLOCATION() \ + public: \ + void* operator new(size_t, NotNullTag, void* location) { return location; } \ + void* operator new(size_t, void* location) { return location; } \ + \ + private: \ + void* operator new(size_t) = delete; #define STATIC_ONLY(Type) \ - private: \ - Type() = delete; + private: \ + Type() = delete; // These macros insert annotations that the Blink GC plugin for clang uses for // verification. STACK_ALLOCATED is used to declare that objects of this type @@ -67,30 +68,32 @@ // GC_PLUGIN_IGNORE a bug-number should be provided as an argument where the // bug describes what needs to happen to remove the GC_PLUGIN_IGNORE again. #if COMPILER(CLANG) -#define STACK_ALLOCATED() \ - private: \ - __attribute__((annotate("blink_stack_allocated"))) \ - void* operator new(size_t) = delete; \ - void* operator new(size_t, NotNullTag, void*) = delete; \ - void* operator new(size_t, void*) = delete; +#define STACK_ALLOCATED() \ + private: \ + __attribute__((annotate("blink_stack_allocated"))) void* operator new( \ + size_t) = delete; \ + void* operator new(size_t, NotNullTag, void*) = delete; \ + void* operator new(size_t, void*) = delete; #else #define STACK_ALLOCATED() DISALLOW_ALLOCATION() #endif #define DECLARE_EMPTY_DESTRUCTOR_WILL_BE_REMOVED(type) \ - public: \ - ~type(); \ - private: + public: \ + ~type(); \ + \ + private: #define DECLARE_EMPTY_VIRTUAL_DESTRUCTOR_WILL_BE_REMOVED(type) \ - public: \ - virtual ~type(); \ - private: + public: \ + virtual ~type(); \ + \ + private: #define DEFINE_EMPTY_DESTRUCTOR_WILL_BE_REMOVED(type) \ - type::~type() { } + type::~type() {} #define DEFINE_STATIC_REF_WILL_BE_PERSISTENT(type, name, arguments) \ - DEFINE_STATIC_REF(type, name, arguments) + DEFINE_STATIC_REF(type, name, arguments) #endif // SKY_ENGINE_PLATFORM_HEAP_HANDLE_H_ diff --git a/sky/engine/platform/testing/RunAllTests.cpp b/sky/engine/platform/testing/RunAllTests.cpp index 1349920e33a0c..3ee6c6c9dccd4 100644 --- a/sky/engine/platform/testing/RunAllTests.cpp +++ b/sky/engine/platform/testing/RunAllTests.cpp @@ -28,7 +28,6 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #include #include "base/test/test_suite.h" #include "flutter/sky/engine/platform/Partitions.h" @@ -36,16 +35,15 @@ #include "flutter/sky/engine/wtf/MainThread.h" #include "flutter/sky/engine/wtf/WTF.h" -int main(int argc, char** argv) -{ - WTF::initialize(); - WTF::initializeMainThread(); +int main(int argc, char** argv) { + WTF::initialize(); + WTF::initializeMainThread(); - blink::TestingPlatformSupport::Config platformConfig; - blink::TestingPlatformSupport platform(platformConfig); + blink::TestingPlatformSupport::Config platformConfig; + blink::TestingPlatformSupport platform(platformConfig); - blink::Partitions::init(); - int result = base::RunUnitTestsUsingBaseTestSuite(argc, argv); - blink::Partitions::shutdown(); - return result; + blink::Partitions::init(); + int result = base::RunUnitTestsUsingBaseTestSuite(argc, argv); + blink::Partitions::shutdown(); + return result; } diff --git a/sky/engine/platform/text/BidiCharacterRun.cpp b/sky/engine/platform/text/BidiCharacterRun.cpp index ca7e75b545aae..7be0f82932559 100644 --- a/sky/engine/platform/text/BidiCharacterRun.cpp +++ b/sky/engine/platform/text/BidiCharacterRun.cpp @@ -30,22 +30,22 @@ using namespace WTF; namespace blink { -DEFINE_DEBUG_ONLY_GLOBAL(RefCountedLeakCounter, bidiRunCounter, ("BidiCharacterRun")); +DEFINE_DEBUG_ONLY_GLOBAL(RefCountedLeakCounter, + bidiRunCounter, + ("BidiCharacterRun")); -void* BidiCharacterRun::operator new(size_t sz) -{ +void* BidiCharacterRun::operator new(size_t sz) { #ifndef NDEBUG - bidiRunCounter.increment(); + bidiRunCounter.increment(); #endif - return partitionAlloc(Partitions::getRenderingPartition(), sz); + return partitionAlloc(Partitions::getRenderingPartition(), sz); } -void BidiCharacterRun::operator delete(void* ptr) -{ +void BidiCharacterRun::operator delete(void* ptr) { #ifndef NDEBUG - bidiRunCounter.decrement(); + bidiRunCounter.decrement(); #endif - partitionFree(ptr); + partitionFree(ptr); } -} +} // namespace blink diff --git a/sky/engine/platform/text/BidiCharacterRun.h b/sky/engine/platform/text/BidiCharacterRun.h index 44a3b79b0830f..1862fc6dbba67 100644 --- a/sky/engine/platform/text/BidiCharacterRun.h +++ b/sky/engine/platform/text/BidiCharacterRun.h @@ -28,54 +28,62 @@ namespace blink { struct BidiCharacterRun { - BidiCharacterRun(int start, int stop, BidiContext* context, WTF::Unicode::Direction dir) - : m_override(context->override()) - , m_next(0) - , m_start(start) - , m_stop(stop) - { - ASSERT(m_start <= m_stop); - if (dir == WTF::Unicode::OtherNeutral) - dir = context->dir(); + BidiCharacterRun(int start, + int stop, + BidiContext* context, + WTF::Unicode::Direction dir) + : m_override(context->override()), + m_next(0), + m_start(start), + m_stop(stop) { + ASSERT(m_start <= m_stop); + if (dir == WTF::Unicode::OtherNeutral) + dir = context->dir(); - m_level = context->level(); + m_level = context->level(); - // add level of run (cases I1 & I2) - if (m_level % 2) { - if (dir == WTF::Unicode::LeftToRight || dir == WTF::Unicode::ArabicNumber || dir == WTF::Unicode::EuropeanNumber) - m_level++; - } else { - if (dir == WTF::Unicode::RightToLeft) - m_level++; - else if (dir == WTF::Unicode::ArabicNumber || dir == WTF::Unicode::EuropeanNumber) - m_level += 2; - } + // add level of run (cases I1 & I2) + if (m_level % 2) { + if (dir == WTF::Unicode::LeftToRight || + dir == WTF::Unicode::ArabicNumber || + dir == WTF::Unicode::EuropeanNumber) + m_level++; + } else { + if (dir == WTF::Unicode::RightToLeft) + m_level++; + else if (dir == WTF::Unicode::ArabicNumber || + dir == WTF::Unicode::EuropeanNumber) + m_level += 2; } + } - // BidiCharacterRun are allocated out of the rendering partition. - PLATFORM_EXPORT void* operator new(size_t); - PLATFORM_EXPORT void operator delete(void*); + // BidiCharacterRun are allocated out of the rendering partition. + PLATFORM_EXPORT void* operator new(size_t); + PLATFORM_EXPORT void operator delete(void*); - int start() const { return m_start; } - int stop() const { return m_stop; } - unsigned char level() const { return m_level; } - bool reversed() const { return m_level % 2; } - bool dirOverride() { return m_override; } - TextDirection direction() const { return reversed() ? RTL : LTR; } + int start() const { return m_start; } + int stop() const { return m_stop; } + unsigned char level() const { return m_level; } + bool reversed() const { return m_level % 2; } + bool dirOverride() { return m_override; } + TextDirection direction() const { return reversed() ? RTL : LTR; } - BidiCharacterRun* next() const { return m_next; } - void setNext(BidiCharacterRun* next) { m_next = next; } + BidiCharacterRun* next() const { return m_next; } + void setNext(BidiCharacterRun* next) { m_next = next; } - // Do not add anything apart from bitfields until after m_next. See https://bugs.webkit.org/show_bug.cgi?id=100173 - bool m_override : 1; - bool m_hasHyphen : 1; // Used by BidiRun subclass which is a layering violation but enables us to save 8 bytes per object on 64-bit. - bool m_hasAddedEllipsis : 1; - unsigned char m_level; - BidiCharacterRun* m_next; - int m_start; - int m_stop; + // Do not add anything apart from bitfields until after m_next. See + // https://bugs.webkit.org/show_bug.cgi?id=100173 + bool m_override : 1; + bool m_hasHyphen : 1; // Used by BidiRun subclass which is a layering + // violation but enables us to save 8 bytes per object + // on 64-bit. + bool m_hasAddedEllipsis : 1; + unsigned char m_level; + BidiCharacterRun* m_next; + int m_start; + int m_stop; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_TEXT_BIDICHARACTERRUN_H_ diff --git a/sky/engine/platform/text/BidiContext.cpp b/sky/engine/platform/text/BidiContext.cpp index ebb2cc9955916..e49403dca0f43 100644 --- a/sky/engine/platform/text/BidiContext.cpp +++ b/sky/engine/platform/text/BidiContext.cpp @@ -1,6 +1,7 @@ /* * Copyright (C) 2000 Lars Knoll (knoll@kde.org) - * Copyright (C) 2003, 2004, 2006, 2007, 2009, 2010 Apple Inc. All right reserved. + * Copyright (C) 2003, 2004, 2006, 2007, 2009, 2010 Apple Inc. + * All right reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -29,84 +30,103 @@ namespace blink { using namespace WTF::Unicode; struct SameSizeAsBidiContext : public RefCounted { - uint32_t bitfields : 16; - void* parent; + uint32_t bitfields : 16; + void* parent; }; -COMPILE_ASSERT(sizeof(BidiContext) == sizeof(SameSizeAsBidiContext), BidiContext_should_stay_small); +COMPILE_ASSERT(sizeof(BidiContext) == sizeof(SameSizeAsBidiContext), + BidiContext_should_stay_small); -inline PassRefPtr BidiContext::createUncached(unsigned char level, Direction direction, bool override, BidiEmbeddingSource source, BidiContext* parent) -{ - return adoptRef(new BidiContext(level, direction, override, source, parent)); +inline PassRefPtr BidiContext::createUncached( + unsigned char level, + Direction direction, + bool override, + BidiEmbeddingSource source, + BidiContext* parent) { + return adoptRef(new BidiContext(level, direction, override, source, parent)); } -PassRefPtr BidiContext::create(unsigned char level, Direction direction, bool override, BidiEmbeddingSource source, BidiContext* parent) -{ - ASSERT(direction == (level % 2 ? RightToLeft : LeftToRight)); +PassRefPtr BidiContext::create(unsigned char level, + Direction direction, + bool override, + BidiEmbeddingSource source, + BidiContext* parent) { + ASSERT(direction == (level % 2 ? RightToLeft : LeftToRight)); - if (parent) - return createUncached(level, direction, override, source, parent); - - ASSERT(level <= 1); - if (!level) { - if (!override) { - DEFINE_STATIC_REF(BidiContext, ltrContext, (createUncached(0, LeftToRight, false, FromStyleOrDOM, 0))); - return ltrContext; - } - - DEFINE_STATIC_REF(BidiContext, ltrOverrideContext, (createUncached(0, LeftToRight, true, FromStyleOrDOM, 0))); - return ltrOverrideContext; - } + if (parent) + return createUncached(level, direction, override, source, parent); + ASSERT(level <= 1); + if (!level) { if (!override) { - DEFINE_STATIC_REF(BidiContext, rtlContext, (createUncached(1, RightToLeft, false, FromStyleOrDOM, 0))); - return rtlContext; + DEFINE_STATIC_REF( + BidiContext, ltrContext, + (createUncached(0, LeftToRight, false, FromStyleOrDOM, 0))); + return ltrContext; } - DEFINE_STATIC_REF(BidiContext, rtlOverrideContext, (createUncached(1, RightToLeft, true, FromStyleOrDOM, 0))); - return rtlOverrideContext; + DEFINE_STATIC_REF( + BidiContext, ltrOverrideContext, + (createUncached(0, LeftToRight, true, FromStyleOrDOM, 0))); + return ltrOverrideContext; + } + + if (!override) { + DEFINE_STATIC_REF( + BidiContext, rtlContext, + (createUncached(1, RightToLeft, false, FromStyleOrDOM, 0))); + return rtlContext; + } + + DEFINE_STATIC_REF(BidiContext, rtlOverrideContext, + (createUncached(1, RightToLeft, true, FromStyleOrDOM, 0))); + return rtlOverrideContext; } -static inline PassRefPtr copyContextAndRebaselineLevel(BidiContext* context, BidiContext* parent) -{ - ASSERT(context); - unsigned char newLevel = parent ? parent->level() : 0; - if (context->dir() == RightToLeft) - newLevel = nextGreaterOddLevel(newLevel); - else if (parent) - newLevel = nextGreaterEvenLevel(newLevel); - - return BidiContext::create(newLevel, context->dir(), context->override(), context->source(), parent); +static inline PassRefPtr copyContextAndRebaselineLevel( + BidiContext* context, + BidiContext* parent) { + ASSERT(context); + unsigned char newLevel = parent ? parent->level() : 0; + if (context->dir() == RightToLeft) + newLevel = nextGreaterOddLevel(newLevel); + else if (parent) + newLevel = nextGreaterEvenLevel(newLevel); + + return BidiContext::create(newLevel, context->dir(), context->override(), + context->source(), parent); } -// The BidiContext stack must be immutable -- they're re-used for re-layout after -// DOM modification/editing -- so we copy all the non-unicode contexts, and -// recalculate their levels. -PassRefPtr BidiContext::copyStackRemovingUnicodeEmbeddingContexts() -{ - Vector contexts; - for (BidiContext* iter = this; iter; iter = iter->parent()) { - if (iter->source() != FromUnicode) - contexts.append(iter); - } - ASSERT(contexts.size()); - - RefPtr topContext = copyContextAndRebaselineLevel(contexts.last(), 0); - for (int i = contexts.size() - 1; i > 0; --i) - topContext = copyContextAndRebaselineLevel(contexts[i - 1], topContext.get()); - - return topContext.release(); +// The BidiContext stack must be immutable -- they're re-used for re-layout +// after DOM modification/editing -- so we copy all the non-unicode contexts, +// and recalculate their levels. +PassRefPtr +BidiContext::copyStackRemovingUnicodeEmbeddingContexts() { + Vector contexts; + for (BidiContext* iter = this; iter; iter = iter->parent()) { + if (iter->source() != FromUnicode) + contexts.append(iter); + } + ASSERT(contexts.size()); + + RefPtr topContext = + copyContextAndRebaselineLevel(contexts.last(), 0); + for (int i = contexts.size() - 1; i > 0; --i) + topContext = + copyContextAndRebaselineLevel(contexts[i - 1], topContext.get()); + + return topContext.release(); } -bool operator==(const BidiContext& c1, const BidiContext& c2) -{ - if (&c1 == &c2) - return true; - if (c1.level() != c2.level() || c1.override() != c2.override() || c1.dir() != c2.dir() || c1.source() != c2.source()) - return false; - if (!c1.parent()) - return !c2.parent(); - return c2.parent() && *c1.parent() == *c2.parent(); +bool operator==(const BidiContext& c1, const BidiContext& c2) { + if (&c1 == &c2) + return true; + if (c1.level() != c2.level() || c1.override() != c2.override() || + c1.dir() != c2.dir() || c1.source() != c2.source()) + return false; + if (!c1.parent()) + return !c2.parent(); + return c2.parent() && *c1.parent() == *c2.parent(); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/text/BidiContext.h b/sky/engine/platform/text/BidiContext.h index d48dcad81bb37..71e041a7aff63 100644 --- a/sky/engine/platform/text/BidiContext.h +++ b/sky/engine/platform/text/BidiContext.h @@ -1,6 +1,7 @@ /* * Copyright (C) 2000 Lars Knoll (knoll@kde.org) - * Copyright (C) 2003, 2004, 2006, 2007, 2009, 2010 Apple Inc. All right reserved. + * Copyright (C) 2003, 2004, 2006, 2007, 2009, 2010 Apple Inc. + * All right reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -31,61 +32,73 @@ namespace blink { -enum BidiEmbeddingSource { - FromStyleOrDOM, - FromUnicode -}; +enum BidiEmbeddingSource { FromStyleOrDOM, FromUnicode }; // Used to keep track of explicit embeddings. class PLATFORM_EXPORT BidiContext : public RefCounted { -public: - static PassRefPtr create(unsigned char level, WTF::Unicode::Direction, bool override = false, BidiEmbeddingSource = FromStyleOrDOM, BidiContext* parent = 0); - - BidiContext* parent() const { return m_parent.get(); } - unsigned char level() const { return m_level; } - WTF::Unicode::Direction dir() const { return static_cast(m_direction); } - bool override() const { return m_override; } - BidiEmbeddingSource source() const { return static_cast(m_source); } - - PassRefPtr copyStackRemovingUnicodeEmbeddingContexts(); - - // http://www.unicode.org/reports/tr9/#Modifications - // 6.3 raised the limit from 61 to 125. - // http://unicode.org/reports/tr9/#BD2 - static const unsigned char kMaxLevel = 125; - -private: - BidiContext(unsigned char level, WTF::Unicode::Direction direction, bool override, BidiEmbeddingSource source, BidiContext* parent) - : m_level(level) - , m_direction(direction) - , m_override(override) - , m_source(source) - , m_parent(parent) - { - ASSERT(level <= kMaxLevel); - } - - static PassRefPtr createUncached(unsigned char level, WTF::Unicode::Direction, bool override, BidiEmbeddingSource, BidiContext* parent); - - unsigned m_level : 7; // The maximium bidi level is 125: http://unicode.org/reports/tr9/#Explicit_Levels_and_Directions - unsigned m_direction : 5; // Direction - unsigned m_override : 1; - unsigned m_source : 1; // BidiEmbeddingSource - RefPtr m_parent; + public: + static PassRefPtr create(unsigned char level, + WTF::Unicode::Direction, + bool override = false, + BidiEmbeddingSource = FromStyleOrDOM, + BidiContext* parent = 0); + + BidiContext* parent() const { return m_parent.get(); } + unsigned char level() const { return m_level; } + WTF::Unicode::Direction dir() const { + return static_cast(m_direction); + } + bool override() const { return m_override; } + BidiEmbeddingSource source() const { + return static_cast(m_source); + } + + PassRefPtr copyStackRemovingUnicodeEmbeddingContexts(); + + // http://www.unicode.org/reports/tr9/#Modifications + // 6.3 raised the limit from 61 to 125. + // http://unicode.org/reports/tr9/#BD2 + static const unsigned char kMaxLevel = 125; + + private: + BidiContext(unsigned char level, + WTF::Unicode::Direction direction, + bool override, + BidiEmbeddingSource source, + BidiContext* parent) + : m_level(level), + m_direction(direction), + m_override(override), + m_source(source), + m_parent(parent) { + ASSERT(level <= kMaxLevel); + } + + static PassRefPtr createUncached(unsigned char level, + WTF::Unicode::Direction, + bool override, + BidiEmbeddingSource, + BidiContext* parent); + + unsigned + m_level : 7; // The maximium bidi level is 125: + // http://unicode.org/reports/tr9/#Explicit_Levels_and_Directions + unsigned m_direction : 5; // Direction + unsigned m_override : 1; + unsigned m_source : 1; // BidiEmbeddingSource + RefPtr m_parent; }; -inline unsigned char nextGreaterOddLevel(unsigned char level) -{ - return (level + 1) | 1; +inline unsigned char nextGreaterOddLevel(unsigned char level) { + return (level + 1) | 1; } -inline unsigned char nextGreaterEvenLevel(unsigned char level) -{ - return (level + 2) & ~1; +inline unsigned char nextGreaterEvenLevel(unsigned char level) { + return (level + 2) & ~1; } PLATFORM_EXPORT bool operator==(const BidiContext&, const BidiContext&); -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_TEXT_BIDICONTEXT_H_ diff --git a/sky/engine/platform/text/BidiResolver.h b/sky/engine/platform/text/BidiResolver.h index ab500cec96414..be1068ced8b42 100644 --- a/sky/engine/platform/text/BidiResolver.h +++ b/sky/engine/platform/text/BidiResolver.h @@ -35,534 +35,578 @@ namespace blink { class RenderObject; -template class MidpointState { -public: - MidpointState() - { - reset(); - } - - void reset() - { - m_numMidpoints = 0; - m_currentMidpoint = 0; - m_betweenMidpoints = false; - } - - void startIgnoringSpaces(const Iterator& midpoint) - { - ASSERT(!(m_numMidpoints % 2)); - addMidpoint(midpoint); - } - - void stopIgnoringSpaces(const Iterator& midpoint) - { - ASSERT(m_numMidpoints % 2); - addMidpoint(midpoint); - } - - // When ignoring spaces, this needs to be called for objects that need line boxes such as RenderInlines or - // hard line breaks to ensure that they're not ignored. - void ensureLineBoxInsideIgnoredSpaces(RenderObject* renderer) - { - Iterator midpoint(0, renderer, 0); - stopIgnoringSpaces(midpoint); - startIgnoringSpaces(midpoint); - } - - // Adding a pair of midpoints before a character will split it out into a new line box. - void ensureCharacterGetsLineBox(Iterator& textParagraphSeparator) - { - startIgnoringSpaces(Iterator(0, textParagraphSeparator.object(), textParagraphSeparator.offset() - 1)); - stopIgnoringSpaces(Iterator(0, textParagraphSeparator.object(), textParagraphSeparator.offset())); - } - - void checkMidpoints(Iterator& lBreak) - { - // Check to see if our last midpoint is a start point beyond the line break. If so, - // shave it off the list, and shave off a trailing space if the previous end point doesn't - // preserve whitespace. - if (lBreak.object() && m_numMidpoints && !(m_numMidpoints % 2)) { - Iterator* midpointsIterator = m_midpoints.data(); - Iterator& endpoint = midpointsIterator[m_numMidpoints - 2]; - const Iterator& startpoint = midpointsIterator[m_numMidpoints - 1]; - Iterator currpoint = endpoint; - while (!currpoint.atEnd() && currpoint != startpoint && currpoint != lBreak) - currpoint.increment(); - if (currpoint == lBreak) { - // We hit the line break before the start point. Shave off the start point. - m_numMidpoints--; - if (endpoint.object()->style()->collapseWhiteSpace() && endpoint.object()->isText()) - endpoint.setOffset(endpoint.offset() - 1); - } - } - } - - Vector& midpoints() { return m_midpoints; } - const unsigned& numMidpoints() const { return m_numMidpoints; } - const unsigned& currentMidpoint() const { return m_currentMidpoint; } - void incrementCurrentMidpoint() { m_currentMidpoint++; } - const bool& betweenMidpoints() const { return m_betweenMidpoints; } - void setBetweenMidpoints(bool betweenMidpoint) { m_betweenMidpoints = betweenMidpoint; } -private: - // The goal is to reuse the line state across multiple - // lines so we just keep an array around for midpoints and never clear it across multiple - // lines. We track the number of items and position using the two other variables. - Vector m_midpoints; - unsigned m_numMidpoints; - unsigned m_currentMidpoint; - bool m_betweenMidpoints; - - void addMidpoint(const Iterator& midpoint) - { - if (m_midpoints.size() <= m_numMidpoints) - m_midpoints.grow(m_numMidpoints + 10); - - Iterator* midpointsIterator = m_midpoints.data(); - midpointsIterator[m_numMidpoints++] = midpoint; +template +class MidpointState { + public: + MidpointState() { reset(); } + + void reset() { + m_numMidpoints = 0; + m_currentMidpoint = 0; + m_betweenMidpoints = false; + } + + void startIgnoringSpaces(const Iterator& midpoint) { + ASSERT(!(m_numMidpoints % 2)); + addMidpoint(midpoint); + } + + void stopIgnoringSpaces(const Iterator& midpoint) { + ASSERT(m_numMidpoints % 2); + addMidpoint(midpoint); + } + + // When ignoring spaces, this needs to be called for objects that need line + // boxes such as RenderInlines or hard line breaks to ensure that they're not + // ignored. + void ensureLineBoxInsideIgnoredSpaces(RenderObject* renderer) { + Iterator midpoint(0, renderer, 0); + stopIgnoringSpaces(midpoint); + startIgnoringSpaces(midpoint); + } + + // Adding a pair of midpoints before a character will split it out into a new + // line box. + void ensureCharacterGetsLineBox(Iterator& textParagraphSeparator) { + startIgnoringSpaces(Iterator(0, textParagraphSeparator.object(), + textParagraphSeparator.offset() - 1)); + stopIgnoringSpaces(Iterator(0, textParagraphSeparator.object(), + textParagraphSeparator.offset())); + } + + void checkMidpoints(Iterator& lBreak) { + // Check to see if our last midpoint is a start point beyond the line break. + // If so, shave it off the list, and shave off a trailing space if the + // previous end point doesn't preserve whitespace. + if (lBreak.object() && m_numMidpoints && !(m_numMidpoints % 2)) { + Iterator* midpointsIterator = m_midpoints.data(); + Iterator& endpoint = midpointsIterator[m_numMidpoints - 2]; + const Iterator& startpoint = midpointsIterator[m_numMidpoints - 1]; + Iterator currpoint = endpoint; + while (!currpoint.atEnd() && currpoint != startpoint && + currpoint != lBreak) + currpoint.increment(); + if (currpoint == lBreak) { + // We hit the line break before the start point. Shave off the start + // point. + m_numMidpoints--; + if (endpoint.object()->style()->collapseWhiteSpace() && + endpoint.object()->isText()) + endpoint.setOffset(endpoint.offset() - 1); + } } + } + + Vector& midpoints() { return m_midpoints; } + const unsigned& numMidpoints() const { return m_numMidpoints; } + const unsigned& currentMidpoint() const { return m_currentMidpoint; } + void incrementCurrentMidpoint() { m_currentMidpoint++; } + const bool& betweenMidpoints() const { return m_betweenMidpoints; } + void setBetweenMidpoints(bool betweenMidpoint) { + m_betweenMidpoints = betweenMidpoint; + } + + private: + // The goal is to reuse the line state across multiple + // lines so we just keep an array around for midpoints and never clear it + // across multiple lines. We track the number of items and position using the + // two other variables. + Vector m_midpoints; + unsigned m_numMidpoints; + unsigned m_currentMidpoint; + bool m_betweenMidpoints; + + void addMidpoint(const Iterator& midpoint) { + if (m_midpoints.size() <= m_numMidpoints) + m_midpoints.grow(m_numMidpoints + 10); + + Iterator* midpointsIterator = m_midpoints.data(); + midpointsIterator[m_numMidpoints++] = midpoint; + } }; // The BidiStatus at a given position (typically the end of a line) can // be cached and then used to restart bidi resolution at that position. struct BidiStatus { - BidiStatus() - : eor(WTF::Unicode::OtherNeutral) - , lastStrong(WTF::Unicode::OtherNeutral) - , last(WTF::Unicode::OtherNeutral) - { - } - - // Creates a BidiStatus representing a new paragraph root with a default direction. - // Uses TextDirection as it only has two possibilities instead of WTF::Unicode::Direction which has 19. - BidiStatus(TextDirection textDirection, bool isOverride) - { - WTF::Unicode::Direction direction = textDirection == LTR ? WTF::Unicode::LeftToRight : WTF::Unicode::RightToLeft; - eor = lastStrong = last = direction; - context = BidiContext::create(textDirection == LTR ? 0 : 1, direction, isOverride); - } - - BidiStatus(WTF::Unicode::Direction eorDir, WTF::Unicode::Direction lastStrongDir, WTF::Unicode::Direction lastDir, PassRefPtr bidiContext) - : eor(eorDir) - , lastStrong(lastStrongDir) - , last(lastDir) - , context(bidiContext) - { - } - - WTF::Unicode::Direction eor; - WTF::Unicode::Direction lastStrong; - WTF::Unicode::Direction last; - RefPtr context; + BidiStatus() + : eor(WTF::Unicode::OtherNeutral), + lastStrong(WTF::Unicode::OtherNeutral), + last(WTF::Unicode::OtherNeutral) {} + + // Creates a BidiStatus representing a new paragraph root with a default + // direction. Uses TextDirection as it only has two possibilities instead of + // WTF::Unicode::Direction which has 19. + BidiStatus(TextDirection textDirection, bool isOverride) { + WTF::Unicode::Direction direction = textDirection == LTR + ? WTF::Unicode::LeftToRight + : WTF::Unicode::RightToLeft; + eor = lastStrong = last = direction; + context = BidiContext::create(textDirection == LTR ? 0 : 1, direction, + isOverride); + } + + BidiStatus(WTF::Unicode::Direction eorDir, + WTF::Unicode::Direction lastStrongDir, + WTF::Unicode::Direction lastDir, + PassRefPtr bidiContext) + : eor(eorDir), + lastStrong(lastStrongDir), + last(lastDir), + context(bidiContext) {} + + WTF::Unicode::Direction eor; + WTF::Unicode::Direction lastStrong; + WTF::Unicode::Direction last; + RefPtr context; }; class BidiEmbedding { -public: - BidiEmbedding(WTF::Unicode::Direction direction, BidiEmbeddingSource source) - : m_direction(direction) - , m_source(source) - { - } + public: + BidiEmbedding(WTF::Unicode::Direction direction, BidiEmbeddingSource source) + : m_direction(direction), m_source(source) {} + + WTF::Unicode::Direction direction() const { return m_direction; } + BidiEmbeddingSource source() const { return m_source; } - WTF::Unicode::Direction direction() const { return m_direction; } - BidiEmbeddingSource source() const { return m_source; } -private: - WTF::Unicode::Direction m_direction; - BidiEmbeddingSource m_source; + private: + WTF::Unicode::Direction m_direction; + BidiEmbeddingSource m_source; }; -inline bool operator==(const BidiStatus& status1, const BidiStatus& status2) -{ - return status1.eor == status2.eor && status1.last == status2.last && status1.lastStrong == status2.lastStrong && *(status1.context) == *(status2.context); +inline bool operator==(const BidiStatus& status1, const BidiStatus& status2) { + return status1.eor == status2.eor && status1.last == status2.last && + status1.lastStrong == status2.lastStrong && + *(status1.context) == *(status2.context); } -inline bool operator!=(const BidiStatus& status1, const BidiStatus& status2) -{ - return !(status1 == status2); +inline bool operator!=(const BidiStatus& status1, const BidiStatus& status2) { + return !(status1 == status2); } enum VisualDirectionOverride { - NoVisualOverride, - VisualLeftToRightOverride, - VisualRightToLeftOverride + NoVisualOverride, + VisualLeftToRightOverride, + VisualRightToLeftOverride }; // BidiResolver is WebKit's implementation of the Unicode Bidi Algorithm // http://unicode.org/reports/tr9 -template class BidiResolver { - WTF_MAKE_NONCOPYABLE(BidiResolver); -public: - BidiResolver() - : m_direction(WTF::Unicode::OtherNeutral) - , m_reachedEndOfLine(false) - , m_emptyRun(true) - , m_nestedIsolateCount(0) - , m_trailingSpaceRun(0) - { - } +template +class BidiResolver { + WTF_MAKE_NONCOPYABLE(BidiResolver); + + public: + BidiResolver() + : m_direction(WTF::Unicode::OtherNeutral), + m_reachedEndOfLine(false), + m_emptyRun(true), + m_nestedIsolateCount(0), + m_trailingSpaceRun(0) {} #if ENABLE(ASSERT) - ~BidiResolver(); + ~BidiResolver(); #endif - const Iterator& position() const { return m_current; } - Iterator& position() { return m_current; } - void setPositionIgnoringNestedIsolates(const Iterator& position) { m_current = position; } - void setPosition(const Iterator& position, unsigned nestedIsolatedCount) - { - m_current = position; - m_nestedIsolateCount = nestedIsolatedCount; - } - - BidiContext* context() const { return m_status.context.get(); } - void setContext(PassRefPtr c) { m_status.context = c; } - - void setLastDir(WTF::Unicode::Direction lastDir) { m_status.last = lastDir; } - void setLastStrongDir(WTF::Unicode::Direction lastStrongDir) { m_status.lastStrong = lastStrongDir; } - void setEorDir(WTF::Unicode::Direction eorDir) { m_status.eor = eorDir; } - - WTF::Unicode::Direction dir() const { return m_direction; } - void setDir(WTF::Unicode::Direction d) { m_direction = d; } - - const BidiStatus& status() const { return m_status; } - void setStatus(const BidiStatus s) - { - ASSERT(s.context); - m_status = s; - m_paragraphDirectionality = s.context->dir() == WTF::Unicode::LeftToRight ? LTR : RTL; - } - - MidpointState& midpointState() { return m_midpointState; } - - // The current algorithm handles nested isolates one layer of nesting at a time. - // But when we layout each isolated span, we will walk into (and ignore) all - // child isolated spans. - void enterIsolate() { m_nestedIsolateCount++; } - void exitIsolate() { ASSERT(m_nestedIsolateCount >= 1); m_nestedIsolateCount--; } - bool inIsolate() const { return m_nestedIsolateCount; } - - void embed(WTF::Unicode::Direction, BidiEmbeddingSource); - bool commitExplicitEmbedding(BidiRunList&); - - void createBidiRunsForLine(const Iterator& end, VisualDirectionOverride = NoVisualOverride, bool hardLineBreak = false, bool reorderRuns = true); - - BidiRunList& runs() { return m_runs; } - - // FIXME: This used to be part of deleteRuns() but was a layering violation. - // It's unclear if this is still needed. - void markCurrentRunEmpty() { m_emptyRun = true; } - - Vector& isolatedRuns() { return m_isolatedRuns; } - - bool isEndOfLine(const Iterator& end) { return m_current == end || m_current.atEnd(); } - - TextDirection determineParagraphDirectionality(bool* hasStrongDirectionality = 0); - - void setMidpointStateForIsolatedRun(Run*, const MidpointState&); - MidpointState midpointStateForIsolatedRun(Run*); - - Iterator endOfLine() const { return m_endOfLine; } - - Run* trailingSpaceRun() const { return m_trailingSpaceRun; } - -protected: - void increment() { m_current.increment(); } - // FIXME: Instead of InlineBidiResolvers subclassing this method, we should - // pass in some sort of Traits object which knows how to create runs for appending. - void appendRun(BidiRunList&); - - Run* addTrailingRun(BidiRunList&, int, int, Run*, BidiContext*, TextDirection) const { return 0; } - Iterator m_current; - // sor and eor are "start of run" and "end of run" respectively and correpond - // to abreviations used in UBA spec: http://unicode.org/reports/tr9/#BD7 - Iterator m_sor; // Points to the first character in the current run. - Iterator m_eor; // Points to the last character in the current run. - Iterator m_last; - BidiStatus m_status; - WTF::Unicode::Direction m_direction; - // m_endOfRunAtEndOfLine is "the position last eor in the end of line" - Iterator m_endOfRunAtEndOfLine; - Iterator m_endOfLine; - bool m_reachedEndOfLine; - Iterator m_lastBeforeET; // Before a EuropeanNumberTerminator - bool m_emptyRun; - - // FIXME: This should not belong to the resolver, but rather be passed - // into createBidiRunsForLine by the caller. - BidiRunList m_runs; - - MidpointState m_midpointState; - - unsigned m_nestedIsolateCount; - Vector m_isolatedRuns; - Run* m_trailingSpaceRun; - TextDirection m_paragraphDirectionality; - -private: - void raiseExplicitEmbeddingLevel(BidiRunList&, WTF::Unicode::Direction from, WTF::Unicode::Direction to); - void lowerExplicitEmbeddingLevel(BidiRunList&, WTF::Unicode::Direction from); - void checkDirectionInLowerRaiseEmbeddingLevel(); - - void updateStatusLastFromCurrentDirection(WTF::Unicode::Direction); - void reorderRunsFromLevels(BidiRunList&) const; - - bool needsToApplyL1Rule(BidiRunList&) { return false; } - int findFirstTrailingSpaceAtRun(Run*) { return 0; } - // http://www.unicode.org/reports/tr9/#L1 - void applyL1Rule(BidiRunList&); - - Vector m_currentExplicitEmbeddingSequence; - HashMap > m_midpointStateForIsolatedRun; + const Iterator& position() const { return m_current; } + Iterator& position() { return m_current; } + void setPositionIgnoringNestedIsolates(const Iterator& position) { + m_current = position; + } + void setPosition(const Iterator& position, unsigned nestedIsolatedCount) { + m_current = position; + m_nestedIsolateCount = nestedIsolatedCount; + } + + BidiContext* context() const { return m_status.context.get(); } + void setContext(PassRefPtr c) { m_status.context = c; } + + void setLastDir(WTF::Unicode::Direction lastDir) { m_status.last = lastDir; } + void setLastStrongDir(WTF::Unicode::Direction lastStrongDir) { + m_status.lastStrong = lastStrongDir; + } + void setEorDir(WTF::Unicode::Direction eorDir) { m_status.eor = eorDir; } + + WTF::Unicode::Direction dir() const { return m_direction; } + void setDir(WTF::Unicode::Direction d) { m_direction = d; } + + const BidiStatus& status() const { return m_status; } + void setStatus(const BidiStatus s) { + ASSERT(s.context); + m_status = s; + m_paragraphDirectionality = + s.context->dir() == WTF::Unicode::LeftToRight ? LTR : RTL; + } + + MidpointState& midpointState() { return m_midpointState; } + + // The current algorithm handles nested isolates one layer of nesting at a + // time. But when we layout each isolated span, we will walk into (and ignore) + // all child isolated spans. + void enterIsolate() { m_nestedIsolateCount++; } + void exitIsolate() { + ASSERT(m_nestedIsolateCount >= 1); + m_nestedIsolateCount--; + } + bool inIsolate() const { return m_nestedIsolateCount; } + + void embed(WTF::Unicode::Direction, BidiEmbeddingSource); + bool commitExplicitEmbedding(BidiRunList&); + + void createBidiRunsForLine(const Iterator& end, + VisualDirectionOverride = NoVisualOverride, + bool hardLineBreak = false, + bool reorderRuns = true); + + BidiRunList& runs() { return m_runs; } + + // FIXME: This used to be part of deleteRuns() but was a layering violation. + // It's unclear if this is still needed. + void markCurrentRunEmpty() { m_emptyRun = true; } + + Vector& isolatedRuns() { return m_isolatedRuns; } + + bool isEndOfLine(const Iterator& end) { + return m_current == end || m_current.atEnd(); + } + + TextDirection determineParagraphDirectionality( + bool* hasStrongDirectionality = 0); + + void setMidpointStateForIsolatedRun(Run*, const MidpointState&); + MidpointState midpointStateForIsolatedRun(Run*); + + Iterator endOfLine() const { return m_endOfLine; } + + Run* trailingSpaceRun() const { return m_trailingSpaceRun; } + + protected: + void increment() { m_current.increment(); } + // FIXME: Instead of InlineBidiResolvers subclassing this method, we should + // pass in some sort of Traits object which knows how to create runs for + // appending. + void appendRun(BidiRunList&); + + Run* addTrailingRun(BidiRunList&, + int, + int, + Run*, + BidiContext*, + TextDirection) const { + return 0; + } + Iterator m_current; + // sor and eor are "start of run" and "end of run" respectively and correpond + // to abreviations used in UBA spec: http://unicode.org/reports/tr9/#BD7 + Iterator m_sor; // Points to the first character in the current run. + Iterator m_eor; // Points to the last character in the current run. + Iterator m_last; + BidiStatus m_status; + WTF::Unicode::Direction m_direction; + // m_endOfRunAtEndOfLine is "the position last eor in the end of line" + Iterator m_endOfRunAtEndOfLine; + Iterator m_endOfLine; + bool m_reachedEndOfLine; + Iterator m_lastBeforeET; // Before a EuropeanNumberTerminator + bool m_emptyRun; + + // FIXME: This should not belong to the resolver, but rather be passed + // into createBidiRunsForLine by the caller. + BidiRunList m_runs; + + MidpointState m_midpointState; + + unsigned m_nestedIsolateCount; + Vector m_isolatedRuns; + Run* m_trailingSpaceRun; + TextDirection m_paragraphDirectionality; + + private: + void raiseExplicitEmbeddingLevel(BidiRunList&, + WTF::Unicode::Direction from, + WTF::Unicode::Direction to); + void lowerExplicitEmbeddingLevel(BidiRunList&, + WTF::Unicode::Direction from); + void checkDirectionInLowerRaiseEmbeddingLevel(); + + void updateStatusLastFromCurrentDirection(WTF::Unicode::Direction); + void reorderRunsFromLevels(BidiRunList&) const; + + bool needsToApplyL1Rule(BidiRunList&) { return false; } + int findFirstTrailingSpaceAtRun(Run*) { return 0; } + // http://www.unicode.org/reports/tr9/#L1 + void applyL1Rule(BidiRunList&); + + Vector m_currentExplicitEmbeddingSequence; + HashMap> m_midpointStateForIsolatedRun; }; #if ENABLE(ASSERT) template -BidiResolver::~BidiResolver() -{ - // The owner of this resolver should have handled the isolated runs. - ASSERT(m_isolatedRuns.isEmpty()); +BidiResolver::~BidiResolver() { + // The owner of this resolver should have handled the isolated runs. + ASSERT(m_isolatedRuns.isEmpty()); } #endif template -void BidiResolver::appendRun(BidiRunList& runs) -{ - if (!m_emptyRun && !m_eor.atEnd()) { - unsigned startOffset = m_sor.offset(); - unsigned endOffset = m_eor.offset(); - - if (!m_endOfRunAtEndOfLine.atEnd() && endOffset >= m_endOfRunAtEndOfLine.offset()) { - m_reachedEndOfLine = true; - endOffset = m_endOfRunAtEndOfLine.offset(); - } +void BidiResolver::appendRun(BidiRunList& runs) { + if (!m_emptyRun && !m_eor.atEnd()) { + unsigned startOffset = m_sor.offset(); + unsigned endOffset = m_eor.offset(); + + if (!m_endOfRunAtEndOfLine.atEnd() && + endOffset >= m_endOfRunAtEndOfLine.offset()) { + m_reachedEndOfLine = true; + endOffset = m_endOfRunAtEndOfLine.offset(); + } - if (endOffset >= startOffset) - runs.addRun(new Run(startOffset, endOffset + 1, context(), m_direction)); + if (endOffset >= startOffset) + runs.addRun(new Run(startOffset, endOffset + 1, context(), m_direction)); - m_eor.increment(); - m_sor = m_eor; - } + m_eor.increment(); + m_sor = m_eor; + } - m_direction = WTF::Unicode::OtherNeutral; - m_status.eor = WTF::Unicode::OtherNeutral; + m_direction = WTF::Unicode::OtherNeutral; + m_status.eor = WTF::Unicode::OtherNeutral; } template -void BidiResolver::embed(WTF::Unicode::Direction dir, BidiEmbeddingSource source) -{ - // Isolated spans compute base directionality during their own UBA run. - // Do not insert fake embed characters once we enter an isolated span. - ASSERT(!inIsolate()); - using namespace WTF::Unicode; - - ASSERT(dir == PopDirectionalFormat || dir == LeftToRightEmbedding || dir == LeftToRightOverride || dir == RightToLeftEmbedding || dir == RightToLeftOverride); - m_currentExplicitEmbeddingSequence.append(BidiEmbedding(dir, source)); +void BidiResolver::embed(WTF::Unicode::Direction dir, + BidiEmbeddingSource source) { + // Isolated spans compute base directionality during their own UBA run. + // Do not insert fake embed characters once we enter an isolated span. + ASSERT(!inIsolate()); + using namespace WTF::Unicode; + + ASSERT(dir == PopDirectionalFormat || dir == LeftToRightEmbedding || + dir == LeftToRightOverride || dir == RightToLeftEmbedding || + dir == RightToLeftOverride); + m_currentExplicitEmbeddingSequence.append(BidiEmbedding(dir, source)); } template -void BidiResolver::checkDirectionInLowerRaiseEmbeddingLevel() -{ - using namespace WTF::Unicode; - - ASSERT(m_status.eor != OtherNeutral || m_eor.atEnd()); - ASSERT(m_status.last != NonSpacingMark - && m_status.last != BoundaryNeutral - && m_status.last != RightToLeftEmbedding - && m_status.last != LeftToRightEmbedding - && m_status.last != RightToLeftOverride - && m_status.last != LeftToRightOverride - && m_status.last != PopDirectionalFormat); - if (m_direction == OtherNeutral) - m_direction = m_status.lastStrong == LeftToRight ? LeftToRight : RightToLeft; +void BidiResolver::checkDirectionInLowerRaiseEmbeddingLevel() { + using namespace WTF::Unicode; + + ASSERT(m_status.eor != OtherNeutral || m_eor.atEnd()); + ASSERT(m_status.last != NonSpacingMark && m_status.last != BoundaryNeutral && + m_status.last != RightToLeftEmbedding && + m_status.last != LeftToRightEmbedding && + m_status.last != RightToLeftOverride && + m_status.last != LeftToRightOverride && + m_status.last != PopDirectionalFormat); + if (m_direction == OtherNeutral) + m_direction = + m_status.lastStrong == LeftToRight ? LeftToRight : RightToLeft; } template -void BidiResolver::lowerExplicitEmbeddingLevel(BidiRunList& runs, WTF::Unicode::Direction from) -{ - using namespace WTF::Unicode; - - if (!m_emptyRun && m_eor != m_last) { - checkDirectionInLowerRaiseEmbeddingLevel(); - // bidi.sor ... bidi.eor ... bidi.last eor; need to append the bidi.sor-bidi.eor run or extend it through bidi.last - if (from == LeftToRight) { - // bidi.sor ... bidi.eor ... bidi.last L - if (m_status.eor == EuropeanNumber) { - if (m_status.lastStrong != LeftToRight) { - m_direction = EuropeanNumber; - appendRun(runs); - } - } else if (m_status.eor == ArabicNumber) { - m_direction = ArabicNumber; - appendRun(runs); - } else if (m_status.lastStrong != LeftToRight) { - appendRun(runs); - m_direction = LeftToRight; - } - } else if (m_status.eor == EuropeanNumber || m_status.eor == ArabicNumber || m_status.lastStrong == LeftToRight) { - appendRun(runs); - m_direction = RightToLeft; +void BidiResolver::lowerExplicitEmbeddingLevel( + BidiRunList& runs, + WTF::Unicode::Direction from) { + using namespace WTF::Unicode; + + if (!m_emptyRun && m_eor != m_last) { + checkDirectionInLowerRaiseEmbeddingLevel(); + // bidi.sor ... bidi.eor ... bidi.last eor; need to append the + // bidi.sor-bidi.eor run or extend it through bidi.last + if (from == LeftToRight) { + // bidi.sor ... bidi.eor ... bidi.last L + if (m_status.eor == EuropeanNumber) { + if (m_status.lastStrong != LeftToRight) { + m_direction = EuropeanNumber; + appendRun(runs); } - m_eor = m_last; + } else if (m_status.eor == ArabicNumber) { + m_direction = ArabicNumber; + appendRun(runs); + } else if (m_status.lastStrong != LeftToRight) { + appendRun(runs); + m_direction = LeftToRight; + } + } else if (m_status.eor == EuropeanNumber || m_status.eor == ArabicNumber || + m_status.lastStrong == LeftToRight) { + appendRun(runs); + m_direction = RightToLeft; } + m_eor = m_last; + } - appendRun(runs); - m_emptyRun = true; + appendRun(runs); + m_emptyRun = true; - // sor for the new run is determined by the higher level (rule X10) - setLastDir(from); - setLastStrongDir(from); - m_eor = Iterator(); + // sor for the new run is determined by the higher level (rule X10) + setLastDir(from); + setLastStrongDir(from); + m_eor = Iterator(); } template -void BidiResolver::raiseExplicitEmbeddingLevel(BidiRunList& runs, WTF::Unicode::Direction from, WTF::Unicode::Direction to) -{ - using namespace WTF::Unicode; - - if (!m_emptyRun && m_eor != m_last) { - checkDirectionInLowerRaiseEmbeddingLevel(); - // bidi.sor ... bidi.eor ... bidi.last eor; need to append the bidi.sor-bidi.eor run or extend it through bidi.last - if (to == LeftToRight) { - // bidi.sor ... bidi.eor ... bidi.last L - if (m_status.eor == EuropeanNumber) { - if (m_status.lastStrong != LeftToRight) { - m_direction = EuropeanNumber; - appendRun(runs); - } - } else if (m_status.eor == ArabicNumber) { - m_direction = ArabicNumber; - appendRun(runs); - } else if (m_status.lastStrong != LeftToRight && from == LeftToRight) { - appendRun(runs); - m_direction = LeftToRight; - } - } else if (m_status.eor == ArabicNumber - || (m_status.eor == EuropeanNumber && (m_status.lastStrong != LeftToRight || from == RightToLeft)) - || (m_status.eor != EuropeanNumber && m_status.lastStrong == LeftToRight && from == RightToLeft)) { - appendRun(runs); - m_direction = RightToLeft; +void BidiResolver::raiseExplicitEmbeddingLevel( + BidiRunList& runs, + WTF::Unicode::Direction from, + WTF::Unicode::Direction to) { + using namespace WTF::Unicode; + + if (!m_emptyRun && m_eor != m_last) { + checkDirectionInLowerRaiseEmbeddingLevel(); + // bidi.sor ... bidi.eor ... bidi.last eor; need to append the + // bidi.sor-bidi.eor run or extend it through bidi.last + if (to == LeftToRight) { + // bidi.sor ... bidi.eor ... bidi.last L + if (m_status.eor == EuropeanNumber) { + if (m_status.lastStrong != LeftToRight) { + m_direction = EuropeanNumber; + appendRun(runs); } - m_eor = m_last; + } else if (m_status.eor == ArabicNumber) { + m_direction = ArabicNumber; + appendRun(runs); + } else if (m_status.lastStrong != LeftToRight && from == LeftToRight) { + appendRun(runs); + m_direction = LeftToRight; + } + } else if (m_status.eor == ArabicNumber || + (m_status.eor == EuropeanNumber && + (m_status.lastStrong != LeftToRight || from == RightToLeft)) || + (m_status.eor != EuropeanNumber && + m_status.lastStrong == LeftToRight && from == RightToLeft)) { + appendRun(runs); + m_direction = RightToLeft; } + m_eor = m_last; + } - appendRun(runs); - m_emptyRun = true; + appendRun(runs); + m_emptyRun = true; - setLastDir(to); - setLastStrongDir(to); - m_eor = Iterator(); + setLastDir(to); + setLastStrongDir(to); + m_eor = Iterator(); } template -void BidiResolver::applyL1Rule(BidiRunList& runs) -{ - ASSERT(runs.runCount()); - if (!needsToApplyL1Rule(runs)) - return; - - Run* trailingSpaceRun = runs.logicallyLastRun(); - - int firstSpace = findFirstTrailingSpaceAtRun(trailingSpaceRun); - if (firstSpace == trailingSpaceRun->stop()) - return; - - bool shouldReorder = trailingSpaceRun != (m_paragraphDirectionality == LTR ? runs.lastRun() : runs.firstRun()); - if (firstSpace != trailingSpaceRun->start()) { - BidiContext* baseContext = context(); - while (BidiContext* parent = baseContext->parent()) - baseContext = parent; - - m_trailingSpaceRun = addTrailingRun(runs, firstSpace, trailingSpaceRun->m_stop, trailingSpaceRun, baseContext, m_paragraphDirectionality); - ASSERT(m_trailingSpaceRun); - trailingSpaceRun->m_stop = firstSpace; - return; - } - if (!shouldReorder) { - m_trailingSpaceRun = trailingSpaceRun; - return; - } - - if (m_paragraphDirectionality == LTR) { - runs.moveRunToEnd(trailingSpaceRun); - trailingSpaceRun->m_level = 0; - } else { - runs.moveRunToBeginning(trailingSpaceRun); - trailingSpaceRun->m_level = 1; - } +void BidiResolver::applyL1Rule(BidiRunList& runs) { + ASSERT(runs.runCount()); + if (!needsToApplyL1Rule(runs)) + return; + + Run* trailingSpaceRun = runs.logicallyLastRun(); + + int firstSpace = findFirstTrailingSpaceAtRun(trailingSpaceRun); + if (firstSpace == trailingSpaceRun->stop()) + return; + + bool shouldReorder = + trailingSpaceRun != + (m_paragraphDirectionality == LTR ? runs.lastRun() : runs.firstRun()); + if (firstSpace != trailingSpaceRun->start()) { + BidiContext* baseContext = context(); + while (BidiContext* parent = baseContext->parent()) + baseContext = parent; + + m_trailingSpaceRun = addTrailingRun( + runs, firstSpace, trailingSpaceRun->m_stop, trailingSpaceRun, + baseContext, m_paragraphDirectionality); + ASSERT(m_trailingSpaceRun); + trailingSpaceRun->m_stop = firstSpace; + return; + } + if (!shouldReorder) { m_trailingSpaceRun = trailingSpaceRun; + return; + } + + if (m_paragraphDirectionality == LTR) { + runs.moveRunToEnd(trailingSpaceRun); + trailingSpaceRun->m_level = 0; + } else { + runs.moveRunToBeginning(trailingSpaceRun); + trailingSpaceRun->m_level = 1; + } + m_trailingSpaceRun = trailingSpaceRun; } template -bool BidiResolver::commitExplicitEmbedding(BidiRunList& runs) -{ - // When we're "inIsolate()" we're resolving the parent context which - // ignores (skips over) the isolated content, including embedding levels. - // We should never accrue embedding levels while skipping over isolated content. - ASSERT(!inIsolate() || m_currentExplicitEmbeddingSequence.isEmpty()); - - using namespace WTF::Unicode; - - unsigned char fromLevel = context()->level(); - RefPtr toContext = context(); - - for (size_t i = 0; i < m_currentExplicitEmbeddingSequence.size(); ++i) { - BidiEmbedding embedding = m_currentExplicitEmbeddingSequence[i]; - if (embedding.direction() == PopDirectionalFormat) { - if (BidiContext* parentContext = toContext->parent()) - toContext = parentContext; - } else { - Direction direction = (embedding.direction() == RightToLeftEmbedding || embedding.direction() == RightToLeftOverride) ? RightToLeft : LeftToRight; - bool override = embedding.direction() == LeftToRightOverride || embedding.direction() == RightToLeftOverride; - unsigned char level = toContext->level(); - if (direction == RightToLeft) - level = nextGreaterOddLevel(level); - else - level = nextGreaterEvenLevel(level); - if (level < BidiContext::kMaxLevel) - toContext = BidiContext::create(level, direction, override, embedding.source(), toContext.get()); - } +bool BidiResolver::commitExplicitEmbedding( + BidiRunList& runs) { + // When we're "inIsolate()" we're resolving the parent context which + // ignores (skips over) the isolated content, including embedding levels. + // We should never accrue embedding levels while skipping over isolated + // content. + ASSERT(!inIsolate() || m_currentExplicitEmbeddingSequence.isEmpty()); + + using namespace WTF::Unicode; + + unsigned char fromLevel = context()->level(); + RefPtr toContext = context(); + + for (size_t i = 0; i < m_currentExplicitEmbeddingSequence.size(); ++i) { + BidiEmbedding embedding = m_currentExplicitEmbeddingSequence[i]; + if (embedding.direction() == PopDirectionalFormat) { + if (BidiContext* parentContext = toContext->parent()) + toContext = parentContext; + } else { + Direction direction = (embedding.direction() == RightToLeftEmbedding || + embedding.direction() == RightToLeftOverride) + ? RightToLeft + : LeftToRight; + bool override = embedding.direction() == LeftToRightOverride || + embedding.direction() == RightToLeftOverride; + unsigned char level = toContext->level(); + if (direction == RightToLeft) + level = nextGreaterOddLevel(level); + else + level = nextGreaterEvenLevel(level); + if (level < BidiContext::kMaxLevel) + toContext = BidiContext::create(level, direction, override, + embedding.source(), toContext.get()); } + } - unsigned char toLevel = toContext->level(); + unsigned char toLevel = toContext->level(); - if (toLevel > fromLevel) - raiseExplicitEmbeddingLevel(runs, fromLevel % 2 ? RightToLeft : LeftToRight, toLevel % 2 ? RightToLeft : LeftToRight); - else if (toLevel < fromLevel) - lowerExplicitEmbeddingLevel(runs, fromLevel % 2 ? RightToLeft : LeftToRight); + if (toLevel > fromLevel) + raiseExplicitEmbeddingLevel(runs, fromLevel % 2 ? RightToLeft : LeftToRight, + toLevel % 2 ? RightToLeft : LeftToRight); + else if (toLevel < fromLevel) + lowerExplicitEmbeddingLevel(runs, + fromLevel % 2 ? RightToLeft : LeftToRight); - setContext(toContext); + setContext(toContext); - m_currentExplicitEmbeddingSequence.clear(); + m_currentExplicitEmbeddingSequence.clear(); - return fromLevel != toLevel; + return fromLevel != toLevel; } template -inline void BidiResolver::updateStatusLastFromCurrentDirection(WTF::Unicode::Direction dirCurrent) -{ - using namespace WTF::Unicode; - switch (dirCurrent) { +inline void BidiResolver::updateStatusLastFromCurrentDirection( + WTF::Unicode::Direction dirCurrent) { + using namespace WTF::Unicode; + switch (dirCurrent) { case EuropeanNumberTerminator: - if (m_status.last != EuropeanNumber) - m_status.last = EuropeanNumberTerminator; - break; + if (m_status.last != EuropeanNumber) + m_status.last = EuropeanNumberTerminator; + break; case EuropeanNumberSeparator: case CommonNumberSeparator: case SegmentSeparator: case WhiteSpaceNeutral: case OtherNeutral: - switch (m_status.last) { + switch (m_status.last) { case LeftToRight: case RightToLeft: case RightToLeftArabic: case EuropeanNumber: case ArabicNumber: - m_status.last = dirCurrent; - break; + m_status.last = dirCurrent; + break; default: - m_status.last = OtherNeutral; - } - break; + m_status.last = OtherNeutral; + } + break; case NonSpacingMark: case BoundaryNeutral: case RightToLeftEmbedding: @@ -570,518 +614,542 @@ inline void BidiResolver::updateStatusLastFromCurrentDirection(WT case RightToLeftOverride: case LeftToRightOverride: case PopDirectionalFormat: - // ignore these - break; + // ignore these + break; case EuropeanNumber: - // fall through + // fall through default: - m_status.last = dirCurrent; - } + m_status.last = dirCurrent; + } } template -inline void BidiResolver::reorderRunsFromLevels(BidiRunList& runs) const -{ - unsigned char levelLow = BidiContext::kMaxLevel; - unsigned char levelHigh = 0; - for (Run* run = runs.firstRun(); run; run = run->next()) { - levelHigh = std::max(run->level(), levelHigh); - levelLow = std::min(run->level(), levelLow); - } - - // This implements reordering of the line (L2 according to Bidi spec): - // http://unicode.org/reports/tr9/#L2 - // L2. From the highest level found in the text to the lowest odd level on each line, - // reverse any contiguous sequence of characters that are at that level or higher. - - // Reversing is only done up to the lowest odd level. - if (!(levelLow % 2)) - levelLow++; - - unsigned count = runs.runCount() - 1; - - while (levelHigh >= levelLow) { - unsigned i = 0; - Run* run = runs.firstRun(); - while (i < count) { - for (;i < count && run && run->level() < levelHigh; i++) - run = run->next(); - unsigned start = i; - for (;i <= count && run && run->level() >= levelHigh; i++) - run = run->next(); - unsigned end = i - 1; - runs.reverseRuns(start, end); - } - levelHigh--; +inline void BidiResolver::reorderRunsFromLevels( + BidiRunList& runs) const { + unsigned char levelLow = BidiContext::kMaxLevel; + unsigned char levelHigh = 0; + for (Run* run = runs.firstRun(); run; run = run->next()) { + levelHigh = std::max(run->level(), levelHigh); + levelLow = std::min(run->level(), levelLow); + } + + // This implements reordering of the line (L2 according to Bidi spec): + // http://unicode.org/reports/tr9/#L2 + // L2. From the highest level found in the text to the lowest odd level on + // each line, reverse any contiguous sequence of characters that are at that + // level or higher. + + // Reversing is only done up to the lowest odd level. + if (!(levelLow % 2)) + levelLow++; + + unsigned count = runs.runCount() - 1; + + while (levelHigh >= levelLow) { + unsigned i = 0; + Run* run = runs.firstRun(); + while (i < count) { + for (; i < count && run && run->level() < levelHigh; i++) + run = run->next(); + unsigned start = i; + for (; i <= count && run && run->level() >= levelHigh; i++) + run = run->next(); + unsigned end = i - 1; + runs.reverseRuns(start, end); } + levelHigh--; + } } template -TextDirection BidiResolver::determineParagraphDirectionality(bool* hasStrongDirectionality) -{ - while (!m_current.atEnd()) { - if (inIsolate()) { - increment(); - continue; - } - if (m_current.atParagraphSeparator()) - break; - UChar32 current = m_current.current(); - if (UNLIKELY(U16_IS_SURROGATE(current))) { - increment(); - // If this not the high part of the surrogate pair, then drop it and move to the next. - if (!U16_IS_SURROGATE_LEAD(current)) - continue; - UChar high = static_cast(current); - if (m_current.atEnd()) - continue; - UChar low = m_current.current(); - // Verify the low part. If invalid, then assume an invalid surrogate pair and retry. - if (!U16_IS_TRAIL(low)) - continue; - current = U16_GET_SUPPLEMENTARY(high, low); - } - WTF::Unicode::Direction charDirection = WTF::Unicode::direction(current); - if (charDirection == WTF::Unicode::LeftToRight) { - if (hasStrongDirectionality) - *hasStrongDirectionality = true; - return LTR; - } - if (charDirection == WTF::Unicode::RightToLeft || charDirection == WTF::Unicode::RightToLeftArabic) { - if (hasStrongDirectionality) - *hasStrongDirectionality = true; - return RTL; - } - increment(); +TextDirection BidiResolver::determineParagraphDirectionality( + bool* hasStrongDirectionality) { + while (!m_current.atEnd()) { + if (inIsolate()) { + increment(); + continue; + } + if (m_current.atParagraphSeparator()) + break; + UChar32 current = m_current.current(); + if (UNLIKELY(U16_IS_SURROGATE(current))) { + increment(); + // If this not the high part of the surrogate pair, then drop it and move + // to the next. + if (!U16_IS_SURROGATE_LEAD(current)) + continue; + UChar high = static_cast(current); + if (m_current.atEnd()) + continue; + UChar low = m_current.current(); + // Verify the low part. If invalid, then assume an invalid surrogate pair + // and retry. + if (!U16_IS_TRAIL(low)) + continue; + current = U16_GET_SUPPLEMENTARY(high, low); + } + WTF::Unicode::Direction charDirection = WTF::Unicode::direction(current); + if (charDirection == WTF::Unicode::LeftToRight) { + if (hasStrongDirectionality) + *hasStrongDirectionality = true; + return LTR; } - if (hasStrongDirectionality) - *hasStrongDirectionality = false; - return LTR; + if (charDirection == WTF::Unicode::RightToLeft || + charDirection == WTF::Unicode::RightToLeftArabic) { + if (hasStrongDirectionality) + *hasStrongDirectionality = true; + return RTL; + } + increment(); + } + if (hasStrongDirectionality) + *hasStrongDirectionality = false; + return LTR; } template -void BidiResolver::createBidiRunsForLine(const Iterator& end, VisualDirectionOverride override, bool hardLineBreak, bool reorderRuns) -{ - using namespace WTF::Unicode; - - ASSERT(m_direction == OtherNeutral); - m_trailingSpaceRun = 0; - - m_endOfLine = end; - - if (override != NoVisualOverride) { - m_emptyRun = false; - m_sor = m_current; - m_eor = Iterator(); - while (m_current != end && !m_current.atEnd()) { - m_eor = m_current; - increment(); - } - m_direction = override == VisualLeftToRightOverride ? LeftToRight : RightToLeft; - appendRun(m_runs); - m_runs.setLogicallyLastRun(m_runs.lastRun()); - if (override == VisualRightToLeftOverride && m_runs.runCount()) - m_runs.reverseRuns(0, m_runs.runCount() - 1); - return; - } +void BidiResolver::createBidiRunsForLine( + const Iterator& end, + VisualDirectionOverride override, + bool hardLineBreak, + bool reorderRuns) { + using namespace WTF::Unicode; - m_emptyRun = true; + ASSERT(m_direction == OtherNeutral); + m_trailingSpaceRun = 0; + m_endOfLine = end; + + if (override != NoVisualOverride) { + m_emptyRun = false; + m_sor = m_current; m_eor = Iterator(); + while (m_current != end && !m_current.atEnd()) { + m_eor = m_current; + increment(); + } + m_direction = + override == VisualLeftToRightOverride ? LeftToRight : RightToLeft; + appendRun(m_runs); + m_runs.setLogicallyLastRun(m_runs.lastRun()); + if (override == VisualRightToLeftOverride && m_runs.runCount()) + m_runs.reverseRuns(0, m_runs.runCount() - 1); + return; + } - m_last = m_current; - bool lastLineEnded = false; - BidiResolver stateAtEnd; + m_emptyRun = true; - while (true) { - if (inIsolate() && m_emptyRun) { - m_sor = m_current; - m_emptyRun = false; - } + m_eor = Iterator(); - if (!lastLineEnded && isEndOfLine(end)) { - if (m_emptyRun) - break; + m_last = m_current; + bool lastLineEnded = false; + BidiResolver stateAtEnd; - stateAtEnd.m_status = m_status; - stateAtEnd.m_sor = m_sor; - stateAtEnd.m_eor = m_eor; - stateAtEnd.m_last = m_last; - stateAtEnd.m_reachedEndOfLine = m_reachedEndOfLine; - stateAtEnd.m_lastBeforeET = m_lastBeforeET; - stateAtEnd.m_emptyRun = m_emptyRun; - m_endOfRunAtEndOfLine = m_last; - lastLineEnded = true; - } - Direction dirCurrent; - if (lastLineEnded && (hardLineBreak || m_current.atEnd())) { - BidiContext* c = context(); - if (hardLineBreak) { - // A deviation from the Unicode Bidi Algorithm in order to match - // WinIE and user expectations: hard line breaks reset bidi state - // coming from unicode bidi control characters, but not those from - // DOM nodes with specified directionality - stateAtEnd.setContext(c->copyStackRemovingUnicodeEmbeddingContexts()); - - dirCurrent = stateAtEnd.context()->dir(); - stateAtEnd.setEorDir(dirCurrent); - stateAtEnd.setLastDir(dirCurrent); - stateAtEnd.setLastStrongDir(dirCurrent); - } else { - while (c->parent()) - c = c->parent(); - dirCurrent = c->dir(); - } - } else { - dirCurrent = m_current.direction(); - if (context()->override() - && dirCurrent != RightToLeftEmbedding - && dirCurrent != LeftToRightEmbedding - && dirCurrent != RightToLeftOverride - && dirCurrent != LeftToRightOverride - && dirCurrent != PopDirectionalFormat) - dirCurrent = context()->dir(); - else if (dirCurrent == NonSpacingMark) - dirCurrent = m_status.last; - } + while (true) { + if (inIsolate() && m_emptyRun) { + m_sor = m_current; + m_emptyRun = false; + } - // We ignore all character directionality while in unicode-bidi: isolate spans. - // We'll handle ordering the isolated characters in a second pass. - if (inIsolate()) - dirCurrent = OtherNeutral; - - ASSERT(m_status.eor != OtherNeutral || m_eor.atEnd()); - switch (dirCurrent) { - - // embedding and overrides (X1-X9 in the Bidi specs) - case RightToLeftEmbedding: - case LeftToRightEmbedding: - case RightToLeftOverride: - case LeftToRightOverride: - case PopDirectionalFormat: - embed(dirCurrent, FromUnicode); - commitExplicitEmbedding(m_runs); - break; + if (!lastLineEnded && isEndOfLine(end)) { + if (m_emptyRun) + break; - // strong types - case LeftToRight: - switch (m_status.last) { - case RightToLeft: - case RightToLeftArabic: - case EuropeanNumber: - case ArabicNumber: - if (m_status.last != EuropeanNumber || m_status.lastStrong != LeftToRight) - appendRun(m_runs); - break; - case LeftToRight: - break; - case EuropeanNumberSeparator: - case EuropeanNumberTerminator: - case CommonNumberSeparator: - case BoundaryNeutral: - case BlockSeparator: - case SegmentSeparator: - case WhiteSpaceNeutral: - case OtherNeutral: - if (m_status.eor == EuropeanNumber) { - if (m_status.lastStrong != LeftToRight) { - // the numbers need to be on a higher embedding level, so let's close that run - m_direction = EuropeanNumber; - appendRun(m_runs); - if (context()->dir() != LeftToRight) { - // the neutrals take the embedding direction, which is R - m_eor = m_last; - m_direction = RightToLeft; - appendRun(m_runs); - } - } - } else if (m_status.eor == ArabicNumber) { - // Arabic numbers are always on a higher embedding level, so let's close that run - m_direction = ArabicNumber; - appendRun(m_runs); - if (context()->dir() != LeftToRight) { - // the neutrals take the embedding direction, which is R - m_eor = m_last; - m_direction = RightToLeft; - appendRun(m_runs); - } - } else if (m_status.lastStrong != LeftToRight) { - // last stuff takes embedding dir - if (context()->dir() == RightToLeft) { - m_eor = m_last; - m_direction = RightToLeft; - } - appendRun(m_runs); - } - default: - break; - } - m_eor = m_current; - m_status.eor = LeftToRight; - m_status.lastStrong = LeftToRight; - m_direction = LeftToRight; + stateAtEnd.m_status = m_status; + stateAtEnd.m_sor = m_sor; + stateAtEnd.m_eor = m_eor; + stateAtEnd.m_last = m_last; + stateAtEnd.m_reachedEndOfLine = m_reachedEndOfLine; + stateAtEnd.m_lastBeforeET = m_lastBeforeET; + stateAtEnd.m_emptyRun = m_emptyRun; + m_endOfRunAtEndOfLine = m_last; + lastLineEnded = true; + } + Direction dirCurrent; + if (lastLineEnded && (hardLineBreak || m_current.atEnd())) { + BidiContext* c = context(); + if (hardLineBreak) { + // A deviation from the Unicode Bidi Algorithm in order to match + // WinIE and user expectations: hard line breaks reset bidi state + // coming from unicode bidi control characters, but not those from + // DOM nodes with specified directionality + stateAtEnd.setContext(c->copyStackRemovingUnicodeEmbeddingContexts()); + + dirCurrent = stateAtEnd.context()->dir(); + stateAtEnd.setEorDir(dirCurrent); + stateAtEnd.setLastDir(dirCurrent); + stateAtEnd.setLastStrongDir(dirCurrent); + } else { + while (c->parent()) + c = c->parent(); + dirCurrent = c->dir(); + } + } else { + dirCurrent = m_current.direction(); + if (context()->override() && dirCurrent != RightToLeftEmbedding && + dirCurrent != LeftToRightEmbedding && + dirCurrent != RightToLeftOverride && + dirCurrent != LeftToRightOverride && + dirCurrent != PopDirectionalFormat) + dirCurrent = context()->dir(); + else if (dirCurrent == NonSpacingMark) + dirCurrent = m_status.last; + } + + // We ignore all character directionality while in unicode-bidi: isolate + // spans. We'll handle ordering the isolated characters in a second pass. + if (inIsolate()) + dirCurrent = OtherNeutral; + + ASSERT(m_status.eor != OtherNeutral || m_eor.atEnd()); + switch (dirCurrent) { + // embedding and overrides (X1-X9 in the Bidi specs) + case RightToLeftEmbedding: + case LeftToRightEmbedding: + case RightToLeftOverride: + case LeftToRightOverride: + case PopDirectionalFormat: + embed(dirCurrent, FromUnicode); + commitExplicitEmbedding(m_runs); + break; + + // strong types + case LeftToRight: + switch (m_status.last) { + case RightToLeft: + case RightToLeftArabic: + case EuropeanNumber: + case ArabicNumber: + if (m_status.last != EuropeanNumber || + m_status.lastStrong != LeftToRight) + appendRun(m_runs); break; - case RightToLeftArabic: - case RightToLeft: - switch (m_status.last) { - case LeftToRight: - case EuropeanNumber: - case ArabicNumber: + case LeftToRight: + break; + case EuropeanNumberSeparator: + case EuropeanNumberTerminator: + case CommonNumberSeparator: + case BoundaryNeutral: + case BlockSeparator: + case SegmentSeparator: + case WhiteSpaceNeutral: + case OtherNeutral: + if (m_status.eor == EuropeanNumber) { + if (m_status.lastStrong != LeftToRight) { + // the numbers need to be on a higher embedding level, so let's + // close that run + m_direction = EuropeanNumber; appendRun(m_runs); - case RightToLeft: - case RightToLeftArabic: - break; - case EuropeanNumberSeparator: - case EuropeanNumberTerminator: - case CommonNumberSeparator: - case BoundaryNeutral: - case BlockSeparator: - case SegmentSeparator: - case WhiteSpaceNeutral: - case OtherNeutral: - if (m_status.eor == EuropeanNumber) { - if (m_status.lastStrong == LeftToRight && context()->dir() == LeftToRight) - m_eor = m_last; - appendRun(m_runs); - } else if (m_status.eor == ArabicNumber) { - appendRun(m_runs); - } else if (m_status.lastStrong == LeftToRight) { - if (context()->dir() == LeftToRight) - m_eor = m_last; - appendRun(m_runs); + if (context()->dir() != LeftToRight) { + // the neutrals take the embedding direction, which is R + m_eor = m_last; + m_direction = RightToLeft; + appendRun(m_runs); } - default: - break; + } + } else if (m_status.eor == ArabicNumber) { + // Arabic numbers are always on a higher embedding level, so let's + // close that run + m_direction = ArabicNumber; + appendRun(m_runs); + if (context()->dir() != LeftToRight) { + // the neutrals take the embedding direction, which is R + m_eor = m_last; + m_direction = RightToLeft; + appendRun(m_runs); + } + } else if (m_status.lastStrong != LeftToRight) { + // last stuff takes embedding dir + if (context()->dir() == RightToLeft) { + m_eor = m_last; + m_direction = RightToLeft; + } + appendRun(m_runs); } - m_eor = m_current; - m_status.eor = RightToLeft; - m_status.lastStrong = dirCurrent; - m_direction = RightToLeft; + default: + break; + } + m_eor = m_current; + m_status.eor = LeftToRight; + m_status.lastStrong = LeftToRight; + m_direction = LeftToRight; + break; + case RightToLeftArabic: + case RightToLeft: + switch (m_status.last) { + case LeftToRight: + case EuropeanNumber: + case ArabicNumber: + appendRun(m_runs); + case RightToLeft: + case RightToLeftArabic: break; + case EuropeanNumberSeparator: + case EuropeanNumberTerminator: + case CommonNumberSeparator: + case BoundaryNeutral: + case BlockSeparator: + case SegmentSeparator: + case WhiteSpaceNeutral: + case OtherNeutral: + if (m_status.eor == EuropeanNumber) { + if (m_status.lastStrong == LeftToRight && + context()->dir() == LeftToRight) + m_eor = m_last; + appendRun(m_runs); + } else if (m_status.eor == ArabicNumber) { + appendRun(m_runs); + } else if (m_status.lastStrong == LeftToRight) { + if (context()->dir() == LeftToRight) + m_eor = m_last; + appendRun(m_runs); + } + default: + break; + } + m_eor = m_current; + m_status.eor = RightToLeft; + m_status.lastStrong = dirCurrent; + m_direction = RightToLeft; + break; - // weak types: + // weak types: - case EuropeanNumber: - if (m_status.lastStrong != RightToLeftArabic) { - // if last strong was AL change EN to AN - switch (m_status.last) { - case EuropeanNumber: - case LeftToRight: - break; - case RightToLeft: - case RightToLeftArabic: - case ArabicNumber: - m_eor = m_last; - appendRun(m_runs); - m_direction = EuropeanNumber; - break; - case EuropeanNumberSeparator: - case CommonNumberSeparator: - if (m_status.eor == EuropeanNumber) - break; - case EuropeanNumberTerminator: - case BoundaryNeutral: - case BlockSeparator: - case SegmentSeparator: - case WhiteSpaceNeutral: - case OtherNeutral: - if (m_status.eor == EuropeanNumber) { - if (m_status.lastStrong == RightToLeft) { - // ENs on both sides behave like Rs, so the neutrals should be R. - // Terminate the EN run. - appendRun(m_runs); - // Make an R run. - m_eor = m_status.last == EuropeanNumberTerminator ? m_lastBeforeET : m_last; - m_direction = RightToLeft; - appendRun(m_runs); - // Begin a new EN run. - m_direction = EuropeanNumber; - } - } else if (m_status.eor == ArabicNumber) { - // Terminate the AN run. - appendRun(m_runs); - if (m_status.lastStrong == RightToLeft || context()->dir() == RightToLeft) { - // Make an R run. - m_eor = m_status.last == EuropeanNumberTerminator ? m_lastBeforeET : m_last; - m_direction = RightToLeft; - appendRun(m_runs); - // Begin a new EN run. - m_direction = EuropeanNumber; - } - } else if (m_status.lastStrong == RightToLeft) { - // Extend the R run to include the neutrals. - m_eor = m_status.last == EuropeanNumberTerminator ? m_lastBeforeET : m_last; - m_direction = RightToLeft; - appendRun(m_runs); - // Begin a new EN run. - m_direction = EuropeanNumber; - } - default: - break; - } - m_eor = m_current; - m_status.eor = EuropeanNumber; - if (m_direction == OtherNeutral) - m_direction = LeftToRight; - break; - } - case ArabicNumber: - dirCurrent = ArabicNumber; - switch (m_status.last) { + case EuropeanNumber: + if (m_status.lastStrong != RightToLeftArabic) { + // if last strong was AL change EN to AN + switch (m_status.last) { + case EuropeanNumber: case LeftToRight: - if (context()->dir() == LeftToRight) - appendRun(m_runs); - break; - case ArabicNumber: - break; + break; case RightToLeft: case RightToLeftArabic: - case EuropeanNumber: - m_eor = m_last; - appendRun(m_runs); - break; - case CommonNumberSeparator: - if (m_status.eor == ArabicNumber) - break; + case ArabicNumber: + m_eor = m_last; + appendRun(m_runs); + m_direction = EuropeanNumber; + break; case EuropeanNumberSeparator: + case CommonNumberSeparator: + if (m_status.eor == EuropeanNumber) + break; case EuropeanNumberTerminator: case BoundaryNeutral: case BlockSeparator: case SegmentSeparator: case WhiteSpaceNeutral: case OtherNeutral: - if (m_status.eor == ArabicNumber - || (m_status.eor == EuropeanNumber && (m_status.lastStrong == RightToLeft || context()->dir() == RightToLeft)) - || (m_status.eor != EuropeanNumber && m_status.lastStrong == LeftToRight && context()->dir() == RightToLeft)) { - // Terminate the run before the neutrals. - appendRun(m_runs); - // Begin an R run for the neutrals. - m_direction = RightToLeft; - } else if (m_direction == OtherNeutral) { - m_direction = m_status.lastStrong == LeftToRight ? LeftToRight : RightToLeft; + if (m_status.eor == EuropeanNumber) { + if (m_status.lastStrong == RightToLeft) { + // ENs on both sides behave like Rs, so the neutrals should be + // R. Terminate the EN run. + appendRun(m_runs); + // Make an R run. + m_eor = m_status.last == EuropeanNumberTerminator + ? m_lastBeforeET + : m_last; + m_direction = RightToLeft; + appendRun(m_runs); + // Begin a new EN run. + m_direction = EuropeanNumber; } - m_eor = m_last; + } else if (m_status.eor == ArabicNumber) { + // Terminate the AN run. + appendRun(m_runs); + if (m_status.lastStrong == RightToLeft || + context()->dir() == RightToLeft) { + // Make an R run. + m_eor = m_status.last == EuropeanNumberTerminator + ? m_lastBeforeET + : m_last; + m_direction = RightToLeft; + appendRun(m_runs); + // Begin a new EN run. + m_direction = EuropeanNumber; + } + } else if (m_status.lastStrong == RightToLeft) { + // Extend the R run to include the neutrals. + m_eor = m_status.last == EuropeanNumberTerminator + ? m_lastBeforeET + : m_last; + m_direction = RightToLeft; appendRun(m_runs); + // Begin a new EN run. + m_direction = EuropeanNumber; + } default: - break; - } - m_eor = m_current; - m_status.eor = ArabicNumber; - if (m_direction == OtherNeutral) - m_direction = ArabicNumber; - break; - case EuropeanNumberSeparator: - case CommonNumberSeparator: - break; - case EuropeanNumberTerminator: - if (m_status.last == EuropeanNumber) { - dirCurrent = EuropeanNumber; - m_eor = m_current; - m_status.eor = dirCurrent; - } else if (m_status.last != EuropeanNumberTerminator) { - m_lastBeforeET = m_emptyRun ? m_eor : m_last; - } - break; - - // boundary neutrals should be ignored - case BoundaryNeutral: - if (m_eor == m_last) - m_eor = m_current; - break; - // neutrals - case BlockSeparator: - // ### what do we do with newline and paragraph seperators that come to here? - break; - case SegmentSeparator: - // ### implement rule L1 + break; + } + m_eor = m_current; + m_status.eor = EuropeanNumber; + if (m_direction == OtherNeutral) + m_direction = LeftToRight; + break; + } + case ArabicNumber: + dirCurrent = ArabicNumber; + switch (m_status.last) { + case LeftToRight: + if (context()->dir() == LeftToRight) + appendRun(m_runs); break; - case WhiteSpaceNeutral: + case ArabicNumber: break; - case OtherNeutral: + case RightToLeft: + case RightToLeftArabic: + case EuropeanNumber: + m_eor = m_last; + appendRun(m_runs); break; - default: + case CommonNumberSeparator: + if (m_status.eor == ArabicNumber) + break; + case EuropeanNumberSeparator: + case EuropeanNumberTerminator: + case BoundaryNeutral: + case BlockSeparator: + case SegmentSeparator: + case WhiteSpaceNeutral: + case OtherNeutral: + if (m_status.eor == ArabicNumber || + (m_status.eor == EuropeanNumber && + (m_status.lastStrong == RightToLeft || + context()->dir() == RightToLeft)) || + (m_status.eor != EuropeanNumber && + m_status.lastStrong == LeftToRight && + context()->dir() == RightToLeft)) { + // Terminate the run before the neutrals. + appendRun(m_runs); + // Begin an R run for the neutrals. + m_direction = RightToLeft; + } else if (m_direction == OtherNeutral) { + m_direction = m_status.lastStrong == LeftToRight ? LeftToRight + : RightToLeft; + } + m_eor = m_last; + appendRun(m_runs); + default: break; } + m_eor = m_current; + m_status.eor = ArabicNumber; + if (m_direction == OtherNeutral) + m_direction = ArabicNumber; + break; + case EuropeanNumberSeparator: + case CommonNumberSeparator: + break; + case EuropeanNumberTerminator: + if (m_status.last == EuropeanNumber) { + dirCurrent = EuropeanNumber; + m_eor = m_current; + m_status.eor = dirCurrent; + } else if (m_status.last != EuropeanNumberTerminator) { + m_lastBeforeET = m_emptyRun ? m_eor : m_last; + } + break; - if (lastLineEnded && m_eor == m_current) { - if (!m_reachedEndOfLine) { - m_eor = m_endOfRunAtEndOfLine; - switch (m_status.eor) { - case LeftToRight: - case RightToLeft: - case ArabicNumber: - m_direction = m_status.eor; - break; - case EuropeanNumber: - m_direction = m_status.lastStrong == LeftToRight ? LeftToRight : EuropeanNumber; - break; - default: - ASSERT_NOT_REACHED(); - } - appendRun(m_runs); - } - m_current = end; - m_status = stateAtEnd.m_status; - m_sor = stateAtEnd.m_sor; - m_eor = stateAtEnd.m_eor; - m_last = stateAtEnd.m_last; - m_reachedEndOfLine = stateAtEnd.m_reachedEndOfLine; - m_lastBeforeET = stateAtEnd.m_lastBeforeET; - m_emptyRun = stateAtEnd.m_emptyRun; - m_direction = OtherNeutral; + // boundary neutrals should be ignored + case BoundaryNeutral: + if (m_eor == m_last) + m_eor = m_current; + break; + // neutrals + case BlockSeparator: + // ### what do we do with newline and paragraph seperators that come to + // here? + break; + case SegmentSeparator: + // ### implement rule L1 + break; + case WhiteSpaceNeutral: + break; + case OtherNeutral: + break; + default: + break; + } + + if (lastLineEnded && m_eor == m_current) { + if (!m_reachedEndOfLine) { + m_eor = m_endOfRunAtEndOfLine; + switch (m_status.eor) { + case LeftToRight: + case RightToLeft: + case ArabicNumber: + m_direction = m_status.eor; break; + case EuropeanNumber: + m_direction = m_status.lastStrong == LeftToRight ? LeftToRight + : EuropeanNumber; + break; + default: + ASSERT_NOT_REACHED(); } + appendRun(m_runs); + } + m_current = end; + m_status = stateAtEnd.m_status; + m_sor = stateAtEnd.m_sor; + m_eor = stateAtEnd.m_eor; + m_last = stateAtEnd.m_last; + m_reachedEndOfLine = stateAtEnd.m_reachedEndOfLine; + m_lastBeforeET = stateAtEnd.m_lastBeforeET; + m_emptyRun = stateAtEnd.m_emptyRun; + m_direction = OtherNeutral; + break; + } - updateStatusLastFromCurrentDirection(dirCurrent); - m_last = m_current; + updateStatusLastFromCurrentDirection(dirCurrent); + m_last = m_current; - if (m_emptyRun) { - m_sor = m_current; - m_emptyRun = false; - } + if (m_emptyRun) { + m_sor = m_current; + m_emptyRun = false; + } - increment(); - if (!m_currentExplicitEmbeddingSequence.isEmpty()) { - bool committed = commitExplicitEmbedding(m_runs); - if (committed && lastLineEnded) { - m_current = end; - m_status = stateAtEnd.m_status; - m_sor = stateAtEnd.m_sor; - m_eor = stateAtEnd.m_eor; - m_last = stateAtEnd.m_last; - m_reachedEndOfLine = stateAtEnd.m_reachedEndOfLine; - m_lastBeforeET = stateAtEnd.m_lastBeforeET; - m_emptyRun = stateAtEnd.m_emptyRun; - m_direction = OtherNeutral; - break; - } - } + increment(); + if (!m_currentExplicitEmbeddingSequence.isEmpty()) { + bool committed = commitExplicitEmbedding(m_runs); + if (committed && lastLineEnded) { + m_current = end; + m_status = stateAtEnd.m_status; + m_sor = stateAtEnd.m_sor; + m_eor = stateAtEnd.m_eor; + m_last = stateAtEnd.m_last; + m_reachedEndOfLine = stateAtEnd.m_reachedEndOfLine; + m_lastBeforeET = stateAtEnd.m_lastBeforeET; + m_emptyRun = stateAtEnd.m_emptyRun; + m_direction = OtherNeutral; + break; + } } + } - m_runs.setLogicallyLastRun(m_runs.lastRun()); - if (reorderRuns) - reorderRunsFromLevels(m_runs); - m_endOfRunAtEndOfLine = Iterator(); - m_endOfLine = Iterator(); + m_runs.setLogicallyLastRun(m_runs.lastRun()); + if (reorderRuns) + reorderRunsFromLevels(m_runs); + m_endOfRunAtEndOfLine = Iterator(); + m_endOfLine = Iterator(); - if (!hardLineBreak && m_runs.runCount()) - applyL1Rule(m_runs); + if (!hardLineBreak && m_runs.runCount()) + applyL1Rule(m_runs); } template -void BidiResolver::setMidpointStateForIsolatedRun(Run* run, const MidpointState& midpoint) -{ - ASSERT(!m_midpointStateForIsolatedRun.contains(run)); - m_midpointStateForIsolatedRun.add(run, midpoint); +void BidiResolver::setMidpointStateForIsolatedRun( + Run* run, + const MidpointState& midpoint) { + ASSERT(!m_midpointStateForIsolatedRun.contains(run)); + m_midpointStateForIsolatedRun.add(run, midpoint); } -template -MidpointState BidiResolver::midpointStateForIsolatedRun(Run* run) -{ - return m_midpointStateForIsolatedRun.take(run); +template +MidpointState +BidiResolver::midpointStateForIsolatedRun(Run* run) { + return m_midpointStateForIsolatedRun.take(run); } - -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_TEXT_BIDIRESOLVER_H_ diff --git a/sky/engine/platform/text/BidiResolverTest.cpp b/sky/engine/platform/text/BidiResolverTest.cpp index 02963e880cd40..2ad444415e9a0 100644 --- a/sky/engine/platform/text/BidiResolverTest.cpp +++ b/sky/engine/platform/text/BidiResolverTest.cpp @@ -41,253 +41,264 @@ namespace { using namespace WTF; using namespace blink; -TEST(BidiResolver, Basic) -{ - bool hasStrongDirectionality; - String value("foo"); - TextRun run(value); - BidiResolver bidiResolver; - bidiResolver.setStatus(BidiStatus(run.direction(), run.directionalOverride())); - bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&run, 0)); - TextDirection direction = bidiResolver.determineParagraphDirectionality(&hasStrongDirectionality); - EXPECT_TRUE(hasStrongDirectionality); - EXPECT_EQ(LTR, direction); +TEST(BidiResolver, Basic) { + bool hasStrongDirectionality; + String value("foo"); + TextRun run(value); + BidiResolver bidiResolver; + bidiResolver.setStatus( + BidiStatus(run.direction(), run.directionalOverride())); + bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&run, 0)); + TextDirection direction = + bidiResolver.determineParagraphDirectionality(&hasStrongDirectionality); + EXPECT_TRUE(hasStrongDirectionality); + EXPECT_EQ(LTR, direction); } -TextDirection determineParagraphDirectionality(const TextRun& textRun, bool* hasStrongDirectionality = 0) -{ - BidiResolver resolver; - resolver.setStatus(BidiStatus(LTR, false)); - resolver.setPositionIgnoringNestedIsolates(TextRunIterator(&textRun, 0)); - return resolver.determineParagraphDirectionality(hasStrongDirectionality); +TextDirection determineParagraphDirectionality( + const TextRun& textRun, + bool* hasStrongDirectionality = 0) { + BidiResolver resolver; + resolver.setStatus(BidiStatus(LTR, false)); + resolver.setPositionIgnoringNestedIsolates(TextRunIterator(&textRun, 0)); + return resolver.determineParagraphDirectionality(hasStrongDirectionality); } struct TestData { - UChar text[3]; - size_t length; - TextDirection expectedDirection; - bool expectedStrong; + UChar text[3]; + size_t length; + TextDirection expectedDirection; + bool expectedStrong; }; -void testDirectionality(const TestData& entry) -{ - bool hasStrongDirectionality; - String data(entry.text, entry.length); - TextRun run(data); - TextDirection direction = determineParagraphDirectionality(run, &hasStrongDirectionality); - EXPECT_EQ(entry.expectedStrong, hasStrongDirectionality); - EXPECT_EQ(entry.expectedDirection, direction); +void testDirectionality(const TestData& entry) { + bool hasStrongDirectionality; + String data(entry.text, entry.length); + TextRun run(data); + TextDirection direction = + determineParagraphDirectionality(run, &hasStrongDirectionality); + EXPECT_EQ(entry.expectedStrong, hasStrongDirectionality); + EXPECT_EQ(entry.expectedDirection, direction); } -TEST(BidiResolver, ParagraphDirectionSurrogates) -{ - const TestData testData[] = { - // Test strong RTL, non-BMP. (U+10858 Imperial Aramaic number one, strong RTL) - { { 0xD802, 0xDC58 }, 2, RTL, true }, +TEST(BidiResolver, ParagraphDirectionSurrogates) { + const TestData testData[] = {// Test strong RTL, non-BMP. (U+10858 Imperial + // Aramaic number one, strong RTL) + {{0xD802, 0xDC58}, 2, RTL, true}, - // Test strong LTR, non-BMP. (U+1D15F Musical symbol quarter note, strong LTR) - { { 0xD834, 0xDD5F }, 2, LTR, true }, + // Test strong LTR, non-BMP. (U+1D15F Musical + // symbol quarter note, strong LTR) + {{0xD834, 0xDD5F}, 2, LTR, true}, - // Test broken surrogate: valid leading, invalid trail. (Lead of U+10858, space) - { { 0xD802, ' ' }, 2, LTR, false }, + // Test broken surrogate: valid leading, invalid + // trail. (Lead of U+10858, space) + {{0xD802, ' '}, 2, LTR, false}, - // Test broken surrogate: invalid leading. (Trail of U+10858, U+05D0 Hebrew Alef) - { { 0xDC58, 0x05D0 }, 2, RTL, true }, + // Test broken surrogate: invalid leading. (Trail + // of U+10858, U+05D0 Hebrew Alef) + {{0xDC58, 0x05D0}, 2, RTL, true}, - // Test broken surrogate: valid leading, invalid trail/valid lead, valid trail. - { { 0xD802, 0xD802, 0xDC58 }, 3, RTL, true }, + // Test broken surrogate: valid leading, invalid + // trail/valid lead, valid trail. + {{0xD802, 0xD802, 0xDC58}, 3, RTL, true}, - // Test broken surrogate: valid leading, no trail (string too short). (Lead of U+10858) - { { 0xD802, 0xDC58 }, 1, LTR, false }, + // Test broken surrogate: valid leading, no trail + // (string too short). (Lead of U+10858) + {{0xD802, 0xDC58}, 1, LTR, false}, - // Test broken surrogate: trail appearing before lead. (U+10858 units reversed) - { { 0xDC58, 0xD802 }, 2, LTR, false } - }; - for (size_t i = 0; i < WTF_ARRAY_LENGTH(testData); ++i) - testDirectionality(testData[i]); + // Test broken surrogate: trail appearing before + // lead. (U+10858 units reversed) + {{0xDC58, 0xD802}, 2, LTR, false}}; + for (size_t i = 0; i < WTF_ARRAY_LENGTH(testData); ++i) + testDirectionality(testData[i]); } class BidiTestRunner { -public: - BidiTestRunner() - : m_testsRun(0) - , m_testsSkipped(0) - , m_ignoredCharFailures(0) - , m_levelFailures(0) - , m_orderFailures(0) - { - } - - void skipTestsWith(UChar codepoint) - { - m_skippedCodePoints.insert(codepoint); - } - - void runTest(const std::basic_string& input, const std::vector& reorder, - const std::vector& levels, bidi_test::ParagraphDirection, - const std::string& line, size_t lineNumber); - - size_t m_testsRun; - size_t m_testsSkipped; - std::set m_skippedCodePoints; - size_t m_ignoredCharFailures; - size_t m_levelFailures; - size_t m_orderFailures; + public: + BidiTestRunner() + : m_testsRun(0), + m_testsSkipped(0), + m_ignoredCharFailures(0), + m_levelFailures(0), + m_orderFailures(0) {} + + void skipTestsWith(UChar codepoint) { m_skippedCodePoints.insert(codepoint); } + + void runTest(const std::basic_string& input, + const std::vector& reorder, + const std::vector& levels, + bidi_test::ParagraphDirection, + const std::string& line, + size_t lineNumber); + + size_t m_testsRun; + size_t m_testsSkipped; + std::set m_skippedCodePoints; + size_t m_ignoredCharFailures; + size_t m_levelFailures; + size_t m_orderFailures; }; // Blink's UBA does not filter out control characters, etc. Maybe it should? // Instead it depends on later layers of Blink to simply ignore them. -// This function helps us emulate that to be compatible with BidiTest.txt expectations. -static bool isNonRenderedCodePoint(UChar c) -{ - // The tests also expect us to ignore soft-hyphen. - if (c == 0xAD) - return true; - // Control characters are not rendered: - return c >= 0x202A && c <= 0x202E; - // But it seems to expect LRI, etc. to be rendered!? +// This function helps us emulate that to be compatible with BidiTest.txt +// expectations. +static bool isNonRenderedCodePoint(UChar c) { + // The tests also expect us to ignore soft-hyphen. + if (c == 0xAD) + return true; + // Control characters are not rendered: + return c >= 0x202A && c <= 0x202E; + // But it seems to expect LRI, etc. to be rendered!? } -std::string diffString(const std::vector& actual, const std::vector& expected) -{ - std::ostringstream diff; - diff << "actual: "; - // This is the magical way to print a vector to a stream, clear, right? - std::copy(actual.begin(), actual.end(), std::ostream_iterator(diff, " ")); - diff << " expected: "; - std::copy(expected.begin(), expected.end(), std::ostream_iterator(diff, " ")); - return diff.str(); +std::string diffString(const std::vector& actual, + const std::vector& expected) { + std::ostringstream diff; + diff << "actual: "; + // This is the magical way to print a vector to a stream, clear, right? + std::copy(actual.begin(), actual.end(), + std::ostream_iterator(diff, " ")); + diff << " expected: "; + std::copy(expected.begin(), expected.end(), + std::ostream_iterator(diff, " ")); + return diff.str(); } -void BidiTestRunner::runTest(const std::basic_string& input, const std::vector& expectedOrder, - const std::vector& expectedLevels, bidi_test::ParagraphDirection paragraphDirection, - const std::string& line, size_t lineNumber) -{ - if (!m_skippedCodePoints.empty()) { - for (size_t i = 0; i < input.size(); i++) { - if (m_skippedCodePoints.count(input[i])) { - m_testsSkipped++; - return; - } - } +void BidiTestRunner::runTest(const std::basic_string& input, + const std::vector& expectedOrder, + const std::vector& expectedLevels, + bidi_test::ParagraphDirection paragraphDirection, + const std::string& line, + size_t lineNumber) { + if (!m_skippedCodePoints.empty()) { + for (size_t i = 0; i < input.size(); i++) { + if (m_skippedCodePoints.count(input[i])) { + m_testsSkipped++; + return; + } } + } - m_testsRun++; + m_testsRun++; - TextRun textRun(input.data(), input.size()); - switch (paragraphDirection) { + TextRun textRun(input.data(), input.size()); + switch (paragraphDirection) { case bidi_test::DirectionAutoLTR: - textRun.setDirection(determineParagraphDirectionality(textRun)); - break; + textRun.setDirection(determineParagraphDirectionality(textRun)); + break; case bidi_test::DirectionLTR: - textRun.setDirection(LTR); - break; + textRun.setDirection(LTR); + break; case bidi_test::DirectionRTL: - textRun.setDirection(RTL); - break; + textRun.setDirection(RTL); + break; + } + BidiResolver resolver; + resolver.setStatus( + BidiStatus(textRun.direction(), textRun.directionalOverride())); + resolver.setPositionIgnoringNestedIsolates(TextRunIterator(&textRun, 0)); + + BidiRunList& runs = resolver.runs(); + resolver.createBidiRunsForLine(TextRunIterator(&textRun, textRun.length())); + + std::ostringstream errorContext; + errorContext << ", line " << lineNumber << " \"" << line << "\""; + errorContext << " context: " + << bidi_test::nameFromParagraphDirection(paragraphDirection); + + std::vector actualOrder; + std::vector actualLevels; + actualLevels.assign(input.size(), -1); + BidiCharacterRun* run = runs.firstRun(); + while (run) { + // Blink's UBA just makes runs, the actual ordering of the display of + // characters is handled later in our pipeline, so we fake it here: + bool reversed = run->reversed(); + ASSERT(run->stop() >= run->start()); + size_t length = run->stop() - run->start(); + for (size_t i = 0; i < length; i++) { + int inputIndex = reversed ? run->stop() - i - 1 : run->start() + i; + if (!isNonRenderedCodePoint(input[inputIndex])) + actualOrder.push_back(inputIndex); + // BidiTest.txt gives expected level data in the order of the original + // input. + actualLevels[inputIndex] = run->level(); } - BidiResolver resolver; - resolver.setStatus(BidiStatus(textRun.direction(), textRun.directionalOverride())); - resolver.setPositionIgnoringNestedIsolates(TextRunIterator(&textRun, 0)); - - BidiRunList& runs = resolver.runs(); - resolver.createBidiRunsForLine(TextRunIterator(&textRun, textRun.length())); - - std::ostringstream errorContext; - errorContext << ", line " << lineNumber << " \"" << line << "\""; - errorContext << " context: " << bidi_test::nameFromParagraphDirection(paragraphDirection); - - std::vector actualOrder; - std::vector actualLevels; - actualLevels.assign(input.size(), -1); - BidiCharacterRun* run = runs.firstRun(); - while (run) { - // Blink's UBA just makes runs, the actual ordering of the display of characters - // is handled later in our pipeline, so we fake it here: - bool reversed = run->reversed(); - ASSERT(run->stop() >= run->start()); - size_t length = run->stop() - run->start(); - for (size_t i = 0; i < length; i++) { - int inputIndex = reversed ? run->stop() - i - 1 : run->start() + i; - if (!isNonRenderedCodePoint(input[inputIndex])) - actualOrder.push_back(inputIndex); - // BidiTest.txt gives expected level data in the order of the original input. - actualLevels[inputIndex] = run->level(); - } - run = run->next(); - } - - if (expectedOrder.size() != actualOrder.size()) { - m_ignoredCharFailures++; - EXPECT_EQ(expectedOrder.size(), actualOrder.size()) << errorContext.str(); - } else if (expectedOrder != actualOrder) { - m_orderFailures++; - printf("ORDER %s%s\n", diffString(actualOrder, expectedOrder).c_str(), errorContext.str().c_str()); + run = run->next(); + } + + if (expectedOrder.size() != actualOrder.size()) { + m_ignoredCharFailures++; + EXPECT_EQ(expectedOrder.size(), actualOrder.size()) << errorContext.str(); + } else if (expectedOrder != actualOrder) { + m_orderFailures++; + printf("ORDER %s%s\n", diffString(actualOrder, expectedOrder).c_str(), + errorContext.str().c_str()); + } + + if (expectedLevels.size() != actualLevels.size()) { + m_ignoredCharFailures++; + EXPECT_EQ(expectedLevels.size(), actualLevels.size()) << errorContext.str(); + } else { + for (size_t i = 0; i < expectedLevels.size(); i++) { + // level == -1 means the level should be ignored. + if (expectedLevels[i] == actualLevels[i] || expectedLevels[i] == -1) + continue; + + printf("LEVELS %s%s\n", diffString(actualLevels, expectedLevels).c_str(), + errorContext.str().c_str()); + m_levelFailures++; + break; } - - if (expectedLevels.size() != actualLevels.size()) { - m_ignoredCharFailures++; - EXPECT_EQ(expectedLevels.size(), actualLevels.size()) << errorContext.str(); - } else { - for (size_t i = 0; i < expectedLevels.size(); i++) { - // level == -1 means the level should be ignored. - if (expectedLevels[i] == actualLevels[i] || expectedLevels[i] == -1) - continue; - - printf("LEVELS %s%s\n", diffString(actualLevels, expectedLevels).c_str(), errorContext.str().c_str()); - m_levelFailures++; - break; - } - } - runs.deleteRuns(); + } + runs.deleteRuns(); } - -TEST(BidiResolver, BidiTest_txt) -{ - BidiTestRunner runner; - // Blink's Unicode Bidi Algorithm (UBA) doesn't yet support the - // new isolate directives from Unicode 6.3: - // http://www.unicode.org/reports/tr9/#Explicit_Directional_Isolates - runner.skipTestsWith(0x2066); // LRI - runner.skipTestsWith(0x2067); // RLI - runner.skipTestsWith(0x2068); // FSI - runner.skipTestsWith(0x2069); // PDI - - // This code wants to use PathService from base/path_service.h - // but we aren't allowed to depend on base/ directly from Blink yet. - // Alternatively we could use: - // blink::Platform::current()->unitTestSupport()->webKitRootDir() - // and a relative path, but that would require running inside - // webkit_unit_tests (to have a functioning Platform object). - // The file we want is: - // src/third_party/icu/source/test/testdata/BidiTest.txt - // but we don't have any good way to find it from this unittest. - // Just assume we're running this test manually for now. On the - // bots we just print a warning that we can't find the test file. - std::string bidiTestPath = "BidiTest.txt"; - std::ifstream bidiTestFile(bidiTestPath.c_str()); - if (!bidiTestFile.is_open()) { - printf("ERROR: Failed to open BidiTest.txt, cannot run tests.\n"); - return; - } - - bidi_test::Harness harness(runner); - harness.parse(bidiTestFile); - bidiTestFile.close(); - - if (runner.m_testsSkipped) - printf("WARNING: Skipped %zu tests.\n", runner.m_testsSkipped); - printf("Ran %zu tests: %zu level failures %zu order failures.\n", - runner.m_testsRun, runner.m_levelFailures, runner.m_orderFailures); - - // The unittest harness only pays attention to GTest output, so we verify - // that the tests behaved as expected: - EXPECT_EQ(352098u, runner.m_testsRun); - EXPECT_EQ(418143u, runner.m_testsSkipped); - EXPECT_EQ(0u, runner.m_ignoredCharFailures); - EXPECT_EQ(44882u, runner.m_levelFailures); - EXPECT_EQ(19151u, runner.m_orderFailures); +TEST(BidiResolver, BidiTest_txt) { + BidiTestRunner runner; + // Blink's Unicode Bidi Algorithm (UBA) doesn't yet support the + // new isolate directives from Unicode 6.3: + // http://www.unicode.org/reports/tr9/#Explicit_Directional_Isolates + runner.skipTestsWith(0x2066); // LRI + runner.skipTestsWith(0x2067); // RLI + runner.skipTestsWith(0x2068); // FSI + runner.skipTestsWith(0x2069); // PDI + + // This code wants to use PathService from base/path_service.h + // but we aren't allowed to depend on base/ directly from Blink yet. + // Alternatively we could use: + // blink::Platform::current()->unitTestSupport()->webKitRootDir() + // and a relative path, but that would require running inside + // webkit_unit_tests (to have a functioning Platform object). + // The file we want is: + // src/third_party/icu/source/test/testdata/BidiTest.txt + // but we don't have any good way to find it from this unittest. + // Just assume we're running this test manually for now. On the + // bots we just print a warning that we can't find the test file. + std::string bidiTestPath = "BidiTest.txt"; + std::ifstream bidiTestFile(bidiTestPath.c_str()); + if (!bidiTestFile.is_open()) { + printf("ERROR: Failed to open BidiTest.txt, cannot run tests.\n"); + return; + } + + bidi_test::Harness harness(runner); + harness.parse(bidiTestFile); + bidiTestFile.close(); + + if (runner.m_testsSkipped) + printf("WARNING: Skipped %zu tests.\n", runner.m_testsSkipped); + printf("Ran %zu tests: %zu level failures %zu order failures.\n", + runner.m_testsRun, runner.m_levelFailures, runner.m_orderFailures); + + // The unittest harness only pays attention to GTest output, so we verify + // that the tests behaved as expected: + EXPECT_EQ(352098u, runner.m_testsRun); + EXPECT_EQ(418143u, runner.m_testsSkipped); + EXPECT_EQ(0u, runner.m_ignoredCharFailures); + EXPECT_EQ(44882u, runner.m_levelFailures); + EXPECT_EQ(19151u, runner.m_orderFailures); } -} +} // namespace diff --git a/sky/engine/platform/text/BidiRunList.h b/sky/engine/platform/text/BidiRunList.h index 669966383bd7b..f562c151ba966 100644 --- a/sky/engine/platform/text/BidiRunList.h +++ b/sky/engine/platform/text/BidiRunList.h @@ -30,224 +30,214 @@ namespace blink { template class BidiRunList { - WTF_MAKE_NONCOPYABLE(BidiRunList); -public: - BidiRunList() - : m_firstRun(0) - , m_lastRun(0) - , m_logicallyLastRun(0) - , m_runCount(0) - { - } + WTF_MAKE_NONCOPYABLE(BidiRunList); - // FIXME: Once BidiResolver no longer owns the BidiRunList, - // then ~BidiRunList should call deleteRuns() automatically. + public: + BidiRunList() + : m_firstRun(0), m_lastRun(0), m_logicallyLastRun(0), m_runCount(0) {} - Run* firstRun() const { return m_firstRun; } - Run* lastRun() const { return m_lastRun; } - Run* logicallyLastRun() const { return m_logicallyLastRun; } - unsigned runCount() const { return m_runCount; } + // FIXME: Once BidiResolver no longer owns the BidiRunList, + // then ~BidiRunList should call deleteRuns() automatically. - void addRun(Run*); - void prependRun(Run*); + Run* firstRun() const { return m_firstRun; } + Run* lastRun() const { return m_lastRun; } + Run* logicallyLastRun() const { return m_logicallyLastRun; } + unsigned runCount() const { return m_runCount; } - void moveRunToEnd(Run*); - void moveRunToBeginning(Run*); + void addRun(Run*); + void prependRun(Run*); - void deleteRuns(); - void reverseRuns(unsigned start, unsigned end); - void reorderRunsFromLevels(); + void moveRunToEnd(Run*); + void moveRunToBeginning(Run*); - void setLogicallyLastRun(Run* run) { m_logicallyLastRun = run; } + void deleteRuns(); + void reverseRuns(unsigned start, unsigned end); + void reorderRunsFromLevels(); - void replaceRunWithRuns(Run* toReplace, BidiRunList& newRuns); + void setLogicallyLastRun(Run* run) { m_logicallyLastRun = run; } -private: - void clearWithoutDestroyingRuns(); + void replaceRunWithRuns(Run* toReplace, BidiRunList& newRuns); - Run* m_firstRun; - Run* m_lastRun; - Run* m_logicallyLastRun; - unsigned m_runCount; -}; + private: + void clearWithoutDestroyingRuns(); -template -inline void BidiRunList::addRun(Run* run) -{ - if (!m_firstRun) - m_firstRun = run; - else - m_lastRun->m_next = run; - m_lastRun = run; - m_runCount++; -} + Run* m_firstRun; + Run* m_lastRun; + Run* m_logicallyLastRun; + unsigned m_runCount; +}; template -inline void BidiRunList::prependRun(Run* run) -{ - ASSERT(!run->m_next); - - if (!m_lastRun) - m_lastRun = run; - else - run->m_next = m_firstRun; +inline void BidiRunList::addRun(Run* run) { + if (!m_firstRun) m_firstRun = run; - m_runCount++; + else + m_lastRun->m_next = run; + m_lastRun = run; + m_runCount++; } template -inline void BidiRunList::moveRunToEnd(Run* run) -{ - ASSERT(m_firstRun); - ASSERT(m_lastRun); - ASSERT(run->m_next); - - Run* current = 0; - Run* next = m_firstRun; - while (next != run) { - current = next; - next = current->next(); - } - - if (!current) - m_firstRun = run->next(); - else - current->m_next = run->m_next; - - run->m_next = 0; - m_lastRun->m_next = run; +inline void BidiRunList::prependRun(Run* run) { + ASSERT(!run->m_next); + + if (!m_lastRun) m_lastRun = run; + else + run->m_next = m_firstRun; + m_firstRun = run; + m_runCount++; } template -inline void BidiRunList::moveRunToBeginning(Run* run) -{ - ASSERT(m_firstRun); - ASSERT(m_lastRun); - ASSERT(run != m_firstRun); - - Run* current = m_firstRun; - Run* next = current->next(); - while (next != run) { - current = next; - next = current->next(); - } - +inline void BidiRunList::moveRunToEnd(Run* run) { + ASSERT(m_firstRun); + ASSERT(m_lastRun); + ASSERT(run->m_next); + + Run* current = 0; + Run* next = m_firstRun; + while (next != run) { + current = next; + next = current->next(); + } + + if (!current) + m_firstRun = run->next(); + else current->m_next = run->m_next; - if (run == m_lastRun) - m_lastRun = current; - run->m_next = m_firstRun; - m_firstRun = run; + run->m_next = 0; + m_lastRun->m_next = run; + m_lastRun = run; +} + +template +inline void BidiRunList::moveRunToBeginning(Run* run) { + ASSERT(m_firstRun); + ASSERT(m_lastRun); + ASSERT(run != m_firstRun); + + Run* current = m_firstRun; + Run* next = current->next(); + while (next != run) { + current = next; + next = current->next(); + } + + current->m_next = run->m_next; + if (run == m_lastRun) + m_lastRun = current; + + run->m_next = m_firstRun; + m_firstRun = run; } template -void BidiRunList::replaceRunWithRuns(Run* toReplace, BidiRunList& newRuns) -{ - ASSERT(newRuns.runCount()); - ASSERT(m_firstRun); - ASSERT(toReplace); - - if (m_firstRun == toReplace) { - m_firstRun = newRuns.firstRun(); - } else { - // Find the run just before "toReplace" in the list of runs. - Run* previousRun = m_firstRun; - while (previousRun->next() != toReplace) - previousRun = previousRun->next(); - ASSERT(previousRun); - previousRun->setNext(newRuns.firstRun()); - } - - newRuns.lastRun()->setNext(toReplace->next()); - - // Fix up any of other pointers which may now be stale. - if (m_lastRun == toReplace) - m_lastRun = newRuns.lastRun(); - if (m_logicallyLastRun == toReplace) - m_logicallyLastRun = newRuns.logicallyLastRun(); - m_runCount += newRuns.runCount() - 1; // We added the new runs and removed toReplace. - - delete toReplace; - newRuns.clearWithoutDestroyingRuns(); +void BidiRunList::replaceRunWithRuns(Run* toReplace, + BidiRunList& newRuns) { + ASSERT(newRuns.runCount()); + ASSERT(m_firstRun); + ASSERT(toReplace); + + if (m_firstRun == toReplace) { + m_firstRun = newRuns.firstRun(); + } else { + // Find the run just before "toReplace" in the list of runs. + Run* previousRun = m_firstRun; + while (previousRun->next() != toReplace) + previousRun = previousRun->next(); + ASSERT(previousRun); + previousRun->setNext(newRuns.firstRun()); + } + + newRuns.lastRun()->setNext(toReplace->next()); + + // Fix up any of other pointers which may now be stale. + if (m_lastRun == toReplace) + m_lastRun = newRuns.lastRun(); + if (m_logicallyLastRun == toReplace) + m_logicallyLastRun = newRuns.logicallyLastRun(); + m_runCount += + newRuns.runCount() - 1; // We added the new runs and removed toReplace. + + delete toReplace; + newRuns.clearWithoutDestroyingRuns(); } template -void BidiRunList::clearWithoutDestroyingRuns() -{ - m_firstRun = 0; - m_lastRun = 0; - m_logicallyLastRun = 0; - m_runCount = 0; +void BidiRunList::clearWithoutDestroyingRuns() { + m_firstRun = 0; + m_lastRun = 0; + m_logicallyLastRun = 0; + m_runCount = 0; } template -void BidiRunList::deleteRuns() -{ - if (!m_firstRun) - return; - - Run* curr = m_firstRun; - while (curr) { - Run* s = curr->next(); - delete curr; - curr = s; - } - - clearWithoutDestroyingRuns(); +void BidiRunList::deleteRuns() { + if (!m_firstRun) + return; + + Run* curr = m_firstRun; + while (curr) { + Run* s = curr->next(); + delete curr; + curr = s; + } + + clearWithoutDestroyingRuns(); } template -void BidiRunList::reverseRuns(unsigned start, unsigned end) -{ - ASSERT(m_runCount); - if (start >= end) - return; - - ASSERT(end < m_runCount); - - // Get the item before the start of the runs to reverse and put it in - // |beforeStart|. |curr| should point to the first run to reverse. - Run* curr = m_firstRun; - Run* beforeStart = 0; - unsigned i = 0; - while (i < start) { - i++; - beforeStart = curr; - curr = curr->next(); - } - - Run* startRun = curr; - while (i < end) { - i++; - curr = curr->next(); - } - Run* endRun = curr; - Run* afterEnd = curr->next(); - - i = start; - curr = startRun; - Run* newNext = afterEnd; - while (i <= end) { - // Do the reversal. - Run* next = curr->next(); - curr->m_next = newNext; - newNext = curr; - curr = next; - i++; - } - - // Now hook up beforeStart and afterEnd to the startRun and endRun. - if (beforeStart) - beforeStart->m_next = endRun; - else - m_firstRun = endRun; - - startRun->m_next = afterEnd; - if (!afterEnd) - m_lastRun = startRun; +void BidiRunList::reverseRuns(unsigned start, unsigned end) { + ASSERT(m_runCount); + if (start >= end) + return; + + ASSERT(end < m_runCount); + + // Get the item before the start of the runs to reverse and put it in + // |beforeStart|. |curr| should point to the first run to reverse. + Run* curr = m_firstRun; + Run* beforeStart = 0; + unsigned i = 0; + while (i < start) { + i++; + beforeStart = curr; + curr = curr->next(); + } + + Run* startRun = curr; + while (i < end) { + i++; + curr = curr->next(); + } + Run* endRun = curr; + Run* afterEnd = curr->next(); + + i = start; + curr = startRun; + Run* newNext = afterEnd; + while (i <= end) { + // Do the reversal. + Run* next = curr->next(); + curr->m_next = newNext; + newNext = curr; + curr = next; + i++; + } + + // Now hook up beforeStart and afterEnd to the startRun and endRun. + if (beforeStart) + beforeStart->m_next = endRun; + else + m_firstRun = endRun; + + startRun->m_next = afterEnd; + if (!afterEnd) + m_lastRun = startRun; } -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_TEXT_BIDIRUNLIST_H_ diff --git a/sky/engine/platform/text/BidiTestHarness.h b/sky/engine/platform/text/BidiTestHarness.h index bf44d14f356b0..1b14d28e4f4b7 100644 --- a/sky/engine/platform/text/BidiTestHarness.h +++ b/sky/engine/platform/text/BidiTestHarness.h @@ -42,7 +42,8 @@ // Unicode.org provides a reference implmentation, including parser: // http://www.unicode.org/Public/PROGRAMS/BidiReferenceC/6.3.0/source/brtest.c // But it, like the other implementations I've found, is rather tied to -// the algorithms it is testing. This file seeks to only implement the parser bits. +// the algorithms it is testing. This file seeks to only implement the parser +// bits. // Other C/C++ implementations of this parser: // https://github.com/googlei18n/fribidi-vs-unicode/blob/master/test.c @@ -57,217 +58,211 @@ namespace bidi_test { enum ParagraphDirection { - DirectionAutoLTR = 1, - DirectionLTR = 2, - DirectionRTL = 4, + DirectionAutoLTR = 1, + DirectionLTR = 2, + DirectionRTL = 4, }; -const int kMaxParagraphDirection = DirectionAutoLTR | DirectionLTR | DirectionRTL; +const int kMaxParagraphDirection = + DirectionAutoLTR | DirectionLTR | DirectionRTL; // For error printing: -std::string nameFromParagraphDirection(ParagraphDirection paragraphDirection) -{ - switch (paragraphDirection) { +std::string nameFromParagraphDirection(ParagraphDirection paragraphDirection) { + switch (paragraphDirection) { case bidi_test::DirectionAutoLTR: - return "Auto-LTR"; + return "Auto-LTR"; case bidi_test::DirectionLTR: - return "LTR"; + return "LTR"; case bidi_test::DirectionRTL: - return "RTL"; - } - // This should never be reached. - return ""; + return "RTL"; + } + // This should never be reached. + return ""; } -template +template class Harness { -public: - Harness(Runner& runner) - : m_runner(runner) - { - } - void parse(std::istream& bidiTestFile); + public: + Harness(Runner& runner) : m_runner(runner) {} + void parse(std::istream& bidiTestFile); -private: - Runner& m_runner; + private: + Runner& m_runner; }; // We could use boost::trim, but no other part of Blink uses boost yet. -inline void ltrim(std::string& s) -{ - static const std::string separators(" \t"); - s.erase(0, s.find_first_not_of(separators)); +inline void ltrim(std::string& s) { + static const std::string separators(" \t"); + s.erase(0, s.find_first_not_of(separators)); } -inline void rtrim(std::string& s) -{ - static const std::string separators(" \t"); - size_t lastNonSpace = s.find_last_not_of(separators); - if (lastNonSpace == std::string::npos) { - s.erase(); - return; - } - size_t firstSpaceAtEndOfString = lastNonSpace + 1; - if (firstSpaceAtEndOfString >= s.size()) - return; // lastNonSpace was the last char. - s.erase(firstSpaceAtEndOfString, std::string::npos); // erase to the end of the string. +inline void rtrim(std::string& s) { + static const std::string separators(" \t"); + size_t lastNonSpace = s.find_last_not_of(separators); + if (lastNonSpace == std::string::npos) { + s.erase(); + return; + } + size_t firstSpaceAtEndOfString = lastNonSpace + 1; + if (firstSpaceAtEndOfString >= s.size()) + return; // lastNonSpace was the last char. + s.erase(firstSpaceAtEndOfString, + std::string::npos); // erase to the end of the string. } -inline void trim(std::string& s) -{ - rtrim(s); - ltrim(s); +inline void trim(std::string& s) { + rtrim(s); + ltrim(s); } -static std::vector parseStringList(const std::string& str) -{ - std::vector strings; - static const std::string separators(" \t"); - size_t lastPos = str.find_first_not_of(separators); // skip leading spaces - size_t pos = str.find_first_of(separators, lastPos); // find next space +static std::vector parseStringList(const std::string& str) { + std::vector strings; + static const std::string separators(" \t"); + size_t lastPos = str.find_first_not_of(separators); // skip leading spaces + size_t pos = str.find_first_of(separators, lastPos); // find next space - while (std::string::npos != pos || std::string::npos != lastPos) { - strings.push_back(str.substr(lastPos, pos - lastPos)); - lastPos = str.find_first_not_of(separators, pos); - pos = str.find_first_of(separators, lastPos); - } - return strings; + while (std::string::npos != pos || std::string::npos != lastPos) { + strings.push_back(str.substr(lastPos, pos - lastPos)); + lastPos = str.find_first_not_of(separators, pos); + pos = str.find_first_of(separators, lastPos); + } + return strings; } -static std::vector parseIntList(const std::string& str) -{ - std::vector ints; - std::vector strings = parseStringList(str); - for (size_t x = 0; x < strings.size(); x++) { - int i = atoi(strings[x].c_str()); - ints.push_back(i); - } - return ints; +static std::vector parseIntList(const std::string& str) { + std::vector ints; + std::vector strings = parseStringList(str); + for (size_t x = 0; x < strings.size(); x++) { + int i = atoi(strings[x].c_str()); + ints.push_back(i); + } + return ints; } -static std::vector parseLevels(const std::string& line) -{ - std::vector levels; - std::vector strings = parseStringList(line); - for (size_t x = 0; x < strings.size(); x++) { - const std::string& levelString = strings[x]; - int i; - if (levelString == "x") - i = -1; - else - i = atoi(levelString.c_str()); - levels.push_back(i); - } - return levels; +static std::vector parseLevels(const std::string& line) { + std::vector levels; + std::vector strings = parseStringList(line); + for (size_t x = 0; x < strings.size(); x++) { + const std::string& levelString = strings[x]; + int i; + if (levelString == "x") + i = -1; + else + i = atoi(levelString.c_str()); + levels.push_back(i); + } + return levels; } // This is not thread-safe as written. -static std::basic_string parseTestString(const std::string& line) -{ - std::basic_string testString; - static std::map charClassExamples; - if (charClassExamples.empty()) { - // FIXME: Explicit make_pair is ugly, but required for C++98 compat. - charClassExamples.insert(std::make_pair("L", 0x6c)); // 'l' for L - charClassExamples.insert(std::make_pair("R", 0x05D0)); // HEBREW ALEF - charClassExamples.insert(std::make_pair("EN", 0x33)); // '3' for EN - charClassExamples.insert(std::make_pair("ES", 0x2d)); // '-' for ES - charClassExamples.insert(std::make_pair("ET", 0x25)); // '%' for ET - charClassExamples.insert(std::make_pair("AN", 0x0660)); // arabic 0 - charClassExamples.insert(std::make_pair("CS", 0x2c)); // ',' for CS - charClassExamples.insert(std::make_pair("B", 0x0A)); // - charClassExamples.insert(std::make_pair("S", 0x09)); // - charClassExamples.insert(std::make_pair("WS", 0x20)); // ' ' for WS - charClassExamples.insert(std::make_pair("ON", 0x3d)); // '=' for ON - charClassExamples.insert(std::make_pair("NSM", 0x05BF)); // HEBREW POINT RAFE - charClassExamples.insert(std::make_pair("AL", 0x0608)); // ARABIC RAY - charClassExamples.insert(std::make_pair("BN", 0x00AD)); // SOFT HYPHEN - charClassExamples.insert(std::make_pair("LRE", 0x202A)); - charClassExamples.insert(std::make_pair("RLE", 0x202B)); - charClassExamples.insert(std::make_pair("PDF", 0x202C)); - charClassExamples.insert(std::make_pair("LRO", 0x202D)); - charClassExamples.insert(std::make_pair("RLO", 0x202E)); - charClassExamples.insert(std::make_pair("LRI", 0x2066)); - charClassExamples.insert(std::make_pair("RLI", 0x2067)); - charClassExamples.insert(std::make_pair("FSI", 0x2068)); - charClassExamples.insert(std::make_pair("PDI", 0x2069)); - } +static std::basic_string parseTestString(const std::string& line) { + std::basic_string testString; + static std::map charClassExamples; + if (charClassExamples.empty()) { + // FIXME: Explicit make_pair is ugly, but required for C++98 compat. + charClassExamples.insert(std::make_pair("L", 0x6c)); // 'l' for L + charClassExamples.insert(std::make_pair("R", 0x05D0)); // HEBREW ALEF + charClassExamples.insert(std::make_pair("EN", 0x33)); // '3' for EN + charClassExamples.insert(std::make_pair("ES", 0x2d)); // '-' for ES + charClassExamples.insert(std::make_pair("ET", 0x25)); // '%' for ET + charClassExamples.insert(std::make_pair("AN", 0x0660)); // arabic 0 + charClassExamples.insert(std::make_pair("CS", 0x2c)); // ',' for CS + charClassExamples.insert(std::make_pair("B", 0x0A)); // + charClassExamples.insert(std::make_pair("S", 0x09)); // + charClassExamples.insert(std::make_pair("WS", 0x20)); // ' ' for WS + charClassExamples.insert(std::make_pair("ON", 0x3d)); // '=' for ON + charClassExamples.insert( + std::make_pair("NSM", 0x05BF)); // HEBREW POINT RAFE + charClassExamples.insert(std::make_pair("AL", 0x0608)); // ARABIC RAY + charClassExamples.insert(std::make_pair("BN", 0x00AD)); // SOFT HYPHEN + charClassExamples.insert(std::make_pair("LRE", 0x202A)); + charClassExamples.insert(std::make_pair("RLE", 0x202B)); + charClassExamples.insert(std::make_pair("PDF", 0x202C)); + charClassExamples.insert(std::make_pair("LRO", 0x202D)); + charClassExamples.insert(std::make_pair("RLO", 0x202E)); + charClassExamples.insert(std::make_pair("LRI", 0x2066)); + charClassExamples.insert(std::make_pair("RLI", 0x2067)); + charClassExamples.insert(std::make_pair("FSI", 0x2068)); + charClassExamples.insert(std::make_pair("PDI", 0x2069)); + } - std::vector charClasses = parseStringList(line); - for (size_t i = 0; i < charClasses.size(); i++) { - // FIXME: If the lookup failed we could return false for a parse error. - testString.push_back(charClassExamples.find(charClasses[i])->second); - } - return testString; + std::vector charClasses = parseStringList(line); + for (size_t i = 0; i < charClasses.size(); i++) { + // FIXME: If the lookup failed we could return false for a parse error. + testString.push_back(charClassExamples.find(charClasses[i])->second); + } + return testString; } -static bool parseParagraphDirectionMask(const std::string& line, int& modeMask) -{ - modeMask = atoi(line.c_str()); - return modeMask >= 1 && modeMask <= kMaxParagraphDirection; +static bool parseParagraphDirectionMask(const std::string& line, + int& modeMask) { + modeMask = atoi(line.c_str()); + return modeMask >= 1 && modeMask <= kMaxParagraphDirection; } -static void parseError(const std::string& line, size_t lineNumber) -{ - // Use printf to avoid the expense of std::cout. - printf("Parse error, line %zu : %s\n", lineNumber, line.c_str()); +static void parseError(const std::string& line, size_t lineNumber) { + // Use printf to avoid the expense of std::cout. + printf("Parse error, line %zu : %s\n", lineNumber, line.c_str()); } -template -void Harness::parse(std::istream& bidiTestFile) -{ - static const std::string levelsPrefix("@Levels"); - static const std::string reorderPrefix("@Reorder"); +template +void Harness::parse(std::istream& bidiTestFile) { + static const std::string levelsPrefix("@Levels"); + static const std::string reorderPrefix("@Reorder"); - // FIXME: UChar is an ICU type and cheating a bit to use here. - // uint16_t might be more portable. - std::basic_string testString; - std::vector levels; - std::vector reorder; - int paragraphDirectionMask; + // FIXME: UChar is an ICU type and cheating a bit to use here. + // uint16_t might be more portable. + std::basic_string testString; + std::vector levels; + std::vector reorder; + int paragraphDirectionMask; - std::string line; - size_t lineNumber = 0; - while (std::getline(bidiTestFile, line)) { - lineNumber++; - const std::string originalLine = line; - size_t commentStart = line.find_first_of('#'); - if (commentStart != std::string::npos) - line = line.substr(0, commentStart); - trim(line); - if (line.empty()) - continue; - if (line[0] == '@') { - if (!line.find(levelsPrefix)) { - levels = parseLevels(line.substr(levelsPrefix.length() + 1)); - continue; - } - if (!line.find(reorderPrefix)) { - reorder = parseIntList(line.substr(reorderPrefix.length() + 1)); - continue; - } - } else { - // Assume it's a data line. - size_t seperatorIndex = line.find_first_of(';'); - if (seperatorIndex == std::string::npos) { - parseError(originalLine, lineNumber); - continue; - } - testString = parseTestString(line.substr(0, seperatorIndex)); - if (!parseParagraphDirectionMask(line.substr(seperatorIndex + 1), paragraphDirectionMask)) { - parseError(originalLine, lineNumber); - continue; - } + std::string line; + size_t lineNumber = 0; + while (std::getline(bidiTestFile, line)) { + lineNumber++; + const std::string originalLine = line; + size_t commentStart = line.find_first_of('#'); + if (commentStart != std::string::npos) + line = line.substr(0, commentStart); + trim(line); + if (line.empty()) + continue; + if (line[0] == '@') { + if (!line.find(levelsPrefix)) { + levels = parseLevels(line.substr(levelsPrefix.length() + 1)); + continue; + } + if (!line.find(reorderPrefix)) { + reorder = parseIntList(line.substr(reorderPrefix.length() + 1)); + continue; + } + } else { + // Assume it's a data line. + size_t seperatorIndex = line.find_first_of(';'); + if (seperatorIndex == std::string::npos) { + parseError(originalLine, lineNumber); + continue; + } + testString = parseTestString(line.substr(0, seperatorIndex)); + if (!parseParagraphDirectionMask(line.substr(seperatorIndex + 1), + paragraphDirectionMask)) { + parseError(originalLine, lineNumber); + continue; + } - if (paragraphDirectionMask & DirectionAutoLTR) - m_runner.runTest(testString, reorder, levels, DirectionAutoLTR, originalLine, lineNumber); - if (paragraphDirectionMask & DirectionLTR) - m_runner.runTest(testString, reorder, levels, DirectionLTR, originalLine, lineNumber); - if (paragraphDirectionMask & DirectionRTL) - m_runner.runTest(testString, reorder, levels, DirectionRTL, originalLine, lineNumber); - } + if (paragraphDirectionMask & DirectionAutoLTR) + m_runner.runTest(testString, reorder, levels, DirectionAutoLTR, + originalLine, lineNumber); + if (paragraphDirectionMask & DirectionLTR) + m_runner.runTest(testString, reorder, levels, DirectionLTR, + originalLine, lineNumber); + if (paragraphDirectionMask & DirectionRTL) + m_runner.runTest(testString, reorder, levels, DirectionRTL, + originalLine, lineNumber); } + } } -} // namespace bidi_test +} // namespace bidi_test #endif // SKY_ENGINE_PLATFORM_TEXT_BIDITESTHARNESS_H_ diff --git a/sky/engine/platform/text/BidiTextRun.cpp b/sky/engine/platform/text/BidiTextRun.cpp index f8a4a774f7f05..09ea083e1502f 100644 --- a/sky/engine/platform/text/BidiTextRun.cpp +++ b/sky/engine/platform/text/BidiTextRun.cpp @@ -35,28 +35,28 @@ namespace blink { -TextDirection directionForRun(TextRun& run, bool& hasStrongDirectionality) -{ - BidiResolver bidiResolver; - bidiResolver.setStatus(BidiStatus(run.direction(), run.directionalOverride())); - bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&run, 0)); - return bidiResolver.determineParagraphDirectionality(&hasStrongDirectionality); +TextDirection directionForRun(TextRun& run, bool& hasStrongDirectionality) { + BidiResolver bidiResolver; + bidiResolver.setStatus( + BidiStatus(run.direction(), run.directionalOverride())); + bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&run, 0)); + return bidiResolver.determineParagraphDirectionality( + &hasStrongDirectionality); } -TextDirection determineDirectionality(const String& value, bool& hasStrongDirectionality) -{ - TextRun run(value); - return directionForRun(run, hasStrongDirectionality); +TextDirection determineDirectionality(const String& value, + bool& hasStrongDirectionality) { + TextRun run(value); + return directionForRun(run, hasStrongDirectionality); } -TextRun textRunWithDirectionality(const String& value, bool& hasStrongDirectionality) -{ - TextRun run(value); - TextDirection direction = directionForRun(run, hasStrongDirectionality); - if (hasStrongDirectionality) - run.setDirection(direction); - return run; +TextRun textRunWithDirectionality(const String& value, + bool& hasStrongDirectionality) { + TextRun run(value); + TextDirection direction = directionForRun(run, hasStrongDirectionality); + if (hasStrongDirectionality) + run.setDirection(direction); + return run; } -} // namespace blink - +} // namespace blink diff --git a/sky/engine/platform/text/BidiTextRun.h b/sky/engine/platform/text/BidiTextRun.h index 5ec317aca5e5a..5e4523ecef132 100644 --- a/sky/engine/platform/text/BidiTextRun.h +++ b/sky/engine/platform/text/BidiTextRun.h @@ -37,10 +37,13 @@ namespace blink { -PLATFORM_EXPORT TextDirection directionForRun(TextRun&, bool& hasStrongDirectionality); -PLATFORM_EXPORT TextDirection determineDirectionality(const String& value, bool& hasStrongDirectionality); -PLATFORM_EXPORT TextRun textRunWithDirectionality(const String& value, bool& hasStrongDirectionality); +PLATFORM_EXPORT TextDirection directionForRun(TextRun&, + bool& hasStrongDirectionality); +PLATFORM_EXPORT TextDirection +determineDirectionality(const String& value, bool& hasStrongDirectionality); +PLATFORM_EXPORT TextRun +textRunWithDirectionality(const String& value, bool& hasStrongDirectionality); -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_TEXT_BIDITEXTRUN_H_ diff --git a/sky/engine/platform/text/LineEnding.cpp b/sky/engine/platform/text/LineEnding.cpp index 9d5b4697ce952..13f8d8a22a4ca 100644 --- a/sky/engine/platform/text/LineEnding.cpp +++ b/sky/engine/platform/text/LineEnding.cpp @@ -37,195 +37,178 @@ namespace { class OutputBuffer { -public: - virtual char* allocate(size_t) = 0; - virtual void copy(const CString&) = 0; - virtual ~OutputBuffer() { } + public: + virtual char* allocate(size_t) = 0; + virtual void copy(const CString&) = 0; + virtual ~OutputBuffer() {} }; class CStringBuffer final : public OutputBuffer { -public: - CStringBuffer(CString& buffer) - : m_buffer(buffer) - { - } - ~CStringBuffer() override { } + public: + CStringBuffer(CString& buffer) : m_buffer(buffer) {} + ~CStringBuffer() override {} - char* allocate(size_t size) override - { - char* ptr; - m_buffer = CString::newUninitialized(size, ptr); - return ptr; - } + char* allocate(size_t size) override { + char* ptr; + m_buffer = CString::newUninitialized(size, ptr); + return ptr; + } - void copy(const CString& source) override - { - m_buffer = source; - } + void copy(const CString& source) override { m_buffer = source; } - const CString& buffer() const { return m_buffer; } + const CString& buffer() const { return m_buffer; } -private: - CString m_buffer; + private: + CString m_buffer; }; class VectorCharAppendBuffer final : public OutputBuffer { -public: - VectorCharAppendBuffer(Vector& buffer) - : m_buffer(buffer) - { - } - ~VectorCharAppendBuffer() override { } - - char* allocate(size_t size) override - { - size_t oldSize = m_buffer.size(); - m_buffer.grow(oldSize + size); - return m_buffer.data() + oldSize; - } - - void copy(const CString& source) override - { - m_buffer.append(source.data(), source.length()); - } - -private: - Vector& m_buffer; + public: + VectorCharAppendBuffer(Vector& buffer) : m_buffer(buffer) {} + ~VectorCharAppendBuffer() override {} + + char* allocate(size_t size) override { + size_t oldSize = m_buffer.size(); + m_buffer.grow(oldSize + size); + return m_buffer.data() + oldSize; + } + + void copy(const CString& source) override { + m_buffer.append(source.data(), source.length()); + } + + private: + Vector& m_buffer; }; -void internalNormalizeLineEndingsToCRLF(const CString& from, OutputBuffer& buffer) -{ - // Compute the new length. - size_t newLen = 0; - const char* p = from.data(); - while (p < from.data() + from.length()) { - char c = *p++; - if (c == '\r') { - // Safe to look ahead because of trailing '\0'. - if (*p != '\n') { - // Turn CR into CRLF. - newLen += 2; - } - } else if (c == '\n') { - // Turn LF into CRLF. - newLen += 2; - } else { - // Leave other characters alone. - newLen += 1; - } +void internalNormalizeLineEndingsToCRLF(const CString& from, + OutputBuffer& buffer) { + // Compute the new length. + size_t newLen = 0; + const char* p = from.data(); + while (p < from.data() + from.length()) { + char c = *p++; + if (c == '\r') { + // Safe to look ahead because of trailing '\0'. + if (*p != '\n') { + // Turn CR into CRLF. + newLen += 2; + } + } else if (c == '\n') { + // Turn LF into CRLF. + newLen += 2; + } else { + // Leave other characters alone. + newLen += 1; } - if (newLen < from.length()) - return; - - if (newLen == from.length()) { - buffer.copy(from); - return; - } - - p = from.data(); - char* q = buffer.allocate(newLen); - - // Make a copy of the string. - while (p < from.data() + from.length()) { - char c = *p++; - if (c == '\r') { - // Safe to look ahead because of trailing '\0'. - if (*p != '\n') { - // Turn CR into CRLF. - *q++ = '\r'; - *q++ = '\n'; - } - } else if (c == '\n') { - // Turn LF into CRLF. - *q++ = '\r'; - *q++ = '\n'; - } else { - // Leave other characters alone. - *q++ = c; - } + } + if (newLen < from.length()) + return; + + if (newLen == from.length()) { + buffer.copy(from); + return; + } + + p = from.data(); + char* q = buffer.allocate(newLen); + + // Make a copy of the string. + while (p < from.data() + from.length()) { + char c = *p++; + if (c == '\r') { + // Safe to look ahead because of trailing '\0'. + if (*p != '\n') { + // Turn CR into CRLF. + *q++ = '\r'; + *q++ = '\n'; + } + } else if (c == '\n') { + // Turn LF into CRLF. + *q++ = '\r'; + *q++ = '\n'; + } else { + // Leave other characters alone. + *q++ = c; } + } } -}; +}; // namespace namespace blink { void normalizeToCROrLF(const CString& from, Vector& result, bool toCR); // Normalize all line-endings to CR or LF. -void normalizeToCROrLF(const CString& from, Vector& result, bool toCR) -{ - // Compute the new length. - size_t newLen = 0; - bool needFix = false; - const char* p = from.data(); - char fromEndingChar = toCR ? '\n' : '\r'; - char toEndingChar = toCR ? '\r' : '\n'; - while (p < from.data() + from.length()) { - char c = *p++; - if (c == '\r' && *p == '\n') { - // Turn CRLF into CR or LF. - p++; - needFix = true; - } else if (c == fromEndingChar) { - // Turn CR/LF into LF/CR. - needFix = true; - } - newLen += 1; +void normalizeToCROrLF(const CString& from, Vector& result, bool toCR) { + // Compute the new length. + size_t newLen = 0; + bool needFix = false; + const char* p = from.data(); + char fromEndingChar = toCR ? '\n' : '\r'; + char toEndingChar = toCR ? '\r' : '\n'; + while (p < from.data() + from.length()) { + char c = *p++; + if (c == '\r' && *p == '\n') { + // Turn CRLF into CR or LF. + p++; + needFix = true; + } else if (c == fromEndingChar) { + // Turn CR/LF into LF/CR. + needFix = true; } - - // Grow the result buffer. - p = from.data(); - size_t oldResultSize = result.size(); - result.grow(oldResultSize + newLen); - char* q = result.data() + oldResultSize; - - // If no need to fix the string, just copy the string over. - if (!needFix) { - memcpy(q, p, from.length()); - return; - } - - // Make a copy of the string. - while (p < from.data() + from.length()) { - char c = *p++; - if (c == '\r' && *p == '\n') { - // Turn CRLF or CR into CR or LF. - p++; - *q++ = toEndingChar; - } else if (c == fromEndingChar) { - // Turn CR/LF into LF/CR. - *q++ = toEndingChar; - } else { - // Leave other characters alone. - *q++ = c; - } + newLen += 1; + } + + // Grow the result buffer. + p = from.data(); + size_t oldResultSize = result.size(); + result.grow(oldResultSize + newLen); + char* q = result.data() + oldResultSize; + + // If no need to fix the string, just copy the string over. + if (!needFix) { + memcpy(q, p, from.length()); + return; + } + + // Make a copy of the string. + while (p < from.data() + from.length()) { + char c = *p++; + if (c == '\r' && *p == '\n') { + // Turn CRLF or CR into CR or LF. + p++; + *q++ = toEndingChar; + } else if (c == fromEndingChar) { + // Turn CR/LF into LF/CR. + *q++ = toEndingChar; + } else { + // Leave other characters alone. + *q++ = c; } + } } -CString normalizeLineEndingsToCRLF(const CString& from) -{ - if (!from.length()) - return from; - CString result; - CStringBuffer buffer(result); - internalNormalizeLineEndingsToCRLF(from, buffer); - return buffer.buffer(); +CString normalizeLineEndingsToCRLF(const CString& from) { + if (!from.length()) + return from; + CString result; + CStringBuffer buffer(result); + internalNormalizeLineEndingsToCRLF(from, buffer); + return buffer.buffer(); } -void normalizeLineEndingsToCR(const CString& from, Vector& result) -{ - normalizeToCROrLF(from, result, true); +void normalizeLineEndingsToCR(const CString& from, Vector& result) { + normalizeToCROrLF(from, result, true); } -void normalizeLineEndingsToLF(const CString& from, Vector& result) -{ - normalizeToCROrLF(from, result, false); +void normalizeLineEndingsToLF(const CString& from, Vector& result) { + normalizeToCROrLF(from, result, false); } -void normalizeLineEndingsToNative(const CString& from, Vector& result) -{ - normalizeLineEndingsToLF(from, result); +void normalizeLineEndingsToNative(const CString& from, Vector& result) { + normalizeLineEndingsToLF(from, result); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/text/LineEnding.h b/sky/engine/platform/text/LineEnding.h index 5bfd66830e629..8b3430b9d7bd5 100644 --- a/sky/engine/platform/text/LineEnding.h +++ b/sky/engine/platform/text/LineEnding.h @@ -41,16 +41,22 @@ namespace blink { // Normalize all line-endings in the given string to CRLF. PLATFORM_EXPORT CString normalizeLineEndingsToCRLF(const CString& from); -// Normalize all line-endings in the given string to CR and append the result to the given buffer. -PLATFORM_EXPORT void normalizeLineEndingsToCR(const CString& from, Vector& result); - -// Normalize all line-endings in the given string to LF and append the result to the given buffer. -PLATFORM_EXPORT void normalizeLineEndingsToLF(const CString& from, Vector& result); - -// Normalize all line-endings in the given string to the native line-endings and append the result to the given buffer. -// (Normalize to CRLF on Windows and normalize to LF on all other platforms.) -PLATFORM_EXPORT void normalizeLineEndingsToNative(const CString& from, Vector& result); - -} // namespace blink +// Normalize all line-endings in the given string to CR and append the result to +// the given buffer. +PLATFORM_EXPORT void normalizeLineEndingsToCR(const CString& from, + Vector& result); + +// Normalize all line-endings in the given string to LF and append the result to +// the given buffer. +PLATFORM_EXPORT void normalizeLineEndingsToLF(const CString& from, + Vector& result); + +// Normalize all line-endings in the given string to the native line-endings and +// append the result to the given buffer. (Normalize to CRLF on Windows and +// normalize to LF on all other platforms.) +PLATFORM_EXPORT void normalizeLineEndingsToNative(const CString& from, + Vector& result); + +} // namespace blink #endif // SKY_ENGINE_PLATFORM_TEXT_LINEENDING_H_ diff --git a/sky/engine/platform/text/LocaleToScriptMapping.cpp b/sky/engine/platform/text/LocaleToScriptMapping.cpp index 6a38cbe8cc7d9..b17a9c1e606d1 100644 --- a/sky/engine/platform/text/LocaleToScriptMapping.cpp +++ b/sky/engine/platform/text/LocaleToScriptMapping.cpp @@ -36,368 +36,371 @@ namespace blink { -UScriptCode scriptNameToCode(const String& scriptName) -{ - struct ScriptNameCode { - const char* name; - UScriptCode code; - }; +UScriptCode scriptNameToCode(const String& scriptName) { + struct ScriptNameCode { + const char* name; + UScriptCode code; + }; - // This generally maps an ISO 15924 script code to its UScriptCode, but certain families of script codes are - // treated as a single script for assigning a per-script font in Settings. For example, "hira" is mapped to - // USCRIPT_KATAKANA_OR_HIRAGANA instead of USCRIPT_HIRAGANA, since we want all Japanese scripts to be rendered - // using the same font setting. - static const ScriptNameCode scriptNameCodeList[] = { - { "zyyy", USCRIPT_COMMON }, - { "qaai", USCRIPT_INHERITED }, - { "arab", USCRIPT_ARABIC }, - { "armn", USCRIPT_ARMENIAN }, - { "beng", USCRIPT_BENGALI }, - { "bopo", USCRIPT_BOPOMOFO }, - { "cher", USCRIPT_CHEROKEE }, - { "copt", USCRIPT_COPTIC }, - { "cyrl", USCRIPT_CYRILLIC }, - { "dsrt", USCRIPT_DESERET }, - { "deva", USCRIPT_DEVANAGARI }, - { "ethi", USCRIPT_ETHIOPIC }, - { "geor", USCRIPT_GEORGIAN }, - { "goth", USCRIPT_GOTHIC }, - { "grek", USCRIPT_GREEK }, - { "gujr", USCRIPT_GUJARATI }, - { "guru", USCRIPT_GURMUKHI }, - { "hani", USCRIPT_HAN }, - { "hang", USCRIPT_HANGUL }, - { "hebr", USCRIPT_HEBREW }, - { "hira", USCRIPT_KATAKANA_OR_HIRAGANA }, - { "knda", USCRIPT_KANNADA }, - { "kana", USCRIPT_KATAKANA_OR_HIRAGANA }, - { "khmr", USCRIPT_KHMER }, - { "laoo", USCRIPT_LAO }, - { "latn", USCRIPT_LATIN }, - { "mlym", USCRIPT_MALAYALAM }, - { "mong", USCRIPT_MONGOLIAN }, - { "mymr", USCRIPT_MYANMAR }, - { "ogam", USCRIPT_OGHAM }, - { "ital", USCRIPT_OLD_ITALIC }, - { "orya", USCRIPT_ORIYA }, - { "runr", USCRIPT_RUNIC }, - { "sinh", USCRIPT_SINHALA }, - { "syrc", USCRIPT_SYRIAC }, - { "taml", USCRIPT_TAMIL }, - { "telu", USCRIPT_TELUGU }, - { "thaa", USCRIPT_THAANA }, - { "thai", USCRIPT_THAI }, - { "tibt", USCRIPT_TIBETAN }, - { "cans", USCRIPT_CANADIAN_ABORIGINAL }, - { "yiii", USCRIPT_YI }, - { "tglg", USCRIPT_TAGALOG }, - { "hano", USCRIPT_HANUNOO }, - { "buhd", USCRIPT_BUHID }, - { "tagb", USCRIPT_TAGBANWA }, - { "brai", USCRIPT_BRAILLE }, - { "cprt", USCRIPT_CYPRIOT }, - { "limb", USCRIPT_LIMBU }, - { "linb", USCRIPT_LINEAR_B }, - { "osma", USCRIPT_OSMANYA }, - { "shaw", USCRIPT_SHAVIAN }, - { "tale", USCRIPT_TAI_LE }, - { "ugar", USCRIPT_UGARITIC }, - { "hrkt", USCRIPT_KATAKANA_OR_HIRAGANA }, - { "bugi", USCRIPT_BUGINESE }, - { "glag", USCRIPT_GLAGOLITIC }, - { "khar", USCRIPT_KHAROSHTHI }, - { "sylo", USCRIPT_SYLOTI_NAGRI }, - { "talu", USCRIPT_NEW_TAI_LUE }, - { "tfng", USCRIPT_TIFINAGH }, - { "xpeo", USCRIPT_OLD_PERSIAN }, - { "bali", USCRIPT_BALINESE }, - { "batk", USCRIPT_BATAK }, - { "blis", USCRIPT_BLISSYMBOLS }, - { "brah", USCRIPT_BRAHMI }, - { "cham", USCRIPT_CHAM }, - { "cirt", USCRIPT_CIRTH }, - { "cyrs", USCRIPT_OLD_CHURCH_SLAVONIC_CYRILLIC }, - { "egyd", USCRIPT_DEMOTIC_EGYPTIAN }, - { "egyh", USCRIPT_HIERATIC_EGYPTIAN }, - { "egyp", USCRIPT_EGYPTIAN_HIEROGLYPHS }, - { "geok", USCRIPT_KHUTSURI }, - { "hans", USCRIPT_SIMPLIFIED_HAN }, - { "hant", USCRIPT_TRADITIONAL_HAN }, - { "hmng", USCRIPT_PAHAWH_HMONG }, - { "hung", USCRIPT_OLD_HUNGARIAN }, - { "inds", USCRIPT_HARAPPAN_INDUS }, - { "java", USCRIPT_JAVANESE }, - { "kali", USCRIPT_KAYAH_LI }, - { "latf", USCRIPT_LATIN_FRAKTUR }, - { "latg", USCRIPT_LATIN_GAELIC }, - { "lepc", USCRIPT_LEPCHA }, - { "lina", USCRIPT_LINEAR_A }, - { "mand", USCRIPT_MANDAEAN }, - { "maya", USCRIPT_MAYAN_HIEROGLYPHS }, - { "mero", USCRIPT_MEROITIC }, - { "nkoo", USCRIPT_NKO }, - { "orkh", USCRIPT_ORKHON }, - { "perm", USCRIPT_OLD_PERMIC }, - { "phag", USCRIPT_PHAGS_PA }, - { "phnx", USCRIPT_PHOENICIAN }, - { "plrd", USCRIPT_PHONETIC_POLLARD }, - { "roro", USCRIPT_RONGORONGO }, - { "sara", USCRIPT_SARATI }, - { "syre", USCRIPT_ESTRANGELO_SYRIAC }, - { "syrj", USCRIPT_WESTERN_SYRIAC }, - { "syrn", USCRIPT_EASTERN_SYRIAC }, - { "teng", USCRIPT_TENGWAR }, - { "vaii", USCRIPT_VAI }, - { "visp", USCRIPT_VISIBLE_SPEECH }, - { "xsux", USCRIPT_CUNEIFORM }, - { "jpan", USCRIPT_KATAKANA_OR_HIRAGANA }, - { "kore", USCRIPT_HANGUL }, - { "zxxx", USCRIPT_UNWRITTEN_LANGUAGES }, - { "zzzz", USCRIPT_UNKNOWN } - }; + // This generally maps an ISO 15924 script code to its UScriptCode, but + // certain families of script codes are treated as a single script for + // assigning a per-script font in Settings. For example, "hira" is mapped to + // USCRIPT_KATAKANA_OR_HIRAGANA instead of USCRIPT_HIRAGANA, since we want all + // Japanese scripts to be rendered using the same font setting. + static const ScriptNameCode scriptNameCodeList[] = { + {"zyyy", USCRIPT_COMMON}, + {"qaai", USCRIPT_INHERITED}, + {"arab", USCRIPT_ARABIC}, + {"armn", USCRIPT_ARMENIAN}, + {"beng", USCRIPT_BENGALI}, + {"bopo", USCRIPT_BOPOMOFO}, + {"cher", USCRIPT_CHEROKEE}, + {"copt", USCRIPT_COPTIC}, + {"cyrl", USCRIPT_CYRILLIC}, + {"dsrt", USCRIPT_DESERET}, + {"deva", USCRIPT_DEVANAGARI}, + {"ethi", USCRIPT_ETHIOPIC}, + {"geor", USCRIPT_GEORGIAN}, + {"goth", USCRIPT_GOTHIC}, + {"grek", USCRIPT_GREEK}, + {"gujr", USCRIPT_GUJARATI}, + {"guru", USCRIPT_GURMUKHI}, + {"hani", USCRIPT_HAN}, + {"hang", USCRIPT_HANGUL}, + {"hebr", USCRIPT_HEBREW}, + {"hira", USCRIPT_KATAKANA_OR_HIRAGANA}, + {"knda", USCRIPT_KANNADA}, + {"kana", USCRIPT_KATAKANA_OR_HIRAGANA}, + {"khmr", USCRIPT_KHMER}, + {"laoo", USCRIPT_LAO}, + {"latn", USCRIPT_LATIN}, + {"mlym", USCRIPT_MALAYALAM}, + {"mong", USCRIPT_MONGOLIAN}, + {"mymr", USCRIPT_MYANMAR}, + {"ogam", USCRIPT_OGHAM}, + {"ital", USCRIPT_OLD_ITALIC}, + {"orya", USCRIPT_ORIYA}, + {"runr", USCRIPT_RUNIC}, + {"sinh", USCRIPT_SINHALA}, + {"syrc", USCRIPT_SYRIAC}, + {"taml", USCRIPT_TAMIL}, + {"telu", USCRIPT_TELUGU}, + {"thaa", USCRIPT_THAANA}, + {"thai", USCRIPT_THAI}, + {"tibt", USCRIPT_TIBETAN}, + {"cans", USCRIPT_CANADIAN_ABORIGINAL}, + {"yiii", USCRIPT_YI}, + {"tglg", USCRIPT_TAGALOG}, + {"hano", USCRIPT_HANUNOO}, + {"buhd", USCRIPT_BUHID}, + {"tagb", USCRIPT_TAGBANWA}, + {"brai", USCRIPT_BRAILLE}, + {"cprt", USCRIPT_CYPRIOT}, + {"limb", USCRIPT_LIMBU}, + {"linb", USCRIPT_LINEAR_B}, + {"osma", USCRIPT_OSMANYA}, + {"shaw", USCRIPT_SHAVIAN}, + {"tale", USCRIPT_TAI_LE}, + {"ugar", USCRIPT_UGARITIC}, + {"hrkt", USCRIPT_KATAKANA_OR_HIRAGANA}, + {"bugi", USCRIPT_BUGINESE}, + {"glag", USCRIPT_GLAGOLITIC}, + {"khar", USCRIPT_KHAROSHTHI}, + {"sylo", USCRIPT_SYLOTI_NAGRI}, + {"talu", USCRIPT_NEW_TAI_LUE}, + {"tfng", USCRIPT_TIFINAGH}, + {"xpeo", USCRIPT_OLD_PERSIAN}, + {"bali", USCRIPT_BALINESE}, + {"batk", USCRIPT_BATAK}, + {"blis", USCRIPT_BLISSYMBOLS}, + {"brah", USCRIPT_BRAHMI}, + {"cham", USCRIPT_CHAM}, + {"cirt", USCRIPT_CIRTH}, + {"cyrs", USCRIPT_OLD_CHURCH_SLAVONIC_CYRILLIC}, + {"egyd", USCRIPT_DEMOTIC_EGYPTIAN}, + {"egyh", USCRIPT_HIERATIC_EGYPTIAN}, + {"egyp", USCRIPT_EGYPTIAN_HIEROGLYPHS}, + {"geok", USCRIPT_KHUTSURI}, + {"hans", USCRIPT_SIMPLIFIED_HAN}, + {"hant", USCRIPT_TRADITIONAL_HAN}, + {"hmng", USCRIPT_PAHAWH_HMONG}, + {"hung", USCRIPT_OLD_HUNGARIAN}, + {"inds", USCRIPT_HARAPPAN_INDUS}, + {"java", USCRIPT_JAVANESE}, + {"kali", USCRIPT_KAYAH_LI}, + {"latf", USCRIPT_LATIN_FRAKTUR}, + {"latg", USCRIPT_LATIN_GAELIC}, + {"lepc", USCRIPT_LEPCHA}, + {"lina", USCRIPT_LINEAR_A}, + {"mand", USCRIPT_MANDAEAN}, + {"maya", USCRIPT_MAYAN_HIEROGLYPHS}, + {"mero", USCRIPT_MEROITIC}, + {"nkoo", USCRIPT_NKO}, + {"orkh", USCRIPT_ORKHON}, + {"perm", USCRIPT_OLD_PERMIC}, + {"phag", USCRIPT_PHAGS_PA}, + {"phnx", USCRIPT_PHOENICIAN}, + {"plrd", USCRIPT_PHONETIC_POLLARD}, + {"roro", USCRIPT_RONGORONGO}, + {"sara", USCRIPT_SARATI}, + {"syre", USCRIPT_ESTRANGELO_SYRIAC}, + {"syrj", USCRIPT_WESTERN_SYRIAC}, + {"syrn", USCRIPT_EASTERN_SYRIAC}, + {"teng", USCRIPT_TENGWAR}, + {"vaii", USCRIPT_VAI}, + {"visp", USCRIPT_VISIBLE_SPEECH}, + {"xsux", USCRIPT_CUNEIFORM}, + {"jpan", USCRIPT_KATAKANA_OR_HIRAGANA}, + {"kore", USCRIPT_HANGUL}, + {"zxxx", USCRIPT_UNWRITTEN_LANGUAGES}, + {"zzzz", USCRIPT_UNKNOWN}}; - typedef HashMap ScriptNameCodeMap; - DEFINE_STATIC_LOCAL(ScriptNameCodeMap, scriptNameCodeMap, ()); - if (scriptNameCodeMap.isEmpty()) { - for (size_t i = 0; i < sizeof(scriptNameCodeList) / sizeof(scriptNameCodeList[0]); ++i) - scriptNameCodeMap.set(scriptNameCodeList[i].name, scriptNameCodeList[i].code); - } + typedef HashMap ScriptNameCodeMap; + DEFINE_STATIC_LOCAL(ScriptNameCodeMap, scriptNameCodeMap, ()); + if (scriptNameCodeMap.isEmpty()) { + for (size_t i = 0; + i < sizeof(scriptNameCodeList) / sizeof(scriptNameCodeList[0]); ++i) + scriptNameCodeMap.set(scriptNameCodeList[i].name, + scriptNameCodeList[i].code); + } - HashMap::iterator it = scriptNameCodeMap.find(scriptName.lower()); - if (it != scriptNameCodeMap.end()) - return it->value; - return USCRIPT_INVALID_CODE; + HashMap::iterator it = + scriptNameCodeMap.find(scriptName.lower()); + if (it != scriptNameCodeMap.end()) + return it->value; + return USCRIPT_INVALID_CODE; } -UScriptCode localeToScriptCodeForFontSelection(const String& locale) -{ - struct LocaleScript { - const char* locale; - UScriptCode script; - }; +UScriptCode localeToScriptCodeForFontSelection(const String& locale) { + struct LocaleScript { + const char* locale; + UScriptCode script; + }; - static const LocaleScript localeScriptList[] = { - { "aa", USCRIPT_LATIN }, - { "ab", USCRIPT_CYRILLIC }, - { "ady", USCRIPT_CYRILLIC }, - { "af", USCRIPT_LATIN }, - { "ak", USCRIPT_LATIN }, - { "am", USCRIPT_ETHIOPIC }, - { "ar", USCRIPT_ARABIC }, - { "as", USCRIPT_BENGALI }, - { "ast", USCRIPT_LATIN }, - { "av", USCRIPT_CYRILLIC }, - { "ay", USCRIPT_LATIN }, - { "az", USCRIPT_LATIN }, - { "ba", USCRIPT_CYRILLIC }, - { "be", USCRIPT_CYRILLIC }, - { "bg", USCRIPT_CYRILLIC }, - { "bi", USCRIPT_LATIN }, - { "bn", USCRIPT_BENGALI }, - { "bo", USCRIPT_TIBETAN }, - { "bs", USCRIPT_LATIN }, - { "ca", USCRIPT_LATIN }, - { "ce", USCRIPT_CYRILLIC }, - { "ceb", USCRIPT_LATIN }, - { "ch", USCRIPT_LATIN }, - { "chk", USCRIPT_LATIN }, - { "cs", USCRIPT_LATIN }, - { "cy", USCRIPT_LATIN }, - { "da", USCRIPT_LATIN }, - { "de", USCRIPT_LATIN }, - { "dv", USCRIPT_THAANA }, - { "dz", USCRIPT_TIBETAN }, - { "ee", USCRIPT_LATIN }, - { "efi", USCRIPT_LATIN }, - { "el", USCRIPT_GREEK }, - { "en", USCRIPT_LATIN }, - { "es", USCRIPT_LATIN }, - { "et", USCRIPT_LATIN }, - { "eu", USCRIPT_LATIN }, - { "fa", USCRIPT_ARABIC }, - { "fi", USCRIPT_LATIN }, - { "fil", USCRIPT_LATIN }, - { "fj", USCRIPT_LATIN }, - { "fo", USCRIPT_LATIN }, - { "fr", USCRIPT_LATIN }, - { "fur", USCRIPT_LATIN }, - { "fy", USCRIPT_LATIN }, - { "ga", USCRIPT_LATIN }, - { "gaa", USCRIPT_LATIN }, - { "gd", USCRIPT_LATIN }, - { "gil", USCRIPT_LATIN }, - { "gl", USCRIPT_LATIN }, - { "gn", USCRIPT_LATIN }, - { "gsw", USCRIPT_LATIN }, - { "gu", USCRIPT_GUJARATI }, - { "ha", USCRIPT_LATIN }, - { "haw", USCRIPT_LATIN }, - { "he", USCRIPT_HEBREW }, - { "hi", USCRIPT_DEVANAGARI }, - { "hil", USCRIPT_LATIN }, - { "ho", USCRIPT_LATIN }, - { "hr", USCRIPT_LATIN }, - { "ht", USCRIPT_LATIN }, - { "hu", USCRIPT_LATIN }, - { "hy", USCRIPT_ARMENIAN }, - { "id", USCRIPT_LATIN }, - { "ig", USCRIPT_LATIN }, - { "ii", USCRIPT_YI }, - { "ilo", USCRIPT_LATIN }, - { "inh", USCRIPT_CYRILLIC }, - { "is", USCRIPT_LATIN }, - { "it", USCRIPT_LATIN }, - { "iu", USCRIPT_CANADIAN_ABORIGINAL }, - { "ja", USCRIPT_KATAKANA_OR_HIRAGANA }, - { "jv", USCRIPT_LATIN }, - { "ka", USCRIPT_GEORGIAN }, - { "kaj", USCRIPT_LATIN }, - { "kam", USCRIPT_LATIN }, - { "kbd", USCRIPT_CYRILLIC }, - { "kha", USCRIPT_LATIN }, - { "kk", USCRIPT_CYRILLIC }, - { "kl", USCRIPT_LATIN }, - { "km", USCRIPT_KHMER }, - { "kn", USCRIPT_KANNADA }, - { "ko", USCRIPT_HANGUL }, - { "kok", USCRIPT_DEVANAGARI }, - { "kos", USCRIPT_LATIN }, - { "kpe", USCRIPT_LATIN }, - { "krc", USCRIPT_CYRILLIC }, - { "ks", USCRIPT_ARABIC }, - { "ku", USCRIPT_ARABIC }, - { "kum", USCRIPT_CYRILLIC }, - { "ky", USCRIPT_CYRILLIC }, - { "la", USCRIPT_LATIN }, - { "lah", USCRIPT_ARABIC }, - { "lb", USCRIPT_LATIN }, - { "lez", USCRIPT_CYRILLIC }, - { "ln", USCRIPT_LATIN }, - { "lo", USCRIPT_LAO }, - { "lt", USCRIPT_LATIN }, - { "lv", USCRIPT_LATIN }, - { "mai", USCRIPT_DEVANAGARI }, - { "mdf", USCRIPT_CYRILLIC }, - { "mg", USCRIPT_LATIN }, - { "mh", USCRIPT_LATIN }, - { "mi", USCRIPT_LATIN }, - { "mk", USCRIPT_CYRILLIC }, - { "ml", USCRIPT_MALAYALAM }, - { "mn", USCRIPT_CYRILLIC }, - { "mr", USCRIPT_DEVANAGARI }, - { "ms", USCRIPT_LATIN }, - { "mt", USCRIPT_LATIN }, - { "my", USCRIPT_MYANMAR }, - { "myv", USCRIPT_CYRILLIC }, - { "na", USCRIPT_LATIN }, - { "nb", USCRIPT_LATIN }, - { "ne", USCRIPT_DEVANAGARI }, - { "niu", USCRIPT_LATIN }, - { "nl", USCRIPT_LATIN }, - { "nn", USCRIPT_LATIN }, - { "nr", USCRIPT_LATIN }, - { "nso", USCRIPT_LATIN }, - { "ny", USCRIPT_LATIN }, - { "oc", USCRIPT_LATIN }, - { "om", USCRIPT_LATIN }, - { "or", USCRIPT_ORIYA }, - { "os", USCRIPT_CYRILLIC }, - { "pa", USCRIPT_GURMUKHI }, - { "pag", USCRIPT_LATIN }, - { "pap", USCRIPT_LATIN }, - { "pau", USCRIPT_LATIN }, - { "pl", USCRIPT_LATIN }, - { "pon", USCRIPT_LATIN }, - { "ps", USCRIPT_ARABIC }, - { "pt", USCRIPT_LATIN }, - { "qu", USCRIPT_LATIN }, - { "rm", USCRIPT_LATIN }, - { "rn", USCRIPT_LATIN }, - { "ro", USCRIPT_LATIN }, - { "ru", USCRIPT_CYRILLIC }, - { "rw", USCRIPT_LATIN }, - { "sa", USCRIPT_DEVANAGARI }, - { "sah", USCRIPT_CYRILLIC }, - { "sat", USCRIPT_LATIN }, - { "sd", USCRIPT_ARABIC }, - { "se", USCRIPT_LATIN }, - { "sg", USCRIPT_LATIN }, - { "si", USCRIPT_SINHALA }, - { "sid", USCRIPT_LATIN }, - { "sk", USCRIPT_LATIN }, - { "sl", USCRIPT_LATIN }, - { "sm", USCRIPT_LATIN }, - { "so", USCRIPT_LATIN }, - { "sq", USCRIPT_LATIN }, - { "sr", USCRIPT_CYRILLIC }, - { "ss", USCRIPT_LATIN }, - { "st", USCRIPT_LATIN }, - { "su", USCRIPT_LATIN }, - { "sv", USCRIPT_LATIN }, - { "sw", USCRIPT_LATIN }, - { "ta", USCRIPT_TAMIL }, - { "te", USCRIPT_TELUGU }, - { "tet", USCRIPT_LATIN }, - { "tg", USCRIPT_CYRILLIC }, - { "th", USCRIPT_THAI }, - { "ti", USCRIPT_ETHIOPIC }, - { "tig", USCRIPT_ETHIOPIC }, - { "tk", USCRIPT_LATIN }, - { "tkl", USCRIPT_LATIN }, - { "tl", USCRIPT_LATIN }, - { "tn", USCRIPT_LATIN }, - { "to", USCRIPT_LATIN }, - { "tpi", USCRIPT_LATIN }, - { "tr", USCRIPT_LATIN }, - { "trv", USCRIPT_LATIN }, - { "ts", USCRIPT_LATIN }, - { "tt", USCRIPT_CYRILLIC }, - { "tvl", USCRIPT_LATIN }, - { "tw", USCRIPT_LATIN }, - { "ty", USCRIPT_LATIN }, - { "tyv", USCRIPT_CYRILLIC }, - { "udm", USCRIPT_CYRILLIC }, - { "ug", USCRIPT_ARABIC }, - { "uk", USCRIPT_CYRILLIC }, - { "und", USCRIPT_LATIN }, - { "ur", USCRIPT_ARABIC }, - { "uz", USCRIPT_CYRILLIC }, - { "ve", USCRIPT_LATIN }, - { "vi", USCRIPT_LATIN }, - { "wal", USCRIPT_ETHIOPIC }, - { "war", USCRIPT_LATIN }, - { "wo", USCRIPT_LATIN }, - { "xh", USCRIPT_LATIN }, - { "yap", USCRIPT_LATIN }, - { "yo", USCRIPT_LATIN }, - { "za", USCRIPT_LATIN }, - { "zh", USCRIPT_SIMPLIFIED_HAN }, - { "zh_hk", USCRIPT_TRADITIONAL_HAN }, - { "zh_tw", USCRIPT_TRADITIONAL_HAN }, - { "zu", USCRIPT_LATIN } - }; + static const LocaleScript localeScriptList[] = { + {"aa", USCRIPT_LATIN}, + {"ab", USCRIPT_CYRILLIC}, + {"ady", USCRIPT_CYRILLIC}, + {"af", USCRIPT_LATIN}, + {"ak", USCRIPT_LATIN}, + {"am", USCRIPT_ETHIOPIC}, + {"ar", USCRIPT_ARABIC}, + {"as", USCRIPT_BENGALI}, + {"ast", USCRIPT_LATIN}, + {"av", USCRIPT_CYRILLIC}, + {"ay", USCRIPT_LATIN}, + {"az", USCRIPT_LATIN}, + {"ba", USCRIPT_CYRILLIC}, + {"be", USCRIPT_CYRILLIC}, + {"bg", USCRIPT_CYRILLIC}, + {"bi", USCRIPT_LATIN}, + {"bn", USCRIPT_BENGALI}, + {"bo", USCRIPT_TIBETAN}, + {"bs", USCRIPT_LATIN}, + {"ca", USCRIPT_LATIN}, + {"ce", USCRIPT_CYRILLIC}, + {"ceb", USCRIPT_LATIN}, + {"ch", USCRIPT_LATIN}, + {"chk", USCRIPT_LATIN}, + {"cs", USCRIPT_LATIN}, + {"cy", USCRIPT_LATIN}, + {"da", USCRIPT_LATIN}, + {"de", USCRIPT_LATIN}, + {"dv", USCRIPT_THAANA}, + {"dz", USCRIPT_TIBETAN}, + {"ee", USCRIPT_LATIN}, + {"efi", USCRIPT_LATIN}, + {"el", USCRIPT_GREEK}, + {"en", USCRIPT_LATIN}, + {"es", USCRIPT_LATIN}, + {"et", USCRIPT_LATIN}, + {"eu", USCRIPT_LATIN}, + {"fa", USCRIPT_ARABIC}, + {"fi", USCRIPT_LATIN}, + {"fil", USCRIPT_LATIN}, + {"fj", USCRIPT_LATIN}, + {"fo", USCRIPT_LATIN}, + {"fr", USCRIPT_LATIN}, + {"fur", USCRIPT_LATIN}, + {"fy", USCRIPT_LATIN}, + {"ga", USCRIPT_LATIN}, + {"gaa", USCRIPT_LATIN}, + {"gd", USCRIPT_LATIN}, + {"gil", USCRIPT_LATIN}, + {"gl", USCRIPT_LATIN}, + {"gn", USCRIPT_LATIN}, + {"gsw", USCRIPT_LATIN}, + {"gu", USCRIPT_GUJARATI}, + {"ha", USCRIPT_LATIN}, + {"haw", USCRIPT_LATIN}, + {"he", USCRIPT_HEBREW}, + {"hi", USCRIPT_DEVANAGARI}, + {"hil", USCRIPT_LATIN}, + {"ho", USCRIPT_LATIN}, + {"hr", USCRIPT_LATIN}, + {"ht", USCRIPT_LATIN}, + {"hu", USCRIPT_LATIN}, + {"hy", USCRIPT_ARMENIAN}, + {"id", USCRIPT_LATIN}, + {"ig", USCRIPT_LATIN}, + {"ii", USCRIPT_YI}, + {"ilo", USCRIPT_LATIN}, + {"inh", USCRIPT_CYRILLIC}, + {"is", USCRIPT_LATIN}, + {"it", USCRIPT_LATIN}, + {"iu", USCRIPT_CANADIAN_ABORIGINAL}, + {"ja", USCRIPT_KATAKANA_OR_HIRAGANA}, + {"jv", USCRIPT_LATIN}, + {"ka", USCRIPT_GEORGIAN}, + {"kaj", USCRIPT_LATIN}, + {"kam", USCRIPT_LATIN}, + {"kbd", USCRIPT_CYRILLIC}, + {"kha", USCRIPT_LATIN}, + {"kk", USCRIPT_CYRILLIC}, + {"kl", USCRIPT_LATIN}, + {"km", USCRIPT_KHMER}, + {"kn", USCRIPT_KANNADA}, + {"ko", USCRIPT_HANGUL}, + {"kok", USCRIPT_DEVANAGARI}, + {"kos", USCRIPT_LATIN}, + {"kpe", USCRIPT_LATIN}, + {"krc", USCRIPT_CYRILLIC}, + {"ks", USCRIPT_ARABIC}, + {"ku", USCRIPT_ARABIC}, + {"kum", USCRIPT_CYRILLIC}, + {"ky", USCRIPT_CYRILLIC}, + {"la", USCRIPT_LATIN}, + {"lah", USCRIPT_ARABIC}, + {"lb", USCRIPT_LATIN}, + {"lez", USCRIPT_CYRILLIC}, + {"ln", USCRIPT_LATIN}, + {"lo", USCRIPT_LAO}, + {"lt", USCRIPT_LATIN}, + {"lv", USCRIPT_LATIN}, + {"mai", USCRIPT_DEVANAGARI}, + {"mdf", USCRIPT_CYRILLIC}, + {"mg", USCRIPT_LATIN}, + {"mh", USCRIPT_LATIN}, + {"mi", USCRIPT_LATIN}, + {"mk", USCRIPT_CYRILLIC}, + {"ml", USCRIPT_MALAYALAM}, + {"mn", USCRIPT_CYRILLIC}, + {"mr", USCRIPT_DEVANAGARI}, + {"ms", USCRIPT_LATIN}, + {"mt", USCRIPT_LATIN}, + {"my", USCRIPT_MYANMAR}, + {"myv", USCRIPT_CYRILLIC}, + {"na", USCRIPT_LATIN}, + {"nb", USCRIPT_LATIN}, + {"ne", USCRIPT_DEVANAGARI}, + {"niu", USCRIPT_LATIN}, + {"nl", USCRIPT_LATIN}, + {"nn", USCRIPT_LATIN}, + {"nr", USCRIPT_LATIN}, + {"nso", USCRIPT_LATIN}, + {"ny", USCRIPT_LATIN}, + {"oc", USCRIPT_LATIN}, + {"om", USCRIPT_LATIN}, + {"or", USCRIPT_ORIYA}, + {"os", USCRIPT_CYRILLIC}, + {"pa", USCRIPT_GURMUKHI}, + {"pag", USCRIPT_LATIN}, + {"pap", USCRIPT_LATIN}, + {"pau", USCRIPT_LATIN}, + {"pl", USCRIPT_LATIN}, + {"pon", USCRIPT_LATIN}, + {"ps", USCRIPT_ARABIC}, + {"pt", USCRIPT_LATIN}, + {"qu", USCRIPT_LATIN}, + {"rm", USCRIPT_LATIN}, + {"rn", USCRIPT_LATIN}, + {"ro", USCRIPT_LATIN}, + {"ru", USCRIPT_CYRILLIC}, + {"rw", USCRIPT_LATIN}, + {"sa", USCRIPT_DEVANAGARI}, + {"sah", USCRIPT_CYRILLIC}, + {"sat", USCRIPT_LATIN}, + {"sd", USCRIPT_ARABIC}, + {"se", USCRIPT_LATIN}, + {"sg", USCRIPT_LATIN}, + {"si", USCRIPT_SINHALA}, + {"sid", USCRIPT_LATIN}, + {"sk", USCRIPT_LATIN}, + {"sl", USCRIPT_LATIN}, + {"sm", USCRIPT_LATIN}, + {"so", USCRIPT_LATIN}, + {"sq", USCRIPT_LATIN}, + {"sr", USCRIPT_CYRILLIC}, + {"ss", USCRIPT_LATIN}, + {"st", USCRIPT_LATIN}, + {"su", USCRIPT_LATIN}, + {"sv", USCRIPT_LATIN}, + {"sw", USCRIPT_LATIN}, + {"ta", USCRIPT_TAMIL}, + {"te", USCRIPT_TELUGU}, + {"tet", USCRIPT_LATIN}, + {"tg", USCRIPT_CYRILLIC}, + {"th", USCRIPT_THAI}, + {"ti", USCRIPT_ETHIOPIC}, + {"tig", USCRIPT_ETHIOPIC}, + {"tk", USCRIPT_LATIN}, + {"tkl", USCRIPT_LATIN}, + {"tl", USCRIPT_LATIN}, + {"tn", USCRIPT_LATIN}, + {"to", USCRIPT_LATIN}, + {"tpi", USCRIPT_LATIN}, + {"tr", USCRIPT_LATIN}, + {"trv", USCRIPT_LATIN}, + {"ts", USCRIPT_LATIN}, + {"tt", USCRIPT_CYRILLIC}, + {"tvl", USCRIPT_LATIN}, + {"tw", USCRIPT_LATIN}, + {"ty", USCRIPT_LATIN}, + {"tyv", USCRIPT_CYRILLIC}, + {"udm", USCRIPT_CYRILLIC}, + {"ug", USCRIPT_ARABIC}, + {"uk", USCRIPT_CYRILLIC}, + {"und", USCRIPT_LATIN}, + {"ur", USCRIPT_ARABIC}, + {"uz", USCRIPT_CYRILLIC}, + {"ve", USCRIPT_LATIN}, + {"vi", USCRIPT_LATIN}, + {"wal", USCRIPT_ETHIOPIC}, + {"war", USCRIPT_LATIN}, + {"wo", USCRIPT_LATIN}, + {"xh", USCRIPT_LATIN}, + {"yap", USCRIPT_LATIN}, + {"yo", USCRIPT_LATIN}, + {"za", USCRIPT_LATIN}, + {"zh", USCRIPT_SIMPLIFIED_HAN}, + {"zh_hk", USCRIPT_TRADITIONAL_HAN}, + {"zh_tw", USCRIPT_TRADITIONAL_HAN}, + {"zu", USCRIPT_LATIN}}; - typedef HashMap LocaleScriptMap; - DEFINE_STATIC_LOCAL(LocaleScriptMap, localeScriptMap, ()); - if (localeScriptMap.isEmpty()) { - for (size_t i = 0; i < sizeof(localeScriptList) / sizeof(localeScriptList[0]); ++i) - localeScriptMap.set(localeScriptList[i].locale, localeScriptList[i].script); - } + typedef HashMap LocaleScriptMap; + DEFINE_STATIC_LOCAL(LocaleScriptMap, localeScriptMap, ()); + if (localeScriptMap.isEmpty()) { + for (size_t i = 0; + i < sizeof(localeScriptList) / sizeof(localeScriptList[0]); ++i) + localeScriptMap.set(localeScriptList[i].locale, + localeScriptList[i].script); + } - String canonicalLocale = locale.lower().replace('-', '_'); - while (!canonicalLocale.isEmpty()) { - HashMap::iterator it = localeScriptMap.find(canonicalLocale); - if (it != localeScriptMap.end()) - return it->value; - size_t pos = canonicalLocale.reverseFind('_'); - if (pos == kNotFound) - break; - UScriptCode code = scriptNameToCode(canonicalLocale.substring(pos + 1)); - if (code != USCRIPT_INVALID_CODE && code != USCRIPT_UNKNOWN) - return code; - canonicalLocale = canonicalLocale.substring(0, pos); - } - return USCRIPT_COMMON; + String canonicalLocale = locale.lower().replace('-', '_'); + while (!canonicalLocale.isEmpty()) { + HashMap::iterator it = + localeScriptMap.find(canonicalLocale); + if (it != localeScriptMap.end()) + return it->value; + size_t pos = canonicalLocale.reverseFind('_'); + if (pos == kNotFound) + break; + UScriptCode code = scriptNameToCode(canonicalLocale.substring(pos + 1)); + if (code != USCRIPT_INVALID_CODE && code != USCRIPT_UNKNOWN) + return code; + canonicalLocale = canonicalLocale.substring(0, pos); + } + return USCRIPT_COMMON; } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/text/LocaleToScriptMapping.h b/sky/engine/platform/text/LocaleToScriptMapping.h index 850181c4b603a..a8ff55367d056 100644 --- a/sky/engine/platform/text/LocaleToScriptMapping.h +++ b/sky/engine/platform/text/LocaleToScriptMapping.h @@ -42,6 +42,6 @@ namespace blink { PLATFORM_EXPORT UScriptCode localeToScriptCodeForFontSelection(const String&); PLATFORM_EXPORT UScriptCode scriptNameToCode(const String&); -} +} // namespace blink #endif // SKY_ENGINE_PLATFORM_TEXT_LOCALETOSCRIPTMAPPING_H_ diff --git a/sky/engine/platform/text/NonCJKGlyphOrientation.h b/sky/engine/platform/text/NonCJKGlyphOrientation.h index 4556f3177a124..0181c9fa1e1c4 100644 --- a/sky/engine/platform/text/NonCJKGlyphOrientation.h +++ b/sky/engine/platform/text/NonCJKGlyphOrientation.h @@ -28,8 +28,10 @@ namespace blink { -enum NonCJKGlyphOrientation { NonCJKGlyphOrientationVerticalRight, NonCJKGlyphOrientationUpright }; - +enum NonCJKGlyphOrientation { + NonCJKGlyphOrientationVerticalRight, + NonCJKGlyphOrientationUpright +}; } #endif // SKY_ENGINE_PLATFORM_TEXT_NONCJKGLYPHORIENTATION_H_ diff --git a/sky/engine/platform/text/SurrogatePairAwareTextIterator.cpp b/sky/engine/platform/text/SurrogatePairAwareTextIterator.cpp index 3810b1737d2c4..18f03ccb498d4 100644 --- a/sky/engine/platform/text/SurrogatePairAwareTextIterator.cpp +++ b/sky/engine/platform/text/SurrogatePairAwareTextIterator.cpp @@ -1,5 +1,6 @@ /* - * Copyright (C) 2003, 2006, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2006, 2008, 2009, 2010, 2011 Apple Inc. + * All rights reserved. * Copyright (C) 2008 Holger Hans Peter Freyther * Copyright (C) Research In Motion Limited 2011. All rights reserved. * @@ -29,66 +30,73 @@ using namespace Unicode; namespace blink { -SurrogatePairAwareTextIterator::SurrogatePairAwareTextIterator(const UChar* characters, int currentCharacter, int lastCharacter, int endCharacter) - : m_characters(characters) - , m_currentCharacter(currentCharacter) - , m_lastCharacter(lastCharacter) - , m_endCharacter(endCharacter) -{ -} - -bool SurrogatePairAwareTextIterator::consumeSlowCase(UChar32& character, unsigned& clusterLength) -{ - if (character <= 0x30FE) { - // Deal with Hiragana and Katakana voiced and semi-voiced syllables. - // Normalize into composed form, and then look for glyph with base + combined mark. - // Check above for character range to minimize performance impact. - if (UChar32 normalized = normalizeVoicingMarks()) { - character = normalized; - clusterLength = 2; - } - return true; +SurrogatePairAwareTextIterator::SurrogatePairAwareTextIterator( + const UChar* characters, + int currentCharacter, + int lastCharacter, + int endCharacter) + : m_characters(characters), + m_currentCharacter(currentCharacter), + m_lastCharacter(lastCharacter), + m_endCharacter(endCharacter) {} + +bool SurrogatePairAwareTextIterator::consumeSlowCase(UChar32& character, + unsigned& clusterLength) { + if (character <= 0x30FE) { + // Deal with Hiragana and Katakana voiced and semi-voiced syllables. + // Normalize into composed form, and then look for glyph with base + + // combined mark. Check above for character range to minimize performance + // impact. + if (UChar32 normalized = normalizeVoicingMarks()) { + character = normalized; + clusterLength = 2; } + return true; + } - if (!U16_IS_SURROGATE(character)) - return true; + if (!U16_IS_SURROGATE(character)) + return true; - // If we have a surrogate pair, make sure it starts with the high part. - if (!U16_IS_SURROGATE_LEAD(character)) - return false; + // If we have a surrogate pair, make sure it starts with the high part. + if (!U16_IS_SURROGATE_LEAD(character)) + return false; - // Do we have a surrogate pair? If so, determine the full Unicode (32 bit) code point before glyph lookup. - // Make sure we have another character and it's a low surrogate. - if (m_currentCharacter + 1 >= m_endCharacter) - return false; + // Do we have a surrogate pair? If so, determine the full Unicode (32 bit) + // code point before glyph lookup. Make sure we have another character and + // it's a low surrogate. + if (m_currentCharacter + 1 >= m_endCharacter) + return false; - UChar low = m_characters[1]; - if (!U16_IS_TRAIL(low)) - return false; + UChar low = m_characters[1]; + if (!U16_IS_TRAIL(low)) + return false; - character = U16_GET_SUPPLEMENTARY(character, low); - clusterLength = 2; - return true; + character = U16_GET_SUPPLEMENTARY(character, low); + clusterLength = 2; + return true; } -UChar32 SurrogatePairAwareTextIterator::normalizeVoicingMarks() -{ - // According to http://www.unicode.org/Public/UNIDATA/UCD.html#Canonical_Combining_Class_Values - static const uint8_t hiraganaKatakanaVoicingMarksCombiningClass = 8; - - if (m_currentCharacter + 1 >= m_endCharacter) - return 0; - - if (combiningClass(m_characters[1]) == hiraganaKatakanaVoicingMarksCombiningClass) { - // Normalize into composed form using 3.2 rules. - UChar normalizedCharacters[2] = { 0, 0 }; - UErrorCode uStatus = U_ZERO_ERROR; - int32_t resultLength = unorm_normalize(m_characters, 2, UNORM_NFC, UNORM_UNICODE_3_2, &normalizedCharacters[0], 2, &uStatus); - if (resultLength == 1 && !uStatus) - return normalizedCharacters[0]; - } +UChar32 SurrogatePairAwareTextIterator::normalizeVoicingMarks() { + // According to + // http://www.unicode.org/Public/UNIDATA/UCD.html#Canonical_Combining_Class_Values + static const uint8_t hiraganaKatakanaVoicingMarksCombiningClass = 8; + if (m_currentCharacter + 1 >= m_endCharacter) return 0; -} + if (combiningClass(m_characters[1]) == + hiraganaKatakanaVoicingMarksCombiningClass) { + // Normalize into composed form using 3.2 rules. + UChar normalizedCharacters[2] = {0, 0}; + UErrorCode uStatus = U_ZERO_ERROR; + int32_t resultLength = + unorm_normalize(m_characters, 2, UNORM_NFC, UNORM_UNICODE_3_2, + &normalizedCharacters[0], 2, &uStatus); + if (resultLength == 1 && !uStatus) + return normalizedCharacters[0]; + } + + return 0; } + +} // namespace blink diff --git a/sky/engine/platform/text/SurrogatePairAwareTextIterator.h b/sky/engine/platform/text/SurrogatePairAwareTextIterator.h index 2f30b913c1ec7..0d666e882a0a5 100644 --- a/sky/engine/platform/text/SurrogatePairAwareTextIterator.h +++ b/sky/engine/platform/text/SurrogatePairAwareTextIterator.h @@ -28,44 +28,47 @@ namespace blink { class PLATFORM_EXPORT SurrogatePairAwareTextIterator { -public: - // The passed in UChar pointer starts at 'currentCharacter'. The iterator operatoes on the range [currentCharacter, lastCharacter]. - // 'endCharacter' denotes the maximum length of the UChar array, which might exceed 'lastCharacter'. - SurrogatePairAwareTextIterator(const UChar*, int currentCharacter, int lastCharacter, int endCharacter); + public: + // The passed in UChar pointer starts at 'currentCharacter'. The iterator + // operatoes on the range [currentCharacter, lastCharacter]. 'endCharacter' + // denotes the maximum length of the UChar array, which might exceed + // 'lastCharacter'. + SurrogatePairAwareTextIterator(const UChar*, + int currentCharacter, + int lastCharacter, + int endCharacter); - inline bool consume(UChar32& character, unsigned& clusterLength) - { - if (m_currentCharacter >= m_lastCharacter) - return false; + inline bool consume(UChar32& character, unsigned& clusterLength) { + if (m_currentCharacter >= m_lastCharacter) + return false; - character = *m_characters; - clusterLength = 1; + character = *m_characters; + clusterLength = 1; - if (character < HiraganaLetterSmallA) - return true; + if (character < HiraganaLetterSmallA) + return true; - return consumeSlowCase(character, clusterLength); - } + return consumeSlowCase(character, clusterLength); + } - void advance(unsigned advanceLength) - { - m_characters += advanceLength; - m_currentCharacter += advanceLength; - } + void advance(unsigned advanceLength) { + m_characters += advanceLength; + m_currentCharacter += advanceLength; + } - int currentCharacter() const { return m_currentCharacter; } - const UChar* characters() const { return m_characters; } + int currentCharacter() const { return m_currentCharacter; } + const UChar* characters() const { return m_characters; } -private: - bool consumeSlowCase(UChar32&, unsigned&); - UChar32 normalizeVoicingMarks(); + private: + bool consumeSlowCase(UChar32&, unsigned&); + UChar32 normalizeVoicingMarks(); - const UChar* m_characters; - int m_currentCharacter; - int m_lastCharacter; - int m_endCharacter; + const UChar* m_characters; + int m_currentCharacter; + int m_lastCharacter; + int m_endCharacter; }; -} +} // namespace blink #endif // SKY_ENGINE_PLATFORM_TEXT_SURROGATEPAIRAWARETEXTITERATOR_H_ diff --git a/sky/engine/platform/text/TextBoundaries.cpp b/sky/engine/platform/text/TextBoundaries.cpp index d3a7b7efa3053..0d37208c579d5 100644 --- a/sky/engine/platform/text/TextBoundaries.cpp +++ b/sky/engine/platform/text/TextBoundaries.cpp @@ -34,75 +34,77 @@ using namespace Unicode; namespace blink { -int endOfFirstWordBoundaryContext(const UChar* characters, int length) -{ - for (int i = 0; i < length; ) { - int first = i; - UChar32 ch; - U16_NEXT(characters, i, length, ch); - if (!requiresContextForWordBoundary(ch)) - return first; - } - return length; +int endOfFirstWordBoundaryContext(const UChar* characters, int length) { + for (int i = 0; i < length;) { + int first = i; + UChar32 ch; + U16_NEXT(characters, i, length, ch); + if (!requiresContextForWordBoundary(ch)) + return first; + } + return length; } -int startOfLastWordBoundaryContext(const UChar* characters, int length) -{ - for (int i = length; i > 0; ) { - int last = i; - UChar32 ch; - U16_PREV(characters, 0, i, ch); - if (!requiresContextForWordBoundary(ch)) - return last; - } - return 0; +int startOfLastWordBoundaryContext(const UChar* characters, int length) { + for (int i = length; i > 0;) { + int last = i; + UChar32 ch; + U16_PREV(characters, 0, i, ch); + if (!requiresContextForWordBoundary(ch)) + return last; + } + return 0; } -int findNextWordFromIndex(const UChar* chars, int len, int position, bool forward) -{ - TextBreakIterator* it = wordBreakIterator(chars, len); - - if (forward) { - position = it->following(position); - while (position != TextBreakDone) { - // We stop searching when the character preceeding the break - // is alphanumeric. - if (position < len && isAlphanumeric(chars[position - 1])) - return position; +int findNextWordFromIndex(const UChar* chars, + int len, + int position, + bool forward) { + TextBreakIterator* it = wordBreakIterator(chars, len); - position = it->following(position); - } + if (forward) { + position = it->following(position); + while (position != TextBreakDone) { + // We stop searching when the character preceeding the break + // is alphanumeric. + if (position < len && isAlphanumeric(chars[position - 1])) + return position; - return len; - } else { - position = it->preceding(position); - while (position != TextBreakDone) { - // We stop searching when the character following the break - // is alphanumeric. - if (position > 0 && isAlphanumeric(chars[position])) - return position; + position = it->following(position); + } - position = it->preceding(position); - } + return len; + } else { + position = it->preceding(position); + while (position != TextBreakDone) { + // We stop searching when the character following the break + // is alphanumeric. + if (position > 0 && isAlphanumeric(chars[position])) + return position; - return 0; + position = it->preceding(position); } + + return 0; + } } -void findWordBoundary(const UChar* chars, int len, int position, int* start, int* end) -{ - TextBreakIterator* it = wordBreakIterator(chars, len); - *end = it->following(position); - if (*end < 0) - *end = it->last(); - *start = it->previous(); +void findWordBoundary(const UChar* chars, + int len, + int position, + int* start, + int* end) { + TextBreakIterator* it = wordBreakIterator(chars, len); + *end = it->following(position); + if (*end < 0) + *end = it->last(); + *start = it->previous(); } -int findWordEndBoundary(const UChar* chars, int len, int position) -{ - TextBreakIterator* it = wordBreakIterator(chars, len); - int end = it->following(position); - return end < 0 ? it->last() : end; +int findWordEndBoundary(const UChar* chars, int len, int position) { + TextBreakIterator* it = wordBreakIterator(chars, len); + int end = it->following(position); + return end < 0 ? it->last() : end; } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/text/TextBoundaries.h b/sky/engine/platform/text/TextBoundaries.h index 612659a6daff3..23862175d92b2 100644 --- a/sky/engine/platform/text/TextBoundaries.h +++ b/sky/engine/platform/text/TextBoundaries.h @@ -31,18 +31,26 @@ namespace blink { -inline bool requiresContextForWordBoundary(UChar32 ch) -{ - return WTF::Unicode::hasLineBreakingPropertyComplexContext(ch); +inline bool requiresContextForWordBoundary(UChar32 ch) { + return WTF::Unicode::hasLineBreakingPropertyComplexContext(ch); } -PLATFORM_EXPORT int endOfFirstWordBoundaryContext(const UChar* characters, int length); -PLATFORM_EXPORT int startOfLastWordBoundaryContext(const UChar* characters, int length); +PLATFORM_EXPORT int endOfFirstWordBoundaryContext(const UChar* characters, + int length); +PLATFORM_EXPORT int startOfLastWordBoundaryContext(const UChar* characters, + int length); -PLATFORM_EXPORT void findWordBoundary(const UChar*, int len, int position, int* start, int* end); +PLATFORM_EXPORT void findWordBoundary(const UChar*, + int len, + int position, + int* start, + int* end); PLATFORM_EXPORT int findWordEndBoundary(const UChar*, int len, int position); -PLATFORM_EXPORT int findNextWordFromIndex(const UChar*, int len, int position, bool forward); +PLATFORM_EXPORT int findNextWordFromIndex(const UChar*, + int len, + int position, + bool forward); -} +} // namespace blink #endif // SKY_ENGINE_PLATFORM_TEXT_TEXTBOUNDARIES_H_ diff --git a/sky/engine/platform/text/TextBreakIterator.cpp b/sky/engine/platform/text/TextBreakIterator.cpp index 0572223144f41..934346ecbf827 100644 --- a/sky/engine/platform/text/TextBreakIterator.cpp +++ b/sky/engine/platform/text/TextBreakIterator.cpp @@ -23,47 +23,46 @@ namespace blink { -unsigned numGraphemeClusters(const String& string) -{ - unsigned stringLength = string.length(); +unsigned numGraphemeClusters(const String& string) { + unsigned stringLength = string.length(); - if (!stringLength) - return 0; + if (!stringLength) + return 0; - // The only Latin-1 Extended Grapheme Cluster is CR LF - if (string.is8Bit() && !string.contains('\r')) - return stringLength; + // The only Latin-1 Extended Grapheme Cluster is CR LF + if (string.is8Bit() && !string.contains('\r')) + return stringLength; - NonSharedCharacterBreakIterator it(string); - if (!it) - return stringLength; + NonSharedCharacterBreakIterator it(string); + if (!it) + return stringLength; - unsigned num = 0; - while (it.next() != TextBreakDone) - ++num; - return num; + unsigned num = 0; + while (it.next() != TextBreakDone) + ++num; + return num; } -unsigned numCharactersInGraphemeClusters(const String& string, unsigned numGraphemeClusters) -{ - unsigned stringLength = string.length(); +unsigned numCharactersInGraphemeClusters(const String& string, + unsigned numGraphemeClusters) { + unsigned stringLength = string.length(); - if (!stringLength) - return 0; + if (!stringLength) + return 0; - // The only Latin-1 Extended Grapheme Cluster is CR LF - if (string.is8Bit() && !string.contains('\r')) - return std::min(stringLength, numGraphemeClusters); + // The only Latin-1 Extended Grapheme Cluster is CR LF + if (string.is8Bit() && !string.contains('\r')) + return std::min(stringLength, numGraphemeClusters); - NonSharedCharacterBreakIterator it(string); - if (!it) - return std::min(stringLength, numGraphemeClusters); + NonSharedCharacterBreakIterator it(string); + if (!it) + return std::min(stringLength, numGraphemeClusters); - for (unsigned i = 0; i < numGraphemeClusters; ++i) { - if (it.next() == TextBreakDone) - return stringLength; - } - return it.current(); + for (unsigned i = 0; i < numGraphemeClusters; ++i) { + if (it.next() == TextBreakDone) + return stringLength; + } + return it.current(); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/text/TextBreakIterator.h b/sky/engine/platform/text/TextBreakIterator.h index e133b907753e0..8fd01344239c4 100644 --- a/sky/engine/platform/text/TextBreakIterator.h +++ b/sky/engine/platform/text/TextBreakIterator.h @@ -32,196 +32,212 @@ namespace blink { typedef icu::BreakIterator TextBreakIterator; -// Note: The returned iterator is good only until you get another iterator, with the exception of acquireLineBreakIterator. +// Note: The returned iterator is good only until you get another iterator, with +// the exception of acquireLineBreakIterator. // This is similar to character break iterator in most cases, but is subject to // platform UI conventions. One notable example where this can be different // from character break iterator is Thai prepend characters, see bug 24342. // Use this for insertion point and selection manipulations. -PLATFORM_EXPORT TextBreakIterator* cursorMovementIterator(const UChar*, int length); +PLATFORM_EXPORT TextBreakIterator* cursorMovementIterator(const UChar*, + int length); -PLATFORM_EXPORT TextBreakIterator* wordBreakIterator(const String&, int start, int length); +PLATFORM_EXPORT TextBreakIterator* wordBreakIterator(const String&, + int start, + int length); PLATFORM_EXPORT TextBreakIterator* wordBreakIterator(const UChar*, int length); -PLATFORM_EXPORT TextBreakIterator* acquireLineBreakIterator(const LChar*, int length, const AtomicString& locale, const UChar* priorContext, unsigned priorContextLength); -PLATFORM_EXPORT TextBreakIterator* acquireLineBreakIterator(const UChar*, int length, const AtomicString& locale, const UChar* priorContext, unsigned priorContextLength); +PLATFORM_EXPORT TextBreakIterator* acquireLineBreakIterator( + const LChar*, + int length, + const AtomicString& locale, + const UChar* priorContext, + unsigned priorContextLength); +PLATFORM_EXPORT TextBreakIterator* acquireLineBreakIterator( + const UChar*, + int length, + const AtomicString& locale, + const UChar* priorContext, + unsigned priorContextLength); PLATFORM_EXPORT void releaseLineBreakIterator(TextBreakIterator*); -PLATFORM_EXPORT TextBreakIterator* sentenceBreakIterator(const UChar*, int length); +PLATFORM_EXPORT TextBreakIterator* sentenceBreakIterator(const UChar*, + int length); PLATFORM_EXPORT bool isWordTextBreak(TextBreakIterator*); const int TextBreakDone = -1; class PLATFORM_EXPORT LazyLineBreakIterator { -public: - LazyLineBreakIterator() - : m_iterator(0) - , m_cachedPriorContext(0) - , m_cachedPriorContextLength(0) - { - resetPriorContext(); + public: + LazyLineBreakIterator() + : m_iterator(0), m_cachedPriorContext(0), m_cachedPriorContextLength(0) { + resetPriorContext(); + } + + LazyLineBreakIterator(String string, + const AtomicString& locale = AtomicString()) + : m_string(string), + m_locale(locale), + m_iterator(0), + m_cachedPriorContext(0), + m_cachedPriorContextLength(0) { + resetPriorContext(); + } + + ~LazyLineBreakIterator() { + if (m_iterator) + releaseLineBreakIterator(m_iterator); + } + + String string() const { return m_string; } + + UChar lastCharacter() const { + COMPILE_ASSERT(WTF_ARRAY_LENGTH(m_priorContext) == 2, + TextBreakIterator_unexpected_prior_context_length); + return m_priorContext[1]; + } + + UChar secondToLastCharacter() const { + COMPILE_ASSERT(WTF_ARRAY_LENGTH(m_priorContext) == 2, + TextBreakIterator_unexpected_prior_context_length); + return m_priorContext[0]; + } + + void setPriorContext(UChar last, UChar secondToLast) { + COMPILE_ASSERT(WTF_ARRAY_LENGTH(m_priorContext) == 2, + TextBreakIterator_unexpected_prior_context_length); + m_priorContext[0] = secondToLast; + m_priorContext[1] = last; + } + + void updatePriorContext(UChar last) { + COMPILE_ASSERT(WTF_ARRAY_LENGTH(m_priorContext) == 2, + TextBreakIterator_unexpected_prior_context_length); + m_priorContext[0] = m_priorContext[1]; + m_priorContext[1] = last; + } + + void resetPriorContext() { + COMPILE_ASSERT(WTF_ARRAY_LENGTH(m_priorContext) == 2, + TextBreakIterator_unexpected_prior_context_length); + m_priorContext[0] = 0; + m_priorContext[1] = 0; + } + + unsigned priorContextLength() const { + unsigned priorContextLength = 0; + COMPILE_ASSERT(WTF_ARRAY_LENGTH(m_priorContext) == 2, + TextBreakIterator_unexpected_prior_context_length); + if (m_priorContext[1]) { + ++priorContextLength; + if (m_priorContext[0]) + ++priorContextLength; } - - LazyLineBreakIterator(String string, const AtomicString& locale = AtomicString()) - : m_string(string) - , m_locale(locale) - , m_iterator(0) - , m_cachedPriorContext(0) - , m_cachedPriorContextLength(0) - { - resetPriorContext(); - } - - ~LazyLineBreakIterator() - { - if (m_iterator) - releaseLineBreakIterator(m_iterator); - } - - String string() const { return m_string; } - - UChar lastCharacter() const - { - COMPILE_ASSERT(WTF_ARRAY_LENGTH(m_priorContext) == 2, TextBreakIterator_unexpected_prior_context_length); - return m_priorContext[1]; - } - - UChar secondToLastCharacter() const - { - COMPILE_ASSERT(WTF_ARRAY_LENGTH(m_priorContext) == 2, TextBreakIterator_unexpected_prior_context_length); - return m_priorContext[0]; - } - - void setPriorContext(UChar last, UChar secondToLast) - { - COMPILE_ASSERT(WTF_ARRAY_LENGTH(m_priorContext) == 2, TextBreakIterator_unexpected_prior_context_length); - m_priorContext[0] = secondToLast; - m_priorContext[1] = last; - } - - void updatePriorContext(UChar last) - { - COMPILE_ASSERT(WTF_ARRAY_LENGTH(m_priorContext) == 2, TextBreakIterator_unexpected_prior_context_length); - m_priorContext[0] = m_priorContext[1]; - m_priorContext[1] = last; - } - - void resetPriorContext() - { - COMPILE_ASSERT(WTF_ARRAY_LENGTH(m_priorContext) == 2, TextBreakIterator_unexpected_prior_context_length); - m_priorContext[0] = 0; - m_priorContext[1] = 0; - } - - unsigned priorContextLength() const - { - unsigned priorContextLength = 0; - COMPILE_ASSERT(WTF_ARRAY_LENGTH(m_priorContext) == 2, TextBreakIterator_unexpected_prior_context_length); - if (m_priorContext[1]) { - ++priorContextLength; - if (m_priorContext[0]) - ++priorContextLength; - } - return priorContextLength; + return priorContextLength; + } + + // Obtain text break iterator, possibly previously cached, where this iterator + // is (or has been) initialized to use the previously stored string as the + // primary breaking context and using previously stored prior context if + // non-empty. + TextBreakIterator* get(unsigned priorContextLength) { + ASSERT(priorContextLength <= priorContextCapacity); + const UChar* priorContext = + priorContextLength + ? &m_priorContext[priorContextCapacity - priorContextLength] + : 0; + if (!m_iterator) { + if (m_string.is8Bit()) + m_iterator = acquireLineBreakIterator(m_string.characters8(), + m_string.length(), m_locale, + priorContext, priorContextLength); + else + m_iterator = acquireLineBreakIterator(m_string.characters16(), + m_string.length(), m_locale, + priorContext, priorContextLength); + m_cachedPriorContext = priorContext; + m_cachedPriorContextLength = priorContextLength; + } else if (priorContext != m_cachedPriorContext || + priorContextLength != m_cachedPriorContextLength) { + this->resetStringAndReleaseIterator(m_string, m_locale); + return this->get(priorContextLength); } + return m_iterator; + } + + void resetStringAndReleaseIterator(String string, + const AtomicString& locale) { + if (m_iterator) + releaseLineBreakIterator(m_iterator); + + m_string = string; + m_locale = locale; + m_iterator = 0; + m_cachedPriorContext = 0; + m_cachedPriorContextLength = 0; + } + + private: + static const unsigned priorContextCapacity = 2; + String m_string; + AtomicString m_locale; + TextBreakIterator* m_iterator; + UChar m_priorContext[priorContextCapacity]; + const UChar* m_cachedPriorContext; + unsigned m_cachedPriorContextLength; +}; - // Obtain text break iterator, possibly previously cached, where this iterator is (or has been) - // initialized to use the previously stored string as the primary breaking context and using - // previously stored prior context if non-empty. - TextBreakIterator* get(unsigned priorContextLength) - { - ASSERT(priorContextLength <= priorContextCapacity); - const UChar* priorContext = priorContextLength ? &m_priorContext[priorContextCapacity - priorContextLength] : 0; - if (!m_iterator) { - if (m_string.is8Bit()) - m_iterator = acquireLineBreakIterator(m_string.characters8(), m_string.length(), m_locale, priorContext, priorContextLength); - else - m_iterator = acquireLineBreakIterator(m_string.characters16(), m_string.length(), m_locale, priorContext, priorContextLength); - m_cachedPriorContext = priorContext; - m_cachedPriorContextLength = priorContextLength; - } else if (priorContext != m_cachedPriorContext || priorContextLength != m_cachedPriorContextLength) { - this->resetStringAndReleaseIterator(m_string, m_locale); - return this->get(priorContextLength); - } - return m_iterator; - } +// Iterates over "extended grapheme clusters", as defined in UAX #29. +// Note that platform implementations may be less sophisticated - e.g. ICU prior +// to version 4.0 only supports "legacy grapheme clusters". Use this for general +// text processing, e.g. string truncation. - void resetStringAndReleaseIterator(String string, const AtomicString& locale) - { - if (m_iterator) - releaseLineBreakIterator(m_iterator); +class PLATFORM_EXPORT NonSharedCharacterBreakIterator { + WTF_MAKE_NONCOPYABLE(NonSharedCharacterBreakIterator); - m_string = string; - m_locale = locale; - m_iterator = 0; - m_cachedPriorContext = 0; - m_cachedPriorContextLength = 0; - } + public: + explicit NonSharedCharacterBreakIterator(const String&); + NonSharedCharacterBreakIterator(const UChar*, unsigned length); + ~NonSharedCharacterBreakIterator(); -private: - static const unsigned priorContextCapacity = 2; - String m_string; - AtomicString m_locale; - TextBreakIterator* m_iterator; - UChar m_priorContext[priorContextCapacity]; - const UChar* m_cachedPriorContext; - unsigned m_cachedPriorContextLength; -}; + int next(); + int current(); -// Iterates over "extended grapheme clusters", as defined in UAX #29. -// Note that platform implementations may be less sophisticated - e.g. ICU prior to -// version 4.0 only supports "legacy grapheme clusters". -// Use this for general text processing, e.g. string truncation. + bool isBreak(int offset) const; + int preceding(int offset) const; + int following(int offset) const; -class PLATFORM_EXPORT NonSharedCharacterBreakIterator { - WTF_MAKE_NONCOPYABLE(NonSharedCharacterBreakIterator); -public: - explicit NonSharedCharacterBreakIterator(const String&); - NonSharedCharacterBreakIterator(const UChar*, unsigned length); - ~NonSharedCharacterBreakIterator(); - - int next(); - int current(); - - bool isBreak(int offset) const; - int preceding(int offset) const; - int following(int offset) const; - - bool operator!() const - { - return !m_is8Bit && !m_iterator; - } + bool operator!() const { return !m_is8Bit && !m_iterator; } -private: - void createIteratorForBuffer(const UChar*, unsigned length); + private: + void createIteratorForBuffer(const UChar*, unsigned length); - unsigned clusterLengthStartingAt(unsigned offset) const - { - ASSERT(m_is8Bit); - // The only Latin-1 Extended Grapheme Cluster is CR LF - return isCRBeforeLF(offset) ? 2 : 1; - } + unsigned clusterLengthStartingAt(unsigned offset) const { + ASSERT(m_is8Bit); + // The only Latin-1 Extended Grapheme Cluster is CR LF + return isCRBeforeLF(offset) ? 2 : 1; + } - bool isCRBeforeLF(unsigned offset) const - { - ASSERT(m_is8Bit); - return m_charaters8[offset] == '\r' && offset + 1 < m_length && m_charaters8[offset + 1] == '\n'; - } + bool isCRBeforeLF(unsigned offset) const { + ASSERT(m_is8Bit); + return m_charaters8[offset] == '\r' && offset + 1 < m_length && + m_charaters8[offset + 1] == '\n'; + } - bool isLFAfterCR(unsigned offset) const - { - ASSERT(m_is8Bit); - return m_charaters8[offset] == '\n' && offset >= 1 && m_charaters8[offset - 1] == '\r'; - } + bool isLFAfterCR(unsigned offset) const { + ASSERT(m_is8Bit); + return m_charaters8[offset] == '\n' && offset >= 1 && + m_charaters8[offset - 1] == '\r'; + } - bool m_is8Bit; + bool m_is8Bit; - // For 8 bit strings, we implement the iterator ourselves. - const LChar* m_charaters8; - unsigned m_offset; - unsigned m_length; + // For 8 bit strings, we implement the iterator ourselves. + const LChar* m_charaters8; + unsigned m_offset; + unsigned m_length; - // For 16 bit strings, we use a TextBreakIterator. - TextBreakIterator* m_iterator; + // For 16 bit strings, we use a TextBreakIterator. + TextBreakIterator* m_iterator; }; // Counts the number of grapheme clusters. A surrogate pair or a sequence @@ -230,8 +246,9 @@ class PLATFORM_EXPORT NonSharedCharacterBreakIterator { PLATFORM_EXPORT unsigned numGraphemeClusters(const String&); // Returns the number of characters which will be less than or equal to // the specified grapheme cluster length. -PLATFORM_EXPORT unsigned numCharactersInGraphemeClusters(const String&, unsigned); +PLATFORM_EXPORT unsigned numCharactersInGraphemeClusters(const String&, + unsigned); -} +} // namespace blink #endif // SKY_ENGINE_PLATFORM_TEXT_TEXTBREAKITERATOR_H_ diff --git a/sky/engine/platform/text/TextBreakIteratorICU.cpp b/sky/engine/platform/text/TextBreakIteratorICU.cpp index f14083ed34f46..9d1e2d3f9001a 100644 --- a/sky/engine/platform/text/TextBreakIteratorICU.cpp +++ b/sky/engine/platform/text/TextBreakIteratorICU.cpp @@ -38,72 +38,79 @@ using namespace WTF; namespace blink { class LineBreakIteratorPool { - WTF_MAKE_NONCOPYABLE(LineBreakIteratorPool); -public: - static LineBreakIteratorPool& sharedPool() - { - static WTF::ThreadSpecific* pool = new WTF::ThreadSpecific; - return **pool; + WTF_MAKE_NONCOPYABLE(LineBreakIteratorPool); + + public: + static LineBreakIteratorPool& sharedPool() { + static WTF::ThreadSpecific* pool = + new WTF::ThreadSpecific; + return **pool; + } + + static PassOwnPtr create() { + return adoptPtr(new LineBreakIteratorPool); + } + + icu::BreakIterator* take(const AtomicString& locale) { + icu::BreakIterator* iterator = 0; + for (size_t i = 0; i < m_pool.size(); ++i) { + if (m_pool[i].first == locale) { + iterator = m_pool[i].second; + m_pool.remove(i); + break; + } } - static PassOwnPtr create() { return adoptPtr(new LineBreakIteratorPool); } - - icu::BreakIterator* take(const AtomicString& locale) - { - icu::BreakIterator* iterator = 0; - for (size_t i = 0; i < m_pool.size(); ++i) { - if (m_pool[i].first == locale) { - iterator = m_pool[i].second; - m_pool.remove(i); - break; - } - } - - if (!iterator) { - UErrorCode openStatus = U_ZERO_ERROR; - bool localeIsEmpty = locale.isEmpty(); - iterator = icu::BreakIterator::createLineInstance(localeIsEmpty ? icu::Locale(currentTextBreakLocaleID()) : icu::Locale(locale.utf8().data()), openStatus); - // locale comes from a web page and it can be invalid, leading ICU - // to fail, in which case we fall back to the default locale. - if (!localeIsEmpty && U_FAILURE(openStatus)) { - openStatus = U_ZERO_ERROR; - iterator = icu::BreakIterator::createLineInstance(icu::Locale(currentTextBreakLocaleID()), openStatus); - } - - if (U_FAILURE(openStatus)) { - WTF_LOG_ERROR("icu::BreakIterator construction failed with status %d", openStatus); - return 0; - } - } - - ASSERT(!m_vendedIterators.contains(iterator)); - m_vendedIterators.set(iterator, locale); - return iterator; + if (!iterator) { + UErrorCode openStatus = U_ZERO_ERROR; + bool localeIsEmpty = locale.isEmpty(); + iterator = icu::BreakIterator::createLineInstance( + localeIsEmpty ? icu::Locale(currentTextBreakLocaleID()) + : icu::Locale(locale.utf8().data()), + openStatus); + // locale comes from a web page and it can be invalid, leading ICU + // to fail, in which case we fall back to the default locale. + if (!localeIsEmpty && U_FAILURE(openStatus)) { + openStatus = U_ZERO_ERROR; + iterator = icu::BreakIterator::createLineInstance( + icu::Locale(currentTextBreakLocaleID()), openStatus); + } + + if (U_FAILURE(openStatus)) { + WTF_LOG_ERROR("icu::BreakIterator construction failed with status %d", + openStatus); + return 0; + } } - void put(icu::BreakIterator* iterator) - { - ASSERT_ARG(iterator, m_vendedIterators.contains(iterator)); + ASSERT(!m_vendedIterators.contains(iterator)); + m_vendedIterators.set(iterator, locale); + return iterator; + } - if (m_pool.size() == capacity) { - delete(m_pool[0].second); - m_pool.remove(0); - } + void put(icu::BreakIterator* iterator) { + ASSERT_ARG(iterator, m_vendedIterators.contains(iterator)); - m_pool.append(Entry(m_vendedIterators.take(iterator), iterator)); + if (m_pool.size() == capacity) { + delete (m_pool[0].second); + m_pool.remove(0); } -private: - LineBreakIteratorPool() { } + m_pool.append(Entry(m_vendedIterators.take(iterator), iterator)); + } + + private: + LineBreakIteratorPool() {} - static const size_t capacity = 4; + static const size_t capacity = 4; - typedef pair Entry; - typedef Vector Pool; - Pool m_pool; - HashMap m_vendedIterators; + typedef pair Entry; + typedef Vector Pool; + Pool m_pool; + HashMap m_vendedIterators; - friend WTF::ThreadSpecific::operator LineBreakIteratorPool*(); + friend WTF::ThreadSpecific:: + operator LineBreakIteratorPool*(); }; enum TextContext { NoContext, PriorContext, PrimaryContext }; @@ -111,735 +118,830 @@ enum TextContext { NoContext, PriorContext, PrimaryContext }; const int textBufferCapacity = 16; typedef struct { - UText text; - UChar buffer[textBufferCapacity]; + UText text; + UChar buffer[textBufferCapacity]; } UTextWithBuffer; -static inline int64_t textPinIndex(int64_t& index, int64_t limit) -{ - if (index < 0) - index = 0; - else if (index > limit) - index = limit; - return index; +static inline int64_t textPinIndex(int64_t& index, int64_t limit) { + if (index < 0) + index = 0; + else if (index > limit) + index = limit; + return index; } -static inline int64_t textNativeLength(UText* text) -{ - return text->a + text->b; +static inline int64_t textNativeLength(UText* text) { + return text->a + text->b; } // Relocate pointer from source into destination as required. -static void textFixPointer(const UText* source, UText* destination, const void*& pointer) -{ - if (pointer >= source->pExtra && pointer < static_cast(source->pExtra) + source->extraSize) { - // Pointer references source extra buffer. - pointer = static_cast(destination->pExtra) + (static_cast(pointer) - static_cast(source->pExtra)); - } else if (pointer >= source && pointer < reinterpret_cast(source) + source->sizeOfStruct) { - // Pointer references source text structure, but not source extra buffer. - pointer = reinterpret_cast(destination) + (static_cast(pointer) - reinterpret_cast(source)); - } -} - -static UText* textClone(UText* destination, const UText* source, UBool deep, UErrorCode* status) -{ - ASSERT_UNUSED(deep, !deep); - if (U_FAILURE(*status)) - return 0; - int32_t extraSize = source->extraSize; - destination = utext_setup(destination, extraSize, status); - if (U_FAILURE(*status)) - return destination; - void* extraNew = destination->pExtra; - int32_t flags = destination->flags; - int sizeToCopy = std::min(source->sizeOfStruct, destination->sizeOfStruct); - memcpy(destination, source, sizeToCopy); - destination->pExtra = extraNew; - destination->flags = flags; - memcpy(destination->pExtra, source->pExtra, extraSize); - textFixPointer(source, destination, destination->context); - textFixPointer(source, destination, destination->p); - textFixPointer(source, destination, destination->q); - ASSERT(!destination->r); - const void * chunkContents = static_cast(destination->chunkContents); - textFixPointer(source, destination, chunkContents); - destination->chunkContents = static_cast(chunkContents); - return destination; -} - -static int32_t textExtract(UText*, int64_t, int64_t, UChar*, int32_t, UErrorCode* errorCode) -{ - // In the present context, this text provider is used only with ICU functions - // that do not perform an extract operation. - ASSERT_NOT_REACHED(); - *errorCode = U_UNSUPPORTED_ERROR; +static void textFixPointer(const UText* source, + UText* destination, + const void*& pointer) { + if (pointer >= source->pExtra && + pointer < static_cast(source->pExtra) + source->extraSize) { + // Pointer references source extra buffer. + pointer = static_cast(destination->pExtra) + + (static_cast(pointer) - + static_cast(source->pExtra)); + } else if (pointer >= source && + pointer < + reinterpret_cast(source) + source->sizeOfStruct) { + // Pointer references source text structure, but not source extra buffer. + pointer = reinterpret_cast(destination) + + (static_cast(pointer) - + reinterpret_cast(source)); + } +} + +static UText* textClone(UText* destination, + const UText* source, + UBool deep, + UErrorCode* status) { + ASSERT_UNUSED(deep, !deep); + if (U_FAILURE(*status)) return 0; -} - -static void textClose(UText* text) -{ - text->context = 0; -} - -static inline TextContext textGetContext(const UText* text, int64_t nativeIndex, UBool forward) -{ - if (!text->b || nativeIndex > text->b) - return PrimaryContext; - if (nativeIndex == text->b) - return forward ? PrimaryContext : PriorContext; - return PriorContext; -} - -static inline TextContext textLatin1GetCurrentContext(const UText* text) -{ - if (!text->chunkContents) - return NoContext; - return text->chunkContents == text->pExtra ? PrimaryContext : PriorContext; -} - -static void textLatin1MoveInPrimaryContext(UText* text, int64_t nativeIndex, int64_t nativeLength, UBool forward) -{ - ASSERT(text->chunkContents == text->pExtra); - if (forward) { - ASSERT(nativeIndex >= text->b && nativeIndex < nativeLength); - text->chunkNativeStart = nativeIndex; - text->chunkNativeLimit = nativeIndex + text->extraSize / sizeof(UChar); - if (text->chunkNativeLimit > nativeLength) - text->chunkNativeLimit = nativeLength; - } else { - ASSERT(nativeIndex > text->b && nativeIndex <= nativeLength); - text->chunkNativeLimit = nativeIndex; - text->chunkNativeStart = nativeIndex - text->extraSize / sizeof(UChar); - if (text->chunkNativeStart < text->b) - text->chunkNativeStart = text->b; + int32_t extraSize = source->extraSize; + destination = utext_setup(destination, extraSize, status); + if (U_FAILURE(*status)) + return destination; + void* extraNew = destination->pExtra; + int32_t flags = destination->flags; + int sizeToCopy = std::min(source->sizeOfStruct, destination->sizeOfStruct); + memcpy(destination, source, sizeToCopy); + destination->pExtra = extraNew; + destination->flags = flags; + memcpy(destination->pExtra, source->pExtra, extraSize); + textFixPointer(source, destination, destination->context); + textFixPointer(source, destination, destination->p); + textFixPointer(source, destination, destination->q); + ASSERT(!destination->r); + const void* chunkContents = + static_cast(destination->chunkContents); + textFixPointer(source, destination, chunkContents); + destination->chunkContents = static_cast(chunkContents); + return destination; +} + +static int32_t textExtract(UText*, + int64_t, + int64_t, + UChar*, + int32_t, + UErrorCode* errorCode) { + // In the present context, this text provider is used only with ICU functions + // that do not perform an extract operation. + ASSERT_NOT_REACHED(); + *errorCode = U_UNSUPPORTED_ERROR; + return 0; +} + +static void textClose(UText* text) { + text->context = 0; +} + +static inline TextContext textGetContext(const UText* text, + int64_t nativeIndex, + UBool forward) { + if (!text->b || nativeIndex > text->b) + return PrimaryContext; + if (nativeIndex == text->b) + return forward ? PrimaryContext : PriorContext; + return PriorContext; +} + +static inline TextContext textLatin1GetCurrentContext(const UText* text) { + if (!text->chunkContents) + return NoContext; + return text->chunkContents == text->pExtra ? PrimaryContext : PriorContext; +} + +static void textLatin1MoveInPrimaryContext(UText* text, + int64_t nativeIndex, + int64_t nativeLength, + UBool forward) { + ASSERT(text->chunkContents == text->pExtra); + if (forward) { + ASSERT(nativeIndex >= text->b && nativeIndex < nativeLength); + text->chunkNativeStart = nativeIndex; + text->chunkNativeLimit = nativeIndex + text->extraSize / sizeof(UChar); + if (text->chunkNativeLimit > nativeLength) + text->chunkNativeLimit = nativeLength; + } else { + ASSERT(nativeIndex > text->b && nativeIndex <= nativeLength); + text->chunkNativeLimit = nativeIndex; + text->chunkNativeStart = nativeIndex - text->extraSize / sizeof(UChar); + if (text->chunkNativeStart < text->b) + text->chunkNativeStart = text->b; + } + int64_t length = text->chunkNativeLimit - text->chunkNativeStart; + // Ensure chunk length is well defined if computed length exceeds int32_t + // range. + ASSERT(length <= std::numeric_limits::max()); + text->chunkLength = length <= std::numeric_limits::max() + ? static_cast(length) + : 0; + text->nativeIndexingLimit = text->chunkLength; + text->chunkOffset = forward ? 0 : text->chunkLength; + StringImpl::copyChars( + const_cast(text->chunkContents), + static_cast(text->p) + (text->chunkNativeStart - text->b), + static_cast(text->chunkLength)); +} + +static void textLatin1SwitchToPrimaryContext(UText* text, + int64_t nativeIndex, + int64_t nativeLength, + UBool forward) { + ASSERT(!text->chunkContents || text->chunkContents == text->q); + text->chunkContents = static_cast(text->pExtra); + textLatin1MoveInPrimaryContext(text, nativeIndex, nativeLength, forward); +} + +static void textLatin1MoveInPriorContext(UText* text, + int64_t nativeIndex, + int64_t nativeLength, + UBool forward) { + ASSERT(text->chunkContents == text->q); + ASSERT(forward ? nativeIndex < text->b : nativeIndex <= text->b); + ASSERT_UNUSED(nativeLength, forward ? nativeIndex < nativeLength + : nativeIndex <= nativeLength); + ASSERT_UNUSED(forward, forward ? nativeIndex < nativeLength + : nativeIndex <= nativeLength); + text->chunkNativeStart = 0; + text->chunkNativeLimit = text->b; + text->chunkLength = text->b; + text->nativeIndexingLimit = text->chunkLength; + int64_t offset = nativeIndex - text->chunkNativeStart; + // Ensure chunk offset is well defined if computed offset exceeds int32_t + // range or chunk length. + ASSERT(offset <= std::numeric_limits::max()); + text->chunkOffset = std::min(offset <= std::numeric_limits::max() + ? static_cast(offset) + : 0, + text->chunkLength); +} + +static void textLatin1SwitchToPriorContext(UText* text, + int64_t nativeIndex, + int64_t nativeLength, + UBool forward) { + ASSERT(!text->chunkContents || text->chunkContents == text->pExtra); + text->chunkContents = static_cast(text->q); + textLatin1MoveInPriorContext(text, nativeIndex, nativeLength, forward); +} + +static inline bool textInChunkOrOutOfRange(UText* text, + int64_t nativeIndex, + int64_t nativeLength, + UBool forward, + UBool& isAccessible) { + if (forward) { + if (nativeIndex >= text->chunkNativeStart && + nativeIndex < text->chunkNativeLimit) { + int64_t offset = nativeIndex - text->chunkNativeStart; + // Ensure chunk offset is well formed if computed offset exceeds int32_t + // range. + ASSERT(offset <= std::numeric_limits::max()); + text->chunkOffset = offset <= std::numeric_limits::max() + ? static_cast(offset) + : 0; + isAccessible = TRUE; + return true; } - int64_t length = text->chunkNativeLimit - text->chunkNativeStart; - // Ensure chunk length is well defined if computed length exceeds int32_t range. - ASSERT(length <= std::numeric_limits::max()); - text->chunkLength = length <= std::numeric_limits::max() ? static_cast(length) : 0; - text->nativeIndexingLimit = text->chunkLength; - text->chunkOffset = forward ? 0 : text->chunkLength; - StringImpl::copyChars(const_cast(text->chunkContents), static_cast(text->p) + (text->chunkNativeStart - text->b), static_cast(text->chunkLength)); -} - -static void textLatin1SwitchToPrimaryContext(UText* text, int64_t nativeIndex, int64_t nativeLength, UBool forward) -{ - ASSERT(!text->chunkContents || text->chunkContents == text->q); - text->chunkContents = static_cast(text->pExtra); - textLatin1MoveInPrimaryContext(text, nativeIndex, nativeLength, forward); -} - -static void textLatin1MoveInPriorContext(UText* text, int64_t nativeIndex, int64_t nativeLength, UBool forward) -{ - ASSERT(text->chunkContents == text->q); - ASSERT(forward ? nativeIndex < text->b : nativeIndex <= text->b); - ASSERT_UNUSED(nativeLength, forward ? nativeIndex < nativeLength : nativeIndex <= nativeLength); - ASSERT_UNUSED(forward, forward ? nativeIndex < nativeLength : nativeIndex <= nativeLength); - text->chunkNativeStart = 0; - text->chunkNativeLimit = text->b; - text->chunkLength = text->b; - text->nativeIndexingLimit = text->chunkLength; - int64_t offset = nativeIndex - text->chunkNativeStart; - // Ensure chunk offset is well defined if computed offset exceeds int32_t range or chunk length. - ASSERT(offset <= std::numeric_limits::max()); - text->chunkOffset = std::min(offset <= std::numeric_limits::max() ? static_cast(offset) : 0, text->chunkLength); -} - -static void textLatin1SwitchToPriorContext(UText* text, int64_t nativeIndex, int64_t nativeLength, UBool forward) -{ - ASSERT(!text->chunkContents || text->chunkContents == text->pExtra); - text->chunkContents = static_cast(text->q); - textLatin1MoveInPriorContext(text, nativeIndex, nativeLength, forward); -} - -static inline bool textInChunkOrOutOfRange(UText* text, int64_t nativeIndex, int64_t nativeLength, UBool forward, UBool& isAccessible) -{ - if (forward) { - if (nativeIndex >= text->chunkNativeStart && nativeIndex < text->chunkNativeLimit) { - int64_t offset = nativeIndex - text->chunkNativeStart; - // Ensure chunk offset is well formed if computed offset exceeds int32_t range. - ASSERT(offset <= std::numeric_limits::max()); - text->chunkOffset = offset <= std::numeric_limits::max() ? static_cast(offset) : 0; - isAccessible = TRUE; - return true; - } - if (nativeIndex >= nativeLength && text->chunkNativeLimit == nativeLength) { - text->chunkOffset = text->chunkLength; - isAccessible = FALSE; - return true; - } - } else { - if (nativeIndex > text->chunkNativeStart && nativeIndex <= text->chunkNativeLimit) { - int64_t offset = nativeIndex - text->chunkNativeStart; - // Ensure chunk offset is well formed if computed offset exceeds int32_t range. - ASSERT(offset <= std::numeric_limits::max()); - text->chunkOffset = offset <= std::numeric_limits::max() ? static_cast(offset) : 0; - isAccessible = TRUE; - return true; - } - if (nativeIndex <= 0 && !text->chunkNativeStart) { - text->chunkOffset = 0; - isAccessible = FALSE; - return true; - } + if (nativeIndex >= nativeLength && text->chunkNativeLimit == nativeLength) { + text->chunkOffset = text->chunkLength; + isAccessible = FALSE; + return true; } - return false; -} - -static UBool textLatin1Access(UText* text, int64_t nativeIndex, UBool forward) -{ - if (!text->context) - return FALSE; - int64_t nativeLength = textNativeLength(text); - UBool isAccessible; - if (textInChunkOrOutOfRange(text, nativeIndex, nativeLength, forward, isAccessible)) - return isAccessible; - nativeIndex = textPinIndex(nativeIndex, nativeLength - 1); - TextContext currentContext = textLatin1GetCurrentContext(text); - TextContext newContext = textGetContext(text, nativeIndex, forward); - ASSERT(newContext != NoContext); - if (newContext == currentContext) { - if (currentContext == PrimaryContext) { - textLatin1MoveInPrimaryContext(text, nativeIndex, nativeLength, forward); - } else { - textLatin1MoveInPriorContext(text, nativeIndex, nativeLength, forward); - } - } else if (newContext == PrimaryContext) { - textLatin1SwitchToPrimaryContext(text, nativeIndex, nativeLength, forward); + } else { + if (nativeIndex > text->chunkNativeStart && + nativeIndex <= text->chunkNativeLimit) { + int64_t offset = nativeIndex - text->chunkNativeStart; + // Ensure chunk offset is well formed if computed offset exceeds int32_t + // range. + ASSERT(offset <= std::numeric_limits::max()); + text->chunkOffset = offset <= std::numeric_limits::max() + ? static_cast(offset) + : 0; + isAccessible = TRUE; + return true; + } + if (nativeIndex <= 0 && !text->chunkNativeStart) { + text->chunkOffset = 0; + isAccessible = FALSE; + return true; + } + } + return false; +} + +static UBool textLatin1Access(UText* text, int64_t nativeIndex, UBool forward) { + if (!text->context) + return FALSE; + int64_t nativeLength = textNativeLength(text); + UBool isAccessible; + if (textInChunkOrOutOfRange(text, nativeIndex, nativeLength, forward, + isAccessible)) + return isAccessible; + nativeIndex = textPinIndex(nativeIndex, nativeLength - 1); + TextContext currentContext = textLatin1GetCurrentContext(text); + TextContext newContext = textGetContext(text, nativeIndex, forward); + ASSERT(newContext != NoContext); + if (newContext == currentContext) { + if (currentContext == PrimaryContext) { + textLatin1MoveInPrimaryContext(text, nativeIndex, nativeLength, forward); } else { - ASSERT(newContext == PriorContext); - textLatin1SwitchToPriorContext(text, nativeIndex, nativeLength, forward); + textLatin1MoveInPriorContext(text, nativeIndex, nativeLength, forward); } - return TRUE; + } else if (newContext == PrimaryContext) { + textLatin1SwitchToPrimaryContext(text, nativeIndex, nativeLength, forward); + } else { + ASSERT(newContext == PriorContext); + textLatin1SwitchToPriorContext(text, nativeIndex, nativeLength, forward); + } + return TRUE; } static const struct UTextFuncs textLatin1Funcs = { - sizeof(UTextFuncs), - 0, 0, 0, - textClone, - textNativeLength, - textLatin1Access, - textExtract, - 0, 0, 0, 0, - textClose, - 0, 0, 0, + sizeof(UTextFuncs), 0, 0, 0, textClone, textNativeLength, + textLatin1Access, textExtract, 0, 0, 0, 0, + textClose, 0, 0, 0, }; -static void textInit(UText* text, const UTextFuncs* funcs, const void* string, unsigned length, const UChar* priorContext, int priorContextLength) -{ - text->pFuncs = funcs; - text->providerProperties = 1 << UTEXT_PROVIDER_STABLE_CHUNKS; - text->context = string; - text->p = string; - text->a = length; - text->q = priorContext; - text->b = priorContextLength; -} - -static UText* textOpenLatin1(UTextWithBuffer* utWithBuffer, const LChar* string, unsigned length, const UChar* priorContext, int priorContextLength, UErrorCode* status) -{ - if (U_FAILURE(*status)) - return 0; +static void textInit(UText* text, + const UTextFuncs* funcs, + const void* string, + unsigned length, + const UChar* priorContext, + int priorContextLength) { + text->pFuncs = funcs; + text->providerProperties = 1 << UTEXT_PROVIDER_STABLE_CHUNKS; + text->context = string; + text->p = string; + text->a = length; + text->q = priorContext; + text->b = priorContextLength; +} + +static UText* textOpenLatin1(UTextWithBuffer* utWithBuffer, + const LChar* string, + unsigned length, + const UChar* priorContext, + int priorContextLength, + UErrorCode* status) { + if (U_FAILURE(*status)) + return 0; - if (!string || length > static_cast(std::numeric_limits::max())) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - UText* text = utext_setup(&utWithBuffer->text, sizeof(utWithBuffer->buffer), status); - if (U_FAILURE(*status)) { - ASSERT(!text); - return 0; - } - textInit(text, &textLatin1Funcs, string, length, priorContext, priorContextLength); - return text; -} - -static inline TextContext textUTF16GetCurrentContext(const UText* text) -{ - if (!text->chunkContents) - return NoContext; - return text->chunkContents == text->p ? PrimaryContext : PriorContext; -} - -static void textUTF16MoveInPrimaryContext(UText* text, int64_t nativeIndex, int64_t nativeLength, UBool forward) -{ - ASSERT(text->chunkContents == text->p); - ASSERT_UNUSED(forward, forward ? nativeIndex >= text->b : nativeIndex > text->b); - ASSERT_UNUSED(forward, forward ? nativeIndex < nativeLength : nativeIndex <= nativeLength); - text->chunkNativeStart = text->b; - text->chunkNativeLimit = nativeLength; - int64_t length = text->chunkNativeLimit - text->chunkNativeStart; - // Ensure chunk length is well defined if computed length exceeds int32_t range. - ASSERT(length <= std::numeric_limits::max()); - text->chunkLength = length <= std::numeric_limits::max() ? static_cast(length) : 0; - text->nativeIndexingLimit = text->chunkLength; - int64_t offset = nativeIndex - text->chunkNativeStart; - // Ensure chunk offset is well defined if computed offset exceeds int32_t range or chunk length. - ASSERT(offset <= std::numeric_limits::max()); - text->chunkOffset = std::min(offset <= std::numeric_limits::max() ? static_cast(offset) : 0, text->chunkLength); -} - -static void textUTF16SwitchToPrimaryContext(UText* text, int64_t nativeIndex, int64_t nativeLength, UBool forward) -{ - ASSERT(!text->chunkContents || text->chunkContents == text->q); - text->chunkContents = static_cast(text->p); - textUTF16MoveInPrimaryContext(text, nativeIndex, nativeLength, forward); -} - -static void textUTF16MoveInPriorContext(UText* text, int64_t nativeIndex, int64_t nativeLength, UBool forward) -{ - ASSERT(text->chunkContents == text->q); - ASSERT(forward ? nativeIndex < text->b : nativeIndex <= text->b); - ASSERT_UNUSED(nativeLength, forward ? nativeIndex < nativeLength : nativeIndex <= nativeLength); - ASSERT_UNUSED(forward, forward ? nativeIndex < nativeLength : nativeIndex <= nativeLength); - text->chunkNativeStart = 0; - text->chunkNativeLimit = text->b; - text->chunkLength = text->b; - text->nativeIndexingLimit = text->chunkLength; - int64_t offset = nativeIndex - text->chunkNativeStart; - // Ensure chunk offset is well defined if computed offset exceeds int32_t range or chunk length. - ASSERT(offset <= std::numeric_limits::max()); - text->chunkOffset = std::min(offset <= std::numeric_limits::max() ? static_cast(offset) : 0, text->chunkLength); -} - -static void textUTF16SwitchToPriorContext(UText* text, int64_t nativeIndex, int64_t nativeLength, UBool forward) -{ - ASSERT(!text->chunkContents || text->chunkContents == text->p); - text->chunkContents = static_cast(text->q); - textUTF16MoveInPriorContext(text, nativeIndex, nativeLength, forward); -} - -static UBool textUTF16Access(UText* text, int64_t nativeIndex, UBool forward) -{ - if (!text->context) - return FALSE; - int64_t nativeLength = textNativeLength(text); - UBool isAccessible; - if (textInChunkOrOutOfRange(text, nativeIndex, nativeLength, forward, isAccessible)) - return isAccessible; - nativeIndex = textPinIndex(nativeIndex, nativeLength - 1); - TextContext currentContext = textUTF16GetCurrentContext(text); - TextContext newContext = textGetContext(text, nativeIndex, forward); - ASSERT(newContext != NoContext); - if (newContext == currentContext) { - if (currentContext == PrimaryContext) { - textUTF16MoveInPrimaryContext(text, nativeIndex, nativeLength, forward); - } else { - textUTF16MoveInPriorContext(text, nativeIndex, nativeLength, forward); - } - } else if (newContext == PrimaryContext) { - textUTF16SwitchToPrimaryContext(text, nativeIndex, nativeLength, forward); + if (!string || + length > static_cast(std::numeric_limits::max())) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + UText* text = + utext_setup(&utWithBuffer->text, sizeof(utWithBuffer->buffer), status); + if (U_FAILURE(*status)) { + ASSERT(!text); + return 0; + } + textInit(text, &textLatin1Funcs, string, length, priorContext, + priorContextLength); + return text; +} + +static inline TextContext textUTF16GetCurrentContext(const UText* text) { + if (!text->chunkContents) + return NoContext; + return text->chunkContents == text->p ? PrimaryContext : PriorContext; +} + +static void textUTF16MoveInPrimaryContext(UText* text, + int64_t nativeIndex, + int64_t nativeLength, + UBool forward) { + ASSERT(text->chunkContents == text->p); + ASSERT_UNUSED(forward, + forward ? nativeIndex >= text->b : nativeIndex > text->b); + ASSERT_UNUSED(forward, forward ? nativeIndex < nativeLength + : nativeIndex <= nativeLength); + text->chunkNativeStart = text->b; + text->chunkNativeLimit = nativeLength; + int64_t length = text->chunkNativeLimit - text->chunkNativeStart; + // Ensure chunk length is well defined if computed length exceeds int32_t + // range. + ASSERT(length <= std::numeric_limits::max()); + text->chunkLength = length <= std::numeric_limits::max() + ? static_cast(length) + : 0; + text->nativeIndexingLimit = text->chunkLength; + int64_t offset = nativeIndex - text->chunkNativeStart; + // Ensure chunk offset is well defined if computed offset exceeds int32_t + // range or chunk length. + ASSERT(offset <= std::numeric_limits::max()); + text->chunkOffset = std::min(offset <= std::numeric_limits::max() + ? static_cast(offset) + : 0, + text->chunkLength); +} + +static void textUTF16SwitchToPrimaryContext(UText* text, + int64_t nativeIndex, + int64_t nativeLength, + UBool forward) { + ASSERT(!text->chunkContents || text->chunkContents == text->q); + text->chunkContents = static_cast(text->p); + textUTF16MoveInPrimaryContext(text, nativeIndex, nativeLength, forward); +} + +static void textUTF16MoveInPriorContext(UText* text, + int64_t nativeIndex, + int64_t nativeLength, + UBool forward) { + ASSERT(text->chunkContents == text->q); + ASSERT(forward ? nativeIndex < text->b : nativeIndex <= text->b); + ASSERT_UNUSED(nativeLength, forward ? nativeIndex < nativeLength + : nativeIndex <= nativeLength); + ASSERT_UNUSED(forward, forward ? nativeIndex < nativeLength + : nativeIndex <= nativeLength); + text->chunkNativeStart = 0; + text->chunkNativeLimit = text->b; + text->chunkLength = text->b; + text->nativeIndexingLimit = text->chunkLength; + int64_t offset = nativeIndex - text->chunkNativeStart; + // Ensure chunk offset is well defined if computed offset exceeds int32_t + // range or chunk length. + ASSERT(offset <= std::numeric_limits::max()); + text->chunkOffset = std::min(offset <= std::numeric_limits::max() + ? static_cast(offset) + : 0, + text->chunkLength); +} + +static void textUTF16SwitchToPriorContext(UText* text, + int64_t nativeIndex, + int64_t nativeLength, + UBool forward) { + ASSERT(!text->chunkContents || text->chunkContents == text->p); + text->chunkContents = static_cast(text->q); + textUTF16MoveInPriorContext(text, nativeIndex, nativeLength, forward); +} + +static UBool textUTF16Access(UText* text, int64_t nativeIndex, UBool forward) { + if (!text->context) + return FALSE; + int64_t nativeLength = textNativeLength(text); + UBool isAccessible; + if (textInChunkOrOutOfRange(text, nativeIndex, nativeLength, forward, + isAccessible)) + return isAccessible; + nativeIndex = textPinIndex(nativeIndex, nativeLength - 1); + TextContext currentContext = textUTF16GetCurrentContext(text); + TextContext newContext = textGetContext(text, nativeIndex, forward); + ASSERT(newContext != NoContext); + if (newContext == currentContext) { + if (currentContext == PrimaryContext) { + textUTF16MoveInPrimaryContext(text, nativeIndex, nativeLength, forward); } else { - ASSERT(newContext == PriorContext); - textUTF16SwitchToPriorContext(text, nativeIndex, nativeLength, forward); + textUTF16MoveInPriorContext(text, nativeIndex, nativeLength, forward); } - return TRUE; + } else if (newContext == PrimaryContext) { + textUTF16SwitchToPrimaryContext(text, nativeIndex, nativeLength, forward); + } else { + ASSERT(newContext == PriorContext); + textUTF16SwitchToPriorContext(text, nativeIndex, nativeLength, forward); + } + return TRUE; } static const struct UTextFuncs textUTF16Funcs = { - sizeof(UTextFuncs), - 0, 0, 0, - textClone, - textNativeLength, - textUTF16Access, - textExtract, - 0, 0, 0, 0, - textClose, - 0, 0, 0, + sizeof(UTextFuncs), 0, 0, 0, textClone, textNativeLength, + textUTF16Access, textExtract, 0, 0, 0, 0, + textClose, 0, 0, 0, }; -static UText* textOpenUTF16(UText* text, const UChar* string, unsigned length, const UChar* priorContext, int priorContextLength, UErrorCode* status) -{ - if (U_FAILURE(*status)) - return 0; +static UText* textOpenUTF16(UText* text, + const UChar* string, + unsigned length, + const UChar* priorContext, + int priorContextLength, + UErrorCode* status) { + if (U_FAILURE(*status)) + return 0; - if (!string || length > static_cast(std::numeric_limits::max())) { - *status = U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } + if (!string || + length > static_cast(std::numeric_limits::max())) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } - text = utext_setup(text, 0, status); - if (U_FAILURE(*status)) { - ASSERT(!text); - return 0; - } - textInit(text, &textUTF16Funcs, string, length, priorContext, priorContextLength); - return text; + text = utext_setup(text, 0, status); + if (U_FAILURE(*status)) { + ASSERT(!text); + return 0; + } + textInit(text, &textUTF16Funcs, string, length, priorContext, + priorContextLength); + return text; } static UText emptyText = UTEXT_INITIALIZER; -static TextBreakIterator* wordBreakIterator(const LChar* string, int length) -{ - UErrorCode errorCode = U_ZERO_ERROR; - static TextBreakIterator* breakIter = 0; - if (!breakIter) { - breakIter = icu::BreakIterator::createWordInstance(icu::Locale(currentTextBreakLocaleID()), errorCode); - ASSERT_WITH_MESSAGE(U_SUCCESS(errorCode), "ICU could not open a break iterator: %s (%d)", u_errorName(errorCode), errorCode); - if (!breakIter) - return 0; - } - - UTextWithBuffer textLocal; - textLocal.text = emptyText; - textLocal.text.extraSize = sizeof(textLocal.buffer); - textLocal.text.pExtra = textLocal.buffer; - - UErrorCode openStatus = U_ZERO_ERROR; - UText* text = textOpenLatin1(&textLocal, string, length, 0, 0, &openStatus); - if (U_FAILURE(openStatus)) { - WTF_LOG_ERROR("textOpenLatin1 failed with status %d", openStatus); - return 0; - } - - UErrorCode setTextStatus = U_ZERO_ERROR; - breakIter->setText(text, setTextStatus); - if (U_FAILURE(setTextStatus)) - WTF_LOG_ERROR("BreakIterator::seText failed with status %d", setTextStatus); - - utext_close(text); - - return breakIter; -} - -static void setText16(TextBreakIterator* iter, const UChar* string, int length) -{ - UErrorCode errorCode = U_ZERO_ERROR; - UText uText = UTEXT_INITIALIZER; - utext_openUChars(&uText, string, length, &errorCode); - if (U_FAILURE(errorCode)) - return; - iter->setText(&uText, errorCode); -} - -TextBreakIterator* wordBreakIterator(const UChar* string, int length) -{ - UErrorCode errorCode = U_ZERO_ERROR; - static TextBreakIterator* breakIter = 0; - if (!breakIter) { - breakIter = icu::BreakIterator::createWordInstance(icu::Locale(currentTextBreakLocaleID()), errorCode); - ASSERT_WITH_MESSAGE(U_SUCCESS(errorCode), "ICU could not open a break iterator: %s (%d)", u_errorName(errorCode), errorCode); - if (!breakIter) - return 0; - } - setText16(breakIter, string, length); - return breakIter; -} - -TextBreakIterator* wordBreakIterator(const String& string, int start, int length) -{ - if (string.isEmpty()) - return 0; - if (string.is8Bit()) - return wordBreakIterator(string.characters8() + start, length); - return wordBreakIterator(string.characters16() + start, length); -} - -TextBreakIterator* acquireLineBreakIterator(const LChar* string, int length, const AtomicString& locale, const UChar* priorContext, unsigned priorContextLength) -{ - TextBreakIterator* iterator = LineBreakIteratorPool::sharedPool().take(locale); - if (!iterator) - return 0; +static TextBreakIterator* wordBreakIterator(const LChar* string, int length) { + UErrorCode errorCode = U_ZERO_ERROR; + static TextBreakIterator* breakIter = 0; + if (!breakIter) { + breakIter = icu::BreakIterator::createWordInstance( + icu::Locale(currentTextBreakLocaleID()), errorCode); + ASSERT_WITH_MESSAGE(U_SUCCESS(errorCode), + "ICU could not open a break iterator: %s (%d)", + u_errorName(errorCode), errorCode); + if (!breakIter) + return 0; + } + + UTextWithBuffer textLocal; + textLocal.text = emptyText; + textLocal.text.extraSize = sizeof(textLocal.buffer); + textLocal.text.pExtra = textLocal.buffer; + + UErrorCode openStatus = U_ZERO_ERROR; + UText* text = textOpenLatin1(&textLocal, string, length, 0, 0, &openStatus); + if (U_FAILURE(openStatus)) { + WTF_LOG_ERROR("textOpenLatin1 failed with status %d", openStatus); + return 0; + } + + UErrorCode setTextStatus = U_ZERO_ERROR; + breakIter->setText(text, setTextStatus); + if (U_FAILURE(setTextStatus)) + WTF_LOG_ERROR("BreakIterator::seText failed with status %d", setTextStatus); + + utext_close(text); + + return breakIter; +} + +static void setText16(TextBreakIterator* iter, + const UChar* string, + int length) { + UErrorCode errorCode = U_ZERO_ERROR; + UText uText = UTEXT_INITIALIZER; + utext_openUChars(&uText, string, length, &errorCode); + if (U_FAILURE(errorCode)) + return; + iter->setText(&uText, errorCode); +} + +TextBreakIterator* wordBreakIterator(const UChar* string, int length) { + UErrorCode errorCode = U_ZERO_ERROR; + static TextBreakIterator* breakIter = 0; + if (!breakIter) { + breakIter = icu::BreakIterator::createWordInstance( + icu::Locale(currentTextBreakLocaleID()), errorCode); + ASSERT_WITH_MESSAGE(U_SUCCESS(errorCode), + "ICU could not open a break iterator: %s (%d)", + u_errorName(errorCode), errorCode); + if (!breakIter) + return 0; + } + setText16(breakIter, string, length); + return breakIter; +} + +TextBreakIterator* wordBreakIterator(const String& string, + int start, + int length) { + if (string.isEmpty()) + return 0; + if (string.is8Bit()) + return wordBreakIterator(string.characters8() + start, length); + return wordBreakIterator(string.characters16() + start, length); +} + +TextBreakIterator* acquireLineBreakIterator(const LChar* string, + int length, + const AtomicString& locale, + const UChar* priorContext, + unsigned priorContextLength) { + TextBreakIterator* iterator = + LineBreakIteratorPool::sharedPool().take(locale); + if (!iterator) + return 0; - UTextWithBuffer textLocal; - textLocal.text = emptyText; - textLocal.text.extraSize = sizeof(textLocal.buffer); - textLocal.text.pExtra = textLocal.buffer; + UTextWithBuffer textLocal; + textLocal.text = emptyText; + textLocal.text.extraSize = sizeof(textLocal.buffer); + textLocal.text.pExtra = textLocal.buffer; - UErrorCode openStatus = U_ZERO_ERROR; - UText* text = textOpenLatin1(&textLocal, string, length, priorContext, priorContextLength, &openStatus); - if (U_FAILURE(openStatus)) { - WTF_LOG_ERROR("textOpenLatin1 failed with status %d", openStatus); - return 0; - } + UErrorCode openStatus = U_ZERO_ERROR; + UText* text = textOpenLatin1(&textLocal, string, length, priorContext, + priorContextLength, &openStatus); + if (U_FAILURE(openStatus)) { + WTF_LOG_ERROR("textOpenLatin1 failed with status %d", openStatus); + return 0; + } - UErrorCode setTextStatus = U_ZERO_ERROR; - iterator->setText(text, setTextStatus); - if (U_FAILURE(setTextStatus)) { - WTF_LOG_ERROR("ubrk_setUText failed with status %d", setTextStatus); - return 0; - } + UErrorCode setTextStatus = U_ZERO_ERROR; + iterator->setText(text, setTextStatus); + if (U_FAILURE(setTextStatus)) { + WTF_LOG_ERROR("ubrk_setUText failed with status %d", setTextStatus); + return 0; + } - utext_close(text); + utext_close(text); - return iterator; + return iterator; } -TextBreakIterator* acquireLineBreakIterator(const UChar* string, int length, const AtomicString& locale, const UChar* priorContext, unsigned priorContextLength) -{ - TextBreakIterator* iterator = LineBreakIteratorPool::sharedPool().take(locale); - if (!iterator) - return 0; +TextBreakIterator* acquireLineBreakIterator(const UChar* string, + int length, + const AtomicString& locale, + const UChar* priorContext, + unsigned priorContextLength) { + TextBreakIterator* iterator = + LineBreakIteratorPool::sharedPool().take(locale); + if (!iterator) + return 0; - UText textLocal = UTEXT_INITIALIZER; + UText textLocal = UTEXT_INITIALIZER; - UErrorCode openStatus = U_ZERO_ERROR; - UText* text = textOpenUTF16(&textLocal, string, length, priorContext, priorContextLength, &openStatus); - if (U_FAILURE(openStatus)) { - WTF_LOG_ERROR("textOpenUTF16 failed with status %d", openStatus); - return 0; - } + UErrorCode openStatus = U_ZERO_ERROR; + UText* text = textOpenUTF16(&textLocal, string, length, priorContext, + priorContextLength, &openStatus); + if (U_FAILURE(openStatus)) { + WTF_LOG_ERROR("textOpenUTF16 failed with status %d", openStatus); + return 0; + } - UErrorCode setTextStatus = U_ZERO_ERROR; - iterator->setText(text, setTextStatus); - if (U_FAILURE(setTextStatus)) { - WTF_LOG_ERROR("ubrk_setUText failed with status %d", setTextStatus); - return 0; - } + UErrorCode setTextStatus = U_ZERO_ERROR; + iterator->setText(text, setTextStatus); + if (U_FAILURE(setTextStatus)) { + WTF_LOG_ERROR("ubrk_setUText failed with status %d", setTextStatus); + return 0; + } - utext_close(text); + utext_close(text); - return iterator; + return iterator; } -void releaseLineBreakIterator(TextBreakIterator* iterator) -{ - ASSERT_ARG(iterator, iterator); +void releaseLineBreakIterator(TextBreakIterator* iterator) { + ASSERT_ARG(iterator, iterator); - LineBreakIteratorPool::sharedPool().put(iterator); + LineBreakIteratorPool::sharedPool().put(iterator); } static TextBreakIterator* nonSharedCharacterBreakIterator; -static inline bool compareAndSwapNonSharedCharacterBreakIterator(TextBreakIterator* expected, TextBreakIterator* newValue) -{ - DEFINE_STATIC_LOCAL(Mutex, nonSharedCharacterBreakIteratorMutex, ()); - MutexLocker locker(nonSharedCharacterBreakIteratorMutex); - if (nonSharedCharacterBreakIterator != expected) - return false; - nonSharedCharacterBreakIterator = newValue; - return true; -} - -NonSharedCharacterBreakIterator::NonSharedCharacterBreakIterator(const String& string) - : m_is8Bit(true) - , m_charaters8(0) - , m_offset(0) - , m_length(0) - , m_iterator(0) -{ - if (string.isEmpty()) - return; - - m_is8Bit = string.is8Bit(); - - if (m_is8Bit) { - m_charaters8 = string.characters8(); - m_offset = 0; - m_length = string.length(); - return; - } - - createIteratorForBuffer(string.characters16(), string.length()); -} - -NonSharedCharacterBreakIterator::NonSharedCharacterBreakIterator(const UChar* buffer, unsigned length) - : m_is8Bit(false) - , m_charaters8(0) - , m_offset(0) - , m_length(0) - , m_iterator(0) -{ - createIteratorForBuffer(buffer, length); -} - -void NonSharedCharacterBreakIterator::createIteratorForBuffer(const UChar* buffer, unsigned length) -{ - m_iterator = nonSharedCharacterBreakIterator; - bool createdIterator = m_iterator && compareAndSwapNonSharedCharacterBreakIterator(m_iterator, 0); - if (!createdIterator) { - UErrorCode errorCode = U_ZERO_ERROR; - m_iterator = icu::BreakIterator::createCharacterInstance(icu::Locale(currentTextBreakLocaleID()), errorCode); - ASSERT_WITH_MESSAGE(U_SUCCESS(errorCode), "ICU could not open a break iterator: %s (%d)", u_errorName(errorCode), errorCode); - } +static inline bool compareAndSwapNonSharedCharacterBreakIterator( + TextBreakIterator* expected, + TextBreakIterator* newValue) { + DEFINE_STATIC_LOCAL(Mutex, nonSharedCharacterBreakIteratorMutex, ()); + MutexLocker locker(nonSharedCharacterBreakIteratorMutex); + if (nonSharedCharacterBreakIterator != expected) + return false; + nonSharedCharacterBreakIterator = newValue; + return true; +} + +NonSharedCharacterBreakIterator::NonSharedCharacterBreakIterator( + const String& string) + : m_is8Bit(true), m_charaters8(0), m_offset(0), m_length(0), m_iterator(0) { + if (string.isEmpty()) + return; + + m_is8Bit = string.is8Bit(); + + if (m_is8Bit) { + m_charaters8 = string.characters8(); + m_offset = 0; + m_length = string.length(); + return; + } + + createIteratorForBuffer(string.characters16(), string.length()); +} + +NonSharedCharacterBreakIterator::NonSharedCharacterBreakIterator( + const UChar* buffer, + unsigned length) + : m_is8Bit(false), + m_charaters8(0), + m_offset(0), + m_length(0), + m_iterator(0) { + createIteratorForBuffer(buffer, length); +} + +void NonSharedCharacterBreakIterator::createIteratorForBuffer( + const UChar* buffer, + unsigned length) { + m_iterator = nonSharedCharacterBreakIterator; + bool createdIterator = + m_iterator && + compareAndSwapNonSharedCharacterBreakIterator(m_iterator, 0); + if (!createdIterator) { + UErrorCode errorCode = U_ZERO_ERROR; + m_iterator = icu::BreakIterator::createCharacterInstance( + icu::Locale(currentTextBreakLocaleID()), errorCode); + ASSERT_WITH_MESSAGE(U_SUCCESS(errorCode), + "ICU could not open a break iterator: %s (%d)", + u_errorName(errorCode), errorCode); + } - setText16(m_iterator, buffer, length); + setText16(m_iterator, buffer, length); } -NonSharedCharacterBreakIterator::~NonSharedCharacterBreakIterator() -{ - if (m_is8Bit) - return; - if (!compareAndSwapNonSharedCharacterBreakIterator(0, m_iterator)) - delete m_iterator; +NonSharedCharacterBreakIterator::~NonSharedCharacterBreakIterator() { + if (m_is8Bit) + return; + if (!compareAndSwapNonSharedCharacterBreakIterator(0, m_iterator)) + delete m_iterator; } -int NonSharedCharacterBreakIterator::next() -{ - if (!m_is8Bit) - return m_iterator->next(); +int NonSharedCharacterBreakIterator::next() { + if (!m_is8Bit) + return m_iterator->next(); - if (m_offset >= m_length) - return TextBreakDone; + if (m_offset >= m_length) + return TextBreakDone; - m_offset += clusterLengthStartingAt(m_offset); - return m_offset; + m_offset += clusterLengthStartingAt(m_offset); + return m_offset; } -int NonSharedCharacterBreakIterator::current() -{ - if (!m_is8Bit) - return m_iterator->current(); - return m_offset; +int NonSharedCharacterBreakIterator::current() { + if (!m_is8Bit) + return m_iterator->current(); + return m_offset; } -bool NonSharedCharacterBreakIterator::isBreak(int offset) const -{ - if (!m_is8Bit) - return m_iterator->isBoundary(offset); - return !isLFAfterCR(offset); +bool NonSharedCharacterBreakIterator::isBreak(int offset) const { + if (!m_is8Bit) + return m_iterator->isBoundary(offset); + return !isLFAfterCR(offset); } -int NonSharedCharacterBreakIterator::preceding(int offset) const -{ - if (!m_is8Bit) - return m_iterator->preceding(offset); - if (offset <= 0) - return TextBreakDone; - if (isLFAfterCR(offset)) - return offset - 2; - return offset - 1; +int NonSharedCharacterBreakIterator::preceding(int offset) const { + if (!m_is8Bit) + return m_iterator->preceding(offset); + if (offset <= 0) + return TextBreakDone; + if (isLFAfterCR(offset)) + return offset - 2; + return offset - 1; } -int NonSharedCharacterBreakIterator::following(int offset) const -{ - if (!m_is8Bit) - return m_iterator->following(offset); - if (static_cast(offset) >= m_length) - return TextBreakDone; - return offset + clusterLengthStartingAt(offset); +int NonSharedCharacterBreakIterator::following(int offset) const { + if (!m_is8Bit) + return m_iterator->following(offset); + if (static_cast(offset) >= m_length) + return TextBreakDone; + return offset + clusterLengthStartingAt(offset); } -TextBreakIterator* sentenceBreakIterator(const UChar* string, int length) -{ - UErrorCode openStatus = U_ZERO_ERROR; - static TextBreakIterator* iterator = 0; - if (!iterator) { - iterator = icu::BreakIterator::createSentenceInstance(icu::Locale(currentTextBreakLocaleID()), openStatus); - ASSERT_WITH_MESSAGE(U_SUCCESS(openStatus), "ICU could not open a break iterator: %s (%d)", u_errorName(openStatus), openStatus); - if (!iterator) - return 0; - } - - setText16(iterator, string, length); - return iterator; -} +TextBreakIterator* sentenceBreakIterator(const UChar* string, int length) { + UErrorCode openStatus = U_ZERO_ERROR; + static TextBreakIterator* iterator = 0; + if (!iterator) { + iterator = icu::BreakIterator::createSentenceInstance( + icu::Locale(currentTextBreakLocaleID()), openStatus); + ASSERT_WITH_MESSAGE(U_SUCCESS(openStatus), + "ICU could not open a break iterator: %s (%d)", + u_errorName(openStatus), openStatus); + if (!iterator) + return 0; + } -bool isWordTextBreak(TextBreakIterator* iterator) -{ - icu::RuleBasedBreakIterator* ruleBasedBreakIterator = static_cast(iterator); - int ruleStatus = ruleBasedBreakIterator->getRuleStatus(); - return ruleStatus != UBRK_WORD_NONE; + setText16(iterator, string, length); + return iterator; } -static TextBreakIterator* setUpIteratorWithRules(const char* breakRules, const UChar* string, int length) -{ - if (!string) - return 0; - - static TextBreakIterator* iterator = 0; - if (!iterator) { - UParseError parseStatus; - UErrorCode openStatus = U_ZERO_ERROR; - Vector rules; - String(breakRules).appendTo(rules); - - iterator = new icu::RuleBasedBreakIterator(icu::UnicodeString(rules.data(), rules.size()), parseStatus, openStatus); - ASSERT_WITH_MESSAGE(U_SUCCESS(openStatus), "ICU could not open a break iterator: %s (%d)", u_errorName(openStatus), openStatus); - if (!iterator) - return 0; - } - - setText16(iterator, string, length); - return iterator; +bool isWordTextBreak(TextBreakIterator* iterator) { + icu::RuleBasedBreakIterator* ruleBasedBreakIterator = + static_cast(iterator); + int ruleStatus = ruleBasedBreakIterator->getRuleStatus(); + return ruleStatus != UBRK_WORD_NONE; } -TextBreakIterator* cursorMovementIterator(const UChar* string, int length) -{ - // This rule set is based on character-break iterator rules of ICU 4.0 - // . - // The major differences from the original ones are listed below: - // * Replaced '[\p{Grapheme_Cluster_Break = SpacingMark}]' with '[\p{General_Category = Spacing Mark} - $Extend]' for ICU 3.8 or earlier; - // * Removed rules that prevent a cursor from moving after prepend characters (Bug 24342); - // * Added rules that prevent a cursor from moving after virama signs of Indic languages except Tamil (Bug 15790), and; - // * Added rules that prevent a cursor from moving before Japanese half-width katakara voiced marks. - // * Added rules for regional indicator symbols. - static const char* const kRules = - "$CR = [\\p{Grapheme_Cluster_Break = CR}];" - "$LF = [\\p{Grapheme_Cluster_Break = LF}];" - "$Control = [\\p{Grapheme_Cluster_Break = Control}];" - "$VoiceMarks = [\\uFF9E\\uFF9F];" // Japanese half-width katakana voiced marks - "$Extend = [\\p{Grapheme_Cluster_Break = Extend} $VoiceMarks - [\\u0E30 \\u0E32 \\u0E45 \\u0EB0 \\u0EB2]];" - "$SpacingMark = [[\\p{General_Category = Spacing Mark}] - $Extend];" - "$L = [\\p{Grapheme_Cluster_Break = L}];" - "$V = [\\p{Grapheme_Cluster_Break = V}];" - "$T = [\\p{Grapheme_Cluster_Break = T}];" - "$LV = [\\p{Grapheme_Cluster_Break = LV}];" - "$LVT = [\\p{Grapheme_Cluster_Break = LVT}];" - "$Hin0 = [\\u0905-\\u0939];" // Devanagari Letter A,...,Ha - "$HinV = \\u094D;" // Devanagari Sign Virama - "$Hin1 = [\\u0915-\\u0939];" // Devanagari Letter Ka,...,Ha - "$Ben0 = [\\u0985-\\u09B9];" // Bengali Letter A,...,Ha - "$BenV = \\u09CD;" // Bengali Sign Virama - "$Ben1 = [\\u0995-\\u09B9];" // Bengali Letter Ka,...,Ha - "$Pan0 = [\\u0A05-\\u0A39];" // Gurmukhi Letter A,...,Ha - "$PanV = \\u0A4D;" // Gurmukhi Sign Virama - "$Pan1 = [\\u0A15-\\u0A39];" // Gurmukhi Letter Ka,...,Ha - "$Guj0 = [\\u0A85-\\u0AB9];" // Gujarati Letter A,...,Ha - "$GujV = \\u0ACD;" // Gujarati Sign Virama - "$Guj1 = [\\u0A95-\\u0AB9];" // Gujarati Letter Ka,...,Ha - "$Ori0 = [\\u0B05-\\u0B39];" // Oriya Letter A,...,Ha - "$OriV = \\u0B4D;" // Oriya Sign Virama - "$Ori1 = [\\u0B15-\\u0B39];" // Oriya Letter Ka,...,Ha - "$Tel0 = [\\u0C05-\\u0C39];" // Telugu Letter A,...,Ha - "$TelV = \\u0C4D;" // Telugu Sign Virama - "$Tel1 = [\\u0C14-\\u0C39];" // Telugu Letter Ka,...,Ha - "$Kan0 = [\\u0C85-\\u0CB9];" // Kannada Letter A,...,Ha - "$KanV = \\u0CCD;" // Kannada Sign Virama - "$Kan1 = [\\u0C95-\\u0CB9];" // Kannada Letter A,...,Ha - "$Mal0 = [\\u0D05-\\u0D39];" // Malayalam Letter A,...,Ha - "$MalV = \\u0D4D;" // Malayalam Sign Virama - "$Mal1 = [\\u0D15-\\u0D39];" // Malayalam Letter A,...,Ha - "$RI = [\\U0001F1E6-\\U0001F1FF];" // Emoji regional indicators - "!!chain;" - "!!forward;" - "$CR $LF;" - "$L ($L | $V | $LV | $LVT);" - "($LV | $V) ($V | $T);" - "($LVT | $T) $T;" - "[^$Control $CR $LF] $Extend;" - "[^$Control $CR $LF] $SpacingMark;" - "$RI $RI / $RI;" - "$RI $RI;" - "$Hin0 $HinV $Hin1;" // Devanagari Virama (forward) - "$Ben0 $BenV $Ben1;" // Bengali Virama (forward) - "$Pan0 $PanV $Pan1;" // Gurmukhi Virama (forward) - "$Guj0 $GujV $Guj1;" // Gujarati Virama (forward) - "$Ori0 $OriV $Ori1;" // Oriya Virama (forward) - "$Tel0 $TelV $Tel1;" // Telugu Virama (forward) - "$Kan0 $KanV $Kan1;" // Kannada Virama (forward) - "$Mal0 $MalV $Mal1;" // Malayalam Virama (forward) - "!!reverse;" - "$LF $CR;" - "($L | $V | $LV | $LVT) $L;" - "($V | $T) ($LV | $V);" - "$T ($LVT | $T);" - "$Extend [^$Control $CR $LF];" - "$SpacingMark [^$Control $CR $LF];" - "$RI $RI / $RI $RI;" - "$RI $RI;" - "$Hin1 $HinV $Hin0;" // Devanagari Virama (backward) - "$Ben1 $BenV $Ben0;" // Bengali Virama (backward) - "$Pan1 $PanV $Pan0;" // Gurmukhi Virama (backward) - "$Guj1 $GujV $Guj0;" // Gujarati Virama (backward) - "$Ori1 $OriV $Ori0;" // Gujarati Virama (backward) - "$Tel1 $TelV $Tel0;" // Telugu Virama (backward) - "$Kan1 $KanV $Kan0;" // Kannada Virama (backward) - "$Mal1 $MalV $Mal0;" // Malayalam Virama (backward) - "!!safe_reverse;" - "!!safe_forward;"; - - return setUpIteratorWithRules(kRules, string, length); -} +static TextBreakIterator* setUpIteratorWithRules(const char* breakRules, + const UChar* string, + int length) { + if (!string) + return 0; -} + static TextBreakIterator* iterator = 0; + if (!iterator) { + UParseError parseStatus; + UErrorCode openStatus = U_ZERO_ERROR; + Vector rules; + String(breakRules).appendTo(rules); + + iterator = new icu::RuleBasedBreakIterator( + icu::UnicodeString(rules.data(), rules.size()), parseStatus, + openStatus); + ASSERT_WITH_MESSAGE(U_SUCCESS(openStatus), + "ICU could not open a break iterator: %s (%d)", + u_errorName(openStatus), openStatus); + if (!iterator) + return 0; + } + + setText16(iterator, string, length); + return iterator; +} + +TextBreakIterator* cursorMovementIterator(const UChar* string, int length) { + // This rule set is based on character-break iterator rules of ICU 4.0 + // . + // The major differences from the original ones are listed below: + // * Replaced '[\p{Grapheme_Cluster_Break = SpacingMark}]' with + // '[\p{General_Category = Spacing Mark} - $Extend]' for ICU 3.8 or earlier; + // * Removed rules that prevent a cursor from moving after prepend characters + // (Bug 24342); + // * Added rules that prevent a cursor from moving after virama signs of Indic + // languages except Tamil (Bug 15790), and; + // * Added rules that prevent a cursor from moving before Japanese half-width + // katakara voiced marks. + // * Added rules for regional indicator symbols. + static const char* const kRules = + "$CR = [\\p{Grapheme_Cluster_Break = CR}];" + "$LF = [\\p{Grapheme_Cluster_Break = LF}];" + "$Control = [\\p{Grapheme_Cluster_Break = Control}];" + "$VoiceMarks = [\\uFF9E\\uFF9F];" // Japanese half-width katakana voiced + // marks + "$Extend = [\\p{Grapheme_Cluster_Break = Extend} $VoiceMarks - [\\u0E30 " + "\\u0E32 \\u0E45 \\u0EB0 \\u0EB2]];" + "$SpacingMark = [[\\p{General_Category = Spacing Mark}] - $Extend];" + "$L = [\\p{Grapheme_Cluster_Break = L}];" + "$V = [\\p{Grapheme_Cluster_Break = V}];" + "$T = [\\p{Grapheme_Cluster_Break = T}];" + "$LV = [\\p{Grapheme_Cluster_Break = LV}];" + "$LVT = [\\p{Grapheme_Cluster_Break = LVT}];" + "$Hin0 = [\\u0905-\\u0939];" // Devanagari Letter A,...,Ha + "$HinV = \\u094D;" // Devanagari Sign Virama + "$Hin1 = [\\u0915-\\u0939];" // Devanagari Letter Ka,...,Ha + "$Ben0 = [\\u0985-\\u09B9];" // Bengali Letter A,...,Ha + "$BenV = \\u09CD;" // Bengali Sign Virama + "$Ben1 = [\\u0995-\\u09B9];" // Bengali Letter Ka,...,Ha + "$Pan0 = [\\u0A05-\\u0A39];" // Gurmukhi Letter A,...,Ha + "$PanV = \\u0A4D;" // Gurmukhi Sign Virama + "$Pan1 = [\\u0A15-\\u0A39];" // Gurmukhi Letter Ka,...,Ha + "$Guj0 = [\\u0A85-\\u0AB9];" // Gujarati Letter A,...,Ha + "$GujV = \\u0ACD;" // Gujarati Sign Virama + "$Guj1 = [\\u0A95-\\u0AB9];" // Gujarati Letter Ka,...,Ha + "$Ori0 = [\\u0B05-\\u0B39];" // Oriya Letter A,...,Ha + "$OriV = \\u0B4D;" // Oriya Sign Virama + "$Ori1 = [\\u0B15-\\u0B39];" // Oriya Letter Ka,...,Ha + "$Tel0 = [\\u0C05-\\u0C39];" // Telugu Letter A,...,Ha + "$TelV = \\u0C4D;" // Telugu Sign Virama + "$Tel1 = [\\u0C14-\\u0C39];" // Telugu Letter Ka,...,Ha + "$Kan0 = [\\u0C85-\\u0CB9];" // Kannada Letter A,...,Ha + "$KanV = \\u0CCD;" // Kannada Sign Virama + "$Kan1 = [\\u0C95-\\u0CB9];" // Kannada Letter A,...,Ha + "$Mal0 = [\\u0D05-\\u0D39];" // Malayalam Letter A,...,Ha + "$MalV = \\u0D4D;" // Malayalam Sign Virama + "$Mal1 = [\\u0D15-\\u0D39];" // Malayalam Letter A,...,Ha + "$RI = [\\U0001F1E6-\\U0001F1FF];" // Emoji regional indicators + "!!chain;" + "!!forward;" + "$CR $LF;" + "$L ($L | $V | $LV | $LVT);" + "($LV | $V) ($V | $T);" + "($LVT | $T) $T;" + "[^$Control $CR $LF] $Extend;" + "[^$Control $CR $LF] $SpacingMark;" + "$RI $RI / $RI;" + "$RI $RI;" + "$Hin0 $HinV $Hin1;" // Devanagari Virama (forward) + "$Ben0 $BenV $Ben1;" // Bengali Virama (forward) + "$Pan0 $PanV $Pan1;" // Gurmukhi Virama (forward) + "$Guj0 $GujV $Guj1;" // Gujarati Virama (forward) + "$Ori0 $OriV $Ori1;" // Oriya Virama (forward) + "$Tel0 $TelV $Tel1;" // Telugu Virama (forward) + "$Kan0 $KanV $Kan1;" // Kannada Virama (forward) + "$Mal0 $MalV $Mal1;" // Malayalam Virama (forward) + "!!reverse;" + "$LF $CR;" + "($L | $V | $LV | $LVT) $L;" + "($V | $T) ($LV | $V);" + "$T ($LVT | $T);" + "$Extend [^$Control $CR $LF];" + "$SpacingMark [^$Control $CR $LF];" + "$RI $RI / $RI $RI;" + "$RI $RI;" + "$Hin1 $HinV $Hin0;" // Devanagari Virama (backward) + "$Ben1 $BenV $Ben0;" // Bengali Virama (backward) + "$Pan1 $PanV $Pan0;" // Gurmukhi Virama (backward) + "$Guj1 $GujV $Guj0;" // Gujarati Virama (backward) + "$Ori1 $OriV $Ori0;" // Gujarati Virama (backward) + "$Tel1 $TelV $Tel0;" // Telugu Virama (backward) + "$Kan1 $KanV $Kan0;" // Kannada Virama (backward) + "$Mal1 $MalV $Mal0;" // Malayalam Virama (backward) + "!!safe_reverse;" + "!!safe_forward;"; + + return setUpIteratorWithRules(kRules, string, length); +} + +} // namespace blink diff --git a/sky/engine/platform/text/TextBreakIteratorInternalICU.cpp b/sky/engine/platform/text/TextBreakIteratorInternalICU.cpp index 748f648687f98..5bd08a315c33a 100644 --- a/sky/engine/platform/text/TextBreakIteratorInternalICU.cpp +++ b/sky/engine/platform/text/TextBreakIteratorInternalICU.cpp @@ -28,22 +28,19 @@ namespace blink { -static const char* UILanguage() -{ - // Chrome's UI language can be different from the OS UI language on Windows. - // We want to return Chrome's UI language here. - DEFINE_STATIC_LOCAL(const CString, locale, (defaultLanguage().latin1())); - return locale.data(); +static const char* UILanguage() { + // Chrome's UI language can be different from the OS UI language on Windows. + // We want to return Chrome's UI language here. + DEFINE_STATIC_LOCAL(const CString, locale, (defaultLanguage().latin1())); + return locale.data(); } -const char* currentSearchLocaleID() -{ - return UILanguage(); +const char* currentSearchLocaleID() { + return UILanguage(); } -const char* currentTextBreakLocaleID() -{ - return UILanguage(); +const char* currentTextBreakLocaleID() { + return UILanguage(); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/text/TextBreakIteratorInternalICU.h b/sky/engine/platform/text/TextBreakIteratorInternalICU.h index f9997c53a584a..a2a6eabd5c336 100644 --- a/sky/engine/platform/text/TextBreakIteratorInternalICU.h +++ b/sky/engine/platform/text/TextBreakIteratorInternalICU.h @@ -31,6 +31,6 @@ namespace blink { PLATFORM_EXPORT const char* currentSearchLocaleID(); PLATFORM_EXPORT const char* currentTextBreakLocaleID(); -} +} // namespace blink #endif // SKY_ENGINE_PLATFORM_TEXT_TEXTBREAKITERATORINTERNALICU_H_ diff --git a/sky/engine/platform/text/TextDecoration.h b/sky/engine/platform/text/TextDecoration.h index 1a2bb3ee402e8..e44426a50bbe5 100644 --- a/sky/engine/platform/text/TextDecoration.h +++ b/sky/engine/platform/text/TextDecoration.h @@ -34,11 +34,10 @@ namespace blink { enum TextDecorationType { - TextDecorationTypeSpelling = 1 << 1, - TextDecorationTypeGrammar = 1 << 2, - TextDecorationTypeInvisibleSpellcheck = 1 << 3, + TextDecorationTypeSpelling = 1 << 1, + TextDecorationTypeGrammar = 1 << 2, + TextDecorationTypeInvisibleSpellcheck = 1 << 3, }; - } #endif // SKY_ENGINE_PLATFORM_TEXT_TEXTDECORATION_H_ diff --git a/sky/engine/platform/text/TextDirection.h b/sky/engine/platform/text/TextDirection.h index d5873d83983ee..1e39c7628e443 100644 --- a/sky/engine/platform/text/TextDirection.h +++ b/sky/engine/platform/text/TextDirection.h @@ -28,11 +28,14 @@ namespace blink { -// The order of this enum must match the order of the values in dart:ui's TextDirection. +// The order of this enum must match the order of the values in dart:ui's +// TextDirection. enum TextDirection { RTL, LTR }; -inline bool isLeftToRightDirection(TextDirection direction) { return direction == LTR; } - +inline bool isLeftToRightDirection(TextDirection direction) { + return direction == LTR; } +} // namespace blink + #endif // SKY_ENGINE_PLATFORM_TEXT_TEXTDIRECTION_H_ diff --git a/sky/engine/platform/text/TextPath.h b/sky/engine/platform/text/TextPath.h index 73f584289f340..6320a8f93a5d8 100644 --- a/sky/engine/platform/text/TextPath.h +++ b/sky/engine/platform/text/TextPath.h @@ -33,8 +33,12 @@ namespace blink { -enum CodePath { AutoPath, SimplePath, ComplexPath, SimpleWithGlyphOverflowPath }; - +enum CodePath { + AutoPath, + SimplePath, + ComplexPath, + SimpleWithGlyphOverflowPath +}; } #endif // SKY_ENGINE_PLATFORM_TEXT_TEXTPATH_H_ diff --git a/sky/engine/platform/text/TextRun.cpp b/sky/engine/platform/text/TextRun.cpp index e987e5a10b7ea..1c2e2d22ce11c 100644 --- a/sky/engine/platform/text/TextRun.cpp +++ b/sky/engine/platform/text/TextRun.cpp @@ -28,31 +28,31 @@ namespace blink { struct ExpectedTextRunSize { - const void* pointer; - int integers[2]; - float float1; - float float2; - float float3; - uint32_t bitfields : 10; - unsigned anUnsigned; - RefPtr renderingContext; + const void* pointer; + int integers[2]; + float float1; + float float2; + float float3; + uint32_t bitfields : 10; + unsigned anUnsigned; + RefPtr renderingContext; }; -COMPILE_ASSERT(sizeof(TextRun) == sizeof(ExpectedTextRunSize), TextRun_is_not_of_expected_size); +COMPILE_ASSERT(sizeof(TextRun) == sizeof(ExpectedTextRunSize), + TextRun_is_not_of_expected_size); -void TextRun::setText(const String& string) -{ - m_len = string.length(); - if (!m_len) { - m_data.characters8 = 0; - m_is8Bit = true; - return; - } - m_is8Bit = string.is8Bit(); - if (m_is8Bit) - m_data.characters8 = string.characters8(); - else - m_data.characters16 = string.characters16(); +void TextRun::setText(const String& string) { + m_len = string.length(); + if (!m_len) { + m_data.characters8 = 0; + m_is8Bit = true; + return; + } + m_is8Bit = string.is8Bit(); + if (m_is8Bit) + m_data.characters8 = string.characters8(); + else + m_data.characters16 = string.characters16(); } -} +} // namespace blink diff --git a/sky/engine/platform/text/TextRun.h b/sky/engine/platform/text/TextRun.h index 1eb3ff9d4d21e..43b09d12ae77f 100644 --- a/sky/engine/platform/text/TextRun.h +++ b/sky/engine/platform/text/TextRun.h @@ -24,9 +24,9 @@ #ifndef SKY_ENGINE_PLATFORM_TEXT_TEXTRUN_H_ #define SKY_ENGINE_PLATFORM_TEXT_TEXTRUN_H_ +#include "flutter/sky/engine/platform/PlatformExport.h" #include "flutter/sky/engine/platform/fonts/Glyph.h" #include "flutter/sky/engine/platform/geometry/FloatRect.h" -#include "flutter/sky/engine/platform/PlatformExport.h" #include "flutter/sky/engine/platform/text/TextDirection.h" #include "flutter/sky/engine/platform/text/TextPath.h" #include "flutter/sky/engine/wtf/RefCounted.h" @@ -46,217 +46,293 @@ struct GlyphData; struct WidthIterator; class PLATFORM_EXPORT TextRun { - WTF_MAKE_FAST_ALLOCATED; -public: - enum ExpansionBehaviorFlags { - ForbidTrailingExpansion = 0 << 0, - AllowTrailingExpansion = 1 << 0, - ForbidLeadingExpansion = 0 << 1, - AllowLeadingExpansion = 1 << 1, - }; - - typedef unsigned ExpansionBehavior; - - TextRun(const LChar* c, unsigned len, float xpos = 0, float expansion = 0, ExpansionBehavior expansionBehavior = AllowTrailingExpansion | ForbidLeadingExpansion, TextDirection direction = LTR, bool directionalOverride = false, bool characterScanForCodePath = true) - : m_charactersLength(len) - , m_len(len) - , m_xpos(xpos) - , m_horizontalGlyphStretch(1) - , m_expansion(expansion) - , m_expansionBehavior(expansionBehavior) - , m_is8Bit(true) - , m_allowTabs(false) - , m_direction(direction) - , m_directionalOverride(directionalOverride) - , m_characterScanForCodePath(characterScanForCodePath) - , m_disableSpacing(false) - , m_tabSize(0) - { - m_data.characters8 = c; - } - - TextRun(const UChar* c, unsigned len, float xpos = 0, float expansion = 0, ExpansionBehavior expansionBehavior = AllowTrailingExpansion | ForbidLeadingExpansion, TextDirection direction = LTR, bool directionalOverride = false, bool characterScanForCodePath = true) - : m_charactersLength(len) - , m_len(len) - , m_xpos(xpos) - , m_horizontalGlyphStretch(1) - , m_expansion(expansion) - , m_expansionBehavior(expansionBehavior) - , m_is8Bit(false) - , m_allowTabs(false) - , m_direction(direction) - , m_directionalOverride(directionalOverride) - , m_characterScanForCodePath(characterScanForCodePath) - , m_disableSpacing(false) - , m_tabSize(0) - { - m_data.characters16 = c; + WTF_MAKE_FAST_ALLOCATED; + + public: + enum ExpansionBehaviorFlags { + ForbidTrailingExpansion = 0 << 0, + AllowTrailingExpansion = 1 << 0, + ForbidLeadingExpansion = 0 << 1, + AllowLeadingExpansion = 1 << 1, + }; + + typedef unsigned ExpansionBehavior; + + TextRun(const LChar* c, + unsigned len, + float xpos = 0, + float expansion = 0, + ExpansionBehavior expansionBehavior = AllowTrailingExpansion | + ForbidLeadingExpansion, + TextDirection direction = LTR, + bool directionalOverride = false, + bool characterScanForCodePath = true) + : m_charactersLength(len), + m_len(len), + m_xpos(xpos), + m_horizontalGlyphStretch(1), + m_expansion(expansion), + m_expansionBehavior(expansionBehavior), + m_is8Bit(true), + m_allowTabs(false), + m_direction(direction), + m_directionalOverride(directionalOverride), + m_characterScanForCodePath(characterScanForCodePath), + m_disableSpacing(false), + m_tabSize(0) { + m_data.characters8 = c; + } + + TextRun(const UChar* c, + unsigned len, + float xpos = 0, + float expansion = 0, + ExpansionBehavior expansionBehavior = AllowTrailingExpansion | + ForbidLeadingExpansion, + TextDirection direction = LTR, + bool directionalOverride = false, + bool characterScanForCodePath = true) + : m_charactersLength(len), + m_len(len), + m_xpos(xpos), + m_horizontalGlyphStretch(1), + m_expansion(expansion), + m_expansionBehavior(expansionBehavior), + m_is8Bit(false), + m_allowTabs(false), + m_direction(direction), + m_directionalOverride(directionalOverride), + m_characterScanForCodePath(characterScanForCodePath), + m_disableSpacing(false), + m_tabSize(0) { + m_data.characters16 = c; + } + + TextRun(const String& string, + float xpos = 0, + float expansion = 0, + ExpansionBehavior expansionBehavior = AllowTrailingExpansion | + ForbidLeadingExpansion, + TextDirection direction = LTR, + bool directionalOverride = false, + bool characterScanForCodePath = true) + : m_charactersLength(string.length()), + m_len(string.length()), + m_xpos(xpos), + m_horizontalGlyphStretch(1), + m_expansion(expansion), + m_expansionBehavior(expansionBehavior), + m_allowTabs(false), + m_direction(direction), + m_directionalOverride(directionalOverride), + m_characterScanForCodePath(characterScanForCodePath), + m_disableSpacing(false), + m_tabSize(0) { + if (!m_charactersLength) { + m_is8Bit = true; + m_data.characters8 = 0; + } else if (string.is8Bit()) { + m_data.characters8 = string.characters8(); + m_is8Bit = true; + } else { + m_data.characters16 = string.characters16(); + m_is8Bit = false; } - - TextRun(const String& string, float xpos = 0, float expansion = 0, ExpansionBehavior expansionBehavior = AllowTrailingExpansion | ForbidLeadingExpansion, TextDirection direction = LTR, bool directionalOverride = false, bool characterScanForCodePath = true) - : m_charactersLength(string.length()) - , m_len(string.length()) - , m_xpos(xpos) - , m_horizontalGlyphStretch(1) - , m_expansion(expansion) - , m_expansionBehavior(expansionBehavior) - , m_allowTabs(false) - , m_direction(direction) - , m_directionalOverride(directionalOverride) - , m_characterScanForCodePath(characterScanForCodePath) - , m_disableSpacing(false) - , m_tabSize(0) - { - if (!m_charactersLength) { - m_is8Bit = true; - m_data.characters8 = 0; - } else if (string.is8Bit()) { - m_data.characters8 = string.characters8(); - m_is8Bit = true; - } else { - m_data.characters16 = string.characters16(); - m_is8Bit = false; - } + } + + TextRun(const StringView& string, + float xpos = 0, + float expansion = 0, + ExpansionBehavior expansionBehavior = AllowTrailingExpansion | + ForbidLeadingExpansion, + TextDirection direction = LTR, + bool directionalOverride = false, + bool characterScanForCodePath = true) + : m_charactersLength(string.length()), + m_len(string.length()), + m_xpos(xpos), + m_horizontalGlyphStretch(1), + m_expansion(expansion), + m_expansionBehavior(expansionBehavior), + m_allowTabs(false), + m_direction(direction), + m_directionalOverride(directionalOverride), + m_characterScanForCodePath(characterScanForCodePath), + m_disableSpacing(false), + m_tabSize(0) { + if (!m_charactersLength) { + m_is8Bit = true; + m_data.characters8 = 0; + } else if (string.is8Bit()) { + m_data.characters8 = string.characters8(); + m_is8Bit = true; + } else { + m_data.characters16 = string.characters16(); + m_is8Bit = false; } + } - TextRun(const StringView& string, float xpos = 0, float expansion = 0, ExpansionBehavior expansionBehavior = AllowTrailingExpansion | ForbidLeadingExpansion, TextDirection direction = LTR, bool directionalOverride = false, bool characterScanForCodePath = true) - : m_charactersLength(string.length()) - , m_len(string.length()) - , m_xpos(xpos) - , m_horizontalGlyphStretch(1) - , m_expansion(expansion) - , m_expansionBehavior(expansionBehavior) - , m_allowTabs(false) - , m_direction(direction) - , m_directionalOverride(directionalOverride) - , m_characterScanForCodePath(characterScanForCodePath) - , m_disableSpacing(false) - , m_tabSize(0) - { - if (!m_charactersLength) { - m_is8Bit = true; - m_data.characters8 = 0; - } else if (string.is8Bit()) { - m_data.characters8 = string.characters8(); - m_is8Bit = true; - } else { - m_data.characters16 = string.characters16(); - m_is8Bit = false; - } - } + TextRun subRun(unsigned startOffset, unsigned length) const { + ASSERT(startOffset < m_len); - TextRun subRun(unsigned startOffset, unsigned length) const - { - ASSERT(startOffset < m_len); + TextRun result = *this; - TextRun result = *this; - - if (is8Bit()) { - result.setText(data8(startOffset), length); - return result; - } - result.setText(data16(startOffset), length); - return result; + if (is8Bit()) { + result.setText(data8(startOffset), length); + return result; } - - UChar operator[](unsigned i) const { ASSERT_WITH_SECURITY_IMPLICATION(i < m_len); return is8Bit() ? m_data.characters8[i] :m_data.characters16[i]; } - const LChar* data8(unsigned i) const { ASSERT_WITH_SECURITY_IMPLICATION(i < m_len); ASSERT(is8Bit()); return &m_data.characters8[i]; } - const UChar* data16(unsigned i) const { ASSERT_WITH_SECURITY_IMPLICATION(i < m_len); ASSERT(!is8Bit()); return &m_data.characters16[i]; } - - const LChar* characters8() const { ASSERT(is8Bit()); return m_data.characters8; } - const UChar* characters16() const { ASSERT(!is8Bit()); return m_data.characters16; } - - bool is8Bit() const { return m_is8Bit; } - int length() const { return m_len; } - int charactersLength() const { return m_charactersLength; } - - void setText(const LChar* c, unsigned len) { m_data.characters8 = c; m_len = len; m_is8Bit = true;} - void setText(const UChar* c, unsigned len) { m_data.characters16 = c; m_len = len; m_is8Bit = false;} - void setText(const String&); - void setCharactersLength(unsigned charactersLength) { m_charactersLength = charactersLength; } - - float horizontalGlyphStretch() const { return m_horizontalGlyphStretch; } - void setHorizontalGlyphStretch(float scale) { m_horizontalGlyphStretch = scale; } - - bool allowTabs() const { return m_allowTabs; } - unsigned tabSize() const { return m_tabSize; } - void setTabSize(bool, unsigned); - - float xPos() const { return m_xpos; } - void setXPos(float xPos) { m_xpos = xPos; } - float expansion() const { return m_expansion; } - bool allowsLeadingExpansion() const { return m_expansionBehavior & AllowLeadingExpansion; } - bool allowsTrailingExpansion() const { return m_expansionBehavior & AllowTrailingExpansion; } - TextDirection direction() const { return static_cast(m_direction); } - bool rtl() const { return m_direction == RTL; } - bool ltr() const { return m_direction == LTR; } - bool directionalOverride() const { return m_directionalOverride; } - bool characterScanForCodePath() const { return m_characterScanForCodePath; } - bool spacingDisabled() const { return m_disableSpacing; } - - void disableSpacing() { m_disableSpacing = true; } - void setDirection(TextDirection direction) { m_direction = direction; } - void setDirectionalOverride(bool override) { m_directionalOverride = override; } - void setCharacterScanForCodePath(bool scan) { m_characterScanForCodePath = scan; } - - class RenderingContext : public RefCounted { - public: - virtual ~RenderingContext() { } - - virtual GlyphData glyphDataForCharacter(const Font&, const TextRun&, WidthIterator&, UChar32 character, bool mirror, int currentCharacter, unsigned& advanceLength) = 0; - virtual float floatWidthUsingSVGFont(const Font&, const TextRun&, int& charsConsumed, Glyph& glyphId) const = 0; - }; - - RenderingContext* renderingContext() const { return m_renderingContext.get(); } - void setRenderingContext(PassRefPtr context) { m_renderingContext = context; } - -private: - union { - const LChar* characters8; - const UChar* characters16; - } m_data; - unsigned m_charactersLength; // Marks the end of the characters buffer. Default equals to m_len. - unsigned m_len; - - // m_xpos is the x position relative to the left start of the text line, not relative to the left - // start of the containing block. In the case of right alignment or center alignment, left start of - // the text line is not the same as left start of the containing block. - float m_xpos; - float m_horizontalGlyphStretch; - - float m_expansion; - ExpansionBehavior m_expansionBehavior : 2; - unsigned m_is8Bit : 1; - unsigned m_allowTabs : 1; - unsigned m_direction : 1; - unsigned m_directionalOverride : 1; // Was this direction set by an override character. - unsigned m_characterScanForCodePath : 1; - unsigned m_disableSpacing : 1; - unsigned m_tabSize; - RefPtr m_renderingContext; + result.setText(data16(startOffset), length); + return result; + } + + UChar operator[](unsigned i) const { + ASSERT_WITH_SECURITY_IMPLICATION(i < m_len); + return is8Bit() ? m_data.characters8[i] : m_data.characters16[i]; + } + const LChar* data8(unsigned i) const { + ASSERT_WITH_SECURITY_IMPLICATION(i < m_len); + ASSERT(is8Bit()); + return &m_data.characters8[i]; + } + const UChar* data16(unsigned i) const { + ASSERT_WITH_SECURITY_IMPLICATION(i < m_len); + ASSERT(!is8Bit()); + return &m_data.characters16[i]; + } + + const LChar* characters8() const { + ASSERT(is8Bit()); + return m_data.characters8; + } + const UChar* characters16() const { + ASSERT(!is8Bit()); + return m_data.characters16; + } + + bool is8Bit() const { return m_is8Bit; } + int length() const { return m_len; } + int charactersLength() const { return m_charactersLength; } + + void setText(const LChar* c, unsigned len) { + m_data.characters8 = c; + m_len = len; + m_is8Bit = true; + } + void setText(const UChar* c, unsigned len) { + m_data.characters16 = c; + m_len = len; + m_is8Bit = false; + } + void setText(const String&); + void setCharactersLength(unsigned charactersLength) { + m_charactersLength = charactersLength; + } + + float horizontalGlyphStretch() const { return m_horizontalGlyphStretch; } + void setHorizontalGlyphStretch(float scale) { + m_horizontalGlyphStretch = scale; + } + + bool allowTabs() const { return m_allowTabs; } + unsigned tabSize() const { return m_tabSize; } + void setTabSize(bool, unsigned); + + float xPos() const { return m_xpos; } + void setXPos(float xPos) { m_xpos = xPos; } + float expansion() const { return m_expansion; } + bool allowsLeadingExpansion() const { + return m_expansionBehavior & AllowLeadingExpansion; + } + bool allowsTrailingExpansion() const { + return m_expansionBehavior & AllowTrailingExpansion; + } + TextDirection direction() const { + return static_cast(m_direction); + } + bool rtl() const { return m_direction == RTL; } + bool ltr() const { return m_direction == LTR; } + bool directionalOverride() const { return m_directionalOverride; } + bool characterScanForCodePath() const { return m_characterScanForCodePath; } + bool spacingDisabled() const { return m_disableSpacing; } + + void disableSpacing() { m_disableSpacing = true; } + void setDirection(TextDirection direction) { m_direction = direction; } + void setDirectionalOverride(bool override) { + m_directionalOverride = override; + } + void setCharacterScanForCodePath(bool scan) { + m_characterScanForCodePath = scan; + } + + class RenderingContext : public RefCounted { + public: + virtual ~RenderingContext() {} + + virtual GlyphData glyphDataForCharacter(const Font&, + const TextRun&, + WidthIterator&, + UChar32 character, + bool mirror, + int currentCharacter, + unsigned& advanceLength) = 0; + virtual float floatWidthUsingSVGFont(const Font&, + const TextRun&, + int& charsConsumed, + Glyph& glyphId) const = 0; + }; + + RenderingContext* renderingContext() const { + return m_renderingContext.get(); + } + void setRenderingContext(PassRefPtr context) { + m_renderingContext = context; + } + + private: + union { + const LChar* characters8; + const UChar* characters16; + } m_data; + unsigned m_charactersLength; // Marks the end of the characters buffer. + // Default equals to m_len. + unsigned m_len; + + // m_xpos is the x position relative to the left start of the text line, not + // relative to the left start of the containing block. In the case of right + // alignment or center alignment, left start of the text line is not the same + // as left start of the containing block. + float m_xpos; + float m_horizontalGlyphStretch; + + float m_expansion; + ExpansionBehavior m_expansionBehavior : 2; + unsigned m_is8Bit : 1; + unsigned m_allowTabs : 1; + unsigned m_direction : 1; + unsigned m_directionalOverride : 1; // Was this direction set by an override + // character. + unsigned m_characterScanForCodePath : 1; + unsigned m_disableSpacing : 1; + unsigned m_tabSize; + RefPtr m_renderingContext; }; -inline void TextRun::setTabSize(bool allow, unsigned size) -{ - m_allowTabs = allow; - m_tabSize = size; +inline void TextRun::setTabSize(bool allow, unsigned size) { + m_allowTabs = allow; + m_tabSize = size; } // Container for parameters needed to paint TextRun. struct TextRunPaintInfo { - explicit TextRunPaintInfo(const TextRun& r) - : run(r) - , from(0) - , to(r.length()) - { - } - - const TextRun& run; - int from; - int to; - FloatRect bounds; - sk_sp* cachedTextBlob; + explicit TextRunPaintInfo(const TextRun& r) + : run(r), from(0), to(r.length()) {} + + const TextRun& run; + int from; + int to; + FloatRect bounds; + sk_sp* cachedTextBlob; }; -} +} // namespace blink #endif // SKY_ENGINE_PLATFORM_TEXT_TEXTRUN_H_ diff --git a/sky/engine/platform/text/TextRunIterator.h b/sky/engine/platform/text/TextRunIterator.h index 884f6a2896816..216824a36fadb 100644 --- a/sky/engine/platform/text/TextRunIterator.h +++ b/sky/engine/platform/text/TextRunIterator.h @@ -34,45 +34,36 @@ namespace blink { class TextRunIterator { -public: - TextRunIterator() - : m_textRun(0) - , m_offset(0) - { - } + public: + TextRunIterator() : m_textRun(0), m_offset(0) {} - TextRunIterator(const TextRun* textRun, unsigned offset) - : m_textRun(textRun) - , m_offset(offset) - { - } + TextRunIterator(const TextRun* textRun, unsigned offset) + : m_textRun(textRun), m_offset(offset) {} - TextRunIterator(const TextRunIterator& other) - : m_textRun(other.m_textRun) - , m_offset(other.m_offset) - { - } + TextRunIterator(const TextRunIterator& other) + : m_textRun(other.m_textRun), m_offset(other.m_offset) {} - unsigned offset() const { return m_offset; } - void increment() { m_offset++; } - bool atEnd() const { return !m_textRun || m_offset >= m_textRun->length(); } - UChar current() const { return (*m_textRun)[m_offset]; } - WTF::Unicode::Direction direction() const { return atEnd() ? WTF::Unicode::OtherNeutral : WTF::Unicode::direction(current()); } - bool atParagraphSeparator() const { return current() == '\n'; } + unsigned offset() const { return m_offset; } + void increment() { m_offset++; } + bool atEnd() const { return !m_textRun || m_offset >= m_textRun->length(); } + UChar current() const { return (*m_textRun)[m_offset]; } + WTF::Unicode::Direction direction() const { + return atEnd() ? WTF::Unicode::OtherNeutral + : WTF::Unicode::direction(current()); + } + bool atParagraphSeparator() const { return current() == '\n'; } - bool operator==(const TextRunIterator& other) - { - return m_offset == other.m_offset && m_textRun == other.m_textRun; - } + bool operator==(const TextRunIterator& other) { + return m_offset == other.m_offset && m_textRun == other.m_textRun; + } - bool operator!=(const TextRunIterator& other) { return !operator==(other); } + bool operator!=(const TextRunIterator& other) { return !operator==(other); } -private: - const TextRun* m_textRun; - int m_offset; + private: + const TextRun* m_textRun; + int m_offset; }; - -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_TEXT_TEXTRUNITERATOR_H_ diff --git a/sky/engine/platform/text/TextStream.cpp b/sky/engine/platform/text/TextStream.cpp index 912a5cfb68e06..deaaafc01540d 100644 --- a/sky/engine/platform/text/TextStream.cpp +++ b/sky/engine/platform/text/TextStream.cpp @@ -36,142 +36,125 @@ namespace blink { -static const size_t printBufferSize = 100; // large enough for any integer or floating point value in string format, including trailing null character +static const size_t printBufferSize = + 100; // large enough for any integer or floating point value in string + // format, including trailing null character -static inline bool hasFractions(double val) -{ - static const double s_epsilon = 0.0001; - int ival = static_cast(val); - double dval = static_cast(ival); - return fabs(val - dval) > s_epsilon; +static inline bool hasFractions(double val) { + static const double s_epsilon = 0.0001; + int ival = static_cast(val); + double dval = static_cast(ival); + return fabs(val - dval) > s_epsilon; } -TextStream& TextStream::operator<<(bool b) -{ - return *this << (b ? "1" : "0"); +TextStream& TextStream::operator<<(bool b) { + return *this << (b ? "1" : "0"); } -TextStream& TextStream::operator<<(int i) -{ - m_text.appendNumber(i); - return *this; +TextStream& TextStream::operator<<(int i) { + m_text.appendNumber(i); + return *this; } -TextStream& TextStream::operator<<(unsigned i) -{ - m_text.appendNumber(i); - return *this; +TextStream& TextStream::operator<<(unsigned i) { + m_text.appendNumber(i); + return *this; } -TextStream& TextStream::operator<<(long i) -{ - m_text.appendNumber(i); - return *this; +TextStream& TextStream::operator<<(long i) { + m_text.appendNumber(i); + return *this; } -TextStream& TextStream::operator<<(unsigned long i) -{ - m_text.appendNumber(i); - return *this; +TextStream& TextStream::operator<<(unsigned long i) { + m_text.appendNumber(i); + return *this; } -TextStream& TextStream::operator<<(long long i) -{ - m_text.appendNumber(i); - return *this; +TextStream& TextStream::operator<<(long long i) { + m_text.appendNumber(i); + return *this; } -TextStream& TextStream::operator<<(unsigned long long i) -{ - m_text.appendNumber(i); - return *this; +TextStream& TextStream::operator<<(unsigned long long i) { + m_text.appendNumber(i); + return *this; } -TextStream& TextStream::operator<<(float f) -{ - m_text.append(String::numberToStringFixedWidth(f, 2)); - return *this; +TextStream& TextStream::operator<<(float f) { + m_text.append(String::numberToStringFixedWidth(f, 2)); + return *this; } -TextStream& TextStream::operator<<(double d) -{ - m_text.append(String::numberToStringFixedWidth(d, 2)); - return *this; +TextStream& TextStream::operator<<(double d) { + m_text.append(String::numberToStringFixedWidth(d, 2)); + return *this; } -TextStream& TextStream::operator<<(const char* string) -{ - m_text.append(string); - return *this; +TextStream& TextStream::operator<<(const char* string) { + m_text.append(string); + return *this; } -TextStream& TextStream::operator<<(const void* p) -{ - char buffer[printBufferSize]; - snprintf(buffer, sizeof(buffer) - 1, "%p", p); - return *this << buffer; +TextStream& TextStream::operator<<(const void* p) { + char buffer[printBufferSize]; + snprintf(buffer, sizeof(buffer) - 1, "%p", p); + return *this << buffer; } -TextStream& TextStream::operator<<(const String& string) -{ - m_text.append(string); - return *this; +TextStream& TextStream::operator<<(const String& string) { + m_text.append(string); + return *this; } -TextStream& TextStream::operator<<(const FormatNumberRespectingIntegers& numberToFormat) -{ - if (hasFractions(numberToFormat.value)) - return *this << numberToFormat.value; +TextStream& TextStream::operator<<( + const FormatNumberRespectingIntegers& numberToFormat) { + if (hasFractions(numberToFormat.value)) + return *this << numberToFormat.value; - m_text.appendNumber(static_cast(numberToFormat.value)); - return *this; + m_text.appendNumber(static_cast(numberToFormat.value)); + return *this; } -String TextStream::release() -{ - String result = m_text.toString(); - m_text.clear(); - return result; +String TextStream::release() { + String result = m_text.toString(); + m_text.clear(); + return result; } -TextStream& operator<<(TextStream& ts, const IntRect& r) -{ - return ts << "at (" << r.x() << "," << r.y() << ") size " << r.width() << "x" << r.height(); +TextStream& operator<<(TextStream& ts, const IntRect& r) { + return ts << "at (" << r.x() << "," << r.y() << ") size " << r.width() << "x" + << r.height(); } -TextStream& operator<<(TextStream& ts, const IntPoint& p) -{ - return ts << "(" << p.x() << "," << p.y() << ")"; +TextStream& operator<<(TextStream& ts, const IntPoint& p) { + return ts << "(" << p.x() << "," << p.y() << ")"; } -TextStream& operator<<(TextStream& ts, const FloatPoint& p) -{ - ts << "(" << TextStream::FormatNumberRespectingIntegers(p.x()); - ts << "," << TextStream::FormatNumberRespectingIntegers(p.y()); - ts << ")"; - return ts; +TextStream& operator<<(TextStream& ts, const FloatPoint& p) { + ts << "(" << TextStream::FormatNumberRespectingIntegers(p.x()); + ts << "," << TextStream::FormatNumberRespectingIntegers(p.y()); + ts << ")"; + return ts; } -TextStream& operator<<(TextStream& ts, const FloatSize& s) -{ - ts << "width=" << TextStream::FormatNumberRespectingIntegers(s.width()); - ts << " height=" << TextStream::FormatNumberRespectingIntegers(s.height()); - return ts; +TextStream& operator<<(TextStream& ts, const FloatSize& s) { + ts << "width=" << TextStream::FormatNumberRespectingIntegers(s.width()); + ts << " height=" << TextStream::FormatNumberRespectingIntegers(s.height()); + return ts; } -TextStream& operator<<(TextStream& ts, const FloatRect& r) -{ - ts << "at (" << TextStream::FormatNumberRespectingIntegers(r.x()); - ts << "," << TextStream::FormatNumberRespectingIntegers(r.y()); - ts << ") size " << TextStream::FormatNumberRespectingIntegers(r.width()); - ts << "x" << TextStream::FormatNumberRespectingIntegers(r.height()); - return ts; +TextStream& operator<<(TextStream& ts, const FloatRect& r) { + ts << "at (" << TextStream::FormatNumberRespectingIntegers(r.x()); + ts << "," << TextStream::FormatNumberRespectingIntegers(r.y()); + ts << ") size " << TextStream::FormatNumberRespectingIntegers(r.width()); + ts << "x" << TextStream::FormatNumberRespectingIntegers(r.height()); + return ts; } -void writeIndent(TextStream& ts, int indent) -{ - for (int i = 0; i != indent; ++i) - ts << " "; +void writeIndent(TextStream& ts, int indent) { + for (int i = 0; i != indent; ++i) + ts << " "; } -} +} // namespace blink diff --git a/sky/engine/platform/text/TextStream.h b/sky/engine/platform/text/TextStream.h index ac4295fe8ec6c..e718358231035 100644 --- a/sky/engine/platform/text/TextStream.h +++ b/sky/engine/platform/text/TextStream.h @@ -41,30 +41,30 @@ class FloatRect; class FloatSize; class PLATFORM_EXPORT TextStream { -public: - struct FormatNumberRespectingIntegers { - FormatNumberRespectingIntegers(double number) : value(number) { } - double value; - }; + public: + struct FormatNumberRespectingIntegers { + FormatNumberRespectingIntegers(double number) : value(number) {} + double value; + }; - TextStream& operator<<(bool); - TextStream& operator<<(int); - TextStream& operator<<(unsigned); - TextStream& operator<<(long); - TextStream& operator<<(unsigned long); - TextStream& operator<<(long long); - TextStream& operator<<(unsigned long long); - TextStream& operator<<(float); - TextStream& operator<<(double); - TextStream& operator<<(const char*); - TextStream& operator<<(const void*); - TextStream& operator<<(const String&); - TextStream& operator<<(const FormatNumberRespectingIntegers&); + TextStream& operator<<(bool); + TextStream& operator<<(int); + TextStream& operator<<(unsigned); + TextStream& operator<<(long); + TextStream& operator<<(unsigned long); + TextStream& operator<<(long long); + TextStream& operator<<(unsigned long long); + TextStream& operator<<(float); + TextStream& operator<<(double); + TextStream& operator<<(const char*); + TextStream& operator<<(const void*); + TextStream& operator<<(const String&); + TextStream& operator<<(const FormatNumberRespectingIntegers&); - String release(); + String release(); -private: - StringBuilder m_text; + private: + StringBuilder m_text; }; PLATFORM_EXPORT TextStream& operator<<(TextStream&, const IntPoint&); @@ -75,22 +75,21 @@ PLATFORM_EXPORT TextStream& operator<<(TextStream&, const FloatRect&); PLATFORM_EXPORT void writeIndent(TextStream&, int indent); -template -TextStream& operator<<(TextStream& ts, const Vector& vector) -{ - ts << "["; +template +TextStream& operator<<(TextStream& ts, const Vector& vector) { + ts << "["; - unsigned size = vector.size(); - for (unsigned i = 0; i < size; ++i) { - ts << vector[i]; - if (i < size - 1) - ts << ", "; - } + unsigned size = vector.size(); + for (unsigned i = 0; i < size; ++i) { + ts << vector[i]; + if (i < size - 1) + ts << ", "; + } - ts << "]"; - return ts; + ts << "]"; + return ts; } -} +} // namespace blink #endif // SKY_ENGINE_PLATFORM_TEXT_TEXTSTREAM_H_ diff --git a/sky/engine/platform/text/UnicodeBidi.h b/sky/engine/platform/text/UnicodeBidi.h index 90f530cc6249c..20c5d32d7fe23 100644 --- a/sky/engine/platform/text/UnicodeBidi.h +++ b/sky/engine/platform/text/UnicodeBidi.h @@ -29,24 +29,23 @@ namespace blink { enum EUnicodeBidi { - UBNormal, - Embed, - Override, - Isolate, - Plaintext, - IsolateOverride, + UBNormal, + Embed, + Override, + Isolate, + Plaintext, + IsolateOverride, }; -inline bool isIsolated(const EUnicodeBidi& unicodeBidi) -{ - return unicodeBidi == Isolate || unicodeBidi == IsolateOverride || unicodeBidi == Plaintext; +inline bool isIsolated(const EUnicodeBidi& unicodeBidi) { + return unicodeBidi == Isolate || unicodeBidi == IsolateOverride || + unicodeBidi == Plaintext; } -inline bool isOverride(EUnicodeBidi unicodeBidi) -{ - return unicodeBidi == Override || unicodeBidi == IsolateOverride; +inline bool isOverride(EUnicodeBidi unicodeBidi) { + return unicodeBidi == Override || unicodeBidi == IsolateOverride; } -} +} // namespace blink #endif // SKY_ENGINE_PLATFORM_TEXT_UNICODEBIDI_H_ diff --git a/sky/engine/platform/text/UnicodeRange.cpp b/sky/engine/platform/text/UnicodeRange.cpp index 7565062f20f31..ff9f9b01baec9 100644 --- a/sky/engine/platform/text/UnicodeRange.cpp +++ b/sky/engine/platform/text/UnicodeRange.cpp @@ -15,7 +15,7 @@ * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Alternatively, the contents of this file may be used under the terms * of either the Mozilla Public License Version 1.1, found at @@ -39,31 +39,11 @@ namespace blink { // This table depends on unicode range definitions. // Each item's index must correspond to a unicode range value // eg. x-cyrillic = LangGroupTable[cRangeCyrillic] -static const char* const gUnicodeRangeToLangGroupTable[] = -{ - "x-cyrillic", - "el", - "tr", - "he", - "ar", - "x-baltic", - "th", - "ko", - "ja", - "zh-CN", - "zh-TW", - "x-devanagari", - "x-tamil", - "x-armn", - "x-beng", - "x-cans", - "x-ethi", - "x-geor", - "x-gujr", - "x-guru", - "x-khmr", - "x-mlym" -}; +static const char* const gUnicodeRangeToLangGroupTable[] = { + "x-cyrillic", "el", "tr", "he", "ar", "x-baltic", + "th", "ko", "ja", "zh-CN", "zh-TW", "x-devanagari", + "x-tamil", "x-armn", "x-beng", "x-cans", "x-ethi", "x-geor", + "x-gujr", "x-guru", "x-khmr", "x-mlym"}; /********************************************************************** * Unicode subranges as defined in unicode 3.0 @@ -207,172 +187,184 @@ static const unsigned cNumSubTables = 9; static const unsigned cSubTableSize = 16; static const unsigned char gUnicodeSubrangeTable[cNumSubTables][cSubTableSize] = -{ - { // table for X--- - cRangeTableBase+1, //u0xxx - cRangeTableBase+2, //u1xxx - cRangeTableBase+3, //u2xxx - cRangeSetCJK, //u3xxx - cRangeSetCJK, //u4xxx - cRangeSetCJK, //u5xxx - cRangeSetCJK, //u6xxx - cRangeSetCJK, //u7xxx - cRangeSetCJK, //u8xxx - cRangeSetCJK, //u9xxx - cRangeTableBase+4, //uaxxx - cRangeKorean, //ubxxx - cRangeKorean, //ucxxx - cRangeTableBase+5, //udxxx - cRangePrivate, //uexxx - cRangeTableBase+6 //ufxxx - }, - { //table for 0X-- - cRangeSetLatin, //u00xx - cRangeSetLatin, //u01xx - cRangeSetLatin, //u02xx - cRangeGreek, //u03xx XXX 0300-036f is in fact cRangeCombiningDiacriticalMarks - cRangeCyrillic, //u04xx - cRangeTableBase+7, //u05xx, includes Cyrillic supplement, Hebrew, and Armenian - cRangeArabic, //u06xx - cRangeTertiaryTable, //u07xx - cRangeUnassigned, //u08xx - cRangeTertiaryTable, //u09xx - cRangeTertiaryTable, //u0axx - cRangeTertiaryTable, //u0bxx - cRangeTertiaryTable, //u0cxx - cRangeTertiaryTable, //u0dxx - cRangeTertiaryTable, //u0exx - cRangeTibetan, //u0fxx - }, - { //table for 1x-- - cRangeTertiaryTable, //u10xx - cRangeKorean, //u11xx - cRangeEthiopic, //u12xx - cRangeTertiaryTable, //u13xx - cRangeCanadian, //u14xx - cRangeCanadian, //u15xx - cRangeTertiaryTable, //u16xx - cRangeKhmer, //u17xx - cRangeMongolian, //u18xx - cRangeUnassigned, //u19xx - cRangeUnassigned, //u1axx - cRangeUnassigned, //u1bxx - cRangeUnassigned, //u1cxx - cRangeUnassigned, //u1dxx - cRangeSetLatin, //u1exx - cRangeGreek, //u1fxx - }, - { //table for 2x-- - cRangeSetLatin, //u20xx - cRangeSetLatin, //u21xx - cRangeMathOperators, //u22xx - cRangeMiscTechnical, //u23xx - cRangeControlOpticalEnclose, //u24xx - cRangeBoxBlockGeometrics, //u25xx - cRangeMiscSymbols, //u26xx - cRangeDingbats, //u27xx - cRangeBraillePattern, //u28xx - cRangeUnassigned, //u29xx - cRangeUnassigned, //u2axx - cRangeUnassigned, //u2bxx - cRangeUnassigned, //u2cxx - cRangeUnassigned, //u2dxx - cRangeSetCJK, //u2exx - cRangeSetCJK, //u2fxx - }, - { //table for ax-- - cRangeYi, //ua0xx - cRangeYi, //ua1xx - cRangeYi, //ua2xx - cRangeYi, //ua3xx - cRangeYi, //ua4xx - cRangeUnassigned, //ua5xx - cRangeUnassigned, //ua6xx - cRangeUnassigned, //ua7xx - cRangeUnassigned, //ua8xx - cRangeUnassigned, //ua9xx - cRangeUnassigned, //uaaxx - cRangeUnassigned, //uabxx - cRangeKorean, //uacxx - cRangeKorean, //uadxx - cRangeKorean, //uaexx - cRangeKorean, //uafxx - }, - { //table for dx-- - cRangeKorean, //ud0xx - cRangeKorean, //ud1xx - cRangeKorean, //ud2xx - cRangeKorean, //ud3xx - cRangeKorean, //ud4xx - cRangeKorean, //ud5xx - cRangeKorean, //ud6xx - cRangeKorean, //ud7xx - cRangeSurrogate, //ud8xx - cRangeSurrogate, //ud9xx - cRangeSurrogate, //udaxx - cRangeSurrogate, //udbxx - cRangeSurrogate, //udcxx - cRangeSurrogate, //uddxx - cRangeSurrogate, //udexx - cRangeSurrogate, //udfxx - }, - { // table for fx-- - cRangePrivate, //uf0xx - cRangePrivate, //uf1xx - cRangePrivate, //uf2xx - cRangePrivate, //uf3xx - cRangePrivate, //uf4xx - cRangePrivate, //uf5xx - cRangePrivate, //uf6xx - cRangePrivate, //uf7xx - cRangePrivate, //uf8xx - cRangeSetCJK, //uf9xx - cRangeSetCJK, //ufaxx - cRangeArabic, //ufbxx, includes alphabic presentation form - cRangeArabic, //ufcxx - cRangeArabic, //ufdxx - cRangeArabic, //ufexx, includes Combining half marks, - // CJK compatibility forms, - // CJK compatibility forms, - // small form variants - cRangeTableBase+8, //uffxx, halfwidth and fullwidth forms, includes Specials - }, - { //table for 0x0500 - 0x05ff - cRangeCyrillic, //u050x - cRangeCyrillic, //u051x - cRangeCyrillic, //u052x - cRangeArmenian, //u053x - cRangeArmenian, //u054x - cRangeArmenian, //u055x - cRangeArmenian, //u056x - cRangeArmenian, //u057x - cRangeArmenian, //u058x - cRangeHebrew, //u059x - cRangeHebrew, //u05ax - cRangeHebrew, //u05bx - cRangeHebrew, //u05cx - cRangeHebrew, //u05dx - cRangeHebrew, //u05ex - cRangeHebrew, //u05fx - }, - { //table for 0xff00 - 0xffff - cRangeSetCJK, //uff0x, fullwidth latin - cRangeSetCJK, //uff1x, fullwidth latin - cRangeSetCJK, //uff2x, fullwidth latin - cRangeSetCJK, //uff3x, fullwidth latin - cRangeSetCJK, //uff4x, fullwidth latin - cRangeSetCJK, //uff5x, fullwidth latin - cRangeSetCJK, //uff6x, halfwidth katakana - cRangeSetCJK, //uff7x, halfwidth katakana - cRangeSetCJK, //uff8x, halfwidth katakana - cRangeSetCJK, //uff9x, halfwidth katakana - cRangeSetCJK, //uffax, halfwidth hangul jamo - cRangeSetCJK, //uffbx, halfwidth hangul jamo - cRangeSetCJK, //uffcx, halfwidth hangul jamo - cRangeSetCJK, //uffdx, halfwidth hangul jamo - cRangeSetCJK, //uffex, fullwidth symbols - cRangeSpecials, //ufffx, Specials - }, + { + { + // table for X--- + cRangeTableBase + 1, // u0xxx + cRangeTableBase + 2, // u1xxx + cRangeTableBase + 3, // u2xxx + cRangeSetCJK, // u3xxx + cRangeSetCJK, // u4xxx + cRangeSetCJK, // u5xxx + cRangeSetCJK, // u6xxx + cRangeSetCJK, // u7xxx + cRangeSetCJK, // u8xxx + cRangeSetCJK, // u9xxx + cRangeTableBase + 4, // uaxxx + cRangeKorean, // ubxxx + cRangeKorean, // ucxxx + cRangeTableBase + 5, // udxxx + cRangePrivate, // uexxx + cRangeTableBase + 6 // ufxxx + }, + { + // table for 0X-- + cRangeSetLatin, // u00xx + cRangeSetLatin, // u01xx + cRangeSetLatin, // u02xx + cRangeGreek, // u03xx XXX 0300-036f is in fact + // cRangeCombiningDiacriticalMarks + cRangeCyrillic, // u04xx + cRangeTableBase + + 7, // u05xx, includes Cyrillic supplement, Hebrew, and Armenian + cRangeArabic, // u06xx + cRangeTertiaryTable, // u07xx + cRangeUnassigned, // u08xx + cRangeTertiaryTable, // u09xx + cRangeTertiaryTable, // u0axx + cRangeTertiaryTable, // u0bxx + cRangeTertiaryTable, // u0cxx + cRangeTertiaryTable, // u0dxx + cRangeTertiaryTable, // u0exx + cRangeTibetan, // u0fxx + }, + { + // table for 1x-- + cRangeTertiaryTable, // u10xx + cRangeKorean, // u11xx + cRangeEthiopic, // u12xx + cRangeTertiaryTable, // u13xx + cRangeCanadian, // u14xx + cRangeCanadian, // u15xx + cRangeTertiaryTable, // u16xx + cRangeKhmer, // u17xx + cRangeMongolian, // u18xx + cRangeUnassigned, // u19xx + cRangeUnassigned, // u1axx + cRangeUnassigned, // u1bxx + cRangeUnassigned, // u1cxx + cRangeUnassigned, // u1dxx + cRangeSetLatin, // u1exx + cRangeGreek, // u1fxx + }, + { + // table for 2x-- + cRangeSetLatin, // u20xx + cRangeSetLatin, // u21xx + cRangeMathOperators, // u22xx + cRangeMiscTechnical, // u23xx + cRangeControlOpticalEnclose, // u24xx + cRangeBoxBlockGeometrics, // u25xx + cRangeMiscSymbols, // u26xx + cRangeDingbats, // u27xx + cRangeBraillePattern, // u28xx + cRangeUnassigned, // u29xx + cRangeUnassigned, // u2axx + cRangeUnassigned, // u2bxx + cRangeUnassigned, // u2cxx + cRangeUnassigned, // u2dxx + cRangeSetCJK, // u2exx + cRangeSetCJK, // u2fxx + }, + { + // table for ax-- + cRangeYi, // ua0xx + cRangeYi, // ua1xx + cRangeYi, // ua2xx + cRangeYi, // ua3xx + cRangeYi, // ua4xx + cRangeUnassigned, // ua5xx + cRangeUnassigned, // ua6xx + cRangeUnassigned, // ua7xx + cRangeUnassigned, // ua8xx + cRangeUnassigned, // ua9xx + cRangeUnassigned, // uaaxx + cRangeUnassigned, // uabxx + cRangeKorean, // uacxx + cRangeKorean, // uadxx + cRangeKorean, // uaexx + cRangeKorean, // uafxx + }, + { + // table for dx-- + cRangeKorean, // ud0xx + cRangeKorean, // ud1xx + cRangeKorean, // ud2xx + cRangeKorean, // ud3xx + cRangeKorean, // ud4xx + cRangeKorean, // ud5xx + cRangeKorean, // ud6xx + cRangeKorean, // ud7xx + cRangeSurrogate, // ud8xx + cRangeSurrogate, // ud9xx + cRangeSurrogate, // udaxx + cRangeSurrogate, // udbxx + cRangeSurrogate, // udcxx + cRangeSurrogate, // uddxx + cRangeSurrogate, // udexx + cRangeSurrogate, // udfxx + }, + { + // table for fx-- + cRangePrivate, // uf0xx + cRangePrivate, // uf1xx + cRangePrivate, // uf2xx + cRangePrivate, // uf3xx + cRangePrivate, // uf4xx + cRangePrivate, // uf5xx + cRangePrivate, // uf6xx + cRangePrivate, // uf7xx + cRangePrivate, // uf8xx + cRangeSetCJK, // uf9xx + cRangeSetCJK, // ufaxx + cRangeArabic, // ufbxx, includes alphabic presentation form + cRangeArabic, // ufcxx + cRangeArabic, // ufdxx + cRangeArabic, // ufexx, includes Combining half marks, + // CJK compatibility forms, + // CJK compatibility forms, + // small form variants + cRangeTableBase + + 8, // uffxx, halfwidth and fullwidth forms, includes Specials + }, + { + // table for 0x0500 - 0x05ff + cRangeCyrillic, // u050x + cRangeCyrillic, // u051x + cRangeCyrillic, // u052x + cRangeArmenian, // u053x + cRangeArmenian, // u054x + cRangeArmenian, // u055x + cRangeArmenian, // u056x + cRangeArmenian, // u057x + cRangeArmenian, // u058x + cRangeHebrew, // u059x + cRangeHebrew, // u05ax + cRangeHebrew, // u05bx + cRangeHebrew, // u05cx + cRangeHebrew, // u05dx + cRangeHebrew, // u05ex + cRangeHebrew, // u05fx + }, + { + // table for 0xff00 - 0xffff + cRangeSetCJK, // uff0x, fullwidth latin + cRangeSetCJK, // uff1x, fullwidth latin + cRangeSetCJK, // uff2x, fullwidth latin + cRangeSetCJK, // uff3x, fullwidth latin + cRangeSetCJK, // uff4x, fullwidth latin + cRangeSetCJK, // uff5x, fullwidth latin + cRangeSetCJK, // uff6x, halfwidth katakana + cRangeSetCJK, // uff7x, halfwidth katakana + cRangeSetCJK, // uff8x, halfwidth katakana + cRangeSetCJK, // uff9x, halfwidth katakana + cRangeSetCJK, // uffax, halfwidth hangul jamo + cRangeSetCJK, // uffbx, halfwidth hangul jamo + cRangeSetCJK, // uffcx, halfwidth hangul jamo + cRangeSetCJK, // uffdx, halfwidth hangul jamo + cRangeSetCJK, // uffex, fullwidth symbols + cRangeSpecials, // ufffx, Specials + }, }; // Most scripts between U+0700 and U+16FF are assigned a chunk of 128 (0x80) @@ -382,40 +374,40 @@ static const unsigned char gUnicodeSubrangeTable[cNumSubTables][cSubTableSize] = // syllabaries take multiple chunks and Ogham and Runic share a single chunk. static const unsigned cTertiaryTableSize = ((0x1700 - 0x0700) / 0x80); -static const unsigned char gUnicodeTertiaryRangeTable[cTertiaryTableSize] = -{ //table for 0x0700 - 0x1600 - cRangeSyriac, //u070x - cRangeThaana, //u078x - cRangeUnassigned, //u080x place holder(resolved in the 2ndary tab.) - cRangeUnassigned, //u088x place holder(resolved in the 2ndary tab.) - cRangeDevanagari, //u090x - cRangeBengali, //u098x - cRangeGurmukhi, //u0a0x - cRangeGujarati, //u0a8x - cRangeOriya, //u0b0x - cRangeTamil, //u0b8x - cRangeTelugu, //u0c0x - cRangeKannada, //u0c8x - cRangeMalayalam, //u0d0x - cRangeSinhala, //u0d8x - cRangeThai, //u0e0x - cRangeLao, //u0e8x - cRangeTibetan, //u0f0x place holder(resolved in the 2ndary tab.) - cRangeTibetan, //u0f8x place holder(resolved in the 2ndary tab.) - cRangeMyanmar, //u100x - cRangeGeorgian, //u108x - cRangeKorean, //u110x place holder(resolved in the 2ndary tab.) - cRangeKorean, //u118x place holder(resolved in the 2ndary tab.) - cRangeEthiopic, //u120x place holder(resolved in the 2ndary tab.) - cRangeEthiopic, //u128x place holder(resolved in the 2ndary tab.) - cRangeEthiopic, //u130x - cRangeCherokee, //u138x - cRangeCanadian, //u140x place holder(resolved in the 2ndary tab.) - cRangeCanadian, //u148x place holder(resolved in the 2ndary tab.) - cRangeCanadian, //u150x place holder(resolved in the 2ndary tab.) - cRangeCanadian, //u158x place holder(resolved in the 2ndary tab.) - cRangeCanadian, //u160x - cRangeOghamRunic, //u168x this contains two scripts, Ogham & Runic +static const unsigned char gUnicodeTertiaryRangeTable[cTertiaryTableSize] = { + // table for 0x0700 - 0x1600 + cRangeSyriac, // u070x + cRangeThaana, // u078x + cRangeUnassigned, // u080x place holder(resolved in the 2ndary tab.) + cRangeUnassigned, // u088x place holder(resolved in the 2ndary tab.) + cRangeDevanagari, // u090x + cRangeBengali, // u098x + cRangeGurmukhi, // u0a0x + cRangeGujarati, // u0a8x + cRangeOriya, // u0b0x + cRangeTamil, // u0b8x + cRangeTelugu, // u0c0x + cRangeKannada, // u0c8x + cRangeMalayalam, // u0d0x + cRangeSinhala, // u0d8x + cRangeThai, // u0e0x + cRangeLao, // u0e8x + cRangeTibetan, // u0f0x place holder(resolved in the 2ndary tab.) + cRangeTibetan, // u0f8x place holder(resolved in the 2ndary tab.) + cRangeMyanmar, // u100x + cRangeGeorgian, // u108x + cRangeKorean, // u110x place holder(resolved in the 2ndary tab.) + cRangeKorean, // u118x place holder(resolved in the 2ndary tab.) + cRangeEthiopic, // u120x place holder(resolved in the 2ndary tab.) + cRangeEthiopic, // u128x place holder(resolved in the 2ndary tab.) + cRangeEthiopic, // u130x + cRangeCherokee, // u138x + cRangeCanadian, // u140x place holder(resolved in the 2ndary tab.) + cRangeCanadian, // u148x place holder(resolved in the 2ndary tab.) + cRangeCanadian, // u150x place holder(resolved in the 2ndary tab.) + cRangeCanadian, // u158x place holder(resolved in the 2ndary tab.) + cRangeCanadian, // u160x + cRangeOghamRunic, // u168x this contains two scripts, Ogham & Runic }; // A two level index is almost enough for locating a range, with the @@ -426,36 +418,34 @@ static const unsigned char gUnicodeTertiaryRangeTable[cTertiaryTableSize] = // there is such a need. // For Indic, Southeast Asian scripts and some other scripts between // U+0700 and U+16FF, it's extended to the third level. -unsigned findCharUnicodeRange(UChar32 ch) -{ - if (ch >= 0xFFFF) - return 0; +unsigned findCharUnicodeRange(UChar32 ch) { + if (ch >= 0xFFFF) + return 0; - unsigned range; + unsigned range; - //search the first table - range = gUnicodeSubrangeTable[0][ch >> 12]; + // search the first table + range = gUnicodeSubrangeTable[0][ch >> 12]; - if (range < cRangeTableBase) - // we try to get a specific range - return range; + if (range < cRangeTableBase) + // we try to get a specific range + return range; - // otherwise, we have one more table to look at - range = gUnicodeSubrangeTable[range - cRangeTableBase][(ch & 0x0f00) >> 8]; - if (range < cRangeTableBase) - return range; - if (range < cRangeTertiaryTable) - return gUnicodeSubrangeTable[range - cRangeTableBase][(ch & 0x00f0) >> 4]; + // otherwise, we have one more table to look at + range = gUnicodeSubrangeTable[range - cRangeTableBase][(ch & 0x0f00) >> 8]; + if (range < cRangeTableBase) + return range; + if (range < cRangeTertiaryTable) + return gUnicodeSubrangeTable[range - cRangeTableBase][(ch & 0x00f0) >> 4]; - // Yet another table to look at : U+0700 - U+16FF : 128 code point blocks - return gUnicodeTertiaryRangeTable[(ch - 0x0700) >> 7]; + // Yet another table to look at : U+0700 - U+16FF : 128 code point blocks + return gUnicodeTertiaryRangeTable[(ch - 0x0700) >> 7]; } -const char* langGroupFromUnicodeRange(unsigned char unicodeRange) -{ - if (cRangeSpecificItemNum > unicodeRange) - return gUnicodeRangeToLangGroupTable[unicodeRange]; - return 0; +const char* langGroupFromUnicodeRange(unsigned char unicodeRange) { + if (cRangeSpecificItemNum > unicodeRange) + return gUnicodeRangeToLangGroupTable[unicodeRange]; + return 0; } -} +} // namespace blink diff --git a/sky/engine/platform/text/UnicodeRange.h b/sky/engine/platform/text/UnicodeRange.h index 29b93f5eb8a1d..dc4e9f9a9ece0 100644 --- a/sky/engine/platform/text/UnicodeRange.h +++ b/sky/engine/platform/text/UnicodeRange.h @@ -15,7 +15,7 @@ * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Alternatively, the contents of this file may be used under the terms * of either the Mozilla Public License Version 1.1, found at @@ -46,74 +46,77 @@ namespace blink { // All ranges we care about should fit within 32 bits. // Frequently used range definitions -const unsigned char cRangeCyrillic = 0; -const unsigned char cRangeGreek = 1; -const unsigned char cRangeTurkish = 2; -const unsigned char cRangeHebrew = 3; -const unsigned char cRangeArabic = 4; -const unsigned char cRangeBaltic = 5; -const unsigned char cRangeThai = 6; -const unsigned char cRangeKorean = 7; -const unsigned char cRangeJapanese = 8; -const unsigned char cRangeSChinese = 9; -const unsigned char cRangeTChinese = 10; -const unsigned char cRangeDevanagari = 11; -const unsigned char cRangeTamil = 12; -const unsigned char cRangeArmenian = 13; -const unsigned char cRangeBengali = 14; -const unsigned char cRangeCanadian = 15; -const unsigned char cRangeEthiopic = 16; -const unsigned char cRangeGeorgian = 17; -const unsigned char cRangeGujarati = 18; -const unsigned char cRangeGurmukhi = 19; -const unsigned char cRangeKhmer = 20; -const unsigned char cRangeMalayalam = 21; +const unsigned char cRangeCyrillic = 0; +const unsigned char cRangeGreek = 1; +const unsigned char cRangeTurkish = 2; +const unsigned char cRangeHebrew = 3; +const unsigned char cRangeArabic = 4; +const unsigned char cRangeBaltic = 5; +const unsigned char cRangeThai = 6; +const unsigned char cRangeKorean = 7; +const unsigned char cRangeJapanese = 8; +const unsigned char cRangeSChinese = 9; +const unsigned char cRangeTChinese = 10; +const unsigned char cRangeDevanagari = 11; +const unsigned char cRangeTamil = 12; +const unsigned char cRangeArmenian = 13; +const unsigned char cRangeBengali = 14; +const unsigned char cRangeCanadian = 15; +const unsigned char cRangeEthiopic = 16; +const unsigned char cRangeGeorgian = 17; +const unsigned char cRangeGujarati = 18; +const unsigned char cRangeGurmukhi = 19; +const unsigned char cRangeKhmer = 20; +const unsigned char cRangeMalayalam = 21; -const unsigned char cRangeSpecificItemNum = 22; +const unsigned char cRangeSpecificItemNum = 22; // range/rangeSet grow to this place 22-29 -const unsigned char cRangeSetStart = 30; // range set definition starts from here -const unsigned char cRangeSetLatin = 30; -const unsigned char cRangeSetCJK = 31; -const unsigned char cRangeSetEnd = 31; // range set definition ends here +const unsigned char cRangeSetStart = + 30; // range set definition starts from here +const unsigned char cRangeSetLatin = 30; +const unsigned char cRangeSetCJK = 31; +const unsigned char cRangeSetEnd = 31; // range set definition ends here // less frequently used range definition -const unsigned char cRangeSurrogate = 32; -const unsigned char cRangePrivate = 33; -const unsigned char cRangeMisc = 34; -const unsigned char cRangeUnassigned = 35; -const unsigned char cRangeSyriac = 36; -const unsigned char cRangeThaana = 37; -const unsigned char cRangeOriya = 38; -const unsigned char cRangeTelugu = 39; -const unsigned char cRangeKannada = 40; -const unsigned char cRangeSinhala = 41; -const unsigned char cRangeLao = 42; -const unsigned char cRangeTibetan = 43; -const unsigned char cRangeMyanmar = 44; -const unsigned char cRangeCherokee = 45; -const unsigned char cRangeOghamRunic = 46; -const unsigned char cRangeMongolian = 47; -const unsigned char cRangeMathOperators = 48; -const unsigned char cRangeMiscTechnical = 49; -const unsigned char cRangeControlOpticalEnclose = 50; -const unsigned char cRangeBoxBlockGeometrics = 51; -const unsigned char cRangeMiscSymbols = 52; -const unsigned char cRangeDingbats = 53; -const unsigned char cRangeBraillePattern = 54; -const unsigned char cRangeYi = 55; -const unsigned char cRangeCombiningDiacriticalMarks = 56; -const unsigned char cRangeSpecials = 57; - -const unsigned char cRangeTableBase = 128; // values over 127 are reserved for internal use only -const unsigned char cRangeTertiaryTable = 145; // leave room for 16 subtable indices (cRangeTableBase + 1 .. cRangeTableBase + 16) - +const unsigned char cRangeSurrogate = 32; +const unsigned char cRangePrivate = 33; +const unsigned char cRangeMisc = 34; +const unsigned char cRangeUnassigned = 35; +const unsigned char cRangeSyriac = 36; +const unsigned char cRangeThaana = 37; +const unsigned char cRangeOriya = 38; +const unsigned char cRangeTelugu = 39; +const unsigned char cRangeKannada = 40; +const unsigned char cRangeSinhala = 41; +const unsigned char cRangeLao = 42; +const unsigned char cRangeTibetan = 43; +const unsigned char cRangeMyanmar = 44; +const unsigned char cRangeCherokee = 45; +const unsigned char cRangeOghamRunic = 46; +const unsigned char cRangeMongolian = 47; +const unsigned char cRangeMathOperators = 48; +const unsigned char cRangeMiscTechnical = 49; +const unsigned char cRangeControlOpticalEnclose = 50; +const unsigned char cRangeBoxBlockGeometrics = 51; +const unsigned char cRangeMiscSymbols = 52; +const unsigned char cRangeDingbats = 53; +const unsigned char cRangeBraillePattern = 54; +const unsigned char cRangeYi = 55; +const unsigned char cRangeCombiningDiacriticalMarks = 56; +const unsigned char cRangeSpecials = 57; +const unsigned char cRangeTableBase = + 128; // values over 127 are reserved for internal use only +const unsigned char cRangeTertiaryTable = 145; // leave room for 16 subtable + // indices (cRangeTableBase + 1 + // .. cRangeTableBase + 16) PLATFORM_EXPORT unsigned findCharUnicodeRange(UChar32); -PLATFORM_EXPORT const char* langGroupFromUnicodeRange(unsigned char unicodeRange); +PLATFORM_EXPORT const char* langGroupFromUnicodeRange( + unsigned char unicodeRange); -} +} // namespace blink #endif // SKY_ENGINE_PLATFORM_TEXT_UNICODERANGE_H_ diff --git a/sky/engine/platform/text/UnicodeUtilities.cpp b/sky/engine/platform/text/UnicodeUtilities.cpp index ce472fe21e061..715e0f3bcfc64 100644 --- a/sky/engine/platform/text/UnicodeUtilities.cpp +++ b/sky/engine/platform/text/UnicodeUtilities.cpp @@ -1,5 +1,6 @@ /* - * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012 Apple Inc. All rights reserved. + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012 Apple Inc. All + * rights reserved. * Copyright (C) 2005 Alexey Proskuryakov. * * Redistribution and use in source and binary forms, with or without @@ -35,83 +36,82 @@ using namespace WTF::Unicode; namespace blink { enum VoicedSoundMarkType { - NoVoicedSoundMark, - VoicedSoundMark, - SemiVoicedSoundMark + NoVoicedSoundMark, + VoicedSoundMark, + SemiVoicedSoundMark }; template -static inline CharType foldQuoteMarkOrSoftHyphen(CharType c) -{ - switch (static_cast(c)) { +static inline CharType foldQuoteMarkOrSoftHyphen(CharType c) { + switch (static_cast(c)) { case hebrewPunctuationGershayim: case leftDoubleQuotationMark: case rightDoubleQuotationMark: - return '"'; + return '"'; case hebrewPunctuationGeresh: case leftSingleQuotationMark: case rightSingleQuotationMark: - return '\''; + return '\''; case softHyphen: - // Replace soft hyphen with an ignorable character so that their presence or absence will - // not affect string comparison. - return 0; + // Replace soft hyphen with an ignorable character so that their presence + // or absence will not affect string comparison. + return 0; default: - return c; - } + return c; + } } -void foldQuoteMarksAndSoftHyphens(UChar* data, size_t length) -{ - for (size_t i = 0; i < length; ++i) - data[i] = foldQuoteMarkOrSoftHyphen(data[i]); +void foldQuoteMarksAndSoftHyphens(UChar* data, size_t length) { + for (size_t i = 0; i < length; ++i) + data[i] = foldQuoteMarkOrSoftHyphen(data[i]); } -void foldQuoteMarksAndSoftHyphens(String& s) -{ - s.replace(hebrewPunctuationGeresh, '\''); - s.replace(hebrewPunctuationGershayim, '"'); - s.replace(leftDoubleQuotationMark, '"'); - s.replace(leftSingleQuotationMark, '\''); - s.replace(rightDoubleQuotationMark, '"'); - s.replace(rightSingleQuotationMark, '\''); - // Replace soft hyphen with an ignorable character so that their presence or absence will - // not affect string comparison. - s.replace(softHyphen, 0); +void foldQuoteMarksAndSoftHyphens(String& s) { + s.replace(hebrewPunctuationGeresh, '\''); + s.replace(hebrewPunctuationGershayim, '"'); + s.replace(leftDoubleQuotationMark, '"'); + s.replace(leftSingleQuotationMark, '\''); + s.replace(rightDoubleQuotationMark, '"'); + s.replace(rightSingleQuotationMark, '\''); + // Replace soft hyphen with an ignorable character so that their presence or + // absence will not affect string comparison. + s.replace(softHyphen, 0); } -static bool isNonLatin1Separator(UChar32 character) -{ - ASSERT_ARG(character, character >= 256); +static bool isNonLatin1Separator(UChar32 character) { + ASSERT_ARG(character, character >= 256); - return U_GET_GC_MASK(character) & (U_GC_S_MASK | U_GC_P_MASK | U_GC_Z_MASK | U_GC_CF_MASK); + return U_GET_GC_MASK(character) & + (U_GC_S_MASK | U_GC_P_MASK | U_GC_Z_MASK | U_GC_CF_MASK); } -bool isSeparator(UChar32 character) -{ - static const bool latin1SeparatorTable[256] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // space ! " # $ % & ' ( ) * + , - . / - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, // : ; < = > ? - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // @ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, // [ \ ] ^ _ - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // ` - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, // { | } ~ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 - }; - - if (character < 256) - return latin1SeparatorTable[character]; - - return isNonLatin1Separator(character); +bool isSeparator(UChar32 character) { + static const bool latin1SeparatorTable[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, // space ! " # $ % & ' ( ) * + , - + // . / + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, + 1, // : ; < = > ? + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, // @ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, + 1, // [ \ ] ^ _ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, // ` + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, + 0, // { | } ~ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, + 0, 0, 0}; + + if (character < 256) + return latin1SeparatorTable[character]; + + return isNonLatin1Separator(character); } // ICU's search ignores the distinction between small kana letters and ones @@ -127,288 +127,301 @@ bool isSeparator(UChar32 character) // We refer to the above technique as the "kana workaround". The next few // functions are helper functinos for the kana workaround. -bool isKanaLetter(UChar character) -{ - // Hiragana letters. - if (character >= 0x3041 && character <= 0x3096) - return true; +bool isKanaLetter(UChar character) { + // Hiragana letters. + if (character >= 0x3041 && character <= 0x3096) + return true; - // Katakana letters. - if (character >= 0x30A1 && character <= 0x30FA) - return true; - if (character >= 0x31F0 && character <= 0x31FF) - return true; + // Katakana letters. + if (character >= 0x30A1 && character <= 0x30FA) + return true; + if (character >= 0x31F0 && character <= 0x31FF) + return true; - // Halfwidth katakana letters. - if (character >= 0xFF66 && character <= 0xFF9D && character != 0xFF70) - return true; + // Halfwidth katakana letters. + if (character >= 0xFF66 && character <= 0xFF9D && character != 0xFF70) + return true; - return false; + return false; } -bool isSmallKanaLetter(UChar character) -{ - ASSERT(isKanaLetter(character)); - - switch (character) { - case 0x3041: // HIRAGANA LETTER SMALL A - case 0x3043: // HIRAGANA LETTER SMALL I - case 0x3045: // HIRAGANA LETTER SMALL U - case 0x3047: // HIRAGANA LETTER SMALL E - case 0x3049: // HIRAGANA LETTER SMALL O - case 0x3063: // HIRAGANA LETTER SMALL TU - case 0x3083: // HIRAGANA LETTER SMALL YA - case 0x3085: // HIRAGANA LETTER SMALL YU - case 0x3087: // HIRAGANA LETTER SMALL YO - case 0x308E: // HIRAGANA LETTER SMALL WA - case 0x3095: // HIRAGANA LETTER SMALL KA - case 0x3096: // HIRAGANA LETTER SMALL KE - case 0x30A1: // KATAKANA LETTER SMALL A - case 0x30A3: // KATAKANA LETTER SMALL I - case 0x30A5: // KATAKANA LETTER SMALL U - case 0x30A7: // KATAKANA LETTER SMALL E - case 0x30A9: // KATAKANA LETTER SMALL O - case 0x30C3: // KATAKANA LETTER SMALL TU - case 0x30E3: // KATAKANA LETTER SMALL YA - case 0x30E5: // KATAKANA LETTER SMALL YU - case 0x30E7: // KATAKANA LETTER SMALL YO - case 0x30EE: // KATAKANA LETTER SMALL WA - case 0x30F5: // KATAKANA LETTER SMALL KA - case 0x30F6: // KATAKANA LETTER SMALL KE - case 0x31F0: // KATAKANA LETTER SMALL KU - case 0x31F1: // KATAKANA LETTER SMALL SI - case 0x31F2: // KATAKANA LETTER SMALL SU - case 0x31F3: // KATAKANA LETTER SMALL TO - case 0x31F4: // KATAKANA LETTER SMALL NU - case 0x31F5: // KATAKANA LETTER SMALL HA - case 0x31F6: // KATAKANA LETTER SMALL HI - case 0x31F7: // KATAKANA LETTER SMALL HU - case 0x31F8: // KATAKANA LETTER SMALL HE - case 0x31F9: // KATAKANA LETTER SMALL HO - case 0x31FA: // KATAKANA LETTER SMALL MU - case 0x31FB: // KATAKANA LETTER SMALL RA - case 0x31FC: // KATAKANA LETTER SMALL RI - case 0x31FD: // KATAKANA LETTER SMALL RU - case 0x31FE: // KATAKANA LETTER SMALL RE - case 0x31FF: // KATAKANA LETTER SMALL RO - case 0xFF67: // HALFWIDTH KATAKANA LETTER SMALL A - case 0xFF68: // HALFWIDTH KATAKANA LETTER SMALL I - case 0xFF69: // HALFWIDTH KATAKANA LETTER SMALL U - case 0xFF6A: // HALFWIDTH KATAKANA LETTER SMALL E - case 0xFF6B: // HALFWIDTH KATAKANA LETTER SMALL O - case 0xFF6C: // HALFWIDTH KATAKANA LETTER SMALL YA - case 0xFF6D: // HALFWIDTH KATAKANA LETTER SMALL YU - case 0xFF6E: // HALFWIDTH KATAKANA LETTER SMALL YO - case 0xFF6F: // HALFWIDTH KATAKANA LETTER SMALL TU - return true; - } - return false; +bool isSmallKanaLetter(UChar character) { + ASSERT(isKanaLetter(character)); + + switch (character) { + case 0x3041: // HIRAGANA LETTER SMALL A + case 0x3043: // HIRAGANA LETTER SMALL I + case 0x3045: // HIRAGANA LETTER SMALL U + case 0x3047: // HIRAGANA LETTER SMALL E + case 0x3049: // HIRAGANA LETTER SMALL O + case 0x3063: // HIRAGANA LETTER SMALL TU + case 0x3083: // HIRAGANA LETTER SMALL YA + case 0x3085: // HIRAGANA LETTER SMALL YU + case 0x3087: // HIRAGANA LETTER SMALL YO + case 0x308E: // HIRAGANA LETTER SMALL WA + case 0x3095: // HIRAGANA LETTER SMALL KA + case 0x3096: // HIRAGANA LETTER SMALL KE + case 0x30A1: // KATAKANA LETTER SMALL A + case 0x30A3: // KATAKANA LETTER SMALL I + case 0x30A5: // KATAKANA LETTER SMALL U + case 0x30A7: // KATAKANA LETTER SMALL E + case 0x30A9: // KATAKANA LETTER SMALL O + case 0x30C3: // KATAKANA LETTER SMALL TU + case 0x30E3: // KATAKANA LETTER SMALL YA + case 0x30E5: // KATAKANA LETTER SMALL YU + case 0x30E7: // KATAKANA LETTER SMALL YO + case 0x30EE: // KATAKANA LETTER SMALL WA + case 0x30F5: // KATAKANA LETTER SMALL KA + case 0x30F6: // KATAKANA LETTER SMALL KE + case 0x31F0: // KATAKANA LETTER SMALL KU + case 0x31F1: // KATAKANA LETTER SMALL SI + case 0x31F2: // KATAKANA LETTER SMALL SU + case 0x31F3: // KATAKANA LETTER SMALL TO + case 0x31F4: // KATAKANA LETTER SMALL NU + case 0x31F5: // KATAKANA LETTER SMALL HA + case 0x31F6: // KATAKANA LETTER SMALL HI + case 0x31F7: // KATAKANA LETTER SMALL HU + case 0x31F8: // KATAKANA LETTER SMALL HE + case 0x31F9: // KATAKANA LETTER SMALL HO + case 0x31FA: // KATAKANA LETTER SMALL MU + case 0x31FB: // KATAKANA LETTER SMALL RA + case 0x31FC: // KATAKANA LETTER SMALL RI + case 0x31FD: // KATAKANA LETTER SMALL RU + case 0x31FE: // KATAKANA LETTER SMALL RE + case 0x31FF: // KATAKANA LETTER SMALL RO + case 0xFF67: // HALFWIDTH KATAKANA LETTER SMALL A + case 0xFF68: // HALFWIDTH KATAKANA LETTER SMALL I + case 0xFF69: // HALFWIDTH KATAKANA LETTER SMALL U + case 0xFF6A: // HALFWIDTH KATAKANA LETTER SMALL E + case 0xFF6B: // HALFWIDTH KATAKANA LETTER SMALL O + case 0xFF6C: // HALFWIDTH KATAKANA LETTER SMALL YA + case 0xFF6D: // HALFWIDTH KATAKANA LETTER SMALL YU + case 0xFF6E: // HALFWIDTH KATAKANA LETTER SMALL YO + case 0xFF6F: // HALFWIDTH KATAKANA LETTER SMALL TU + return true; + } + return false; } -static inline VoicedSoundMarkType composedVoicedSoundMark(UChar character) -{ - ASSERT(isKanaLetter(character)); - - switch (character) { - case 0x304C: // HIRAGANA LETTER GA - case 0x304E: // HIRAGANA LETTER GI - case 0x3050: // HIRAGANA LETTER GU - case 0x3052: // HIRAGANA LETTER GE - case 0x3054: // HIRAGANA LETTER GO - case 0x3056: // HIRAGANA LETTER ZA - case 0x3058: // HIRAGANA LETTER ZI - case 0x305A: // HIRAGANA LETTER ZU - case 0x305C: // HIRAGANA LETTER ZE - case 0x305E: // HIRAGANA LETTER ZO - case 0x3060: // HIRAGANA LETTER DA - case 0x3062: // HIRAGANA LETTER DI - case 0x3065: // HIRAGANA LETTER DU - case 0x3067: // HIRAGANA LETTER DE - case 0x3069: // HIRAGANA LETTER DO - case 0x3070: // HIRAGANA LETTER BA - case 0x3073: // HIRAGANA LETTER BI - case 0x3076: // HIRAGANA LETTER BU - case 0x3079: // HIRAGANA LETTER BE - case 0x307C: // HIRAGANA LETTER BO - case 0x3094: // HIRAGANA LETTER VU - case 0x30AC: // KATAKANA LETTER GA - case 0x30AE: // KATAKANA LETTER GI - case 0x30B0: // KATAKANA LETTER GU - case 0x30B2: // KATAKANA LETTER GE - case 0x30B4: // KATAKANA LETTER GO - case 0x30B6: // KATAKANA LETTER ZA - case 0x30B8: // KATAKANA LETTER ZI - case 0x30BA: // KATAKANA LETTER ZU - case 0x30BC: // KATAKANA LETTER ZE - case 0x30BE: // KATAKANA LETTER ZO - case 0x30C0: // KATAKANA LETTER DA - case 0x30C2: // KATAKANA LETTER DI - case 0x30C5: // KATAKANA LETTER DU - case 0x30C7: // KATAKANA LETTER DE - case 0x30C9: // KATAKANA LETTER DO - case 0x30D0: // KATAKANA LETTER BA - case 0x30D3: // KATAKANA LETTER BI - case 0x30D6: // KATAKANA LETTER BU - case 0x30D9: // KATAKANA LETTER BE - case 0x30DC: // KATAKANA LETTER BO - case 0x30F4: // KATAKANA LETTER VU - case 0x30F7: // KATAKANA LETTER VA - case 0x30F8: // KATAKANA LETTER VI - case 0x30F9: // KATAKANA LETTER VE - case 0x30FA: // KATAKANA LETTER VO - return VoicedSoundMark; - case 0x3071: // HIRAGANA LETTER PA - case 0x3074: // HIRAGANA LETTER PI - case 0x3077: // HIRAGANA LETTER PU - case 0x307A: // HIRAGANA LETTER PE - case 0x307D: // HIRAGANA LETTER PO - case 0x30D1: // KATAKANA LETTER PA - case 0x30D4: // KATAKANA LETTER PI - case 0x30D7: // KATAKANA LETTER PU - case 0x30DA: // KATAKANA LETTER PE - case 0x30DD: // KATAKANA LETTER PO - return SemiVoicedSoundMark; - } - return NoVoicedSoundMark; +static inline VoicedSoundMarkType composedVoicedSoundMark(UChar character) { + ASSERT(isKanaLetter(character)); + + switch (character) { + case 0x304C: // HIRAGANA LETTER GA + case 0x304E: // HIRAGANA LETTER GI + case 0x3050: // HIRAGANA LETTER GU + case 0x3052: // HIRAGANA LETTER GE + case 0x3054: // HIRAGANA LETTER GO + case 0x3056: // HIRAGANA LETTER ZA + case 0x3058: // HIRAGANA LETTER ZI + case 0x305A: // HIRAGANA LETTER ZU + case 0x305C: // HIRAGANA LETTER ZE + case 0x305E: // HIRAGANA LETTER ZO + case 0x3060: // HIRAGANA LETTER DA + case 0x3062: // HIRAGANA LETTER DI + case 0x3065: // HIRAGANA LETTER DU + case 0x3067: // HIRAGANA LETTER DE + case 0x3069: // HIRAGANA LETTER DO + case 0x3070: // HIRAGANA LETTER BA + case 0x3073: // HIRAGANA LETTER BI + case 0x3076: // HIRAGANA LETTER BU + case 0x3079: // HIRAGANA LETTER BE + case 0x307C: // HIRAGANA LETTER BO + case 0x3094: // HIRAGANA LETTER VU + case 0x30AC: // KATAKANA LETTER GA + case 0x30AE: // KATAKANA LETTER GI + case 0x30B0: // KATAKANA LETTER GU + case 0x30B2: // KATAKANA LETTER GE + case 0x30B4: // KATAKANA LETTER GO + case 0x30B6: // KATAKANA LETTER ZA + case 0x30B8: // KATAKANA LETTER ZI + case 0x30BA: // KATAKANA LETTER ZU + case 0x30BC: // KATAKANA LETTER ZE + case 0x30BE: // KATAKANA LETTER ZO + case 0x30C0: // KATAKANA LETTER DA + case 0x30C2: // KATAKANA LETTER DI + case 0x30C5: // KATAKANA LETTER DU + case 0x30C7: // KATAKANA LETTER DE + case 0x30C9: // KATAKANA LETTER DO + case 0x30D0: // KATAKANA LETTER BA + case 0x30D3: // KATAKANA LETTER BI + case 0x30D6: // KATAKANA LETTER BU + case 0x30D9: // KATAKANA LETTER BE + case 0x30DC: // KATAKANA LETTER BO + case 0x30F4: // KATAKANA LETTER VU + case 0x30F7: // KATAKANA LETTER VA + case 0x30F8: // KATAKANA LETTER VI + case 0x30F9: // KATAKANA LETTER VE + case 0x30FA: // KATAKANA LETTER VO + return VoicedSoundMark; + case 0x3071: // HIRAGANA LETTER PA + case 0x3074: // HIRAGANA LETTER PI + case 0x3077: // HIRAGANA LETTER PU + case 0x307A: // HIRAGANA LETTER PE + case 0x307D: // HIRAGANA LETTER PO + case 0x30D1: // KATAKANA LETTER PA + case 0x30D4: // KATAKANA LETTER PI + case 0x30D7: // KATAKANA LETTER PU + case 0x30DA: // KATAKANA LETTER PE + case 0x30DD: // KATAKANA LETTER PO + return SemiVoicedSoundMark; + } + return NoVoicedSoundMark; } -static inline bool isCombiningVoicedSoundMark(UChar character) -{ - switch (character) { - case 0x3099: // COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK - case 0x309A: // COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK - return true; - } - return false; +static inline bool isCombiningVoicedSoundMark(UChar character) { + switch (character) { + case 0x3099: // COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK + case 0x309A: // COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK + return true; + } + return false; } -bool containsKanaLetters(const String& pattern) -{ - const unsigned length = pattern.length(); - for (unsigned i = 0; i < length; ++i) { - if (isKanaLetter(pattern[i])) - return true; - } - return false; +bool containsKanaLetters(const String& pattern) { + const unsigned length = pattern.length(); + for (unsigned i = 0; i < length; ++i) { + if (isKanaLetter(pattern[i])) + return true; + } + return false; } -void normalizeCharactersIntoNFCForm(const UChar* characters, unsigned length, Vector& buffer) -{ - ASSERT(length); +void normalizeCharactersIntoNFCForm(const UChar* characters, + unsigned length, + Vector& buffer) { + ASSERT(length); - buffer.resize(length); + buffer.resize(length); - UErrorCode status = U_ZERO_ERROR; - size_t bufferSize = unorm_normalize(characters, length, UNORM_NFC, 0, buffer.data(), length, &status); - ASSERT(status == U_ZERO_ERROR || status == U_STRING_NOT_TERMINATED_WARNING || status == U_BUFFER_OVERFLOW_ERROR); - ASSERT(bufferSize); + UErrorCode status = U_ZERO_ERROR; + size_t bufferSize = unorm_normalize(characters, length, UNORM_NFC, 0, + buffer.data(), length, &status); + ASSERT(status == U_ZERO_ERROR || status == U_STRING_NOT_TERMINATED_WARNING || + status == U_BUFFER_OVERFLOW_ERROR); + ASSERT(bufferSize); - buffer.resize(bufferSize); + buffer.resize(bufferSize); - if (status == U_ZERO_ERROR || status == U_STRING_NOT_TERMINATED_WARNING) - return; + if (status == U_ZERO_ERROR || status == U_STRING_NOT_TERMINATED_WARNING) + return; - status = U_ZERO_ERROR; - unorm_normalize(characters, length, UNORM_NFC, 0, buffer.data(), bufferSize, &status); - ASSERT(status == U_STRING_NOT_TERMINATED_WARNING); + status = U_ZERO_ERROR; + unorm_normalize(characters, length, UNORM_NFC, 0, buffer.data(), bufferSize, + &status); + ASSERT(status == U_STRING_NOT_TERMINATED_WARNING); } -// This function returns kNotFound if |first| and |second| contain different Kana letters. -// If |first| and |second| contain the same Kana letter -// then function returns offset in characters from |first|. -// Pointers to both strings increase simultaneously so so it is possible to use one offset value. -static inline size_t compareKanaLetterAndComposedVoicedSoundMarks(const UChar* first, const UChar* firstEnd, const UChar* second, const UChar* secondEnd) -{ - const UChar* start = first; - // Check for differences in the kana letter character itself. - if (isSmallKanaLetter(*first) != isSmallKanaLetter(*second)) - return kNotFound; - if (composedVoicedSoundMark(*first) != composedVoicedSoundMark(*second)) - return kNotFound; +// This function returns kNotFound if |first| and |second| contain different +// Kana letters. If |first| and |second| contain the same Kana letter then +// function returns offset in characters from |first|. Pointers to both strings +// increase simultaneously so so it is possible to use one offset value. +static inline size_t compareKanaLetterAndComposedVoicedSoundMarks( + const UChar* first, + const UChar* firstEnd, + const UChar* second, + const UChar* secondEnd) { + const UChar* start = first; + // Check for differences in the kana letter character itself. + if (isSmallKanaLetter(*first) != isSmallKanaLetter(*second)) + return kNotFound; + if (composedVoicedSoundMark(*first) != composedVoicedSoundMark(*second)) + return kNotFound; + ++first; + ++second; + + // Check for differences in combining voiced sound marks found after the + // letter. + while (true) { + const bool secondIsNotSoundMark = + second == secondEnd || !isCombiningVoicedSoundMark(*second); + if (first == firstEnd || !isCombiningVoicedSoundMark(*first)) { + return secondIsNotSoundMark ? first - start : kNotFound; + } + if (secondIsNotSoundMark) + return kNotFound; + if (*first != *second) + return kNotFound; ++first; ++second; + } +} - // Check for differences in combining voiced sound marks found after the letter. - while (true) { - const bool secondIsNotSoundMark = second == secondEnd || !isCombiningVoicedSoundMark(*second); - if (first == firstEnd || !isCombiningVoicedSoundMark(*first)) { - return secondIsNotSoundMark ? first - start : kNotFound; - } - if (secondIsNotSoundMark) - return kNotFound; - if (*first != *second) - return kNotFound; - ++first; - ++second; +bool checkOnlyKanaLettersInStrings(const UChar* firstData, + unsigned firstLength, + const UChar* secondData, + unsigned secondLength) { + const UChar* a = firstData; + const UChar* aEnd = firstData + firstLength; + + const UChar* b = secondData; + const UChar* bEnd = secondData + secondLength; + while (true) { + // Skip runs of non-kana-letter characters. This is necessary so we can + // correctly handle strings where the |firstData| and |secondData| have + // different-length runs of characters that match, while still double + // checking the correctness of matches of kana letters with other kana + // letters. + while (a != aEnd && !isKanaLetter(*a)) + ++a; + while (b != bEnd && !isKanaLetter(*b)) + ++b; + + // If we reached the end of either the target or the match, we should have + // reached the end of both; both should have the same number of kana + // letters. + if (a == aEnd || b == bEnd) { + return a == aEnd && b == bEnd; } + + // Check that single Kana letters in |a| and |b| are the same. + const size_t offset = + compareKanaLetterAndComposedVoicedSoundMarks(a, aEnd, b, bEnd); + if (offset == kNotFound) + return false; + + // Update values of |a| and |b| after comparing. + a += offset; + b += offset; + } } -bool checkOnlyKanaLettersInStrings(const UChar* firstData, unsigned firstLength, const UChar* secondData, unsigned secondLength) -{ - const UChar* a = firstData; - const UChar* aEnd = firstData + firstLength; - - const UChar* b = secondData; - const UChar* bEnd = secondData + secondLength; - while (true) { - // Skip runs of non-kana-letter characters. This is necessary so we can - // correctly handle strings where the |firstData| and |secondData| have different-length - // runs of characters that match, while still double checking the correctness - // of matches of kana letters with other kana letters. - while (a != aEnd && !isKanaLetter(*a)) - ++a; - while (b != bEnd && !isKanaLetter(*b)) - ++b; - - // If we reached the end of either the target or the match, we should have - // reached the end of both; both should have the same number of kana letters. - if (a == aEnd || b == bEnd) { - return a == aEnd && b == bEnd; - } - - // Check that single Kana letters in |a| and |b| are the same. - const size_t offset = compareKanaLetterAndComposedVoicedSoundMarks(a, aEnd, b, bEnd); - if (offset == kNotFound) - return false; - - // Update values of |a| and |b| after comparing. - a += offset; - b += offset; +bool checkKanaStringsEqual(const UChar* firstData, + unsigned firstLength, + const UChar* secondData, + unsigned secondLength) { + const UChar* a = firstData; + const UChar* aEnd = firstData + firstLength; + + const UChar* b = secondData; + const UChar* bEnd = secondData + secondLength; + while (true) { + // Check for non-kana-letter characters. + while (a != aEnd && !isKanaLetter(*a) && b != bEnd && !isKanaLetter(*b)) { + if (*a++ != *b++) + return false; } -} -bool checkKanaStringsEqual(const UChar* firstData, unsigned firstLength, const UChar* secondData, unsigned secondLength) -{ - const UChar* a = firstData; - const UChar* aEnd = firstData + firstLength; - - const UChar* b = secondData; - const UChar* bEnd = secondData + secondLength; - while (true) { - // Check for non-kana-letter characters. - while (a != aEnd && !isKanaLetter(*a) && b != bEnd && !isKanaLetter(*b)) { - if (*a++ != *b++) - return false; - } - - // If we reached the end of either the target or the match, we should have - // reached the end of both; both should have the same number of kana letters. - if (a == aEnd || b == bEnd) { - return a == aEnd && b == bEnd; - } - - if (isKanaLetter(*a) != isKanaLetter(*b)) - return false; - - // Check that single Kana letters in |a| and |b| are the same. - const size_t offset = compareKanaLetterAndComposedVoicedSoundMarks(a, aEnd, b, bEnd); - if (offset == kNotFound) - return false; - - // Update values of |a| and |b| after comparing. - a += offset; - b += offset; + // If we reached the end of either the target or the match, we should have + // reached the end of both; both should have the same number of kana + // letters. + if (a == aEnd || b == bEnd) { + return a == aEnd && b == bEnd; } -} + if (isKanaLetter(*a) != isKanaLetter(*b)) + return false; + + // Check that single Kana letters in |a| and |b| are the same. + const size_t offset = + compareKanaLetterAndComposedVoicedSoundMarks(a, aEnd, b, bEnd); + if (offset == kNotFound) + return false; + + // Update values of |a| and |b| after comparing. + a += offset; + b += offset; + } } + +} // namespace blink diff --git a/sky/engine/platform/text/UnicodeUtilities.h b/sky/engine/platform/text/UnicodeUtilities.h index 2f28e1f69c2c2..4ea99067c75b5 100644 --- a/sky/engine/platform/text/UnicodeUtilities.h +++ b/sky/engine/platform/text/UnicodeUtilities.h @@ -36,12 +36,20 @@ namespace blink { PLATFORM_EXPORT bool isSeparator(UChar32); PLATFORM_EXPORT bool isKanaLetter(UChar character); PLATFORM_EXPORT bool containsKanaLetters(const String&); -PLATFORM_EXPORT void normalizeCharactersIntoNFCForm(const UChar* characters, unsigned length, Vector& buffer); +PLATFORM_EXPORT void normalizeCharactersIntoNFCForm(const UChar* characters, + unsigned length, + Vector& buffer); PLATFORM_EXPORT void foldQuoteMarksAndSoftHyphens(UChar* data, size_t length); PLATFORM_EXPORT void foldQuoteMarksAndSoftHyphens(String&); -PLATFORM_EXPORT bool checkOnlyKanaLettersInStrings(const UChar* firstData, unsigned firstLength, const UChar* secondData, unsigned secondLength); -PLATFORM_EXPORT bool checkKanaStringsEqual(const UChar* firstData, unsigned firstLength, const UChar* secondData, unsigned secondLength); +PLATFORM_EXPORT bool checkOnlyKanaLettersInStrings(const UChar* firstData, + unsigned firstLength, + const UChar* secondData, + unsigned secondLength); +PLATFORM_EXPORT bool checkKanaStringsEqual(const UChar* firstData, + unsigned firstLength, + const UChar* secondData, + unsigned secondLength); -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_TEXT_UNICODEUTILITIES_H_ diff --git a/sky/engine/platform/text/UnicodeUtilitiesTest.cpp b/sky/engine/platform/text/UnicodeUtilitiesTest.cpp index 4a4cf14c241cf..d666682b6d537 100644 --- a/sky/engine/platform/text/UnicodeUtilitiesTest.cpp +++ b/sky/engine/platform/text/UnicodeUtilitiesTest.cpp @@ -43,205 +43,207 @@ namespace { static const UChar32 kMaxLatinCharCount = 256; static bool isTestFirstAndLastCharsInCategoryFailed = false; -UBool U_CALLCONV testFirstAndLastCharsInCategory(const void *context, UChar32 start, UChar32 limit, UCharCategory type) -{ - if (start >= kMaxLatinCharCount - && U_MASK(type) & (U_GC_S_MASK | U_GC_P_MASK | U_GC_Z_MASK | U_GC_CF_MASK) - && (!isSeparator(start) || !isSeparator(limit - 1))) { - isTestFirstAndLastCharsInCategoryFailed = true; - - // Break enumeration process - return 0; - } - - return 1; +UBool U_CALLCONV testFirstAndLastCharsInCategory(const void* context, + UChar32 start, + UChar32 limit, + UCharCategory type) { + if (start >= kMaxLatinCharCount && + U_MASK(type) & (U_GC_S_MASK | U_GC_P_MASK | U_GC_Z_MASK | U_GC_CF_MASK) && + (!isSeparator(start) || !isSeparator(limit - 1))) { + isTestFirstAndLastCharsInCategoryFailed = true; + + // Break enumeration process + return 0; + } + + return 1; } -TEST(WebCoreUnicodeUnit, Separators) -{ - static const bool latinSeparatorTable[kMaxLatinCharCount] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // space ! " # $ % & ' ( ) * + , - . / - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, // : ; < = > ? - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // @ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, // [ \ ] ^ _ - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // ` - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, // { | } ~ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 - }; - - for (UChar32 character = 0; character < kMaxLatinCharCount; ++character) { - EXPECT_EQ(isSeparator(character), latinSeparatorTable[character]); - } - - isTestFirstAndLastCharsInCategoryFailed = false; - u_enumCharTypes(&testFirstAndLastCharsInCategory, 0); - EXPECT_FALSE(isTestFirstAndLastCharsInCategoryFailed); +TEST(WebCoreUnicodeUnit, Separators) { + static const bool latinSeparatorTable[kMaxLatinCharCount] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, // space ! " # $ % & ' ( ) * + , - . + // / + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, + 1, // : ; < = > ? + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, // @ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, + 1, // [ \ ] ^ _ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, // ` + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, + 0, // { | } ~ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, + 0, 0, 0}; + + for (UChar32 character = 0; character < kMaxLatinCharCount; ++character) { + EXPECT_EQ(isSeparator(character), latinSeparatorTable[character]); + } + + isTestFirstAndLastCharsInCategoryFailed = false; + u_enumCharTypes(&testFirstAndLastCharsInCategory, 0); + EXPECT_FALSE(isTestFirstAndLastCharsInCategoryFailed); } -TEST(WebCoreUnicodeUnit, KanaLetters) -{ - // Non Kana symbols - for (UChar character = 0; character < 0x3041; ++character) - EXPECT_FALSE(isKanaLetter(character)); +TEST(WebCoreUnicodeUnit, KanaLetters) { + // Non Kana symbols + for (UChar character = 0; character < 0x3041; ++character) + EXPECT_FALSE(isKanaLetter(character)); - // Hiragana letters. - for (UChar character = 0x3041; character <= 0x3096; ++character) - EXPECT_TRUE(isKanaLetter(character)); + // Hiragana letters. + for (UChar character = 0x3041; character <= 0x3096; ++character) + EXPECT_TRUE(isKanaLetter(character)); - // Katakana letters. - for (UChar character = 0x30A1; character <= 0x30FA; ++character) - EXPECT_TRUE(isKanaLetter(character)); + // Katakana letters. + for (UChar character = 0x30A1; character <= 0x30FA; ++character) + EXPECT_TRUE(isKanaLetter(character)); } -TEST(WebCoreUnicodeUnit, ContainsKanaLetters) -{ - // Non Kana symbols - String nonKanaString; - for (UChar character = 0; character < 0x3041; ++character) - nonKanaString.append(character); - EXPECT_FALSE(containsKanaLetters(nonKanaString)); - - // Hiragana letters. - for (UChar character = 0x3041; character <= 0x3096; ++character) { - String str(nonKanaString); - str.append(character); - EXPECT_TRUE(containsKanaLetters(str)); - } - - // Katakana letters. - for (UChar character = 0x30A1; character <= 0x30FA; ++character) { - String str(nonKanaString); - str.append(character); - EXPECT_TRUE(containsKanaLetters(str)); - } +TEST(WebCoreUnicodeUnit, ContainsKanaLetters) { + // Non Kana symbols + String nonKanaString; + for (UChar character = 0; character < 0x3041; ++character) + nonKanaString.append(character); + EXPECT_FALSE(containsKanaLetters(nonKanaString)); + + // Hiragana letters. + for (UChar character = 0x3041; character <= 0x3096; ++character) { + String str(nonKanaString); + str.append(character); + EXPECT_TRUE(containsKanaLetters(str)); + } + + // Katakana letters. + for (UChar character = 0x30A1; character <= 0x30FA; ++character) { + String str(nonKanaString); + str.append(character); + EXPECT_TRUE(containsKanaLetters(str)); + } } -TEST(WebCoreUnicodeUnit, FoldQuoteMarkOrSoftHyphenTest) -{ - const UChar charactersToFold[] = { - hebrewPunctuationGershayim, leftDoubleQuotationMark, rightDoubleQuotationMark, - hebrewPunctuationGeresh, leftSingleQuotationMark, rightSingleQuotationMark, - softHyphen - }; +TEST(WebCoreUnicodeUnit, FoldQuoteMarkOrSoftHyphenTest) { + const UChar charactersToFold[] = {hebrewPunctuationGershayim, + leftDoubleQuotationMark, + rightDoubleQuotationMark, + hebrewPunctuationGeresh, + leftSingleQuotationMark, + rightSingleQuotationMark, + softHyphen}; - String stringToFold(charactersToFold, WTF_ARRAY_LENGTH(charactersToFold)); - Vector buffer; - stringToFold.appendTo(buffer); + String stringToFold(charactersToFold, WTF_ARRAY_LENGTH(charactersToFold)); + Vector buffer; + stringToFold.appendTo(buffer); - foldQuoteMarksAndSoftHyphens(stringToFold); + foldQuoteMarksAndSoftHyphens(stringToFold); - const String foldedString("\"\"\"\'\'\'\0", WTF_ARRAY_LENGTH(charactersToFold)); - EXPECT_EQ(stringToFold, foldedString); + const String foldedString("\"\"\"\'\'\'\0", + WTF_ARRAY_LENGTH(charactersToFold)); + EXPECT_EQ(stringToFold, foldedString); - foldQuoteMarksAndSoftHyphens(buffer.data(), buffer.size()); - EXPECT_EQ(String(buffer), foldedString); + foldQuoteMarksAndSoftHyphens(buffer.data(), buffer.size()); + EXPECT_EQ(String(buffer), foldedString); } -TEST(WebCoreUnicodeUnit, OnlyKanaLettersEqualityTest) -{ - const UChar nonKanaString1[] = { 'a', 'b', 'c', 'd' }; - const UChar nonKanaString2[] = { 'e', 'f', 'g' }; - - // Check that non-Kana letters will be skipped. - EXPECT_TRUE(checkOnlyKanaLettersInStrings( - nonKanaString1, WTF_ARRAY_LENGTH(nonKanaString1), - nonKanaString2, WTF_ARRAY_LENGTH(nonKanaString2))); - - const UChar kanaString[] = { 'e', 'f', 'g', 0x3041 }; - EXPECT_FALSE(checkOnlyKanaLettersInStrings( - kanaString, WTF_ARRAY_LENGTH(kanaString), - nonKanaString2, WTF_ARRAY_LENGTH(nonKanaString2))); - - // Compare with self. - EXPECT_TRUE(checkOnlyKanaLettersInStrings( - kanaString, WTF_ARRAY_LENGTH(kanaString), - kanaString, WTF_ARRAY_LENGTH(kanaString))); - - UChar voicedKanaString1[] = { 0x3042, 0x3099 }; - UChar voicedKanaString2[] = { 0x3042, 0x309A }; - - // Comparing strings with different sound marks should fail. - EXPECT_FALSE(checkOnlyKanaLettersInStrings( - voicedKanaString1, WTF_ARRAY_LENGTH(voicedKanaString1), - voicedKanaString2, WTF_ARRAY_LENGTH(voicedKanaString2))); - - // Now strings will be the same. - voicedKanaString2[1] = 0x3099; - EXPECT_TRUE(checkOnlyKanaLettersInStrings( - voicedKanaString1, WTF_ARRAY_LENGTH(voicedKanaString1), - voicedKanaString2, WTF_ARRAY_LENGTH(voicedKanaString2))); - - voicedKanaString2[0] = 0x3043; - EXPECT_FALSE(checkOnlyKanaLettersInStrings( - voicedKanaString1, WTF_ARRAY_LENGTH(voicedKanaString1), - voicedKanaString2, WTF_ARRAY_LENGTH(voicedKanaString2))); +TEST(WebCoreUnicodeUnit, OnlyKanaLettersEqualityTest) { + const UChar nonKanaString1[] = {'a', 'b', 'c', 'd'}; + const UChar nonKanaString2[] = {'e', 'f', 'g'}; + + // Check that non-Kana letters will be skipped. + EXPECT_TRUE(checkOnlyKanaLettersInStrings( + nonKanaString1, WTF_ARRAY_LENGTH(nonKanaString1), nonKanaString2, + WTF_ARRAY_LENGTH(nonKanaString2))); + + const UChar kanaString[] = {'e', 'f', 'g', 0x3041}; + EXPECT_FALSE(checkOnlyKanaLettersInStrings( + kanaString, WTF_ARRAY_LENGTH(kanaString), nonKanaString2, + WTF_ARRAY_LENGTH(nonKanaString2))); + + // Compare with self. + EXPECT_TRUE( + checkOnlyKanaLettersInStrings(kanaString, WTF_ARRAY_LENGTH(kanaString), + kanaString, WTF_ARRAY_LENGTH(kanaString))); + + UChar voicedKanaString1[] = {0x3042, 0x3099}; + UChar voicedKanaString2[] = {0x3042, 0x309A}; + + // Comparing strings with different sound marks should fail. + EXPECT_FALSE(checkOnlyKanaLettersInStrings( + voicedKanaString1, WTF_ARRAY_LENGTH(voicedKanaString1), voicedKanaString2, + WTF_ARRAY_LENGTH(voicedKanaString2))); + + // Now strings will be the same. + voicedKanaString2[1] = 0x3099; + EXPECT_TRUE(checkOnlyKanaLettersInStrings( + voicedKanaString1, WTF_ARRAY_LENGTH(voicedKanaString1), voicedKanaString2, + WTF_ARRAY_LENGTH(voicedKanaString2))); + + voicedKanaString2[0] = 0x3043; + EXPECT_FALSE(checkOnlyKanaLettersInStrings( + voicedKanaString1, WTF_ARRAY_LENGTH(voicedKanaString1), voicedKanaString2, + WTF_ARRAY_LENGTH(voicedKanaString2))); } -TEST(WebCoreUnicodeUnit, StringsWithKanaLettersTest) -{ - const UChar nonKanaString1[] = { 'a', 'b', 'c' }; - const UChar nonKanaString2[] = { 'a', 'b', 'c' }; - - // Check that non-Kana letters will be compared. - EXPECT_TRUE(checkKanaStringsEqual( - nonKanaString1, WTF_ARRAY_LENGTH(nonKanaString1), - nonKanaString2, WTF_ARRAY_LENGTH(nonKanaString2))); - - const UChar kanaString[] = { 'a', 'b', 'c', 0x3041 }; - EXPECT_FALSE(checkKanaStringsEqual( - kanaString, WTF_ARRAY_LENGTH(kanaString), - nonKanaString2, WTF_ARRAY_LENGTH(nonKanaString2))); - - // Compare with self. - EXPECT_TRUE(checkKanaStringsEqual( - kanaString, WTF_ARRAY_LENGTH(kanaString), - kanaString, WTF_ARRAY_LENGTH(kanaString))); - - const UChar kanaString2[] = { 'x', 'y', 'z', 0x3041 }; - // Comparing strings with different non-Kana letters should fail. - EXPECT_FALSE(checkKanaStringsEqual( - kanaString, WTF_ARRAY_LENGTH(kanaString), - kanaString2, WTF_ARRAY_LENGTH(kanaString2))); - - const UChar kanaString3[] = { 'a', 'b', 'c', 0x3042, 0x3099, 'm', 'n', 'o' }; - // Check that non-Kana letters after Kana letters will be compared. - EXPECT_TRUE(checkKanaStringsEqual( - kanaString3, WTF_ARRAY_LENGTH(kanaString3), - kanaString3, WTF_ARRAY_LENGTH(kanaString3))); - - const UChar kanaString4[] = { 'a', 'b', 'c', 0x3042, 0x3099, 'm', 'n', 'o', 'p' }; - // And now comparing should fail. - EXPECT_FALSE(checkKanaStringsEqual( - kanaString3, WTF_ARRAY_LENGTH(kanaString3), - kanaString4, WTF_ARRAY_LENGTH(kanaString4))); - - UChar voicedKanaString1[] = { 0x3042, 0x3099 }; - UChar voicedKanaString2[] = { 0x3042, 0x309A }; - - // Comparing strings with different sound marks should fail. - EXPECT_FALSE(checkKanaStringsEqual( - voicedKanaString1, WTF_ARRAY_LENGTH(voicedKanaString1), - voicedKanaString2, WTF_ARRAY_LENGTH(voicedKanaString2))); - - // Now strings will be the same. - voicedKanaString2[1] = 0x3099; - EXPECT_TRUE(checkKanaStringsEqual( - voicedKanaString1, WTF_ARRAY_LENGTH(voicedKanaString1), - voicedKanaString2, WTF_ARRAY_LENGTH(voicedKanaString2))); - - voicedKanaString2[0] = 0x3043; - EXPECT_FALSE(checkKanaStringsEqual( - voicedKanaString1, WTF_ARRAY_LENGTH(voicedKanaString1), - voicedKanaString2, WTF_ARRAY_LENGTH(voicedKanaString2))); +TEST(WebCoreUnicodeUnit, StringsWithKanaLettersTest) { + const UChar nonKanaString1[] = {'a', 'b', 'c'}; + const UChar nonKanaString2[] = {'a', 'b', 'c'}; + + // Check that non-Kana letters will be compared. + EXPECT_TRUE( + checkKanaStringsEqual(nonKanaString1, WTF_ARRAY_LENGTH(nonKanaString1), + nonKanaString2, WTF_ARRAY_LENGTH(nonKanaString2))); + + const UChar kanaString[] = {'a', 'b', 'c', 0x3041}; + EXPECT_FALSE(checkKanaStringsEqual(kanaString, WTF_ARRAY_LENGTH(kanaString), + nonKanaString2, + WTF_ARRAY_LENGTH(nonKanaString2))); + + // Compare with self. + EXPECT_TRUE(checkKanaStringsEqual(kanaString, WTF_ARRAY_LENGTH(kanaString), + kanaString, WTF_ARRAY_LENGTH(kanaString))); + + const UChar kanaString2[] = {'x', 'y', 'z', 0x3041}; + // Comparing strings with different non-Kana letters should fail. + EXPECT_FALSE(checkKanaStringsEqual(kanaString, WTF_ARRAY_LENGTH(kanaString), + kanaString2, + WTF_ARRAY_LENGTH(kanaString2))); + + const UChar kanaString3[] = {'a', 'b', 'c', 0x3042, 0x3099, 'm', 'n', 'o'}; + // Check that non-Kana letters after Kana letters will be compared. + EXPECT_TRUE(checkKanaStringsEqual(kanaString3, WTF_ARRAY_LENGTH(kanaString3), + kanaString3, + WTF_ARRAY_LENGTH(kanaString3))); + + const UChar kanaString4[] = {'a', 'b', 'c', 0x3042, 0x3099, + 'm', 'n', 'o', 'p'}; + // And now comparing should fail. + EXPECT_FALSE(checkKanaStringsEqual(kanaString3, WTF_ARRAY_LENGTH(kanaString3), + kanaString4, + WTF_ARRAY_LENGTH(kanaString4))); + + UChar voicedKanaString1[] = {0x3042, 0x3099}; + UChar voicedKanaString2[] = {0x3042, 0x309A}; + + // Comparing strings with different sound marks should fail. + EXPECT_FALSE(checkKanaStringsEqual( + voicedKanaString1, WTF_ARRAY_LENGTH(voicedKanaString1), voicedKanaString2, + WTF_ARRAY_LENGTH(voicedKanaString2))); + + // Now strings will be the same. + voicedKanaString2[1] = 0x3099; + EXPECT_TRUE(checkKanaStringsEqual( + voicedKanaString1, WTF_ARRAY_LENGTH(voicedKanaString1), voicedKanaString2, + WTF_ARRAY_LENGTH(voicedKanaString2))); + + voicedKanaString2[0] = 0x3043; + EXPECT_FALSE(checkKanaStringsEqual( + voicedKanaString1, WTF_ARRAY_LENGTH(voicedKanaString1), voicedKanaString2, + WTF_ARRAY_LENGTH(voicedKanaString2))); } -} // namespace +} // namespace diff --git a/sky/engine/platform/transforms/AffineTransform.cpp b/sky/engine/platform/transforms/AffineTransform.cpp index 8db2648157155..afd101708a36b 100644 --- a/sky/engine/platform/transforms/AffineTransform.cpp +++ b/sky/engine/platform/transforms/AffineTransform.cpp @@ -35,337 +35,324 @@ namespace blink { -AffineTransform::AffineTransform() -{ - setMatrix(1, 0, 0, 1, 0, 0); +AffineTransform::AffineTransform() { + setMatrix(1, 0, 0, 1, 0, 0); } -AffineTransform::AffineTransform(double a, double b, double c, double d, double e, double f) -{ - setMatrix(a, b, c, d, e, f); +AffineTransform::AffineTransform(double a, + double b, + double c, + double d, + double e, + double f) { + setMatrix(a, b, c, d, e, f); } -void AffineTransform::makeIdentity() -{ - setMatrix(1, 0, 0, 1, 0, 0); +void AffineTransform::makeIdentity() { + setMatrix(1, 0, 0, 1, 0, 0); } -void AffineTransform::setMatrix(double a, double b, double c, double d, double e, double f) -{ - m_transform[0] = a; - m_transform[1] = b; - m_transform[2] = c; - m_transform[3] = d; - m_transform[4] = e; - m_transform[5] = f; +void AffineTransform::setMatrix(double a, + double b, + double c, + double d, + double e, + double f) { + m_transform[0] = a; + m_transform[1] = b; + m_transform[2] = c; + m_transform[3] = d; + m_transform[4] = e; + m_transform[5] = f; } -bool AffineTransform::isIdentity() const -{ - return (m_transform[0] == 1 && m_transform[1] == 0 - && m_transform[2] == 0 && m_transform[3] == 1 - && m_transform[4] == 0 && m_transform[5] == 0); +bool AffineTransform::isIdentity() const { + return (m_transform[0] == 1 && m_transform[1] == 0 && m_transform[2] == 0 && + m_transform[3] == 1 && m_transform[4] == 0 && m_transform[5] == 0); } -double AffineTransform::xScale() const -{ - return sqrt(m_transform[0] * m_transform[0] + m_transform[1] * m_transform[1]); +double AffineTransform::xScale() const { + return sqrt(m_transform[0] * m_transform[0] + + m_transform[1] * m_transform[1]); } -double AffineTransform::yScale() const -{ - return sqrt(m_transform[2] * m_transform[2] + m_transform[3] * m_transform[3]); +double AffineTransform::yScale() const { + return sqrt(m_transform[2] * m_transform[2] + + m_transform[3] * m_transform[3]); } -double AffineTransform::det() const -{ - return m_transform[0] * m_transform[3] - m_transform[1] * m_transform[2]; +double AffineTransform::det() const { + return m_transform[0] * m_transform[3] - m_transform[1] * m_transform[2]; } -bool AffineTransform::isInvertible() const -{ - return det() != 0.0; +bool AffineTransform::isInvertible() const { + return det() != 0.0; } -AffineTransform AffineTransform::inverse() const -{ - double determinant = det(); - if (determinant == 0.0) - return AffineTransform(); +AffineTransform AffineTransform::inverse() const { + double determinant = det(); + if (determinant == 0.0) + return AffineTransform(); - AffineTransform result; - if (isIdentityOrTranslation()) { - result.m_transform[4] = -m_transform[4]; - result.m_transform[5] = -m_transform[5]; - return result; - } + AffineTransform result; + if (isIdentityOrTranslation()) { + result.m_transform[4] = -m_transform[4]; + result.m_transform[5] = -m_transform[5]; + return result; + } - result.m_transform[0] = m_transform[3] / determinant; - result.m_transform[1] = -m_transform[1] / determinant; - result.m_transform[2] = -m_transform[2] / determinant; - result.m_transform[3] = m_transform[0] / determinant; - result.m_transform[4] = (m_transform[2] * m_transform[5] - - m_transform[3] * m_transform[4]) / determinant; - result.m_transform[5] = (m_transform[1] * m_transform[4] - - m_transform[0] * m_transform[5]) / determinant; + result.m_transform[0] = m_transform[3] / determinant; + result.m_transform[1] = -m_transform[1] / determinant; + result.m_transform[2] = -m_transform[2] / determinant; + result.m_transform[3] = m_transform[0] / determinant; + result.m_transform[4] = + (m_transform[2] * m_transform[5] - m_transform[3] * m_transform[4]) / + determinant; + result.m_transform[5] = + (m_transform[1] * m_transform[4] - m_transform[0] * m_transform[5]) / + determinant; - return result; + return result; } - // Multiplies this AffineTransform by the provided AffineTransform - i.e. // this = this * other; -AffineTransform& AffineTransform::multiply(const AffineTransform& other) -{ - AffineTransform trans; - - trans.m_transform[0] = other.m_transform[0] * m_transform[0] + other.m_transform[1] * m_transform[2]; - trans.m_transform[1] = other.m_transform[0] * m_transform[1] + other.m_transform[1] * m_transform[3]; - trans.m_transform[2] = other.m_transform[2] * m_transform[0] + other.m_transform[3] * m_transform[2]; - trans.m_transform[3] = other.m_transform[2] * m_transform[1] + other.m_transform[3] * m_transform[3]; - trans.m_transform[4] = other.m_transform[4] * m_transform[0] + other.m_transform[5] * m_transform[2] + m_transform[4]; - trans.m_transform[5] = other.m_transform[4] * m_transform[1] + other.m_transform[5] * m_transform[3] + m_transform[5]; - - setMatrix(trans.m_transform); - return *this; +AffineTransform& AffineTransform::multiply(const AffineTransform& other) { + AffineTransform trans; + + trans.m_transform[0] = other.m_transform[0] * m_transform[0] + + other.m_transform[1] * m_transform[2]; + trans.m_transform[1] = other.m_transform[0] * m_transform[1] + + other.m_transform[1] * m_transform[3]; + trans.m_transform[2] = other.m_transform[2] * m_transform[0] + + other.m_transform[3] * m_transform[2]; + trans.m_transform[3] = other.m_transform[2] * m_transform[1] + + other.m_transform[3] * m_transform[3]; + trans.m_transform[4] = other.m_transform[4] * m_transform[0] + + other.m_transform[5] * m_transform[2] + m_transform[4]; + trans.m_transform[5] = other.m_transform[4] * m_transform[1] + + other.m_transform[5] * m_transform[3] + m_transform[5]; + + setMatrix(trans.m_transform); + return *this; } -AffineTransform& AffineTransform::rotate(double a) -{ - // angle is in degree. Switch to radian - return rotateRadians(deg2rad(a)); +AffineTransform& AffineTransform::rotate(double a) { + // angle is in degree. Switch to radian + return rotateRadians(deg2rad(a)); } -AffineTransform& AffineTransform::rotateRadians(double a) -{ - double cosAngle = cos(a); - double sinAngle = sin(a); - AffineTransform rot(cosAngle, sinAngle, -sinAngle, cosAngle, 0, 0); +AffineTransform& AffineTransform::rotateRadians(double a) { + double cosAngle = cos(a); + double sinAngle = sin(a); + AffineTransform rot(cosAngle, sinAngle, -sinAngle, cosAngle, 0, 0); - multiply(rot); - return *this; + multiply(rot); + return *this; } -AffineTransform& AffineTransform::scale(double s) -{ - return scale(s, s); +AffineTransform& AffineTransform::scale(double s) { + return scale(s, s); } -AffineTransform& AffineTransform::scale(double sx, double sy) -{ - m_transform[0] *= sx; - m_transform[1] *= sx; - m_transform[2] *= sy; - m_transform[3] *= sy; - return *this; +AffineTransform& AffineTransform::scale(double sx, double sy) { + m_transform[0] *= sx; + m_transform[1] *= sx; + m_transform[2] *= sy; + m_transform[3] *= sy; + return *this; } // *this = *this * translation -AffineTransform& AffineTransform::translate(double tx, double ty) -{ - if (isIdentityOrTranslation()) { - m_transform[4] += tx; - m_transform[5] += ty; - return *this; - } - - m_transform[4] += tx * m_transform[0] + ty * m_transform[2]; - m_transform[5] += tx * m_transform[1] + ty * m_transform[3]; +AffineTransform& AffineTransform::translate(double tx, double ty) { + if (isIdentityOrTranslation()) { + m_transform[4] += tx; + m_transform[5] += ty; return *this; + } + + m_transform[4] += tx * m_transform[0] + ty * m_transform[2]; + m_transform[5] += tx * m_transform[1] + ty * m_transform[3]; + return *this; } -AffineTransform& AffineTransform::scaleNonUniform(double sx, double sy) -{ - return scale(sx, sy); +AffineTransform& AffineTransform::scaleNonUniform(double sx, double sy) { + return scale(sx, sy); } -AffineTransform& AffineTransform::rotateFromVector(double x, double y) -{ - return rotateRadians(atan2(y, x)); +AffineTransform& AffineTransform::rotateFromVector(double x, double y) { + return rotateRadians(atan2(y, x)); } -AffineTransform& AffineTransform::flipX() -{ - return scale(-1, 1); +AffineTransform& AffineTransform::flipX() { + return scale(-1, 1); } -AffineTransform& AffineTransform::flipY() -{ - return scale(1, -1); +AffineTransform& AffineTransform::flipY() { + return scale(1, -1); } -AffineTransform& AffineTransform::shear(double sx, double sy) -{ - double a = m_transform[0]; - double b = m_transform[1]; +AffineTransform& AffineTransform::shear(double sx, double sy) { + double a = m_transform[0]; + double b = m_transform[1]; - m_transform[0] += sy * m_transform[2]; - m_transform[1] += sy * m_transform[3]; - m_transform[2] += sx * a; - m_transform[3] += sx * b; + m_transform[0] += sy * m_transform[2]; + m_transform[1] += sy * m_transform[3]; + m_transform[2] += sx * a; + m_transform[3] += sx * b; - return *this; + return *this; } -AffineTransform& AffineTransform::skew(double angleX, double angleY) -{ - return shear(tan(deg2rad(angleX)), tan(deg2rad(angleY))); +AffineTransform& AffineTransform::skew(double angleX, double angleY) { + return shear(tan(deg2rad(angleX)), tan(deg2rad(angleY))); } -AffineTransform& AffineTransform::skewX(double angle) -{ - return shear(tan(deg2rad(angle)), 0); +AffineTransform& AffineTransform::skewX(double angle) { + return shear(tan(deg2rad(angle)), 0); } -AffineTransform& AffineTransform::skewY(double angle) -{ - return shear(0, tan(deg2rad(angle))); +AffineTransform& AffineTransform::skewY(double angle) { + return shear(0, tan(deg2rad(angle))); } -AffineTransform makeMapBetweenRects(const FloatRect& source, const FloatRect& dest) -{ - AffineTransform transform; - transform.translate(dest.x() - source.x(), dest.y() - source.y()); - transform.scale(dest.width() / source.width(), dest.height() / source.height()); - return transform; +AffineTransform makeMapBetweenRects(const FloatRect& source, + const FloatRect& dest) { + AffineTransform transform; + transform.translate(dest.x() - source.x(), dest.y() - source.y()); + transform.scale(dest.width() / source.width(), + dest.height() / source.height()); + return transform; } -void AffineTransform::map(double x, double y, double& x2, double& y2) const -{ - x2 = (m_transform[0] * x + m_transform[2] * y + m_transform[4]); - y2 = (m_transform[1] * x + m_transform[3] * y + m_transform[5]); +void AffineTransform::map(double x, double y, double& x2, double& y2) const { + x2 = (m_transform[0] * x + m_transform[2] * y + m_transform[4]); + y2 = (m_transform[1] * x + m_transform[3] * y + m_transform[5]); } -IntPoint AffineTransform::mapPoint(const IntPoint& point) const -{ - double x2, y2; - map(point.x(), point.y(), x2, y2); +IntPoint AffineTransform::mapPoint(const IntPoint& point) const { + double x2, y2; + map(point.x(), point.y(), x2, y2); - // Round the point. - return IntPoint(lround(x2), lround(y2)); + // Round the point. + return IntPoint(lround(x2), lround(y2)); } -FloatPoint AffineTransform::mapPoint(const FloatPoint& point) const -{ - double x2, y2; - map(point.x(), point.y(), x2, y2); +FloatPoint AffineTransform::mapPoint(const FloatPoint& point) const { + double x2, y2; + map(point.x(), point.y(), x2, y2); - return FloatPoint(narrowPrecisionToFloat(x2), narrowPrecisionToFloat(y2)); + return FloatPoint(narrowPrecisionToFloat(x2), narrowPrecisionToFloat(y2)); } -IntSize AffineTransform::mapSize(const IntSize& size) const -{ - double width2 = size.width() * xScale(); - double height2 = size.height() * yScale(); +IntSize AffineTransform::mapSize(const IntSize& size) const { + double width2 = size.width() * xScale(); + double height2 = size.height() * yScale(); - return IntSize(lround(width2), lround(height2)); + return IntSize(lround(width2), lround(height2)); } -FloatSize AffineTransform::mapSize(const FloatSize& size) const -{ - double width2 = size.width() * xScale(); - double height2 = size.height() * yScale(); +FloatSize AffineTransform::mapSize(const FloatSize& size) const { + double width2 = size.width() * xScale(); + double height2 = size.height() * yScale(); - return FloatSize(narrowPrecisionToFloat(width2), narrowPrecisionToFloat(height2)); + return FloatSize(narrowPrecisionToFloat(width2), + narrowPrecisionToFloat(height2)); } -IntRect AffineTransform::mapRect(const IntRect &rect) const -{ - return enclosingIntRect(mapRect(FloatRect(rect))); +IntRect AffineTransform::mapRect(const IntRect& rect) const { + return enclosingIntRect(mapRect(FloatRect(rect))); } -FloatRect AffineTransform::mapRect(const FloatRect& rect) const -{ - if (isIdentityOrTranslation()) { - if (!m_transform[4] && !m_transform[5]) - return rect; +FloatRect AffineTransform::mapRect(const FloatRect& rect) const { + if (isIdentityOrTranslation()) { + if (!m_transform[4] && !m_transform[5]) + return rect; - FloatRect mappedRect(rect); - mappedRect.move(narrowPrecisionToFloat(m_transform[4]), narrowPrecisionToFloat(m_transform[5])); - return mappedRect; - } + FloatRect mappedRect(rect); + mappedRect.move(narrowPrecisionToFloat(m_transform[4]), + narrowPrecisionToFloat(m_transform[5])); + return mappedRect; + } - FloatQuad result; - result.setP1(mapPoint(rect.location())); - result.setP2(mapPoint(FloatPoint(rect.maxX(), rect.y()))); - result.setP3(mapPoint(FloatPoint(rect.maxX(), rect.maxY()))); - result.setP4(mapPoint(FloatPoint(rect.x(), rect.maxY()))); - return result.boundingBox(); + FloatQuad result; + result.setP1(mapPoint(rect.location())); + result.setP2(mapPoint(FloatPoint(rect.maxX(), rect.y()))); + result.setP3(mapPoint(FloatPoint(rect.maxX(), rect.maxY()))); + result.setP4(mapPoint(FloatPoint(rect.x(), rect.maxY()))); + return result.boundingBox(); } -FloatQuad AffineTransform::mapQuad(const FloatQuad& q) const -{ - if (isIdentityOrTranslation()) { - FloatQuad mappedQuad(q); - mappedQuad.move(narrowPrecisionToFloat(m_transform[4]), narrowPrecisionToFloat(m_transform[5])); - return mappedQuad; - } +FloatQuad AffineTransform::mapQuad(const FloatQuad& q) const { + if (isIdentityOrTranslation()) { + FloatQuad mappedQuad(q); + mappedQuad.move(narrowPrecisionToFloat(m_transform[4]), + narrowPrecisionToFloat(m_transform[5])); + return mappedQuad; + } - FloatQuad result; - result.setP1(mapPoint(q.p1())); - result.setP2(mapPoint(q.p2())); - result.setP3(mapPoint(q.p3())); - result.setP4(mapPoint(q.p4())); - return result; + FloatQuad result; + result.setP1(mapPoint(q.p1())); + result.setP2(mapPoint(q.p2())); + result.setP3(mapPoint(q.p3())); + result.setP4(mapPoint(q.p4())); + return result; } -TransformationMatrix AffineTransform::toTransformationMatrix() const -{ - return TransformationMatrix(m_transform[0], m_transform[1], m_transform[2], - m_transform[3], m_transform[4], m_transform[5]); +TransformationMatrix AffineTransform::toTransformationMatrix() const { + return TransformationMatrix(m_transform[0], m_transform[1], m_transform[2], + m_transform[3], m_transform[4], m_transform[5]); } -bool AffineTransform::decompose(DecomposedType& decomp) const -{ - AffineTransform m(*this); +bool AffineTransform::decompose(DecomposedType& decomp) const { + AffineTransform m(*this); - // Compute scaling factors - double sx = xScale(); - double sy = yScale(); + // Compute scaling factors + double sx = xScale(); + double sy = yScale(); - // Compute cross product of transformed unit vectors. If negative, - // one axis was flipped. - if (m.a() * m.d() - m.c() * m.b() < 0) { - // Flip axis with minimum unit vector dot product - if (m.a() < m.d()) - sx = -sx; - else - sy = -sy; - } + // Compute cross product of transformed unit vectors. If negative, + // one axis was flipped. + if (m.a() * m.d() - m.c() * m.b() < 0) { + // Flip axis with minimum unit vector dot product + if (m.a() < m.d()) + sx = -sx; + else + sy = -sy; + } - // Remove scale from matrix - m.scale(1 / sx, 1 / sy); + // Remove scale from matrix + m.scale(1 / sx, 1 / sy); - // Compute rotation - double angle = atan2(m.b(), m.a()); + // Compute rotation + double angle = atan2(m.b(), m.a()); - // Remove rotation from matrix - m.rotateRadians(-angle); + // Remove rotation from matrix + m.rotateRadians(-angle); - // Return results - decomp.scaleX = sx; - decomp.scaleY = sy; - decomp.angle = angle; - decomp.remainderA = m.a(); - decomp.remainderB = m.b(); - decomp.remainderC = m.c(); - decomp.remainderD = m.d(); - decomp.translateX = m.e(); - decomp.translateY = m.f(); + // Return results + decomp.scaleX = sx; + decomp.scaleY = sy; + decomp.angle = angle; + decomp.remainderA = m.a(); + decomp.remainderB = m.b(); + decomp.remainderC = m.c(); + decomp.remainderD = m.d(); + decomp.translateX = m.e(); + decomp.translateY = m.f(); - return true; + return true; } -void AffineTransform::recompose(const DecomposedType& decomp) -{ - this->setA(decomp.remainderA); - this->setB(decomp.remainderB); - this->setC(decomp.remainderC); - this->setD(decomp.remainderD); - this->setE(decomp.translateX); - this->setF(decomp.translateY); - this->rotateRadians(decomp.angle); - this->scale(decomp.scaleX, decomp.scaleY); +void AffineTransform::recompose(const DecomposedType& decomp) { + this->setA(decomp.remainderA); + this->setB(decomp.remainderB); + this->setC(decomp.remainderC); + this->setD(decomp.remainderD); + this->setE(decomp.translateX); + this->setF(decomp.translateY); + this->rotateRadians(decomp.angle); + this->scale(decomp.scaleX, decomp.scaleY); } -} +} // namespace blink diff --git a/sky/engine/platform/transforms/AffineTransform.h b/sky/engine/platform/transforms/AffineTransform.h index d959c1a5766ec..e323a2bcccbc6 100644 --- a/sky/engine/platform/transforms/AffineTransform.h +++ b/sky/engine/platform/transforms/AffineTransform.h @@ -29,7 +29,7 @@ #include "flutter/sky/engine/platform/transforms/TransformationMatrix.h" -#include // for memcpy +#include // for memcpy #include "flutter/sky/engine/wtf/FastAllocBase.h" namespace blink { @@ -42,143 +42,140 @@ class IntRect; class TransformationMatrix; class PLATFORM_EXPORT AffineTransform { - WTF_MAKE_FAST_ALLOCATED; -public: - typedef double Transform[6]; - - AffineTransform(); - AffineTransform(double a, double b, double c, double d, double e, double f); - - void setMatrix(double a, double b, double c, double d, double e, double f); - - void map(double x, double y, double& x2, double& y2) const; - - // Rounds the mapped point to the nearest integer value. - IntPoint mapPoint(const IntPoint&) const; - - FloatPoint mapPoint(const FloatPoint&) const; - - IntSize mapSize(const IntSize&) const; - - FloatSize mapSize(const FloatSize&) const; - - // Rounds the resulting mapped rectangle out. This is helpful for bounding - // box computations but may not be what is wanted in other contexts. - IntRect mapRect(const IntRect&) const; - - FloatRect mapRect(const FloatRect&) const; - FloatQuad mapQuad(const FloatQuad&) const; - - bool isIdentity() const; - - double a() const { return m_transform[0]; } - void setA(double a) { m_transform[0] = a; } - double b() const { return m_transform[1]; } - void setB(double b) { m_transform[1] = b; } - double c() const { return m_transform[2]; } - void setC(double c) { m_transform[2] = c; } - double d() const { return m_transform[3]; } - void setD(double d) { m_transform[3] = d; } - double e() const { return m_transform[4]; } - void setE(double e) { m_transform[4] = e; } - double f() const { return m_transform[5]; } - void setF(double f) { m_transform[5] = f; } - - void makeIdentity(); - - AffineTransform& multiply(const AffineTransform& other); - AffineTransform& scale(double); - AffineTransform& scale(double sx, double sy); - AffineTransform& scaleNonUniform(double sx, double sy); - AffineTransform& rotate(double a); - AffineTransform& rotateRadians(double a); - AffineTransform& rotateFromVector(double x, double y); - AffineTransform& translate(double tx, double ty); - AffineTransform& shear(double sx, double sy); - AffineTransform& flipX(); - AffineTransform& flipY(); - AffineTransform& skew(double angleX, double angleY); - AffineTransform& skewX(double angle); - AffineTransform& skewY(double angle); - - double xScale() const; - double yScale() const; - - double det() const; - bool isInvertible() const; - AffineTransform inverse() const; - - TransformationMatrix toTransformationMatrix() const; - - bool isIdentityOrTranslation() const - { - return m_transform[0] == 1 && m_transform[1] == 0 && m_transform[2] == 0 && m_transform[3] == 1; - } - - bool isIdentityOrTranslationOrFlipped() const - { - return m_transform[0] == 1 && m_transform[1] == 0 && m_transform[2] == 0 && (m_transform[3] == 1 || m_transform[3] == -1); - } - - bool preservesAxisAlignment() const - { - return (m_transform[1] == 0 && m_transform[2] == 0) || (m_transform[0] == 0 && m_transform[3] == 0); - } - - bool operator== (const AffineTransform& m2) const - { - return (m_transform[0] == m2.m_transform[0] - && m_transform[1] == m2.m_transform[1] - && m_transform[2] == m2.m_transform[2] - && m_transform[3] == m2.m_transform[3] - && m_transform[4] == m2.m_transform[4] - && m_transform[5] == m2.m_transform[5]); - } - - bool operator!=(const AffineTransform& other) const { return !(*this == other); } - - // *this = *this * t (i.e., a multRight) - AffineTransform& operator*=(const AffineTransform& t) - { - return multiply(t); - } - - // result = *this * t (i.e., a multRight) - AffineTransform operator*(const AffineTransform& t) const - { - AffineTransform result = *this; - result *= t; - return result; - } - - static AffineTransform translation(double x, double y) - { - return AffineTransform(1, 0, 0, 1, x, y); - } - - // decompose the matrix into its component parts - typedef struct { - double scaleX, scaleY; - double angle; - double remainderA, remainderB, remainderC, remainderD; - double translateX, translateY; - } DecomposedType; - - bool decompose(DecomposedType&) const; - void recompose(const DecomposedType&); - -private: - void setMatrix(const Transform m) - { - if (m && m != m_transform) - memcpy(m_transform, m, sizeof(Transform)); - } - - Transform m_transform; + WTF_MAKE_FAST_ALLOCATED; + + public: + typedef double Transform[6]; + + AffineTransform(); + AffineTransform(double a, double b, double c, double d, double e, double f); + + void setMatrix(double a, double b, double c, double d, double e, double f); + + void map(double x, double y, double& x2, double& y2) const; + + // Rounds the mapped point to the nearest integer value. + IntPoint mapPoint(const IntPoint&) const; + + FloatPoint mapPoint(const FloatPoint&) const; + + IntSize mapSize(const IntSize&) const; + + FloatSize mapSize(const FloatSize&) const; + + // Rounds the resulting mapped rectangle out. This is helpful for bounding + // box computations but may not be what is wanted in other contexts. + IntRect mapRect(const IntRect&) const; + + FloatRect mapRect(const FloatRect&) const; + FloatQuad mapQuad(const FloatQuad&) const; + + bool isIdentity() const; + + double a() const { return m_transform[0]; } + void setA(double a) { m_transform[0] = a; } + double b() const { return m_transform[1]; } + void setB(double b) { m_transform[1] = b; } + double c() const { return m_transform[2]; } + void setC(double c) { m_transform[2] = c; } + double d() const { return m_transform[3]; } + void setD(double d) { m_transform[3] = d; } + double e() const { return m_transform[4]; } + void setE(double e) { m_transform[4] = e; } + double f() const { return m_transform[5]; } + void setF(double f) { m_transform[5] = f; } + + void makeIdentity(); + + AffineTransform& multiply(const AffineTransform& other); + AffineTransform& scale(double); + AffineTransform& scale(double sx, double sy); + AffineTransform& scaleNonUniform(double sx, double sy); + AffineTransform& rotate(double a); + AffineTransform& rotateRadians(double a); + AffineTransform& rotateFromVector(double x, double y); + AffineTransform& translate(double tx, double ty); + AffineTransform& shear(double sx, double sy); + AffineTransform& flipX(); + AffineTransform& flipY(); + AffineTransform& skew(double angleX, double angleY); + AffineTransform& skewX(double angle); + AffineTransform& skewY(double angle); + + double xScale() const; + double yScale() const; + + double det() const; + bool isInvertible() const; + AffineTransform inverse() const; + + TransformationMatrix toTransformationMatrix() const; + + bool isIdentityOrTranslation() const { + return m_transform[0] == 1 && m_transform[1] == 0 && m_transform[2] == 0 && + m_transform[3] == 1; + } + + bool isIdentityOrTranslationOrFlipped() const { + return m_transform[0] == 1 && m_transform[1] == 0 && m_transform[2] == 0 && + (m_transform[3] == 1 || m_transform[3] == -1); + } + + bool preservesAxisAlignment() const { + return (m_transform[1] == 0 && m_transform[2] == 0) || + (m_transform[0] == 0 && m_transform[3] == 0); + } + + bool operator==(const AffineTransform& m2) const { + return (m_transform[0] == m2.m_transform[0] && + m_transform[1] == m2.m_transform[1] && + m_transform[2] == m2.m_transform[2] && + m_transform[3] == m2.m_transform[3] && + m_transform[4] == m2.m_transform[4] && + m_transform[5] == m2.m_transform[5]); + } + + bool operator!=(const AffineTransform& other) const { + return !(*this == other); + } + + // *this = *this * t (i.e., a multRight) + AffineTransform& operator*=(const AffineTransform& t) { return multiply(t); } + + // result = *this * t (i.e., a multRight) + AffineTransform operator*(const AffineTransform& t) const { + AffineTransform result = *this; + result *= t; + return result; + } + + static AffineTransform translation(double x, double y) { + return AffineTransform(1, 0, 0, 1, x, y); + } + + // decompose the matrix into its component parts + typedef struct { + double scaleX, scaleY; + double angle; + double remainderA, remainderB, remainderC, remainderD; + double translateX, translateY; + } DecomposedType; + + bool decompose(DecomposedType&) const; + void recompose(const DecomposedType&); + + private: + void setMatrix(const Transform m) { + if (m && m != m_transform) + memcpy(m_transform, m, sizeof(Transform)); + } + + Transform m_transform; }; -PLATFORM_EXPORT AffineTransform makeMapBetweenRects(const FloatRect& source, const FloatRect& dest); +PLATFORM_EXPORT AffineTransform makeMapBetweenRects(const FloatRect& source, + const FloatRect& dest); -} +} // namespace blink #endif // SKY_ENGINE_PLATFORM_TRANSFORMS_AFFINETRANSFORM_H_ diff --git a/sky/engine/platform/transforms/IdentityTransformOperation.h b/sky/engine/platform/transforms/IdentityTransformOperation.h index 234d9adc4cce3..66977c9faa175 100644 --- a/sky/engine/platform/transforms/IdentityTransformOperation.h +++ b/sky/engine/platform/transforms/IdentityTransformOperation.h @@ -30,39 +30,34 @@ namespace blink { class PLATFORM_EXPORT IdentityTransformOperation : public TransformOperation { -public: - static PassRefPtr create() - { - return adoptRef(new IdentityTransformOperation()); - } + public: + static PassRefPtr create() { + return adoptRef(new IdentityTransformOperation()); + } - virtual bool canBlendWith(const TransformOperation& other) const - { - return isSameType(other); - } + virtual bool canBlendWith(const TransformOperation& other) const { + return isSameType(other); + } -private: - virtual bool isIdentity() const override final { return true; } - virtual OperationType type() const override { return Identity; } + private: + virtual bool isIdentity() const override final { return true; } + virtual OperationType type() const override { return Identity; } - virtual bool operator==(const TransformOperation& o) const override - { - return isSameType(o); - } + virtual bool operator==(const TransformOperation& o) const override { + return isSameType(o); + } - virtual void apply(TransformationMatrix&, const FloatSize&) const override { } + virtual void apply(TransformationMatrix&, const FloatSize&) const override {} - virtual PassRefPtr blend(const TransformOperation*, double, bool = false) override - { - return this; - } - - IdentityTransformOperation() - { - } + virtual PassRefPtr blend(const TransformOperation*, + double, + bool = false) override { + return this; + } + IdentityTransformOperation() {} }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_TRANSFORMS_IDENTITYTRANSFORMOPERATION_H_ diff --git a/sky/engine/platform/transforms/InterpolatedTransformOperation.cpp b/sky/engine/platform/transforms/InterpolatedTransformOperation.cpp index faad887f8bef8..978ac691f5194 100644 --- a/sky/engine/platform/transforms/InterpolatedTransformOperation.cpp +++ b/sky/engine/platform/transforms/InterpolatedTransformOperation.cpp @@ -34,39 +34,43 @@ namespace blink { -bool InterpolatedTransformOperation::operator==(const TransformOperation& o) const -{ - if (!isSameType(o)) - return false; - const InterpolatedTransformOperation* t = static_cast(&o); - return progress == t->progress && from == t->from && to == t->to; +bool InterpolatedTransformOperation::operator==( + const TransformOperation& o) const { + if (!isSameType(o)) + return false; + const InterpolatedTransformOperation* t = + static_cast(&o); + return progress == t->progress && from == t->from && to == t->to; } -void InterpolatedTransformOperation::apply(TransformationMatrix& transform, const FloatSize& borderBoxSize) const -{ - TransformationMatrix fromTransform; - TransformationMatrix toTransform; - from.apply(borderBoxSize, fromTransform); - to.apply(borderBoxSize, toTransform); +void InterpolatedTransformOperation::apply( + TransformationMatrix& transform, + const FloatSize& borderBoxSize) const { + TransformationMatrix fromTransform; + TransformationMatrix toTransform; + from.apply(borderBoxSize, fromTransform); + to.apply(borderBoxSize, toTransform); - toTransform.blend(fromTransform, progress); - transform.multiply(toTransform); + toTransform.blend(fromTransform, progress); + transform.multiply(toTransform); } -PassRefPtr InterpolatedTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity) -{ - if (from && !from->isSameType(*this)) - return this; +PassRefPtr InterpolatedTransformOperation::blend( + const TransformOperation* from, + double progress, + bool blendToIdentity) { + if (from && !from->isSameType(*this)) + return this; - TransformOperations thisOperations; - thisOperations.operations().append(this); - TransformOperations fromOperations; - if (blendToIdentity) - fromOperations.operations().append(IdentityTransformOperation::create()); - else - fromOperations.operations().append(const_cast(from)); - return InterpolatedTransformOperation::create(thisOperations, fromOperations, progress); + TransformOperations thisOperations; + thisOperations.operations().append(this); + TransformOperations fromOperations; + if (blendToIdentity) + fromOperations.operations().append(IdentityTransformOperation::create()); + else + fromOperations.operations().append(const_cast(from)); + return InterpolatedTransformOperation::create(thisOperations, fromOperations, + progress); } -} // namespace blink - +} // namespace blink diff --git a/sky/engine/platform/transforms/InterpolatedTransformOperation.h b/sky/engine/platform/transforms/InterpolatedTransformOperation.h index fd8d4c4773b5c..2a9df8acc3e36 100644 --- a/sky/engine/platform/transforms/InterpolatedTransformOperation.h +++ b/sky/engine/platform/transforms/InterpolatedTransformOperation.h @@ -37,45 +37,48 @@ namespace blink { // This class is an implementation detail for deferred interpolations. -class PLATFORM_EXPORT InterpolatedTransformOperation : public TransformOperation { -public: - static PassRefPtr create(const TransformOperations& from, const TransformOperations& to, double progress) - { - return adoptRef(new InterpolatedTransformOperation(from, to, progress)); - } +class PLATFORM_EXPORT InterpolatedTransformOperation + : public TransformOperation { + public: + static PassRefPtr create( + const TransformOperations& from, + const TransformOperations& to, + double progress) { + return adoptRef(new InterpolatedTransformOperation(from, to, progress)); + } - virtual bool canBlendWith(const TransformOperation& other) const - { - return isSameType(other); - } + virtual bool canBlendWith(const TransformOperation& other) const { + return isSameType(other); + } -private: - virtual bool isIdentity() const override { return false; } + private: + virtual bool isIdentity() const override { return false; } - virtual OperationType type() const override { return Interpolated; } + virtual OperationType type() const override { return Interpolated; } - virtual bool operator==(const TransformOperation&) const override; - virtual void apply(TransformationMatrix&, const FloatSize& borderBoxSize) const override; + virtual bool operator==(const TransformOperation&) const override; + virtual void apply(TransformationMatrix&, + const FloatSize& borderBoxSize) const override; - virtual PassRefPtr blend(const TransformOperation* from, double progress, bool blendToIdentity = false) override; + virtual PassRefPtr blend( + const TransformOperation* from, + double progress, + bool blendToIdentity = false) override; - virtual bool dependsOnBoxSize() const override - { - return from.dependsOnBoxSize() || to.dependsOnBoxSize(); - } + virtual bool dependsOnBoxSize() const override { + return from.dependsOnBoxSize() || to.dependsOnBoxSize(); + } - InterpolatedTransformOperation(const TransformOperations& from, const TransformOperations& to, double progress) - : from(from) - , to(to) - , progress(progress) - { } + InterpolatedTransformOperation(const TransformOperations& from, + const TransformOperations& to, + double progress) + : from(from), to(to), progress(progress) {} - const TransformOperations from; - const TransformOperations to; - double progress; + const TransformOperations from; + const TransformOperations to; + double progress; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_TRANSFORMS_INTERPOLATEDTRANSFORMOPERATION_H_ - diff --git a/sky/engine/platform/transforms/Matrix3DTransformOperation.cpp b/sky/engine/platform/transforms/Matrix3DTransformOperation.cpp index 05c3b4a0f8856..b034d1d40e26f 100644 --- a/sky/engine/platform/transforms/Matrix3DTransformOperation.cpp +++ b/sky/engine/platform/transforms/Matrix3DTransformOperation.cpp @@ -29,25 +29,27 @@ namespace blink { -PassRefPtr Matrix3DTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity) -{ - if (from && !from->isSameType(*this)) - return this; - - // Convert the TransformOperations into matrices - FloatSize size; - TransformationMatrix fromT; - TransformationMatrix toT; - if (from) - from->apply(fromT, size); - - apply(toT, size); - - if (blendToIdentity) - std::swap(fromT, toT); - - toT.blend(fromT, progress); - return Matrix3DTransformOperation::create(toT); +PassRefPtr Matrix3DTransformOperation::blend( + const TransformOperation* from, + double progress, + bool blendToIdentity) { + if (from && !from->isSameType(*this)) + return this; + + // Convert the TransformOperations into matrices + FloatSize size; + TransformationMatrix fromT; + TransformationMatrix toT; + if (from) + from->apply(fromT, size); + + apply(toT, size); + + if (blendToIdentity) + std::swap(fromT, toT); + + toT.blend(fromT, progress); + return Matrix3DTransformOperation::create(toT); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/transforms/Matrix3DTransformOperation.h b/sky/engine/platform/transforms/Matrix3DTransformOperation.h index 2195c7aca08d9..ae48975f96f96 100644 --- a/sky/engine/platform/transforms/Matrix3DTransformOperation.h +++ b/sky/engine/platform/transforms/Matrix3DTransformOperation.h @@ -31,47 +31,48 @@ namespace blink { class PLATFORM_EXPORT Matrix3DTransformOperation : public TransformOperation { -public: - static PassRefPtr create(const TransformationMatrix& matrix) - { - return adoptRef(new Matrix3DTransformOperation(matrix)); - } + public: + static PassRefPtr create( + const TransformationMatrix& matrix) { + return adoptRef(new Matrix3DTransformOperation(matrix)); + } - TransformationMatrix matrix() const {return m_matrix; } + TransformationMatrix matrix() const { return m_matrix; } - virtual bool canBlendWith(const TransformOperation& other) const - { - return false; - } + virtual bool canBlendWith(const TransformOperation& other) const { + return false; + } -private: - virtual bool isIdentity() const override { return m_matrix.isIdentity(); } + private: + virtual bool isIdentity() const override { return m_matrix.isIdentity(); } - virtual OperationType type() const override { return Matrix3D; } + virtual OperationType type() const override { return Matrix3D; } - virtual bool operator==(const TransformOperation& o) const override - { - if (!isSameType(o)) - return false; - const Matrix3DTransformOperation* m = static_cast(&o); - return m_matrix == m->m_matrix; - } + virtual bool operator==(const TransformOperation& o) const override { + if (!isSameType(o)) + return false; + const Matrix3DTransformOperation* m = + static_cast(&o); + return m_matrix == m->m_matrix; + } - virtual void apply(TransformationMatrix& transform, const FloatSize&) const override - { - transform.multiply(TransformationMatrix(m_matrix)); - } + virtual void apply(TransformationMatrix& transform, + const FloatSize&) const override { + transform.multiply(TransformationMatrix(m_matrix)); + } - virtual PassRefPtr blend(const TransformOperation* from, double progress, bool blendToIdentity = false) override; + virtual PassRefPtr blend( + const TransformOperation* from, + double progress, + bool blendToIdentity = false) override; - Matrix3DTransformOperation(const TransformationMatrix& mat) - { - m_matrix = mat; - } + Matrix3DTransformOperation(const TransformationMatrix& mat) { + m_matrix = mat; + } - TransformationMatrix m_matrix; + TransformationMatrix m_matrix; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_TRANSFORMS_MATRIX3DTRANSFORMOPERATION_H_ diff --git a/sky/engine/platform/transforms/MatrixTransformOperation.cpp b/sky/engine/platform/transforms/MatrixTransformOperation.cpp index 81d6b1fe3077a..8e0fe3bd1c108 100644 --- a/sky/engine/platform/transforms/MatrixTransformOperation.cpp +++ b/sky/engine/platform/transforms/MatrixTransformOperation.cpp @@ -25,25 +25,29 @@ namespace blink { -PassRefPtr MatrixTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity) -{ - if (from && !from->isSameType(*this)) - return this; - - // convert the TransformOperations into matrices - FloatSize size; - TransformationMatrix fromT; - TransformationMatrix toT(m_a, m_b, m_c, m_d, m_e, m_f); - if (from) { - const MatrixTransformOperation* m = static_cast(from); - fromT.setMatrix(m->m_a, m->m_b, m->m_c, m->m_d, m->m_e, m->m_f); - } - - if (blendToIdentity) - std::swap(fromT, toT); - - toT.blend(fromT, progress); - return MatrixTransformOperation::create(toT.a(), toT.b(), toT.c(), toT.d(), toT.e(), toT.f()); +PassRefPtr MatrixTransformOperation::blend( + const TransformOperation* from, + double progress, + bool blendToIdentity) { + if (from && !from->isSameType(*this)) + return this; + + // convert the TransformOperations into matrices + FloatSize size; + TransformationMatrix fromT; + TransformationMatrix toT(m_a, m_b, m_c, m_d, m_e, m_f); + if (from) { + const MatrixTransformOperation* m = + static_cast(from); + fromT.setMatrix(m->m_a, m->m_b, m->m_c, m->m_d, m->m_e, m->m_f); + } + + if (blendToIdentity) + std::swap(fromT, toT); + + toT.blend(fromT, progress); + return MatrixTransformOperation::create(toT.a(), toT.b(), toT.c(), toT.d(), + toT.e(), toT.f()); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/transforms/MatrixTransformOperation.h b/sky/engine/platform/transforms/MatrixTransformOperation.h index edc64397e6b7c..73aa1bc82549c 100644 --- a/sky/engine/platform/transforms/MatrixTransformOperation.h +++ b/sky/engine/platform/transforms/MatrixTransformOperation.h @@ -31,74 +31,81 @@ namespace blink { class PLATFORM_EXPORT MatrixTransformOperation : public TransformOperation { -public: - static PassRefPtr create(double a, double b, double c, double d, double e, double f) - { - return adoptRef(new MatrixTransformOperation(a, b, c, d, e, f)); - } - - static PassRefPtr create(const TransformationMatrix& t) - { - return adoptRef(new MatrixTransformOperation(t)); - } - - TransformationMatrix matrix() const { return TransformationMatrix(m_a, m_b, m_c, m_d, m_e, m_f); } - - virtual bool canBlendWith(const TransformOperation& other) const - { - return false; - } - -private: - virtual bool isIdentity() const override { return m_a == 1 && !m_b && !m_c && m_d == 1 && !m_e && !m_f; } - - virtual OperationType type() const override { return Matrix; } - - virtual bool operator==(const TransformOperation& o) const override - { - if (!isSameType(o)) - return false; - - const MatrixTransformOperation* m = static_cast(&o); - return m_a == m->m_a && m_b == m->m_b && m_c == m->m_c && m_d == m->m_d && m_e == m->m_e && m_f == m->m_f; - } - - virtual void apply(TransformationMatrix& transform, const FloatSize&) const override - { - TransformationMatrix matrix(m_a, m_b, m_c, m_d, m_e, m_f); - transform.multiply(matrix); - } - - virtual PassRefPtr blend(const TransformOperation* from, double progress, bool blendToIdentity = false) override; - - MatrixTransformOperation(double a, double b, double c, double d, double e, double f) - : m_a(a) - , m_b(b) - , m_c(c) - , m_d(d) - , m_e(e) - , m_f(f) - { - } - - MatrixTransformOperation(const TransformationMatrix& t) - : m_a(t.a()) - , m_b(t.b()) - , m_c(t.c()) - , m_d(t.d()) - , m_e(t.e()) - , m_f(t.f()) - { - } - - double m_a; - double m_b; - double m_c; - double m_d; - double m_e; - double m_f; + public: + static PassRefPtr create(double a, + double b, + double c, + double d, + double e, + double f) { + return adoptRef(new MatrixTransformOperation(a, b, c, d, e, f)); + } + + static PassRefPtr create( + const TransformationMatrix& t) { + return adoptRef(new MatrixTransformOperation(t)); + } + + TransformationMatrix matrix() const { + return TransformationMatrix(m_a, m_b, m_c, m_d, m_e, m_f); + } + + virtual bool canBlendWith(const TransformOperation& other) const { + return false; + } + + private: + virtual bool isIdentity() const override { + return m_a == 1 && !m_b && !m_c && m_d == 1 && !m_e && !m_f; + } + + virtual OperationType type() const override { return Matrix; } + + virtual bool operator==(const TransformOperation& o) const override { + if (!isSameType(o)) + return false; + + const MatrixTransformOperation* m = + static_cast(&o); + return m_a == m->m_a && m_b == m->m_b && m_c == m->m_c && m_d == m->m_d && + m_e == m->m_e && m_f == m->m_f; + } + + virtual void apply(TransformationMatrix& transform, + const FloatSize&) const override { + TransformationMatrix matrix(m_a, m_b, m_c, m_d, m_e, m_f); + transform.multiply(matrix); + } + + virtual PassRefPtr blend( + const TransformOperation* from, + double progress, + bool blendToIdentity = false) override; + + MatrixTransformOperation(double a, + double b, + double c, + double d, + double e, + double f) + : m_a(a), m_b(b), m_c(c), m_d(d), m_e(e), m_f(f) {} + + MatrixTransformOperation(const TransformationMatrix& t) + : m_a(t.a()), + m_b(t.b()), + m_c(t.c()), + m_d(t.d()), + m_e(t.e()), + m_f(t.f()) {} + + double m_a; + double m_b; + double m_c; + double m_d; + double m_e; + double m_f; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_TRANSFORMS_MATRIXTRANSFORMOPERATION_H_ diff --git a/sky/engine/platform/transforms/PerspectiveTransformOperation.cpp b/sky/engine/platform/transforms/PerspectiveTransformOperation.cpp index 4fffea3ff5ca1..313447d510900 100644 --- a/sky/engine/platform/transforms/PerspectiveTransformOperation.cpp +++ b/sky/engine/platform/transforms/PerspectiveTransformOperation.cpp @@ -30,31 +30,36 @@ namespace blink { -PassRefPtr PerspectiveTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity) -{ - if (from && !from->isSameType(*this)) - return this; - - if (blendToIdentity) { - double p = blink::blend(m_p, 1., progress); // FIXME: this seems wrong. https://bugs.webkit.org/show_bug.cgi?id=52700 - return PerspectiveTransformOperation::create(clampToPositiveInteger(p)); - } - - const PerspectiveTransformOperation* fromOp = static_cast(from); - - TransformationMatrix fromT; - TransformationMatrix toT; - fromT.applyPerspective(fromOp ? fromOp->m_p : 0); - toT.applyPerspective(m_p); - toT.blend(fromT, progress); - TransformationMatrix::DecomposedType decomp; - toT.decompose(decomp); - - if (decomp.perspectiveZ) { - double val = -1.0 / decomp.perspectiveZ; - return PerspectiveTransformOperation::create(clampToPositiveInteger(val)); - } - return PerspectiveTransformOperation::create(0); +PassRefPtr PerspectiveTransformOperation::blend( + const TransformOperation* from, + double progress, + bool blendToIdentity) { + if (from && !from->isSameType(*this)) + return this; + + if (blendToIdentity) { + double p = blink::blend( + m_p, 1., progress); // FIXME: this seems wrong. + // https://bugs.webkit.org/show_bug.cgi?id=52700 + return PerspectiveTransformOperation::create(clampToPositiveInteger(p)); + } + + const PerspectiveTransformOperation* fromOp = + static_cast(from); + + TransformationMatrix fromT; + TransformationMatrix toT; + fromT.applyPerspective(fromOp ? fromOp->m_p : 0); + toT.applyPerspective(m_p); + toT.blend(fromT, progress); + TransformationMatrix::DecomposedType decomp; + toT.decompose(decomp); + + if (decomp.perspectiveZ) { + double val = -1.0 / decomp.perspectiveZ; + return PerspectiveTransformOperation::create(clampToPositiveInteger(val)); + } + return PerspectiveTransformOperation::create(0); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/transforms/PerspectiveTransformOperation.h b/sky/engine/platform/transforms/PerspectiveTransformOperation.h index 1974fd25b6e12..0a14f4d7133d5 100644 --- a/sky/engine/platform/transforms/PerspectiveTransformOperation.h +++ b/sky/engine/platform/transforms/PerspectiveTransformOperation.h @@ -30,47 +30,46 @@ namespace blink { -class PLATFORM_EXPORT PerspectiveTransformOperation : public TransformOperation { -public: - static PassRefPtr create(double p) - { - return adoptRef(new PerspectiveTransformOperation(p)); - } +class PLATFORM_EXPORT PerspectiveTransformOperation + : public TransformOperation { + public: + static PassRefPtr create(double p) { + return adoptRef(new PerspectiveTransformOperation(p)); + } - double perspective() const { return m_p; } + double perspective() const { return m_p; } - virtual bool canBlendWith(const TransformOperation& other) const - { - return isSameType(other); - } + virtual bool canBlendWith(const TransformOperation& other) const { + return isSameType(other); + } -private: - virtual bool isIdentity() const override { return !m_p; } - virtual OperationType type() const override { return Perspective; } + private: + virtual bool isIdentity() const override { return !m_p; } + virtual OperationType type() const override { return Perspective; } - virtual bool operator==(const TransformOperation& o) const override - { - if (!isSameType(o)) - return false; - const PerspectiveTransformOperation* p = static_cast(&o); - return m_p == p->m_p; - } + virtual bool operator==(const TransformOperation& o) const override { + if (!isSameType(o)) + return false; + const PerspectiveTransformOperation* p = + static_cast(&o); + return m_p == p->m_p; + } - virtual void apply(TransformationMatrix& transform, const FloatSize&) const override - { - transform.applyPerspective(m_p); - } + virtual void apply(TransformationMatrix& transform, + const FloatSize&) const override { + transform.applyPerspective(m_p); + } - virtual PassRefPtr blend(const TransformOperation* from, double progress, bool blendToIdentity = false) override; + virtual PassRefPtr blend( + const TransformOperation* from, + double progress, + bool blendToIdentity = false) override; - PerspectiveTransformOperation(double p) - : m_p(p) - { - } + PerspectiveTransformOperation(double p) : m_p(p) {} - double m_p; + double m_p; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_TRANSFORMS_PERSPECTIVETRANSFORMOPERATION_H_ diff --git a/sky/engine/platform/transforms/RotateTransformOperation.cpp b/sky/engine/platform/transforms/RotateTransformOperation.cpp index 48daf63177d86..c284bf261514a 100644 --- a/sky/engine/platform/transforms/RotateTransformOperation.cpp +++ b/sky/engine/platform/transforms/RotateTransformOperation.cpp @@ -30,127 +30,132 @@ namespace blink { static const double angleEpsilon = 1e-4; -FloatPoint3D RotateTransformOperation::axis() const -{ - return FloatPoint3D(x(), y(), z()); +FloatPoint3D RotateTransformOperation::axis() const { + return FloatPoint3D(x(), y(), z()); } -bool RotateTransformOperation::shareSameAxis(const RotateTransformOperation* from, const RotateTransformOperation* to, FloatPoint3D* axis, double* fromAngle, double* toAngle) -{ - *axis = FloatPoint3D(0, 0, 1); - *fromAngle = 0; - *toAngle = 0; - - if (!from && !to) - return true; +bool RotateTransformOperation::shareSameAxis( + const RotateTransformOperation* from, + const RotateTransformOperation* to, + FloatPoint3D* axis, + double* fromAngle, + double* toAngle) { + *axis = FloatPoint3D(0, 0, 1); + *fromAngle = 0; + *toAngle = 0; + + if (!from && !to) + return true; - bool fromZero = !from || from->axis().isZero(); - bool toZero = !to || to->axis().isZero(); + bool fromZero = !from || from->axis().isZero(); + bool toZero = !to || to->axis().isZero(); - if (fromZero && toZero) - return true; + if (fromZero && toZero) + return true; - if (fromZero) { - *axis = to->axis(); - *toAngle = to->angle(); - return true; - } + if (fromZero) { + *axis = to->axis(); + *toAngle = to->angle(); + return true; + } - if (toZero) { - *axis = from->axis(); - *fromAngle = from->angle(); - return true; - } + if (toZero) { + *axis = from->axis(); + *fromAngle = from->angle(); + return true; + } - FloatPoint3D fromAxis = from->axis(); - FloatPoint3D toAxis = to->axis(); + FloatPoint3D fromAxis = from->axis(); + FloatPoint3D toAxis = to->axis(); - double fromSquared = fromAxis.lengthSquared(); - double toSquared = toAxis.lengthSquared(); + double fromSquared = fromAxis.lengthSquared(); + double toSquared = toAxis.lengthSquared(); - double dot = fromAxis.dot(toAxis); - double error = std::abs(1 - (dot * dot) / (fromSquared * toSquared)); + double dot = fromAxis.dot(toAxis); + double error = std::abs(1 - (dot * dot) / (fromSquared * toSquared)); - if (error > angleEpsilon) - return false; - *axis = from->axis(); - *fromAngle = from->angle(); - *toAngle = to->angle(); - return true; + if (error > angleEpsilon) + return false; + *axis = from->axis(); + *fromAngle = from->angle(); + *toAngle = to->angle(); + return true; } -PassRefPtr RotateTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity) -{ - if (from && !from->isSameType(*this)) - return this; - - if (blendToIdentity) - return RotateTransformOperation::create(m_x, m_y, m_z, m_angle - m_angle * progress, m_type); - - const RotateTransformOperation* fromOp = static_cast(from); - - // Optimize for single axis rotation - if (!fromOp || (fromOp->m_x == 0 && fromOp->m_y == 0 && fromOp->m_z == 1) || - (fromOp->m_x == 0 && fromOp->m_y == 1 && fromOp->m_z == 0) || - (fromOp->m_x == 1 && fromOp->m_y == 0 && fromOp->m_z == 0)) { - double fromAngle = fromOp ? fromOp->m_angle : 0; - return RotateTransformOperation::create(fromOp ? fromOp->m_x : m_x, - fromOp ? fromOp->m_y : m_y, - fromOp ? fromOp->m_z : m_z, - blink::blend(fromAngle, m_angle, progress), m_type); - } - double fromAngle; - double toAngle; - FloatPoint3D axis; - - if (shareSameAxis(fromOp, this, &axis, &fromAngle, &toAngle)) - return RotateTransformOperation::create(axis.x(), axis.y(), axis.z(), blink::blend(fromAngle, toAngle, progress), m_type); - - const RotateTransformOperation* toOp = this; - - // Create the 2 rotation matrices - TransformationMatrix fromT; - TransformationMatrix toT; - fromT.rotate3d((fromOp ? fromOp->m_x : 0), - (fromOp ? fromOp->m_y : 0), - (fromOp ? fromOp->m_z : 1), - (fromOp ? fromOp->m_angle : 0)); - - toT.rotate3d((toOp ? toOp->m_x : 0), - (toOp ? toOp->m_y : 0), - (toOp ? toOp->m_z : 1), - (toOp ? toOp->m_angle : 0)); - - // Blend them - toT.blend(fromT, progress); - - // Extract the result as a quaternion - TransformationMatrix::DecomposedType decomp; - toT.decompose(decomp); - - // Convert that to Axis/Angle form - double x = -decomp.quaternionX; - double y = -decomp.quaternionY; - double z = -decomp.quaternionZ; - double length = std::sqrt(x * x + y * y + z * z); - double angle = 0; - - if (length > 0.00001) { - x /= length; - y /= length; - z /= length; - angle = rad2deg(std::acos(decomp.quaternionW) * 2); - } else { - x = 0; - y = 0; - z = 1; - } - return RotateTransformOperation::create(x, y, z, angle, Rotate3D); +PassRefPtr RotateTransformOperation::blend( + const TransformOperation* from, + double progress, + bool blendToIdentity) { + if (from && !from->isSameType(*this)) + return this; + + if (blendToIdentity) + return RotateTransformOperation::create( + m_x, m_y, m_z, m_angle - m_angle * progress, m_type); + + const RotateTransformOperation* fromOp = + static_cast(from); + + // Optimize for single axis rotation + if (!fromOp || (fromOp->m_x == 0 && fromOp->m_y == 0 && fromOp->m_z == 1) || + (fromOp->m_x == 0 && fromOp->m_y == 1 && fromOp->m_z == 0) || + (fromOp->m_x == 1 && fromOp->m_y == 0 && fromOp->m_z == 0)) { + double fromAngle = fromOp ? fromOp->m_angle : 0; + return RotateTransformOperation::create( + fromOp ? fromOp->m_x : m_x, fromOp ? fromOp->m_y : m_y, + fromOp ? fromOp->m_z : m_z, blink::blend(fromAngle, m_angle, progress), + m_type); + } + double fromAngle; + double toAngle; + FloatPoint3D axis; + + if (shareSameAxis(fromOp, this, &axis, &fromAngle, &toAngle)) + return RotateTransformOperation::create( + axis.x(), axis.y(), axis.z(), + blink::blend(fromAngle, toAngle, progress), m_type); + + const RotateTransformOperation* toOp = this; + + // Create the 2 rotation matrices + TransformationMatrix fromT; + TransformationMatrix toT; + fromT.rotate3d((fromOp ? fromOp->m_x : 0), (fromOp ? fromOp->m_y : 0), + (fromOp ? fromOp->m_z : 1), (fromOp ? fromOp->m_angle : 0)); + + toT.rotate3d((toOp ? toOp->m_x : 0), (toOp ? toOp->m_y : 0), + (toOp ? toOp->m_z : 1), (toOp ? toOp->m_angle : 0)); + + // Blend them + toT.blend(fromT, progress); + + // Extract the result as a quaternion + TransformationMatrix::DecomposedType decomp; + toT.decompose(decomp); + + // Convert that to Axis/Angle form + double x = -decomp.quaternionX; + double y = -decomp.quaternionY; + double z = -decomp.quaternionZ; + double length = std::sqrt(x * x + y * y + z * z); + double angle = 0; + + if (length > 0.00001) { + x /= length; + y /= length; + z /= length; + angle = rad2deg(std::acos(decomp.quaternionW) * 2); + } else { + x = 0; + y = 0; + z = 1; + } + return RotateTransformOperation::create(x, y, z, angle, Rotate3D); } -bool RotateTransformOperation::canBlendWith(const TransformOperation& other) const -{ - return other.isSameType(*this); +bool RotateTransformOperation::canBlendWith( + const TransformOperation& other) const { + return other.isSameType(*this); } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/transforms/RotateTransformOperation.h b/sky/engine/platform/transforms/RotateTransformOperation.h index cb029a6e40790..1fabd70d6917e 100644 --- a/sky/engine/platform/transforms/RotateTransformOperation.h +++ b/sky/engine/platform/transforms/RotateTransformOperation.h @@ -30,63 +30,74 @@ namespace blink { class PLATFORM_EXPORT RotateTransformOperation : public TransformOperation { -public: - static PassRefPtr create(double angle, OperationType type) - { - return adoptRef(new RotateTransformOperation(0, 0, 1, angle, type)); - } - - static PassRefPtr create(double x, double y, double z, double angle, OperationType type) - { - return adoptRef(new RotateTransformOperation(x, y, z, angle, type)); - } - - double x() const { return m_x; } - double y() const { return m_y; } - double z() const { return m_z; } - double angle() const { return m_angle; } - - FloatPoint3D axis() const; - static bool shareSameAxis(const RotateTransformOperation* fromRotation, const RotateTransformOperation* toRotation, FloatPoint3D* axis, double* fromAngle, double* toAngle); - - virtual bool canBlendWith(const TransformOperation& other) const; - virtual OperationType type() const override { return m_type; } - -private: - virtual bool isIdentity() const override { return !m_angle; } - - virtual bool operator==(const TransformOperation& o) const override - { - if (!isSameType(o)) - return false; - const RotateTransformOperation* r = static_cast(&o); - return m_x == r->m_x && m_y == r->m_y && m_z == r->m_z && m_angle == r->m_angle; - } - - virtual void apply(TransformationMatrix& transform, const FloatSize& /*borderBoxSize*/) const override - { - transform.rotate3d(m_x, m_y, m_z, m_angle); - } - - virtual PassRefPtr blend(const TransformOperation* from, double progress, bool blendToIdentity = false) override; - - RotateTransformOperation(double x, double y, double z, double angle, OperationType type) - : m_x(x) - , m_y(y) - , m_z(z) - , m_angle(angle) - , m_type(type) - { - ASSERT(type == RotateX || type == RotateY || type == RotateZ || type == Rotate3D); - } - - double m_x; - double m_y; - double m_z; - double m_angle; - OperationType m_type; + public: + static PassRefPtr create(double angle, + OperationType type) { + return adoptRef(new RotateTransformOperation(0, 0, 1, angle, type)); + } + + static PassRefPtr create(double x, + double y, + double z, + double angle, + OperationType type) { + return adoptRef(new RotateTransformOperation(x, y, z, angle, type)); + } + + double x() const { return m_x; } + double y() const { return m_y; } + double z() const { return m_z; } + double angle() const { return m_angle; } + + FloatPoint3D axis() const; + static bool shareSameAxis(const RotateTransformOperation* fromRotation, + const RotateTransformOperation* toRotation, + FloatPoint3D* axis, + double* fromAngle, + double* toAngle); + + virtual bool canBlendWith(const TransformOperation& other) const; + virtual OperationType type() const override { return m_type; } + + private: + virtual bool isIdentity() const override { return !m_angle; } + + virtual bool operator==(const TransformOperation& o) const override { + if (!isSameType(o)) + return false; + const RotateTransformOperation* r = + static_cast(&o); + return m_x == r->m_x && m_y == r->m_y && m_z == r->m_z && + m_angle == r->m_angle; + } + + virtual void apply(TransformationMatrix& transform, + const FloatSize& /*borderBoxSize*/) const override { + transform.rotate3d(m_x, m_y, m_z, m_angle); + } + + virtual PassRefPtr blend( + const TransformOperation* from, + double progress, + bool blendToIdentity = false) override; + + RotateTransformOperation(double x, + double y, + double z, + double angle, + OperationType type) + : m_x(x), m_y(y), m_z(z), m_angle(angle), m_type(type) { + ASSERT(type == RotateX || type == RotateY || type == RotateZ || + type == Rotate3D); + } + + double m_x; + double m_y; + double m_z; + double m_angle; + OperationType m_type; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_TRANSFORMS_ROTATETRANSFORMOPERATION_H_ diff --git a/sky/engine/platform/transforms/ScaleTransformOperation.cpp b/sky/engine/platform/transforms/ScaleTransformOperation.cpp index c2434b99db502..c332f8290aa11 100644 --- a/sky/engine/platform/transforms/ScaleTransformOperation.cpp +++ b/sky/engine/platform/transforms/ScaleTransformOperation.cpp @@ -25,33 +25,33 @@ namespace blink { -PassRefPtr ScaleTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity) -{ - if (from && !from->canBlendWith(*this)) - return this; - - if (blendToIdentity) - return ScaleTransformOperation::create(blink::blend(m_x, 1.0, progress), - blink::blend(m_y, 1.0, progress), - blink::blend(m_z, 1.0, progress), m_type); - - const ScaleTransformOperation* fromOp = static_cast(from); - double fromX = fromOp ? fromOp->m_x : 1.0; - double fromY = fromOp ? fromOp->m_y : 1.0; - double fromZ = fromOp ? fromOp->m_z : 1.0; - return ScaleTransformOperation::create(blink::blend(fromX, m_x, progress), - blink::blend(fromY, m_y, progress), - blink::blend(fromZ, m_z, progress), m_type); +PassRefPtr ScaleTransformOperation::blend( + const TransformOperation* from, + double progress, + bool blendToIdentity) { + if (from && !from->canBlendWith(*this)) + return this; + + if (blendToIdentity) + return ScaleTransformOperation::create( + blink::blend(m_x, 1.0, progress), blink::blend(m_y, 1.0, progress), + blink::blend(m_z, 1.0, progress), m_type); + + const ScaleTransformOperation* fromOp = + static_cast(from); + double fromX = fromOp ? fromOp->m_x : 1.0; + double fromY = fromOp ? fromOp->m_y : 1.0; + double fromZ = fromOp ? fromOp->m_z : 1.0; + return ScaleTransformOperation::create( + blink::blend(fromX, m_x, progress), blink::blend(fromY, m_y, progress), + blink::blend(fromZ, m_z, progress), m_type); } - -bool ScaleTransformOperation::canBlendWith(const TransformOperation& other) const -{ - return other.type() == ScaleX - || other.type() == ScaleY - || other.type() == ScaleZ - || other.type() == Scale3D - || other.type() == Scale; +bool ScaleTransformOperation::canBlendWith( + const TransformOperation& other) const { + return other.type() == ScaleX || other.type() == ScaleY || + other.type() == ScaleZ || other.type() == Scale3D || + other.type() == Scale; } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/transforms/ScaleTransformOperation.h b/sky/engine/platform/transforms/ScaleTransformOperation.h index 29239d82f7021..88c733b6afe5a 100644 --- a/sky/engine/platform/transforms/ScaleTransformOperation.h +++ b/sky/engine/platform/transforms/ScaleTransformOperation.h @@ -30,58 +30,63 @@ namespace blink { class PLATFORM_EXPORT ScaleTransformOperation : public TransformOperation { -public: - static PassRefPtr create(double sx, double sy, OperationType type) - { - return adoptRef(new ScaleTransformOperation(sx, sy, 1, type)); - } - - static PassRefPtr create(double sx, double sy, double sz, OperationType type) - { - return adoptRef(new ScaleTransformOperation(sx, sy, sz, type)); - } - - double x() const { return m_x; } - double y() const { return m_y; } - double z() const { return m_z; } - - virtual bool canBlendWith(const TransformOperation& other) const; - -private: - virtual bool isIdentity() const override { return m_x == 1 && m_y == 1 && m_z == 1; } - - virtual OperationType type() const override { return m_type; } - - virtual bool operator==(const TransformOperation& o) const override - { - if (!isSameType(o)) - return false; - const ScaleTransformOperation* s = static_cast(&o); - return m_x == s->m_x && m_y == s->m_y && m_z == s->m_z; - } - - virtual void apply(TransformationMatrix& transform, const FloatSize&) const override - { - transform.scale3d(m_x, m_y, m_z); - } - - virtual PassRefPtr blend(const TransformOperation* from, double progress, bool blendToIdentity = false) override; - - ScaleTransformOperation(double sx, double sy, double sz, OperationType type) - : m_x(sx) - , m_y(sy) - , m_z(sz) - , m_type(type) - { - ASSERT(type == ScaleX || type == ScaleY || type == ScaleZ || type == Scale || type == Scale3D); - } - - double m_x; - double m_y; - double m_z; - OperationType m_type; + public: + static PassRefPtr create(double sx, + double sy, + OperationType type) { + return adoptRef(new ScaleTransformOperation(sx, sy, 1, type)); + } + + static PassRefPtr create(double sx, + double sy, + double sz, + OperationType type) { + return adoptRef(new ScaleTransformOperation(sx, sy, sz, type)); + } + + double x() const { return m_x; } + double y() const { return m_y; } + double z() const { return m_z; } + + virtual bool canBlendWith(const TransformOperation& other) const; + + private: + virtual bool isIdentity() const override { + return m_x == 1 && m_y == 1 && m_z == 1; + } + + virtual OperationType type() const override { return m_type; } + + virtual bool operator==(const TransformOperation& o) const override { + if (!isSameType(o)) + return false; + const ScaleTransformOperation* s = + static_cast(&o); + return m_x == s->m_x && m_y == s->m_y && m_z == s->m_z; + } + + virtual void apply(TransformationMatrix& transform, + const FloatSize&) const override { + transform.scale3d(m_x, m_y, m_z); + } + + virtual PassRefPtr blend( + const TransformOperation* from, + double progress, + bool blendToIdentity = false) override; + + ScaleTransformOperation(double sx, double sy, double sz, OperationType type) + : m_x(sx), m_y(sy), m_z(sz), m_type(type) { + ASSERT(type == ScaleX || type == ScaleY || type == ScaleZ || + type == Scale || type == Scale3D); + } + + double m_x; + double m_y; + double m_z; + OperationType m_type; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_TRANSFORMS_SCALETRANSFORMOPERATION_H_ diff --git a/sky/engine/platform/transforms/SkewTransformOperation.cpp b/sky/engine/platform/transforms/SkewTransformOperation.cpp index 04333f1a2b26b..a4b4174034edb 100644 --- a/sky/engine/platform/transforms/SkewTransformOperation.cpp +++ b/sky/engine/platform/transforms/SkewTransformOperation.cpp @@ -25,25 +25,30 @@ namespace blink { -PassRefPtr SkewTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity) -{ - if (from && !from->canBlendWith(*this)) - return this; - - if (blendToIdentity) - return SkewTransformOperation::create(blink::blend(m_angleX, 0.0, progress), blink::blend(m_angleY, 0.0, progress), m_type); - - const SkewTransformOperation* fromOp = static_cast(from); - double fromAngleX = fromOp ? fromOp->m_angleX : 0; - double fromAngleY = fromOp ? fromOp->m_angleY : 0; - return SkewTransformOperation::create(blink::blend(fromAngleX, m_angleX, progress), blink::blend(fromAngleY, m_angleY, progress), m_type); +PassRefPtr SkewTransformOperation::blend( + const TransformOperation* from, + double progress, + bool blendToIdentity) { + if (from && !from->canBlendWith(*this)) + return this; + + if (blendToIdentity) + return SkewTransformOperation::create(blink::blend(m_angleX, 0.0, progress), + blink::blend(m_angleY, 0.0, progress), + m_type); + + const SkewTransformOperation* fromOp = + static_cast(from); + double fromAngleX = fromOp ? fromOp->m_angleX : 0; + double fromAngleY = fromOp ? fromOp->m_angleY : 0; + return SkewTransformOperation::create( + blink::blend(fromAngleX, m_angleX, progress), + blink::blend(fromAngleY, m_angleY, progress), m_type); } -bool SkewTransformOperation::canBlendWith(const TransformOperation& other) const -{ - return other.type() == Skew - || other.type() == SkewX - || other.type() == SkewY; +bool SkewTransformOperation::canBlendWith( + const TransformOperation& other) const { + return other.type() == Skew || other.type() == SkewX || other.type() == SkewY; } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/transforms/SkewTransformOperation.h b/sky/engine/platform/transforms/SkewTransformOperation.h index 657ef9dd587af..c7cf36774dce5 100644 --- a/sky/engine/platform/transforms/SkewTransformOperation.h +++ b/sky/engine/platform/transforms/SkewTransformOperation.h @@ -30,47 +30,48 @@ namespace blink { class PLATFORM_EXPORT SkewTransformOperation : public TransformOperation { -public: - static PassRefPtr create(double angleX, double angleY, OperationType type) - { - return adoptRef(new SkewTransformOperation(angleX, angleY, type)); - } + public: + static PassRefPtr create(double angleX, + double angleY, + OperationType type) { + return adoptRef(new SkewTransformOperation(angleX, angleY, type)); + } - double angleX() const { return m_angleX; } - double angleY() const { return m_angleY; } + double angleX() const { return m_angleX; } + double angleY() const { return m_angleY; } - virtual bool canBlendWith(const TransformOperation& other) const; -private: - virtual bool isIdentity() const override { return !m_angleX && !m_angleY; } - virtual OperationType type() const override { return m_type; } + virtual bool canBlendWith(const TransformOperation& other) const; - virtual bool operator==(const TransformOperation& o) const override - { - if (!isSameType(o)) - return false; - const SkewTransformOperation* s = static_cast(&o); - return m_angleX == s->m_angleX && m_angleY == s->m_angleY; - } + private: + virtual bool isIdentity() const override { return !m_angleX && !m_angleY; } + virtual OperationType type() const override { return m_type; } - virtual void apply(TransformationMatrix& transform, const FloatSize&) const override - { - transform.skew(m_angleX, m_angleY); - } + virtual bool operator==(const TransformOperation& o) const override { + if (!isSameType(o)) + return false; + const SkewTransformOperation* s = + static_cast(&o); + return m_angleX == s->m_angleX && m_angleY == s->m_angleY; + } - virtual PassRefPtr blend(const TransformOperation* from, double progress, bool blendToIdentity = false) override; + virtual void apply(TransformationMatrix& transform, + const FloatSize&) const override { + transform.skew(m_angleX, m_angleY); + } - SkewTransformOperation(double angleX, double angleY, OperationType type) - : m_angleX(angleX) - , m_angleY(angleY) - , m_type(type) - { - } + virtual PassRefPtr blend( + const TransformOperation* from, + double progress, + bool blendToIdentity = false) override; - double m_angleX; - double m_angleY; - OperationType m_type; + SkewTransformOperation(double angleX, double angleY, OperationType type) + : m_angleX(angleX), m_angleY(angleY), m_type(type) {} + + double m_angleX; + double m_angleY; + OperationType m_type; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_TRANSFORMS_SKEWTRANSFORMOPERATION_H_ diff --git a/sky/engine/platform/transforms/TransformOperation.h b/sky/engine/platform/transforms/TransformOperation.h index 11ccd88c974d7..a23a3854d99a4 100644 --- a/sky/engine/platform/transforms/TransformOperation.h +++ b/sky/engine/platform/transforms/TransformOperation.h @@ -34,57 +34,68 @@ namespace blink { // CSS Transforms (may become part of CSS3) -class PLATFORM_EXPORT TransformOperation : public RefCounted { -public: - enum OperationType { - ScaleX, ScaleY, Scale, - TranslateX, TranslateY, Translate, - Rotate, - RotateZ = Rotate, - SkewX, SkewY, Skew, - Matrix, - ScaleZ, Scale3D, - TranslateZ, Translate3D, - RotateX, RotateY, Rotate3D, - Matrix3D, - Perspective, - Interpolated, - Identity, None - }; +class PLATFORM_EXPORT TransformOperation + : public RefCounted { + public: + enum OperationType { + ScaleX, + ScaleY, + Scale, + TranslateX, + TranslateY, + Translate, + Rotate, + RotateZ = Rotate, + SkewX, + SkewY, + Skew, + Matrix, + ScaleZ, + Scale3D, + TranslateZ, + Translate3D, + RotateX, + RotateY, + Rotate3D, + Matrix3D, + Perspective, + Interpolated, + Identity, + None + }; - virtual ~TransformOperation() { } + virtual ~TransformOperation() {} - virtual bool operator==(const TransformOperation&) const = 0; - bool operator!=(const TransformOperation& o) const { return !(*this == o); } + virtual bool operator==(const TransformOperation&) const = 0; + bool operator!=(const TransformOperation& o) const { return !(*this == o); } - virtual bool isIdentity() const = 0; + virtual bool isIdentity() const = 0; - virtual void apply(TransformationMatrix&, const FloatSize& borderBoxSize) const = 0; + virtual void apply(TransformationMatrix&, + const FloatSize& borderBoxSize) const = 0; - virtual PassRefPtr blend(const TransformOperation* from, double progress, bool blendToIdentity = false) = 0; + virtual PassRefPtr blend( + const TransformOperation* from, + double progress, + bool blendToIdentity = false) = 0; - virtual OperationType type() const = 0; - bool isSameType(const TransformOperation& other) const { return other.type() == type(); } - virtual bool canBlendWith(const TransformOperation& other) const = 0; + virtual OperationType type() const = 0; + bool isSameType(const TransformOperation& other) const { + return other.type() == type(); + } + virtual bool canBlendWith(const TransformOperation& other) const = 0; - bool is3DOperation() const - { - OperationType opType = type(); - return opType == ScaleZ - || opType == Scale3D - || opType == TranslateZ - || opType == Translate3D - || opType == RotateX - || opType == RotateY - || opType == Rotate3D - || opType == Matrix3D - || opType == Perspective - || opType == Interpolated; - } + bool is3DOperation() const { + OperationType opType = type(); + return opType == ScaleZ || opType == Scale3D || opType == TranslateZ || + opType == Translate3D || opType == RotateX || opType == RotateY || + opType == Rotate3D || opType == Matrix3D || opType == Perspective || + opType == Interpolated; + } - virtual bool dependsOnBoxSize() const { return false; } + virtual bool dependsOnBoxSize() const { return false; } }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_TRANSFORMS_TRANSFORMOPERATION_H_ diff --git a/sky/engine/platform/transforms/TransformOperations.cpp b/sky/engine/platform/transforms/TransformOperations.cpp index 04742152192f6..35ef092f5809a 100644 --- a/sky/engine/platform/transforms/TransformOperations.cpp +++ b/sky/engine/platform/transforms/TransformOperations.cpp @@ -30,352 +30,389 @@ namespace blink { -TransformOperations::TransformOperations(bool makeIdentity) -{ - if (makeIdentity) - m_operations.append(IdentityTransformOperation::create()); +TransformOperations::TransformOperations(bool makeIdentity) { + if (makeIdentity) + m_operations.append(IdentityTransformOperation::create()); } -bool TransformOperations::operator==(const TransformOperations& o) const -{ - if (m_operations.size() != o.m_operations.size()) - return false; +bool TransformOperations::operator==(const TransformOperations& o) const { + if (m_operations.size() != o.m_operations.size()) + return false; - unsigned s = m_operations.size(); - for (unsigned i = 0; i < s; i++) { - if (*m_operations[i] != *o.m_operations[i]) - return false; - } + unsigned s = m_operations.size(); + for (unsigned i = 0; i < s; i++) { + if (*m_operations[i] != *o.m_operations[i]) + return false; + } - return true; + return true; } -bool TransformOperations::operationsMatch(const TransformOperations& other) const -{ - size_t numOperations = operations().size(); - // If the sizes of the function lists don't match, the lists don't match - if (numOperations != other.operations().size()) - return false; - - // If the types of each function are not the same, the lists don't match - for (size_t i = 0; i < numOperations; ++i) { - if (!operations()[i]->isSameType(*other.operations()[i])) - return false; - } - return true; +bool TransformOperations::operationsMatch( + const TransformOperations& other) const { + size_t numOperations = operations().size(); + // If the sizes of the function lists don't match, the lists don't match + if (numOperations != other.operations().size()) + return false; + + // If the types of each function are not the same, the lists don't match + for (size_t i = 0; i < numOperations; ++i) { + if (!operations()[i]->isSameType(*other.operations()[i])) + return false; + } + return true; } -TransformOperations TransformOperations::blendByMatchingOperations(const TransformOperations& from, const double& progress) const -{ - TransformOperations result; - - unsigned fromSize = from.operations().size(); - unsigned toSize = operations().size(); - unsigned size = std::max(fromSize, toSize); - for (unsigned i = 0; i < size; i++) { - RefPtr fromOperation = (i < fromSize) ? from.operations()[i].get() : 0; - RefPtr toOperation = (i < toSize) ? operations()[i].get() : 0; - RefPtr blendedOperation = toOperation ? toOperation->blend(fromOperation.get(), progress) : (fromOperation ? fromOperation->blend(0, progress, true) : nullptr); - if (blendedOperation) - result.operations().append(blendedOperation); - else { - RefPtr identityOperation = IdentityTransformOperation::create(); - if (progress > 0.5) - result.operations().append(toOperation ? toOperation : identityOperation); - else - result.operations().append(fromOperation ? fromOperation : identityOperation); - } +TransformOperations TransformOperations::blendByMatchingOperations( + const TransformOperations& from, + const double& progress) const { + TransformOperations result; + + unsigned fromSize = from.operations().size(); + unsigned toSize = operations().size(); + unsigned size = std::max(fromSize, toSize); + for (unsigned i = 0; i < size; i++) { + RefPtr fromOperation = + (i < fromSize) ? from.operations()[i].get() : 0; + RefPtr toOperation = + (i < toSize) ? operations()[i].get() : 0; + RefPtr blendedOperation = + toOperation ? toOperation->blend(fromOperation.get(), progress) + : (fromOperation ? fromOperation->blend(0, progress, true) + : nullptr); + if (blendedOperation) + result.operations().append(blendedOperation); + else { + RefPtr identityOperation = + IdentityTransformOperation::create(); + if (progress > 0.5) + result.operations().append(toOperation ? toOperation + : identityOperation); + else + result.operations().append(fromOperation ? fromOperation + : identityOperation); } + } - return result; + return result; } -TransformOperations TransformOperations::blendByUsingMatrixInterpolation(const TransformOperations& from, double progress) const -{ - TransformOperations result; - result.operations().append(InterpolatedTransformOperation::create(from, *this, progress)); - return result; +TransformOperations TransformOperations::blendByUsingMatrixInterpolation( + const TransformOperations& from, + double progress) const { + TransformOperations result; + result.operations().append( + InterpolatedTransformOperation::create(from, *this, progress)); + return result; } -TransformOperations TransformOperations::blend(const TransformOperations& from, double progress) const -{ - if (from == *this || (!from.size() && !size())) - return *this; +TransformOperations TransformOperations::blend(const TransformOperations& from, + double progress) const { + if (from == *this || (!from.size() && !size())) + return *this; - // If either list is empty, use blendByMatchingOperations which has special logic for this case. - if (!from.size() || !size() || from.operationsMatch(*this)) - return blendByMatchingOperations(from, progress); + // If either list is empty, use blendByMatchingOperations which has special + // logic for this case. + if (!from.size() || !size() || from.operationsMatch(*this)) + return blendByMatchingOperations(from, progress); - return blendByUsingMatrixInterpolation(from, progress); + return blendByUsingMatrixInterpolation(from, progress); } -static void findCandidatesInPlane(double px, double py, double nz, double* candidates, int* numCandidates) -{ - // The angle that this point is rotated with respect to the plane nz - double phi = atan2(px, py); - - *numCandidates = 4; - candidates[0] = phi; // The element at 0deg (maximum x) - - for (int i = 1; i < *numCandidates; ++i) - candidates[i] = candidates[i - 1] + M_PI_2; // every 90 deg - if (nz < 0.f) { - for (int i = 0; i < *numCandidates; ++i) - candidates[i] *= -1; - } +static void findCandidatesInPlane(double px, + double py, + double nz, + double* candidates, + int* numCandidates) { + // The angle that this point is rotated with respect to the plane nz + double phi = atan2(px, py); + + *numCandidates = 4; + candidates[0] = phi; // The element at 0deg (maximum x) + + for (int i = 1; i < *numCandidates; ++i) + candidates[i] = candidates[i - 1] + M_PI_2; // every 90 deg + if (nz < 0.f) { + for (int i = 0; i < *numCandidates; ++i) + candidates[i] *= -1; + } } // This method returns the bounding box that contains the starting point, // the ending point, and any of the extrema (in each dimension) found across // the circle described by the arc. These are then filtered to points that // actually reside on the arc. -static void boundingBoxForArc(const FloatPoint3D& point, const RotateTransformOperation& fromTransform, const RotateTransformOperation& toTransform, double minProgress, double maxProgress, FloatBox& box) -{ - double candidates[6]; - int numCandidates = 0; - - FloatPoint3D axis(fromTransform.axis()); - double fromDegrees = fromTransform.angle(); - double toDegrees = toTransform.angle(); - - if (axis.dot(toTransform.axis()) < 0) - toDegrees *= -1; - - fromDegrees = blend(fromDegrees, toTransform.angle(), minProgress); - toDegrees = blend(toDegrees, fromTransform.angle(), 1.0 - maxProgress); - if (fromDegrees > toDegrees) - std::swap(fromDegrees, toDegrees); - - TransformationMatrix fromMatrix; - TransformationMatrix toMatrix; - fromMatrix.rotate3d(fromTransform.x(), fromTransform.y(), fromTransform.z(), fromDegrees); - toMatrix.rotate3d(fromTransform.x(), fromTransform.y(), fromTransform.z(), toDegrees); +static void boundingBoxForArc(const FloatPoint3D& point, + const RotateTransformOperation& fromTransform, + const RotateTransformOperation& toTransform, + double minProgress, + double maxProgress, + FloatBox& box) { + double candidates[6]; + int numCandidates = 0; + + FloatPoint3D axis(fromTransform.axis()); + double fromDegrees = fromTransform.angle(); + double toDegrees = toTransform.angle(); + + if (axis.dot(toTransform.axis()) < 0) + toDegrees *= -1; + + fromDegrees = blend(fromDegrees, toTransform.angle(), minProgress); + toDegrees = blend(toDegrees, fromTransform.angle(), 1.0 - maxProgress); + if (fromDegrees > toDegrees) + std::swap(fromDegrees, toDegrees); + + TransformationMatrix fromMatrix; + TransformationMatrix toMatrix; + fromMatrix.rotate3d(fromTransform.x(), fromTransform.y(), fromTransform.z(), + fromDegrees); + toMatrix.rotate3d(fromTransform.x(), fromTransform.y(), fromTransform.z(), + toDegrees); + + FloatPoint3D fromPoint = fromMatrix.mapPoint(point); + FloatPoint3D toPoint = toMatrix.mapPoint(point); + + if (box.isEmpty()) + box.setOrigin(fromPoint); + else + box.expandTo(fromPoint); + + box.expandTo(toPoint); + + switch (fromTransform.type()) { + case TransformOperation::RotateX: + findCandidatesInPlane(point.y(), point.z(), fromTransform.x(), candidates, + &numCandidates); + break; + case TransformOperation::RotateY: + findCandidatesInPlane(point.z(), point.x(), fromTransform.y(), candidates, + &numCandidates); + break; + case TransformOperation::RotateZ: + findCandidatesInPlane(point.x(), point.y(), fromTransform.z(), candidates, + &numCandidates); + break; + default: { + FloatPoint3D normal = axis; + if (normal.isZero()) + return; + normal.normalize(); + FloatPoint3D origin; + FloatPoint3D toPoint = point - origin; + FloatPoint3D center = origin + normal * toPoint.dot(normal); + FloatPoint3D v1 = point - center; + if (v1.isZero()) + return; + + v1.normalize(); + FloatPoint3D v2 = normal.cross(v1); + // v1 is the basis vector in the direction of the point. + // i.e. with a rotation of 0, v1 is our +x vector. + // v2 is a perpenticular basis vector of our plane (+y). + + // Take the parametric equation of a circle. + // (x = r*cos(t); y = r*sin(t); + // We can treat that as a circle on the plane v1xv2 + // From that we get the parametric equations for a circle on the + // plane in 3d space of + // x(t) = r*cos(t)*v1.x + r*sin(t)*v2.x + cx + // y(t) = r*cos(t)*v1.y + r*sin(t)*v2.y + cy + // z(t) = r*cos(t)*v1.z + r*sin(t)*v2.z + cz + // taking the derivative of (x, y, z) and solving for 0 gives us our + // maximum/minimum x, y, z values + // x'(t) = r*cos(t)*v2.x - r*sin(t)*v1.x = 0 + // tan(t) = v2.x/v1.x + // t = atan2(v2.x, v1.x) + n*M_PI; + + candidates[0] = atan2(v2.x(), v1.x()); + candidates[1] = candidates[0] + M_PI; + candidates[2] = atan2(v2.y(), v1.y()); + candidates[3] = candidates[2] + M_PI; + candidates[4] = atan2(v2.z(), v1.z()); + candidates[5] = candidates[4] + M_PI; + numCandidates = 6; + } break; + } + + double minRadians = deg2rad(fromDegrees); + double maxRadians = deg2rad(toDegrees); + // Once we have the candidates, we now filter them down to ones that + // actually live on the arc, rather than the entire circle. + for (int i = 0; i < numCandidates; ++i) { + double radians = candidates[i]; + + while (radians < minRadians) + radians += 2.0 * M_PI; + while (radians > maxRadians) + radians -= 2.0 * M_PI; + if (radians < minRadians) + continue; + + TransformationMatrix rotation; + rotation.rotate3d(axis.x(), axis.y(), axis.z(), rad2deg(radians)); + box.expandTo(rotation.mapPoint(point)); + } +} - FloatPoint3D fromPoint = fromMatrix.mapPoint(point); - FloatPoint3D toPoint = toMatrix.mapPoint(point); +bool TransformOperations::blendedBoundsForBox(const FloatBox& box, + const TransformOperations& from, + const double& minProgress, + const double& maxProgress, + FloatBox* bounds) const { + int fromSize = from.operations().size(); + int toSize = operations().size(); + int size = std::max(fromSize, toSize); + + *bounds = box; + for (int i = size - 1; i >= 0; i--) { + RefPtr fromOperation = + (i < fromSize) ? from.operations()[i] : nullptr; + RefPtr toOperation = + (i < toSize) ? operations()[i] : nullptr; + if (fromOperation && fromOperation->type() == TransformOperation::None) + fromOperation = nullptr; + + if (toOperation && toOperation->type() == TransformOperation::None) + toOperation = nullptr; + + TransformOperation::OperationType interpolationType = + toOperation + ? toOperation->type() + : fromOperation ? fromOperation->type() : TransformOperation::None; + if (fromOperation && toOperation && + !fromOperation->canBlendWith(*toOperation.get())) + return false; + + switch (interpolationType) { + case TransformOperation::Identity: + bounds->expandTo(box); + continue; + case TransformOperation::Translate: + case TransformOperation::TranslateX: + case TransformOperation::TranslateY: + case TransformOperation::TranslateZ: + case TransformOperation::Translate3D: + case TransformOperation::Scale: + case TransformOperation::ScaleX: + case TransformOperation::ScaleY: + case TransformOperation::ScaleZ: + case TransformOperation::Scale3D: + case TransformOperation::Skew: + case TransformOperation::SkewX: + case TransformOperation::SkewY: + case TransformOperation::Perspective: { + RefPtr fromTransform; + RefPtr toTransform; + if (!toOperation) { + fromTransform = + fromOperation->blend(toOperation.get(), 1 - minProgress, false); + toTransform = + fromOperation->blend(toOperation.get(), 1 - maxProgress, false); + } else { + fromTransform = + toOperation->blend(fromOperation.get(), minProgress, false); + toTransform = + toOperation->blend(fromOperation.get(), maxProgress, false); + } + if (!fromTransform || !toTransform) + continue; + TransformationMatrix fromMatrix; + TransformationMatrix toMatrix; + fromTransform->apply(fromMatrix, FloatSize()); + toTransform->apply(toMatrix, FloatSize()); + FloatBox fromBox = *bounds; + FloatBox toBox = *bounds; + fromMatrix.transformBox(fromBox); + toMatrix.transformBox(toBox); + *bounds = fromBox; + bounds->expandTo(toBox); + continue; + } + case TransformOperation::Rotate: // This is also RotateZ + case TransformOperation::Rotate3D: + case TransformOperation::RotateX: + case TransformOperation::RotateY: { + RefPtr identityRotation; + const RotateTransformOperation* fromRotation = nullptr; + const RotateTransformOperation* toRotation = nullptr; + if (fromOperation) { + fromRotation = + static_cast(fromOperation.get()); + if (fromRotation->axis().isZero()) + fromRotation = nullptr; + } - if (box.isEmpty()) - box.setOrigin(fromPoint); - else - box.expandTo(fromPoint); + if (toOperation) { + toRotation = + static_cast(toOperation.get()); + if (toRotation->axis().isZero()) + toRotation = nullptr; + } - box.expandTo(toPoint); + double fromAngle; + double toAngle; + FloatPoint3D axis; + if (!RotateTransformOperation::shareSameAxis( + fromRotation, toRotation, &axis, &fromAngle, &toAngle)) { + return (false); + } - switch (fromTransform.type()) { - case TransformOperation::RotateX: - findCandidatesInPlane(point.y(), point.z(), fromTransform.x(), candidates, &numCandidates); - break; - case TransformOperation::RotateY: - findCandidatesInPlane(point.z(), point.x(), fromTransform.y(), candidates, &numCandidates); - break; - case TransformOperation::RotateZ: - findCandidatesInPlane(point.x(), point.y(), fromTransform.z(), candidates, &numCandidates); - break; - default: - { - FloatPoint3D normal = axis; - if (normal.isZero()) - return; - normal.normalize(); - FloatPoint3D origin; - FloatPoint3D toPoint = point - origin; - FloatPoint3D center = origin + normal * toPoint.dot(normal); - FloatPoint3D v1 = point - center; - if (v1.isZero()) - return; - - v1.normalize(); - FloatPoint3D v2 = normal.cross(v1); - // v1 is the basis vector in the direction of the point. - // i.e. with a rotation of 0, v1 is our +x vector. - // v2 is a perpenticular basis vector of our plane (+y). - - // Take the parametric equation of a circle. - // (x = r*cos(t); y = r*sin(t); - // We can treat that as a circle on the plane v1xv2 - // From that we get the parametric equations for a circle on the - // plane in 3d space of - // x(t) = r*cos(t)*v1.x + r*sin(t)*v2.x + cx - // y(t) = r*cos(t)*v1.y + r*sin(t)*v2.y + cy - // z(t) = r*cos(t)*v1.z + r*sin(t)*v2.z + cz - // taking the derivative of (x, y, z) and solving for 0 gives us our - // maximum/minimum x, y, z values - // x'(t) = r*cos(t)*v2.x - r*sin(t)*v1.x = 0 - // tan(t) = v2.x/v1.x - // t = atan2(v2.x, v1.x) + n*M_PI; - - candidates[0] = atan2(v2.x(), v1.x()); - candidates[1] = candidates[0] + M_PI; - candidates[2] = atan2(v2.y(), v1.y()); - candidates[3] = candidates[2] + M_PI; - candidates[4] = atan2(v2.z(), v1.z()); - candidates[5] = candidates[4] + M_PI; - numCandidates = 6; + if (!fromRotation) { + identityRotation = RotateTransformOperation::create( + axis.x(), axis.y(), axis.z(), 0, + fromOperation ? fromOperation->type() : toOperation->type()); + fromRotation = identityRotation.get(); } - break; - } - double minRadians = deg2rad(fromDegrees); - double maxRadians = deg2rad(toDegrees); - // Once we have the candidates, we now filter them down to ones that - // actually live on the arc, rather than the entire circle. - for (int i = 0; i < numCandidates; ++i) { - double radians = candidates[i]; - - while (radians < minRadians) - radians += 2.0 * M_PI; - while (radians > maxRadians) - radians -= 2.0 * M_PI; - if (radians < minRadians) - continue; - - TransformationMatrix rotation; - rotation.rotate3d(axis.x(), axis.y(), axis.z(), rad2deg(radians)); - box.expandTo(rotation.mapPoint(point)); - } -} + if (!toRotation) { + if (!identityRotation) + identityRotation = RotateTransformOperation::create( + axis.x(), axis.y(), axis.z(), 0, + fromOperation ? fromOperation->type() : toOperation->type()); + toRotation = identityRotation.get(); + } -bool TransformOperations::blendedBoundsForBox(const FloatBox& box, const TransformOperations& from, const double& minProgress, const double& maxProgress, FloatBox* bounds) const -{ - - int fromSize = from.operations().size(); - int toSize = operations().size(); - int size = std::max(fromSize, toSize); - - *bounds = box; - for (int i = size - 1; i >= 0; i--) { - RefPtr fromOperation = (i < fromSize) ? from.operations()[i] : nullptr; - RefPtr toOperation = (i < toSize) ? operations()[i] : nullptr; - if (fromOperation && fromOperation->type() == TransformOperation::None) - fromOperation = nullptr; - - if (toOperation && toOperation->type() == TransformOperation::None) - toOperation = nullptr; - - TransformOperation::OperationType interpolationType = toOperation ? toOperation->type() : - fromOperation ? fromOperation->type() : - TransformOperation::None; - if (fromOperation && toOperation && !fromOperation->canBlendWith(*toOperation.get())) - return false; - - switch (interpolationType) { - case TransformOperation::Identity: - bounds->expandTo(box); - continue; - case TransformOperation::Translate: - case TransformOperation::TranslateX: - case TransformOperation::TranslateY: - case TransformOperation::TranslateZ: - case TransformOperation::Translate3D: - case TransformOperation::Scale: - case TransformOperation::ScaleX: - case TransformOperation::ScaleY: - case TransformOperation::ScaleZ: - case TransformOperation::Scale3D: - case TransformOperation::Skew: - case TransformOperation::SkewX: - case TransformOperation::SkewY: - case TransformOperation::Perspective: - { - RefPtr fromTransform; - RefPtr toTransform; - if (!toOperation) { - fromTransform = fromOperation->blend(toOperation.get(), 1-minProgress, false); - toTransform = fromOperation->blend(toOperation.get(), 1-maxProgress, false); - } else { - fromTransform = toOperation->blend(fromOperation.get(), minProgress, false); - toTransform = toOperation->blend(fromOperation.get(), maxProgress, false); - } - if (!fromTransform || !toTransform) - continue; - TransformationMatrix fromMatrix; - TransformationMatrix toMatrix; - fromTransform->apply(fromMatrix, FloatSize()); - toTransform->apply(toMatrix, FloatSize()); - FloatBox fromBox = *bounds; - FloatBox toBox = *bounds; - fromMatrix.transformBox(fromBox); - toMatrix.transformBox(toBox); - *bounds = fromBox; - bounds->expandTo(toBox); - continue; - } - case TransformOperation::Rotate: // This is also RotateZ - case TransformOperation::Rotate3D: - case TransformOperation::RotateX: - case TransformOperation::RotateY: - { - RefPtr identityRotation; - const RotateTransformOperation* fromRotation = nullptr; - const RotateTransformOperation* toRotation = nullptr; - if (fromOperation) { - fromRotation = static_cast(fromOperation.get()); - if (fromRotation->axis().isZero()) - fromRotation = nullptr; - } - - if (toOperation) { - toRotation = static_cast(toOperation.get()); - if (toRotation->axis().isZero()) - toRotation = nullptr; - } - - double fromAngle; - double toAngle; - FloatPoint3D axis; - if (!RotateTransformOperation::shareSameAxis(fromRotation, toRotation, &axis, &fromAngle, &toAngle)) { - return(false); - } - - if (!fromRotation) { - identityRotation = RotateTransformOperation::create(axis.x(), axis.y(), axis.z(), 0, fromOperation ? fromOperation->type() : toOperation->type()); - fromRotation = identityRotation.get(); - } - - if (!toRotation) { - if (!identityRotation) - identityRotation = RotateTransformOperation::create(axis.x(), axis.y(), axis.z(), 0, fromOperation ? fromOperation->type() : toOperation->type()); - toRotation = identityRotation.get(); - } - - FloatBox fromBox = *bounds; - bool first = true; - for (size_t i = 0; i < 2; ++i) { - for (size_t j = 0; j < 2; ++j) { - for (size_t k = 0; k < 2; ++k) { - FloatBox boundsForArc; - FloatPoint3D corner(fromBox.x(), fromBox.y(), fromBox.z()); - corner += FloatPoint3D(i * fromBox.width(), j * fromBox.height(), k * fromBox.depth()); - boundingBoxForArc(corner, *fromRotation, *toRotation, minProgress, maxProgress, boundsForArc); - if (first) { - *bounds = boundsForArc; - first = false; - } else { - bounds->expandTo(boundsForArc); - } - } - } - } + FloatBox fromBox = *bounds; + bool first = true; + for (size_t i = 0; i < 2; ++i) { + for (size_t j = 0; j < 2; ++j) { + for (size_t k = 0; k < 2; ++k) { + FloatBox boundsForArc; + FloatPoint3D corner(fromBox.x(), fromBox.y(), fromBox.z()); + corner += FloatPoint3D(i * fromBox.width(), j * fromBox.height(), + k * fromBox.depth()); + boundingBoxForArc(corner, *fromRotation, *toRotation, minProgress, + maxProgress, boundsForArc); + if (first) { + *bounds = boundsForArc; + first = false; + } else { + bounds->expandTo(boundsForArc); + } } - continue; - case TransformOperation::None: - continue; - case TransformOperation::Matrix: - case TransformOperation::Matrix3D: - case TransformOperation::Interpolated: - return(false); + } } + } + continue; + case TransformOperation::None: + continue; + case TransformOperation::Matrix: + case TransformOperation::Matrix3D: + case TransformOperation::Interpolated: + return (false); } + } - return true; + return true; } -TransformOperations TransformOperations::add(const TransformOperations& addend) const -{ - TransformOperations result; - result.m_operations = operations(); - result.m_operations.appendVector(addend.operations()); - return result; +TransformOperations TransformOperations::add( + const TransformOperations& addend) const { + TransformOperations result; + result.m_operations = operations(); + result.m_operations.appendVector(addend.operations()); + return result; } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/transforms/TransformOperations.h b/sky/engine/platform/transforms/TransformOperations.h index 21abb6bf7c424..abd3c149b9870 100644 --- a/sky/engine/platform/transforms/TransformOperations.h +++ b/sky/engine/platform/transforms/TransformOperations.h @@ -33,64 +33,68 @@ namespace blink { class FloatBox; class PLATFORM_EXPORT TransformOperations { - WTF_MAKE_FAST_ALLOCATED; -public: - explicit TransformOperations(bool makeIdentity = false); - - bool operator==(const TransformOperations& o) const; - bool operator!=(const TransformOperations& o) const - { - return !(*this == o); - } + WTF_MAKE_FAST_ALLOCATED; - void apply(const FloatSize& sz, TransformationMatrix& t) const - { - for (unsigned i = 0; i < m_operations.size(); ++i) - m_operations[i]->apply(t, sz); - } + public: + explicit TransformOperations(bool makeIdentity = false); - // Return true if any of the operation types are 3D operation types (even if the - // values describe affine transforms) - bool has3DOperation() const - { - for (unsigned i = 0; i < m_operations.size(); ++i) - if (m_operations[i]->is3DOperation()) - return true; - return false; - } + bool operator==(const TransformOperations& o) const; + bool operator!=(const TransformOperations& o) const { return !(*this == o); } - bool dependsOnBoxSize() const - { - for (unsigned i = 0; i < m_operations.size(); ++i) { - if (m_operations[i]->dependsOnBoxSize()) - return true; - } - return false; - } + void apply(const FloatSize& sz, TransformationMatrix& t) const { + for (unsigned i = 0; i < m_operations.size(); ++i) + m_operations[i]->apply(t, sz); + } - bool operationsMatch(const TransformOperations&) const; + // Return true if any of the operation types are 3D operation types (even if + // the values describe affine transforms) + bool has3DOperation() const { + for (unsigned i = 0; i < m_operations.size(); ++i) + if (m_operations[i]->is3DOperation()) + return true; + return false; + } - void clear() - { - m_operations.clear(); + bool dependsOnBoxSize() const { + for (unsigned i = 0; i < m_operations.size(); ++i) { + if (m_operations[i]->dependsOnBoxSize()) + return true; } + return false; + } + + bool operationsMatch(const TransformOperations&) const; + + void clear() { m_operations.clear(); } - Vector >& operations() { return m_operations; } - const Vector >& operations() const { return m_operations; } + Vector>& operations() { return m_operations; } + const Vector>& operations() const { + return m_operations; + } - size_t size() const { return m_operations.size(); } - const TransformOperation* at(size_t index) const { return index < m_operations.size() ? m_operations.at(index).get() : 0; } + size_t size() const { return m_operations.size(); } + const TransformOperation* at(size_t index) const { + return index < m_operations.size() ? m_operations.at(index).get() : 0; + } - bool blendedBoundsForBox(const FloatBox&, const TransformOperations& from, const double& minProgress, const double& maxProgress, FloatBox* bounds) const; - TransformOperations blendByMatchingOperations(const TransformOperations& from, const double& progress) const; - TransformOperations blendByUsingMatrixInterpolation(const TransformOperations& from, double progress) const; - TransformOperations blend(const TransformOperations& from, double progress) const; - TransformOperations add(const TransformOperations& addend) const; + bool blendedBoundsForBox(const FloatBox&, + const TransformOperations& from, + const double& minProgress, + const double& maxProgress, + FloatBox* bounds) const; + TransformOperations blendByMatchingOperations(const TransformOperations& from, + const double& progress) const; + TransformOperations blendByUsingMatrixInterpolation( + const TransformOperations& from, + double progress) const; + TransformOperations blend(const TransformOperations& from, + double progress) const; + TransformOperations add(const TransformOperations& addend) const; -private: - Vector > m_operations; + private: + Vector> m_operations; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_TRANSFORMS_TRANSFORMOPERATIONS_H_ diff --git a/sky/engine/platform/transforms/TransformOperationsTest.cpp b/sky/engine/platform/transforms/TransformOperationsTest.cpp index 8edfe2b7eade6..045f64f9ac1e3 100644 --- a/sky/engine/platform/transforms/TransformOperationsTest.cpp +++ b/sky/engine/platform/transforms/TransformOperationsTest.cpp @@ -10,16 +10,16 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "flutter/sky/engine/platform/transforms/TransformOperations.h" @@ -43,220 +43,247 @@ namespace { static const TransformOperations identityOperations; static void EmpiricallyTestBounds(const TransformOperations& from, - const TransformOperations& to, - const double& minProgress, - const double& maxProgress) -{ - FloatBox box(200, 500, 100, 100, 300, 200); - FloatBox bounds; - - EXPECT_TRUE(to.blendedBoundsForBox(box, from, minProgress, maxProgress, &bounds)); - bool firstTime = true; - - FloatBox empiricalBounds; - static const size_t numSteps = 10; - for (size_t step = 0; step < numSteps; ++step) { - float t = step / (numSteps - 1); - t = minProgress + (maxProgress - minProgress) * t; - TransformOperations operations = from.blend(to, t); - TransformationMatrix matrix; - operations.apply(FloatSize(0, 0), matrix); - FloatBox transformed = box; - matrix.transformBox(transformed); - - if (firstTime) - empiricalBounds = transformed; - else - empiricalBounds.unionBounds(transformed); - firstTime = false; - } - - ASSERT_PRED_FORMAT2(FloatBoxTest::AssertContains, bounds, empiricalBounds); + const TransformOperations& to, + const double& minProgress, + const double& maxProgress) { + FloatBox box(200, 500, 100, 100, 300, 200); + FloatBox bounds; + + EXPECT_TRUE( + to.blendedBoundsForBox(box, from, minProgress, maxProgress, &bounds)); + bool firstTime = true; + + FloatBox empiricalBounds; + static const size_t numSteps = 10; + for (size_t step = 0; step < numSteps; ++step) { + float t = step / (numSteps - 1); + t = minProgress + (maxProgress - minProgress) * t; + TransformOperations operations = from.blend(to, t); + TransformationMatrix matrix; + operations.apply(FloatSize(0, 0), matrix); + FloatBox transformed = box; + matrix.transformBox(transformed); + + if (firstTime) + empiricalBounds = transformed; + else + empiricalBounds.unionBounds(transformed); + firstTime = false; + } + + ASSERT_PRED_FORMAT2(FloatBoxTest::AssertContains, bounds, empiricalBounds); } -TEST(TransformOperationsTest, AbsoluteAnimatedTranslatedBoundsTest) -{ - TransformOperations fromOps; - TransformOperations toOps; - fromOps.operations().append(TranslateTransformOperation::create(Length(-30, blink::Fixed), Length(20, blink::Fixed), 15, TransformOperation::Translate3D)); - toOps.operations().append(TranslateTransformOperation::create(Length(10, blink::Fixed), Length(10, blink::Fixed), 200, TransformOperation::Translate3D)); - FloatBox box(0, 0, 0, 10, 10, 10); - FloatBox bounds; - - EXPECT_TRUE(toOps.blendedBoundsForBox(box, identityOperations, 0, 1, &bounds)); - EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(0, 0, 0, 20, 20, 210), bounds); - - EXPECT_TRUE(identityOperations.blendedBoundsForBox(box, toOps, 0, 1, &bounds)); - EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(0, 0, 0, 20, 20, 210), bounds); - - EXPECT_TRUE(identityOperations.blendedBoundsForBox(box, fromOps, 0, 1, &bounds)); - EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(-30, 0, 0, 40, 30, 25), bounds); - - EXPECT_TRUE(toOps.blendedBoundsForBox(box, fromOps, 0, 1, &bounds)); - EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(-30, 10, 15, 50, 20, 195), bounds); - - EXPECT_TRUE(toOps.blendedBoundsForBox(box, fromOps, -0.5, 1.25, &bounds)); - EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(-50, 7.5, -77.5, 80, 27.5, 333.75), bounds); +TEST(TransformOperationsTest, AbsoluteAnimatedTranslatedBoundsTest) { + TransformOperations fromOps; + TransformOperations toOps; + fromOps.operations().append(TranslateTransformOperation::create( + Length(-30, blink::Fixed), Length(20, blink::Fixed), 15, + TransformOperation::Translate3D)); + toOps.operations().append(TranslateTransformOperation::create( + Length(10, blink::Fixed), Length(10, blink::Fixed), 200, + TransformOperation::Translate3D)); + FloatBox box(0, 0, 0, 10, 10, 10); + FloatBox bounds; + + EXPECT_TRUE( + toOps.blendedBoundsForBox(box, identityOperations, 0, 1, &bounds)); + EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, + FloatBox(0, 0, 0, 20, 20, 210), bounds); + + EXPECT_TRUE( + identityOperations.blendedBoundsForBox(box, toOps, 0, 1, &bounds)); + EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, + FloatBox(0, 0, 0, 20, 20, 210), bounds); + + EXPECT_TRUE( + identityOperations.blendedBoundsForBox(box, fromOps, 0, 1, &bounds)); + EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, + FloatBox(-30, 0, 0, 40, 30, 25), bounds); + + EXPECT_TRUE(toOps.blendedBoundsForBox(box, fromOps, 0, 1, &bounds)); + EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, + FloatBox(-30, 10, 15, 50, 20, 195), bounds); + + EXPECT_TRUE(toOps.blendedBoundsForBox(box, fromOps, -0.5, 1.25, &bounds)); + EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, + FloatBox(-50, 7.5, -77.5, 80, 27.5, 333.75), bounds); } -TEST(TransformOperationsTest, EmpiricalAnimatedTranslatedBoundsTest) -{ - float testTransforms[][2][3] = { - { { 0, 0, 0 }, { 10, 10, 0 } } , - { { -100, 202.5, -32.6 }, { 43.2, 56.1, 89.75 } }, - { { 43.2, 56.1, 89.75 }, { -100, 202.5, -32.6 } } - }; - - // All progressions for animations start and end at 0, 1 respectively, - // we can go outside of these bounds, but will always at least contain - // [0,1]. - float progress[][2] = { - { 0, 1 }, - { -.25, 1.25 } - }; - - for (size_t i = 0; i < WTF_ARRAY_LENGTH(testTransforms); ++i) { - for (size_t j = 0; j < WTF_ARRAY_LENGTH(progress); ++j) { - TransformOperations fromOps; - TransformOperations toOps; - fromOps.operations().append(TranslateTransformOperation::create(Length(testTransforms[i][0][0], blink::Fixed), Length(testTransforms[i][0][1], blink::Fixed), testTransforms[i][0][2], TransformOperation::Translate3D)); - toOps.operations().append(TranslateTransformOperation::create(Length(testTransforms[i][1][0], blink::Fixed), Length(testTransforms[i][1][1], blink::Fixed), testTransforms[i][1][2], TransformOperation::Translate3D)); - EmpiricallyTestBounds(fromOps, toOps, 0, 1); - } +TEST(TransformOperationsTest, EmpiricalAnimatedTranslatedBoundsTest) { + float testTransforms[][2][3] = {{{0, 0, 0}, {10, 10, 0}}, + {{-100, 202.5, -32.6}, {43.2, 56.1, 89.75}}, + {{43.2, 56.1, 89.75}, {-100, 202.5, -32.6}}}; + + // All progressions for animations start and end at 0, 1 respectively, + // we can go outside of these bounds, but will always at least contain + // [0,1]. + float progress[][2] = {{0, 1}, {-.25, 1.25}}; + + for (size_t i = 0; i < WTF_ARRAY_LENGTH(testTransforms); ++i) { + for (size_t j = 0; j < WTF_ARRAY_LENGTH(progress); ++j) { + TransformOperations fromOps; + TransformOperations toOps; + fromOps.operations().append(TranslateTransformOperation::create( + Length(testTransforms[i][0][0], blink::Fixed), + Length(testTransforms[i][0][1], blink::Fixed), + testTransforms[i][0][2], TransformOperation::Translate3D)); + toOps.operations().append(TranslateTransformOperation::create( + Length(testTransforms[i][1][0], blink::Fixed), + Length(testTransforms[i][1][1], blink::Fixed), + testTransforms[i][1][2], TransformOperation::Translate3D)); + EmpiricallyTestBounds(fromOps, toOps, 0, 1); } + } } -TEST(TransformOperationsTest, AbsoluteAnimatedScaleBoundsTest) -{ - TransformOperations fromOps; - TransformOperations toOps; - fromOps.operations().append(ScaleTransformOperation::create(4, -3, TransformOperation::Scale)); - toOps.operations().append(ScaleTransformOperation::create(5, 2, TransformOperation::Scale)); - - FloatBox box(0, 0, 0, 10, 10, 10); - FloatBox bounds; - - EXPECT_TRUE(toOps.blendedBoundsForBox(box, identityOperations, 0, 1, &bounds)); - EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(0, 0, 0, 50, 20, 10), bounds); - - EXPECT_TRUE(identityOperations.blendedBoundsForBox(box, toOps, 0, 1, &bounds)); - EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(0, 0, 0, 50, 20, 10), bounds); - - EXPECT_TRUE(identityOperations.blendedBoundsForBox(box, fromOps, 0, 1, &bounds)); - EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(0, -30, 0, 40, 40, 10), bounds); - - EXPECT_TRUE(toOps.blendedBoundsForBox(box, fromOps, 0, 1, &bounds)); - EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(0, -30, 0, 50, 50, 10), bounds); - - EXPECT_TRUE(toOps.blendedBoundsForBox(box, fromOps, -0.5, 1.25, &bounds)); - EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(0, -55, 0, 52.5, 87.5, 10), bounds); -} - -TEST(TransformOperationsTest, EmpiricalAnimatedScaleBoundsTest) -{ - - float testTransforms[][2][3] = { - { { 1, 1, 1 }, { 10, 10, -32}}, - { { 1, 2, 5 }, { -1, -2, -4}}, - { { 0, 0, 0 }, { 1, 2, 3}}, - { { 0, 0, 0 }, { 0, 0, 0}} - }; - - // All progressions for animations start and end at 0, 1 respectively, - // we can go outside of these bounds, but will always at least contain - // [0,1]. - float progress[][2] = { - { 0, 1 }, - { -.25f, 1.25f } - }; - - for (size_t i = 0; i < WTF_ARRAY_LENGTH(testTransforms); ++i) { - for (size_t j = 0; j < WTF_ARRAY_LENGTH(progress); ++j) { - TransformOperations fromOps; - TransformOperations toOps; - fromOps.operations().append(TranslateTransformOperation::create(Length(testTransforms[i][0][0], blink::Fixed), Length(testTransforms[i][0][1], blink::Fixed), testTransforms[i][0][2], TransformOperation::Translate3D)); - toOps.operations().append(TranslateTransformOperation::create(Length(testTransforms[i][1][0], blink::Fixed), Length(testTransforms[i][1][1], blink::Fixed), testTransforms[i][1][2], TransformOperation::Translate3D)); - EmpiricallyTestBounds(fromOps, toOps, 0, 1); - } - } +TEST(TransformOperationsTest, AbsoluteAnimatedScaleBoundsTest) { + TransformOperations fromOps; + TransformOperations toOps; + fromOps.operations().append( + ScaleTransformOperation::create(4, -3, TransformOperation::Scale)); + toOps.operations().append( + ScaleTransformOperation::create(5, 2, TransformOperation::Scale)); + + FloatBox box(0, 0, 0, 10, 10, 10); + FloatBox bounds; + + EXPECT_TRUE( + toOps.blendedBoundsForBox(box, identityOperations, 0, 1, &bounds)); + EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, + FloatBox(0, 0, 0, 50, 20, 10), bounds); + + EXPECT_TRUE( + identityOperations.blendedBoundsForBox(box, toOps, 0, 1, &bounds)); + EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, + FloatBox(0, 0, 0, 50, 20, 10), bounds); + + EXPECT_TRUE( + identityOperations.blendedBoundsForBox(box, fromOps, 0, 1, &bounds)); + EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, + FloatBox(0, -30, 0, 40, 40, 10), bounds); + + EXPECT_TRUE(toOps.blendedBoundsForBox(box, fromOps, 0, 1, &bounds)); + EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, + FloatBox(0, -30, 0, 50, 50, 10), bounds); + + EXPECT_TRUE(toOps.blendedBoundsForBox(box, fromOps, -0.5, 1.25, &bounds)); + EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, + FloatBox(0, -55, 0, 52.5, 87.5, 10), bounds); } -TEST(TransformOperationsTest, AbsoluteAnimatedRotationBounds) -{ - TransformOperations fromOps; - TransformOperations toOps; - fromOps.operations().append(RotateTransformOperation::create(0, TransformOperation::Rotate)); - toOps.operations().append(RotateTransformOperation::create(360, TransformOperation::Rotate)); - float sqrt2 = sqrt(2.0f); - FloatBox box(-sqrt2, -sqrt2, 0, sqrt2, sqrt2, 0); - FloatBox bounds; - - // Since we're rotating 360 degrees, any box with dimensions between 0 and - // 2 * sqrt(2) should give the same result. - float sizes[] = { 0, 0.1f, sqrt2, 2*sqrt2 }; - toOps.blendedBoundsForBox(box, fromOps, 0, 1, &bounds); - for (size_t i = 0; i < WTF_ARRAY_LENGTH(sizes); ++i) { - box.setSize(FloatPoint3D(sizes[i], sizes[i], 0)); - - EXPECT_TRUE(toOps.blendedBoundsForBox(box, fromOps, 0, 1, &bounds)); - EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(-2, -2, 0, 4, 4, 0), bounds); +TEST(TransformOperationsTest, EmpiricalAnimatedScaleBoundsTest) { + float testTransforms[][2][3] = {{{1, 1, 1}, {10, 10, -32}}, + {{1, 2, 5}, {-1, -2, -4}}, + {{0, 0, 0}, {1, 2, 3}}, + {{0, 0, 0}, {0, 0, 0}}}; + + // All progressions for animations start and end at 0, 1 respectively, + // we can go outside of these bounds, but will always at least contain + // [0,1]. + float progress[][2] = {{0, 1}, {-.25f, 1.25f}}; + + for (size_t i = 0; i < WTF_ARRAY_LENGTH(testTransforms); ++i) { + for (size_t j = 0; j < WTF_ARRAY_LENGTH(progress); ++j) { + TransformOperations fromOps; + TransformOperations toOps; + fromOps.operations().append(TranslateTransformOperation::create( + Length(testTransforms[i][0][0], blink::Fixed), + Length(testTransforms[i][0][1], blink::Fixed), + testTransforms[i][0][2], TransformOperation::Translate3D)); + toOps.operations().append(TranslateTransformOperation::create( + Length(testTransforms[i][1][0], blink::Fixed), + Length(testTransforms[i][1][1], blink::Fixed), + testTransforms[i][1][2], TransformOperation::Translate3D)); + EmpiricallyTestBounds(fromOps, toOps, 0, 1); } + } } -TEST(TransformOperationsTest, AbsoluteAnimatedExtremeRotationBounds) -{ - // If the normal is off-plane, we can have up to 6 exrema (min/max in each - // dimension between) the endpoints of the arg. This makes sure we are - // catching all 6. - TransformOperations fromOps; - TransformOperations toOps; - fromOps.operations().append(RotateTransformOperation::create(1, 1, 1, 30, TransformOperation::Rotate3D)); - toOps.operations().append(RotateTransformOperation::create(1, 1, 1, 390, TransformOperation::Rotate3D)); +TEST(TransformOperationsTest, AbsoluteAnimatedRotationBounds) { + TransformOperations fromOps; + TransformOperations toOps; + fromOps.operations().append( + RotateTransformOperation::create(0, TransformOperation::Rotate)); + toOps.operations().append( + RotateTransformOperation::create(360, TransformOperation::Rotate)); + float sqrt2 = sqrt(2.0f); + FloatBox box(-sqrt2, -sqrt2, 0, sqrt2, sqrt2, 0); + FloatBox bounds; + + // Since we're rotating 360 degrees, any box with dimensions between 0 and + // 2 * sqrt(2) should give the same result. + float sizes[] = {0, 0.1f, sqrt2, 2 * sqrt2}; + toOps.blendedBoundsForBox(box, fromOps, 0, 1, &bounds); + for (size_t i = 0; i < WTF_ARRAY_LENGTH(sizes); ++i) { + box.setSize(FloatPoint3D(sizes[i], sizes[i], 0)); - FloatBox box(1, 0, 0, 0, 0, 0); - FloatBox bounds; - float min = -1 / 3.0f; - float max = 1; - float size = max - min; EXPECT_TRUE(toOps.blendedBoundsForBox(box, fromOps, 0, 1, &bounds)); - EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(min, min, min, size, size, size), bounds); + EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, + FloatBox(-2, -2, 0, 4, 4, 0), bounds); + } } -TEST(TransformOperationsTest, AbsoluteAnimatedAxisRotationBounds) -{ - // We can handle rotations about a single axis. If the axes are different, - // we revert to matrix interpolation for which inflated bounds cannot be - // computed. - TransformOperations fromOps; - TransformOperations toSame; - TransformOperations toOpposite; - TransformOperations toDifferent; - fromOps.operations().append(RotateTransformOperation::create(1, 1, 1, 30, TransformOperation::Rotate3D)); - toSame.operations().append(RotateTransformOperation::create(1, 1, 1, 390, TransformOperation::Rotate3D)); - toOpposite.operations().append(RotateTransformOperation::create(-1, -1, -1, 390, TransformOperation::Rotate3D)); - toDifferent.operations().append(RotateTransformOperation::create(1, 3, 1, 390, TransformOperation::Rotate3D)); - - FloatBox box(1, 0, 0, 0, 0, 0); - FloatBox bounds; - EXPECT_TRUE(toSame.blendedBoundsForBox(box, fromOps, 0, 1, &bounds)); - EXPECT_TRUE(toOpposite.blendedBoundsForBox(box, fromOps, 0, 1, &bounds)); - EXPECT_FALSE(toDifferent.blendedBoundsForBox(box, fromOps, 0, 1, &bounds)); +TEST(TransformOperationsTest, AbsoluteAnimatedExtremeRotationBounds) { + // If the normal is off-plane, we can have up to 6 exrema (min/max in each + // dimension between) the endpoints of the arg. This makes sure we are + // catching all 6. + TransformOperations fromOps; + TransformOperations toOps; + fromOps.operations().append(RotateTransformOperation::create( + 1, 1, 1, 30, TransformOperation::Rotate3D)); + toOps.operations().append(RotateTransformOperation::create( + 1, 1, 1, 390, TransformOperation::Rotate3D)); + + FloatBox box(1, 0, 0, 0, 0, 0); + FloatBox bounds; + float min = -1 / 3.0f; + float max = 1; + float size = max - min; + EXPECT_TRUE(toOps.blendedBoundsForBox(box, fromOps, 0, 1, &bounds)); + EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, + FloatBox(min, min, min, size, size, size), bounds); } -TEST(TransformOperationsTest, AbsoluteAnimatedOnAxisRotationBounds) -{ - // If we rotate a point that is on the axis of rotation, the box should not - // change at all. - TransformOperations fromOps; - TransformOperations toOps; - fromOps.operations().append(RotateTransformOperation::create(1, 1, 1, 30, TransformOperation::Rotate3D)); - toOps.operations().append(RotateTransformOperation::create(1, 1, 1, 390, TransformOperation::Rotate3D)); - - FloatBox box(1, 1, 1, 0, 0, 0); - FloatBox bounds; +TEST(TransformOperationsTest, AbsoluteAnimatedAxisRotationBounds) { + // We can handle rotations about a single axis. If the axes are different, + // we revert to matrix interpolation for which inflated bounds cannot be + // computed. + TransformOperations fromOps; + TransformOperations toSame; + TransformOperations toOpposite; + TransformOperations toDifferent; + fromOps.operations().append(RotateTransformOperation::create( + 1, 1, 1, 30, TransformOperation::Rotate3D)); + toSame.operations().append(RotateTransformOperation::create( + 1, 1, 1, 390, TransformOperation::Rotate3D)); + toOpposite.operations().append(RotateTransformOperation::create( + -1, -1, -1, 390, TransformOperation::Rotate3D)); + toDifferent.operations().append(RotateTransformOperation::create( + 1, 3, 1, 390, TransformOperation::Rotate3D)); + + FloatBox box(1, 0, 0, 0, 0, 0); + FloatBox bounds; + EXPECT_TRUE(toSame.blendedBoundsForBox(box, fromOps, 0, 1, &bounds)); + EXPECT_TRUE(toOpposite.blendedBoundsForBox(box, fromOps, 0, 1, &bounds)); + EXPECT_FALSE(toDifferent.blendedBoundsForBox(box, fromOps, 0, 1, &bounds)); +} - EXPECT_TRUE(toOps.blendedBoundsForBox(box, fromOps, 0, 1, &bounds)); - EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, box, bounds); +TEST(TransformOperationsTest, AbsoluteAnimatedOnAxisRotationBounds) { + // If we rotate a point that is on the axis of rotation, the box should not + // change at all. + TransformOperations fromOps; + TransformOperations toOps; + fromOps.operations().append(RotateTransformOperation::create( + 1, 1, 1, 30, TransformOperation::Rotate3D)); + toOps.operations().append(RotateTransformOperation::create( + 1, 1, 1, 390, TransformOperation::Rotate3D)); + + FloatBox box(1, 1, 1, 0, 0, 0); + FloatBox bounds; + + EXPECT_TRUE(toOps.blendedBoundsForBox(box, fromOps, 0, 1, &bounds)); + EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, box, bounds); } // This would have been best as anonymous structs, but |WTF_ARRAY_LENGTH| @@ -264,229 +291,218 @@ TEST(TransformOperationsTest, AbsoluteAnimatedOnAxisRotationBounds) // WTF_ARRAY_LENGTH will automatically support anonymous structs. struct ProblematicAxisTest { - double x; - double y; - double z; - FloatBox expected; + double x; + double y; + double z; + FloatBox expected; }; -TEST(TransformOperationsTest, AbsoluteAnimatedProblematicAxisRotationBounds) -{ - // Zeros in the components of the axis osf rotation turned out to be tricky to - // deal with in practice. This function tests some potentially problematic - // axes to ensure sane behavior. - - // Some common values used in the expected boxes. - float dim1 = 0.292893f; - float dim2 = sqrt(2.0f); - float dim3 = 2 * dim2; - - ProblematicAxisTest tests[] = { - { 0, 0, 0, FloatBox(1, 1, 1, 0, 0, 0) }, - { 1, 0, 0, FloatBox(1, -dim2, -dim2, 0, dim3, dim3) }, - { 0, 1, 0, FloatBox(-dim2, 1, -dim2, dim3, 0, dim3) }, - { 0, 0, 1, FloatBox(-dim2, -dim2, 1, dim3, dim3, 0) }, - { 1, 1, 0, FloatBox(dim1, dim1, -1, dim2, dim2, 2) }, - { 0, 1, 1, FloatBox(-1, dim1, dim1, 2, dim2, dim2) }, - { 1, 0, 1, FloatBox(dim1, -1, dim1, dim2, 2, dim2) } - }; - - for (size_t i = 0; i < WTF_ARRAY_LENGTH(tests); ++i) { - float x = tests[i].x; - float y = tests[i].y; - float z = tests[i].z; - TransformOperations fromOps; - fromOps.operations().append(RotateTransformOperation::create(x, y, z, 0, TransformOperation::Rotate3D)); - TransformOperations toOps; - toOps.operations().append(RotateTransformOperation::create(x, y, z, 360, TransformOperation::Rotate3D)); - FloatBox box(1, 1, 1, 0, 0, 0); - FloatBox bounds; - - EXPECT_TRUE(toOps.blendedBoundsForBox( - box, fromOps, 0, 1, &bounds)); - EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, tests[i].expected, bounds); - } -} - - -TEST(TransformOperationsTest, BlendedBoundsForRotationEmpiricalTests) -{ - float axes[][3] = { - { 1, 1, 1 }, - { -1, -1, -1 }, - { -1, 2, 3 }, - { 1, -2, 3 }, - { 0, 0, 0 }, - { 1, 0, 0 }, - { 0, 1, 0 }, - { 0, 0, 1 }, - { 1, 1, 0 }, - { 0, 1, 1 }, - { 1, 0, 1 }, - { -1, 0, 0 }, - { 0, -1, 0 }, - { 0, 0, -1 }, - { -1, -1, 0 }, - { 0, -1, -1 }, - { -1, 0, -1 } - }; - - float angles[][2] = { - { 5, 100 }, - { 10, 5 }, - { 0, 360 }, - { 20, 180 }, - { -20, -180 }, - { 180, -220 }, - { 220, 320 }, - { 1020, 1120 }, - {-3200, 120 }, - {-9000, -9050 } - }; - - float progress[][2] = { - { 0, 1 }, - { -0.25f, 1.25f } - }; - - for (size_t i = 0; i < WTF_ARRAY_LENGTH(axes); ++i) { - for (size_t j = 0; j < WTF_ARRAY_LENGTH(angles); ++j) { - for (size_t k = 0; k < WTF_ARRAY_LENGTH(progress); ++k) { - float x = axes[i][0]; - float y = axes[i][1]; - float z = axes[i][2]; - - TransformOperations fromOps; - TransformOperations toOps; - - fromOps.operations().append(RotateTransformOperation::create(x, y, z, angles[j][0], TransformOperation::Rotate3D)); - toOps.operations().append(RotateTransformOperation::create(x, y, z, angles[j][1], TransformOperation::Rotate3D)); - EmpiricallyTestBounds(fromOps, toOps, progress[k][0], progress[k][1]); - } - } - } -} - -TEST(TransformOperationsTest, AbsoluteAnimatedPerspectiveBoundsTest) -{ +TEST(TransformOperationsTest, AbsoluteAnimatedProblematicAxisRotationBounds) { + // Zeros in the components of the axis osf rotation turned out to be tricky to + // deal with in practice. This function tests some potentially problematic + // axes to ensure sane behavior. + + // Some common values used in the expected boxes. + float dim1 = 0.292893f; + float dim2 = sqrt(2.0f); + float dim3 = 2 * dim2; + + ProblematicAxisTest tests[] = { + {0, 0, 0, FloatBox(1, 1, 1, 0, 0, 0)}, + {1, 0, 0, FloatBox(1, -dim2, -dim2, 0, dim3, dim3)}, + {0, 1, 0, FloatBox(-dim2, 1, -dim2, dim3, 0, dim3)}, + {0, 0, 1, FloatBox(-dim2, -dim2, 1, dim3, dim3, 0)}, + {1, 1, 0, FloatBox(dim1, dim1, -1, dim2, dim2, 2)}, + {0, 1, 1, FloatBox(-1, dim1, dim1, 2, dim2, dim2)}, + {1, 0, 1, FloatBox(dim1, -1, dim1, dim2, 2, dim2)}}; + + for (size_t i = 0; i < WTF_ARRAY_LENGTH(tests); ++i) { + float x = tests[i].x; + float y = tests[i].y; + float z = tests[i].z; TransformOperations fromOps; + fromOps.operations().append(RotateTransformOperation::create( + x, y, z, 0, TransformOperation::Rotate3D)); TransformOperations toOps; - fromOps.operations().append(PerspectiveTransformOperation::create(10)); - toOps.operations().append(PerspectiveTransformOperation::create(30)); - FloatBox box(0, 0, 0, 10, 10, 10); + toOps.operations().append(RotateTransformOperation::create( + x, y, z, 360, TransformOperation::Rotate3D)); + FloatBox box(1, 1, 1, 0, 0, 0); FloatBox bounds; - toOps.blendedBoundsForBox(box, fromOps, 0, 1, &bounds); - EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(0, 0, 0, 15, 15, 15), bounds); - - fromOps.blendedBoundsForBox(box, toOps, -0.25, 1.25, &bounds); - EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(-40, -40, -40, 52, 52, 52), bounds); -} -TEST(TransformOperationsTest, EmpiricalAnimatedPerspectiveBoundsTest) -{ - float depths[][2] = { - { 600, 400 }, - { 800, 1000 }, - { 800, std::numeric_limits::infinity() } - }; - - float progress[][2] = { - { 0, 1 }, - {-0.1f, 1.1f } - }; - - for (size_t i = 0; i < WTF_ARRAY_LENGTH(depths); ++i) { - for (size_t j = 0; j < WTF_ARRAY_LENGTH(progress); ++j) { - TransformOperations fromOps; - TransformOperations toOps; - - fromOps.operations().append(PerspectiveTransformOperation::create(depths[i][0])); - toOps.operations().append(PerspectiveTransformOperation::create(depths[i][1])); - - EmpiricallyTestBounds(fromOps, toOps, progress[j][0], progress[j][1]); - } - } + EXPECT_TRUE(toOps.blendedBoundsForBox(box, fromOps, 0, 1, &bounds)); + EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, tests[i].expected, + bounds); + } } +TEST(TransformOperationsTest, BlendedBoundsForRotationEmpiricalTests) { + float axes[][3] = {{1, 1, 1}, {-1, -1, -1}, {-1, 2, 3}, {1, -2, 3}, + {0, 0, 0}, {1, 0, 0}, {0, 1, 0}, {0, 0, 1}, + {1, 1, 0}, {0, 1, 1}, {1, 0, 1}, {-1, 0, 0}, + {0, -1, 0}, {0, 0, -1}, {-1, -1, 0}, {0, -1, -1}, + {-1, 0, -1}}; -TEST(TransformOperationsTest, AnimatedSkewBoundsTest) -{ - TransformOperations fromOps; - TransformOperations toOps; - fromOps.operations().append(SkewTransformOperation::create(-45, 0, TransformOperation::Skew)); - toOps.operations().append(SkewTransformOperation::create(0, 45, TransformOperation::Skew)); - FloatBox box(0, 0, 0, 10, 10, 10); - FloatBox bounds; + float angles[][2] = {{5, 100}, {10, 5}, {0, 360}, {20, 180}, + {-20, -180}, {180, -220}, {220, 320}, {1020, 1120}, + {-3200, 120}, {-9000, -9050}}; - toOps.blendedBoundsForBox(box, identityOperations, 0, 1, &bounds); - ASSERT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(0, 0, 0, 10, 20, 10), bounds); + float progress[][2] = {{0, 1}, {-0.25f, 1.25f}}; - identityOperations.blendedBoundsForBox(box, fromOps, 0, 1, &bounds); - ASSERT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(-10, 0, 0, 20, 10, 10), bounds); + for (size_t i = 0; i < WTF_ARRAY_LENGTH(axes); ++i) { + for (size_t j = 0; j < WTF_ARRAY_LENGTH(angles); ++j) { + for (size_t k = 0; k < WTF_ARRAY_LENGTH(progress); ++k) { + float x = axes[i][0]; + float y = axes[i][1]; + float z = axes[i][2]; - toOps.blendedBoundsForBox(box, fromOps, 0, 1, &bounds); - ASSERT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(-10, 0, 0, 20, 20, 10), bounds); + TransformOperations fromOps; + TransformOperations toOps; - fromOps.blendedBoundsForBox(box, toOps, 0, 1, &bounds); - ASSERT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(-10, 0, 0, 20, 20, 10), bounds); + fromOps.operations().append(RotateTransformOperation::create( + x, y, z, angles[j][0], TransformOperation::Rotate3D)); + toOps.operations().append(RotateTransformOperation::create( + x, y, z, angles[j][1], TransformOperation::Rotate3D)); + EmpiricallyTestBounds(fromOps, toOps, progress[k][0], progress[k][1]); + } + } + } } -TEST(TransformOperationsTest, NonCommutativeRotations) -{ - TransformOperations fromOps; - fromOps.operations().append(RotateTransformOperation::create(1, 0, 0, 0, TransformOperation::Rotate3D)); - fromOps.operations().append(RotateTransformOperation::create(0, 1, 0, 0, TransformOperation::Rotate3D)); - TransformOperations toOps; - toOps.operations().append(RotateTransformOperation::create(1, 0, 0, 45, TransformOperation::Rotate3D)); - toOps.operations().append(RotateTransformOperation::create(0, 1, 0, 135, TransformOperation::Rotate3D)); - - FloatBox box(0, 0, 0, 1, 1, 1); - FloatBox bounds; - - double minProgress = 0; - double maxProgress = 1; - EXPECT_TRUE(toOps.blendedBoundsForBox( - box, fromOps, minProgress, maxProgress, &bounds)); - - TransformOperations operations = toOps.blend(fromOps, maxProgress); - TransformationMatrix blendedTransform; - operations.apply(FloatSize(0, 0), blendedTransform); - - FloatPoint3D blendedPoint(0.9f, 0.9f, 0); - blendedPoint = blendedTransform.mapPoint(blendedPoint); - FloatBox expandedBounds = bounds; - expandedBounds.expandTo(blendedPoint); - - ASSERT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, bounds, expandedBounds); +TEST(TransformOperationsTest, AbsoluteAnimatedPerspectiveBoundsTest) { + TransformOperations fromOps; + TransformOperations toOps; + fromOps.operations().append(PerspectiveTransformOperation::create(10)); + toOps.operations().append(PerspectiveTransformOperation::create(30)); + FloatBox box(0, 0, 0, 10, 10, 10); + FloatBox bounds; + toOps.blendedBoundsForBox(box, fromOps, 0, 1, &bounds); + EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, + FloatBox(0, 0, 0, 15, 15, 15), bounds); + + fromOps.blendedBoundsForBox(box, toOps, -0.25, 1.25, &bounds); + EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, + FloatBox(-40, -40, -40, 52, 52, 52), bounds); } -TEST(TransformOperationsTest, AbsoluteSequenceBoundsTest) -{ - TransformOperations fromOps; - TransformOperations toOps; +TEST(TransformOperationsTest, EmpiricalAnimatedPerspectiveBoundsTest) { + float depths[][2] = { + {600, 400}, {800, 1000}, {800, std::numeric_limits::infinity()}}; - fromOps.operations().append(TranslateTransformOperation::create(Length(1, Fixed), Length(-5, Fixed), 1, TransformOperation::Translate3D)); - fromOps.operations().append(ScaleTransformOperation::create(-1, 2, 3, TransformOperation::Scale3D)); - fromOps.operations().append(TranslateTransformOperation::create(Length(2, Fixed), Length(4, Fixed), -1, TransformOperation::Translate3D)); + float progress[][2] = {{0, 1}, {-0.1f, 1.1f}}; - toOps.operations().append(TranslateTransformOperation::create(Length(13, Fixed), Length(-1, Fixed), 5, TransformOperation::Translate3D)); - toOps.operations().append(ScaleTransformOperation::create(-3, -2, 5, TransformOperation::Scale3D)); - toOps.operations().append(TranslateTransformOperation::create(Length(6, Fixed), Length(-2, Fixed), 3, TransformOperation::Translate3D)); + for (size_t i = 0; i < WTF_ARRAY_LENGTH(depths); ++i) { + for (size_t j = 0; j < WTF_ARRAY_LENGTH(progress); ++j) { + TransformOperations fromOps; + TransformOperations toOps; - FloatBox box(1, 2, 3, 4, 4, 4); - FloatBox bounds; + fromOps.operations().append( + PerspectiveTransformOperation::create(depths[i][0])); + toOps.operations().append( + PerspectiveTransformOperation::create(depths[i][1])); - EXPECT_TRUE(toOps.blendedBoundsForBox(box, fromOps, -0.5, 1.5, &bounds)); - EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(-57, -59, -1, 76, 112, 80), bounds); + EmpiricallyTestBounds(fromOps, toOps, progress[j][0], progress[j][1]); + } + } +} - EXPECT_TRUE(toOps.blendedBoundsForBox(box, fromOps, 0, 1, &bounds)); - EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(-32, -25, 7, 42, 44, 48), bounds); +TEST(TransformOperationsTest, AnimatedSkewBoundsTest) { + TransformOperations fromOps; + TransformOperations toOps; + fromOps.operations().append( + SkewTransformOperation::create(-45, 0, TransformOperation::Skew)); + toOps.operations().append( + SkewTransformOperation::create(0, 45, TransformOperation::Skew)); + FloatBox box(0, 0, 0, 10, 10, 10); + FloatBox bounds; + + toOps.blendedBoundsForBox(box, identityOperations, 0, 1, &bounds); + ASSERT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, + FloatBox(0, 0, 0, 10, 20, 10), bounds); + + identityOperations.blendedBoundsForBox(box, fromOps, 0, 1, &bounds); + ASSERT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, + FloatBox(-10, 0, 0, 20, 10, 10), bounds); + + toOps.blendedBoundsForBox(box, fromOps, 0, 1, &bounds); + ASSERT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, + FloatBox(-10, 0, 0, 20, 20, 10), bounds); + + fromOps.blendedBoundsForBox(box, toOps, 0, 1, &bounds); + ASSERT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, + FloatBox(-10, 0, 0, 20, 20, 10), bounds); +} - EXPECT_TRUE(toOps.blendedBoundsForBox(box, identityOperations, 0, 1, &bounds)); - EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(-33, -13, 3, 57, 19, 52), bounds); +TEST(TransformOperationsTest, NonCommutativeRotations) { + TransformOperations fromOps; + fromOps.operations().append(RotateTransformOperation::create( + 1, 0, 0, 0, TransformOperation::Rotate3D)); + fromOps.operations().append(RotateTransformOperation::create( + 0, 1, 0, 0, TransformOperation::Rotate3D)); + TransformOperations toOps; + toOps.operations().append(RotateTransformOperation::create( + 1, 0, 0, 45, TransformOperation::Rotate3D)); + toOps.operations().append(RotateTransformOperation::create( + 0, 1, 0, 135, TransformOperation::Rotate3D)); + + FloatBox box(0, 0, 0, 1, 1, 1); + FloatBox bounds; + + double minProgress = 0; + double maxProgress = 1; + EXPECT_TRUE(toOps.blendedBoundsForBox(box, fromOps, minProgress, maxProgress, + &bounds)); + + TransformOperations operations = toOps.blend(fromOps, maxProgress); + TransformationMatrix blendedTransform; + operations.apply(FloatSize(0, 0), blendedTransform); + + FloatPoint3D blendedPoint(0.9f, 0.9f, 0); + blendedPoint = blendedTransform.mapPoint(blendedPoint); + FloatBox expandedBounds = bounds; + expandedBounds.expandTo(blendedPoint); + + ASSERT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, bounds, expandedBounds); +} - EXPECT_TRUE(identityOperations.blendedBoundsForBox(box, fromOps, 0, 1, &bounds)); - EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, FloatBox(-7, -3, 2, 15, 23, 20), bounds); +TEST(TransformOperationsTest, AbsoluteSequenceBoundsTest) { + TransformOperations fromOps; + TransformOperations toOps; + + fromOps.operations().append(TranslateTransformOperation::create( + Length(1, Fixed), Length(-5, Fixed), 1, TransformOperation::Translate3D)); + fromOps.operations().append( + ScaleTransformOperation::create(-1, 2, 3, TransformOperation::Scale3D)); + fromOps.operations().append(TranslateTransformOperation::create( + Length(2, Fixed), Length(4, Fixed), -1, TransformOperation::Translate3D)); + + toOps.operations().append( + TranslateTransformOperation::create(Length(13, Fixed), Length(-1, Fixed), + 5, TransformOperation::Translate3D)); + toOps.operations().append( + ScaleTransformOperation::create(-3, -2, 5, TransformOperation::Scale3D)); + toOps.operations().append(TranslateTransformOperation::create( + Length(6, Fixed), Length(-2, Fixed), 3, TransformOperation::Translate3D)); + + FloatBox box(1, 2, 3, 4, 4, 4); + FloatBox bounds; + + EXPECT_TRUE(toOps.blendedBoundsForBox(box, fromOps, -0.5, 1.5, &bounds)); + EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, + FloatBox(-57, -59, -1, 76, 112, 80), bounds); + + EXPECT_TRUE(toOps.blendedBoundsForBox(box, fromOps, 0, 1, &bounds)); + EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, + FloatBox(-32, -25, 7, 42, 44, 48), bounds); + + EXPECT_TRUE( + toOps.blendedBoundsForBox(box, identityOperations, 0, 1, &bounds)); + EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, + FloatBox(-33, -13, 3, 57, 19, 52), bounds); + + EXPECT_TRUE( + identityOperations.blendedBoundsForBox(box, fromOps, 0, 1, &bounds)); + EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, + FloatBox(-7, -3, 2, 15, 23, 20), bounds); } -} // namespace +} // namespace diff --git a/sky/engine/platform/transforms/TransformationMatrix.cpp b/sky/engine/platform/transforms/TransformationMatrix.cpp index 584155af20797..503c370539bf2 100644 --- a/sky/engine/platform/transforms/TransformationMatrix.cpp +++ b/sky/engine/platform/transforms/TransformationMatrix.cpp @@ -46,24 +46,29 @@ namespace blink { // // Supporting Math Functions // -// This is a set of function from various places (attributed inline) to do things like -// inversion and decomposition of a 4x4 matrix. They are used throughout the code +// This is a set of function from various places (attributed inline) to do +// things like inversion and decomposition of a 4x4 matrix. They are used +// throughout the code // // -// Adapted from Matrix Inversion by Richard Carling, Graphics Gems . - -// EULA: The Graphics Gems code is copyright-protected. In other words, you cannot claim the text of the code -// as your own and resell it. Using the code is permitted in any program, product, or library, non-commercial -// or commercial. Giving credit is not required, though is a nice gesture. The code comes as-is, and if there -// are any flaws or problems with any Gems code, nobody involved with Gems - authors, editors, publishers, or -// webmasters - are to be held responsible. Basically, don't be a jerk, and remember that anything free comes -// with no guarantee. +// Adapted from Matrix Inversion by Richard Carling, Graphics Gems +// . + +// EULA: The Graphics Gems code is copyright-protected. In other words, you +// cannot claim the text of the code as your own and resell it. Using the code +// is permitted in any program, product, or library, non-commercial or +// commercial. Giving credit is not required, though is a nice gesture. The code +// comes as-is, and if there are any flaws or problems with any Gems code, +// nobody involved with Gems - authors, editors, publishers, or webmasters - are +// to be held responsible. Basically, don't be a jerk, and remember that +// anything free comes with no guarantee. // A clarification about the storage of matrix elements // -// This class uses a 2 dimensional array internally to store the elements of the matrix. The first index into -// the array refers to the column that the element lies in; the second index refers to the row. +// This class uses a 2 dimensional array internally to store the elements of the +// matrix. The first index into the array refers to the column that the element +// lies in; the second index refers to the row. // // In other words, this is the layout of the matrix: // @@ -89,9 +94,8 @@ const double SMALL_NUMBER = 1.e-8; // // calculate the determinant of a 2x2 matrix. -static double determinant2x2(double a, double b, double c, double d) -{ - return a * d - b * c; +static double determinant2x2(double a, double b, double c, double d) { + return a * d - b * c; } // double = determinant3x3(a1, a2, a3, b1, b2, b3, c1, c2, c3) @@ -103,46 +107,52 @@ static double determinant2x2(double a, double b, double c, double d) // | a2, b2, c2 | // | a3, b3, c3 | -static double determinant3x3(double a1, double a2, double a3, double b1, double b2, double b3, double c1, double c2, double c3) -{ - return a1 * determinant2x2(b2, b3, c2, c3) - - b1 * determinant2x2(a2, a3, c2, c3) - + c1 * determinant2x2(a2, a3, b2, b3); +static double determinant3x3(double a1, + double a2, + double a3, + double b1, + double b2, + double b3, + double c1, + double c2, + double c3) { + return a1 * determinant2x2(b2, b3, c2, c3) - + b1 * determinant2x2(a2, a3, c2, c3) + + c1 * determinant2x2(a2, a3, b2, b3); } // double = determinant4x4(matrix) // // calculate the determinant of a 4x4 matrix. -static double determinant4x4(const TransformationMatrix::Matrix4& m) -{ - // Assign to individual variable names to aid selecting - // correct elements - - double a1 = m[0][0]; - double b1 = m[0][1]; - double c1 = m[0][2]; - double d1 = m[0][3]; - - double a2 = m[1][0]; - double b2 = m[1][1]; - double c2 = m[1][2]; - double d2 = m[1][3]; - - double a3 = m[2][0]; - double b3 = m[2][1]; - double c3 = m[2][2]; - double d3 = m[2][3]; - - double a4 = m[3][0]; - double b4 = m[3][1]; - double c4 = m[3][2]; - double d4 = m[3][3]; - - return a1 * determinant3x3(b2, b3, b4, c2, c3, c4, d2, d3, d4) - - b1 * determinant3x3(a2, a3, a4, c2, c3, c4, d2, d3, d4) - + c1 * determinant3x3(a2, a3, a4, b2, b3, b4, d2, d3, d4) - - d1 * determinant3x3(a2, a3, a4, b2, b3, b4, c2, c3, c4); +static double determinant4x4(const TransformationMatrix::Matrix4& m) { + // Assign to individual variable names to aid selecting + // correct elements + + double a1 = m[0][0]; + double b1 = m[0][1]; + double c1 = m[0][2]; + double d1 = m[0][3]; + + double a2 = m[1][0]; + double b2 = m[1][1]; + double c2 = m[1][2]; + double d2 = m[1][3]; + + double a3 = m[2][0]; + double b3 = m[2][1]; + double c3 = m[2][2]; + double d3 = m[2][3]; + + double a4 = m[3][0]; + double b4 = m[3][1]; + double c4 = m[3][2]; + double d4 = m[3][3]; + + return a1 * determinant3x3(b2, b3, b4, c2, c3, c4, d2, d3, d4) - + b1 * determinant3x3(a2, a3, a4, c2, c3, c4, d2, d3, d4) + + c1 * determinant3x3(a2, a3, a4, b2, b3, b4, d2, d3, d4) - + d1 * determinant3x3(a2, a3, a4, b2, b3, b4, c2, c3, c4); } // adjoint( original_matrix, inverse_matrix ) @@ -161,73 +171,73 @@ static double determinant4x4(const TransformationMatrix::Matrix4& m) // The matrix B = (b ) is the adjoint of A // ij -static void adjoint(const TransformationMatrix::Matrix4& matrix, TransformationMatrix::Matrix4& result) -{ - // Assign to individual variable names to aid - // selecting correct values - double a1 = matrix[0][0]; - double b1 = matrix[0][1]; - double c1 = matrix[0][2]; - double d1 = matrix[0][3]; - - double a2 = matrix[1][0]; - double b2 = matrix[1][1]; - double c2 = matrix[1][2]; - double d2 = matrix[1][3]; - - double a3 = matrix[2][0]; - double b3 = matrix[2][1]; - double c3 = matrix[2][2]; - double d3 = matrix[2][3]; - - double a4 = matrix[3][0]; - double b4 = matrix[3][1]; - double c4 = matrix[3][2]; - double d4 = matrix[3][3]; - - // Row column labeling reversed since we transpose rows & columns - result[0][0] = determinant3x3(b2, b3, b4, c2, c3, c4, d2, d3, d4); - result[1][0] = - determinant3x3(a2, a3, a4, c2, c3, c4, d2, d3, d4); - result[2][0] = determinant3x3(a2, a3, a4, b2, b3, b4, d2, d3, d4); - result[3][0] = - determinant3x3(a2, a3, a4, b2, b3, b4, c2, c3, c4); - - result[0][1] = - determinant3x3(b1, b3, b4, c1, c3, c4, d1, d3, d4); - result[1][1] = determinant3x3(a1, a3, a4, c1, c3, c4, d1, d3, d4); - result[2][1] = - determinant3x3(a1, a3, a4, b1, b3, b4, d1, d3, d4); - result[3][1] = determinant3x3(a1, a3, a4, b1, b3, b4, c1, c3, c4); - - result[0][2] = determinant3x3(b1, b2, b4, c1, c2, c4, d1, d2, d4); - result[1][2] = - determinant3x3(a1, a2, a4, c1, c2, c4, d1, d2, d4); - result[2][2] = determinant3x3(a1, a2, a4, b1, b2, b4, d1, d2, d4); - result[3][2] = - determinant3x3(a1, a2, a4, b1, b2, b4, c1, c2, c4); - - result[0][3] = - determinant3x3(b1, b2, b3, c1, c2, c3, d1, d2, d3); - result[1][3] = determinant3x3(a1, a2, a3, c1, c2, c3, d1, d2, d3); - result[2][3] = - determinant3x3(a1, a2, a3, b1, b2, b3, d1, d2, d3); - result[3][3] = determinant3x3(a1, a2, a3, b1, b2, b3, c1, c2, c3); +static void adjoint(const TransformationMatrix::Matrix4& matrix, + TransformationMatrix::Matrix4& result) { + // Assign to individual variable names to aid + // selecting correct values + double a1 = matrix[0][0]; + double b1 = matrix[0][1]; + double c1 = matrix[0][2]; + double d1 = matrix[0][3]; + + double a2 = matrix[1][0]; + double b2 = matrix[1][1]; + double c2 = matrix[1][2]; + double d2 = matrix[1][3]; + + double a3 = matrix[2][0]; + double b3 = matrix[2][1]; + double c3 = matrix[2][2]; + double d3 = matrix[2][3]; + + double a4 = matrix[3][0]; + double b4 = matrix[3][1]; + double c4 = matrix[3][2]; + double d4 = matrix[3][3]; + + // Row column labeling reversed since we transpose rows & columns + result[0][0] = determinant3x3(b2, b3, b4, c2, c3, c4, d2, d3, d4); + result[1][0] = -determinant3x3(a2, a3, a4, c2, c3, c4, d2, d3, d4); + result[2][0] = determinant3x3(a2, a3, a4, b2, b3, b4, d2, d3, d4); + result[3][0] = -determinant3x3(a2, a3, a4, b2, b3, b4, c2, c3, c4); + + result[0][1] = -determinant3x3(b1, b3, b4, c1, c3, c4, d1, d3, d4); + result[1][1] = determinant3x3(a1, a3, a4, c1, c3, c4, d1, d3, d4); + result[2][1] = -determinant3x3(a1, a3, a4, b1, b3, b4, d1, d3, d4); + result[3][1] = determinant3x3(a1, a3, a4, b1, b3, b4, c1, c3, c4); + + result[0][2] = determinant3x3(b1, b2, b4, c1, c2, c4, d1, d2, d4); + result[1][2] = -determinant3x3(a1, a2, a4, c1, c2, c4, d1, d2, d4); + result[2][2] = determinant3x3(a1, a2, a4, b1, b2, b4, d1, d2, d4); + result[3][2] = -determinant3x3(a1, a2, a4, b1, b2, b4, c1, c2, c4); + + result[0][3] = -determinant3x3(b1, b2, b3, c1, c2, c3, d1, d2, d3); + result[1][3] = determinant3x3(a1, a2, a3, c1, c2, c3, d1, d2, d3); + result[2][3] = -determinant3x3(a1, a2, a3, b1, b2, b3, d1, d2, d3); + result[3][3] = determinant3x3(a1, a2, a3, b1, b2, b3, c1, c2, c3); } // Returns false if the matrix is not invertible -static bool inverse(const TransformationMatrix::Matrix4& matrix, TransformationMatrix::Matrix4& result) -{ - // Calculate the adjoint matrix - adjoint(matrix, result); +static bool inverse(const TransformationMatrix::Matrix4& matrix, + TransformationMatrix::Matrix4& result) { + // Calculate the adjoint matrix + adjoint(matrix, result); - // Calculate the 4x4 determinant - // If the determinant is zero, - // then the inverse matrix is not unique. - double det = determinant4x4(matrix); + // Calculate the 4x4 determinant + // If the determinant is zero, + // then the inverse matrix is not unique. + double det = determinant4x4(matrix); - if (fabs(det) < SMALL_NUMBER) - return false; + if (fabs(det) < SMALL_NUMBER) + return false; - // Scale the adjoint matrix to get the inverse + // Scale the adjoint matrix to get the inverse - for (int i = 0; i < 4; i++) - for (int j = 0; j < 4; j++) - result[i][j] = result[i][j] / det; + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + result[i][j] = result[i][j] / det; - return true; + return true; } // End of code adapted from Matrix Inversion by Richard Carling @@ -236,642 +246,574 @@ static bool inverse(const TransformationMatrix::Matrix4& matrix, TransformationM // From Graphics Gems: unmatrix.c // Transpose rotation portion of matrix a, return b -static void transposeMatrix4(const TransformationMatrix::Matrix4& a, TransformationMatrix::Matrix4& b) -{ - for (int i = 0; i < 4; i++) - for (int j = 0; j < 4; j++) - b[i][j] = a[j][i]; +static void transposeMatrix4(const TransformationMatrix::Matrix4& a, + TransformationMatrix::Matrix4& b) { + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + b[i][j] = a[j][i]; } // Multiply a homogeneous point by a matrix and return the transformed point -static void v4MulPointByMatrix(const Vector4 p, const TransformationMatrix::Matrix4& m, Vector4 result) -{ - result[0] = (p[0] * m[0][0]) + (p[1] * m[1][0]) + - (p[2] * m[2][0]) + (p[3] * m[3][0]); - result[1] = (p[0] * m[0][1]) + (p[1] * m[1][1]) + - (p[2] * m[2][1]) + (p[3] * m[3][1]); - result[2] = (p[0] * m[0][2]) + (p[1] * m[1][2]) + - (p[2] * m[2][2]) + (p[3] * m[3][2]); - result[3] = (p[0] * m[0][3]) + (p[1] * m[1][3]) + - (p[2] * m[2][3]) + (p[3] * m[3][3]); +static void v4MulPointByMatrix(const Vector4 p, + const TransformationMatrix::Matrix4& m, + Vector4 result) { + result[0] = + (p[0] * m[0][0]) + (p[1] * m[1][0]) + (p[2] * m[2][0]) + (p[3] * m[3][0]); + result[1] = + (p[0] * m[0][1]) + (p[1] * m[1][1]) + (p[2] * m[2][1]) + (p[3] * m[3][1]); + result[2] = + (p[0] * m[0][2]) + (p[1] * m[1][2]) + (p[2] * m[2][2]) + (p[3] * m[3][2]); + result[3] = + (p[0] * m[0][3]) + (p[1] * m[1][3]) + (p[2] * m[2][3]) + (p[3] * m[3][3]); } -static double v3Length(Vector3 a) -{ - return std::sqrt((a[0] * a[0]) + (a[1] * a[1]) + (a[2] * a[2])); +static double v3Length(Vector3 a) { + return std::sqrt((a[0] * a[0]) + (a[1] * a[1]) + (a[2] * a[2])); } -static void v3Scale(Vector3 v, double desiredLength) -{ - double len = v3Length(v); - if (len != 0) { - double l = desiredLength / len; - v[0] *= l; - v[1] *= l; - v[2] *= l; - } +static void v3Scale(Vector3 v, double desiredLength) { + double len = v3Length(v); + if (len != 0) { + double l = desiredLength / len; + v[0] *= l; + v[1] *= l; + v[2] *= l; + } } -static double v3Dot(const Vector3 a, const Vector3 b) -{ - return (a[0] * b[0]) + (a[1] * b[1]) + (a[2] * b[2]); +static double v3Dot(const Vector3 a, const Vector3 b) { + return (a[0] * b[0]) + (a[1] * b[1]) + (a[2] * b[2]); } // Make a linear combination of two vectors and return the result. // result = (a * ascl) + (b * bscl) -static void v3Combine(const Vector3 a, const Vector3 b, Vector3 result, double ascl, double bscl) -{ - result[0] = (ascl * a[0]) + (bscl * b[0]); - result[1] = (ascl * a[1]) + (bscl * b[1]); - result[2] = (ascl * a[2]) + (bscl * b[2]); +static void v3Combine(const Vector3 a, + const Vector3 b, + Vector3 result, + double ascl, + double bscl) { + result[0] = (ascl * a[0]) + (bscl * b[0]); + result[1] = (ascl * a[1]) + (bscl * b[1]); + result[2] = (ascl * a[2]) + (bscl * b[2]); } // Return the cross product result = a cross b */ -static void v3Cross(const Vector3 a, const Vector3 b, Vector3 result) -{ - result[0] = (a[1] * b[2]) - (a[2] * b[1]); - result[1] = (a[2] * b[0]) - (a[0] * b[2]); - result[2] = (a[0] * b[1]) - (a[1] * b[0]); +static void v3Cross(const Vector3 a, const Vector3 b, Vector3 result) { + result[0] = (a[1] * b[2]) - (a[2] * b[1]); + result[1] = (a[2] * b[0]) - (a[0] * b[2]); + result[2] = (a[0] * b[1]) - (a[1] * b[0]); } -static bool decompose(const TransformationMatrix::Matrix4& mat, TransformationMatrix::DecomposedType& result) -{ - TransformationMatrix::Matrix4 localMatrix; - memcpy(localMatrix, mat, sizeof(TransformationMatrix::Matrix4)); - - // Normalize the matrix. - if (localMatrix[3][3] == 0) - return false; - - int i, j; - for (i = 0; i < 4; i++) - for (j = 0; j < 4; j++) - localMatrix[i][j] /= localMatrix[3][3]; - - // perspectiveMatrix is used to solve for perspective, but it also provides - // an easy way to test for singularity of the upper 3x3 component. - TransformationMatrix::Matrix4 perspectiveMatrix; - memcpy(perspectiveMatrix, localMatrix, sizeof(TransformationMatrix::Matrix4)); - for (i = 0; i < 3; i++) - perspectiveMatrix[i][3] = 0; - perspectiveMatrix[3][3] = 1; - - if (determinant4x4(perspectiveMatrix) == 0) - return false; - - // First, isolate perspective. This is the messiest. - if (localMatrix[0][3] != 0 || localMatrix[1][3] != 0 || localMatrix[2][3] != 0) { - // rightHandSide is the right hand side of the equation. - Vector4 rightHandSide; - rightHandSide[0] = localMatrix[0][3]; - rightHandSide[1] = localMatrix[1][3]; - rightHandSide[2] = localMatrix[2][3]; - rightHandSide[3] = localMatrix[3][3]; - - // Solve the equation by inverting perspectiveMatrix and multiplying - // rightHandSide by the inverse. (This is the easiest way, not - // necessarily the best.) - TransformationMatrix::Matrix4 inversePerspectiveMatrix, transposedInversePerspectiveMatrix; - inverse(perspectiveMatrix, inversePerspectiveMatrix); - transposeMatrix4(inversePerspectiveMatrix, transposedInversePerspectiveMatrix); - - Vector4 perspectivePoint; - v4MulPointByMatrix(rightHandSide, transposedInversePerspectiveMatrix, perspectivePoint); - - result.perspectiveX = perspectivePoint[0]; - result.perspectiveY = perspectivePoint[1]; - result.perspectiveZ = perspectivePoint[2]; - result.perspectiveW = perspectivePoint[3]; - - // Clear the perspective partition - localMatrix[0][3] = localMatrix[1][3] = localMatrix[2][3] = 0; - localMatrix[3][3] = 1; - } else { - // No perspective. - result.perspectiveX = result.perspectiveY = result.perspectiveZ = 0; - result.perspectiveW = 1; - } - - // Next take care of translation (easy). - result.translateX = localMatrix[3][0]; - localMatrix[3][0] = 0; - result.translateY = localMatrix[3][1]; - localMatrix[3][1] = 0; - result.translateZ = localMatrix[3][2]; - localMatrix[3][2] = 0; - - // Vector4 type and functions need to be added to the common set. - Vector3 row[3], pdum3; +static bool decompose(const TransformationMatrix::Matrix4& mat, + TransformationMatrix::DecomposedType& result) { + TransformationMatrix::Matrix4 localMatrix; + memcpy(localMatrix, mat, sizeof(TransformationMatrix::Matrix4)); + + // Normalize the matrix. + if (localMatrix[3][3] == 0) + return false; + + int i, j; + for (i = 0; i < 4; i++) + for (j = 0; j < 4; j++) + localMatrix[i][j] /= localMatrix[3][3]; + + // perspectiveMatrix is used to solve for perspective, but it also provides + // an easy way to test for singularity of the upper 3x3 component. + TransformationMatrix::Matrix4 perspectiveMatrix; + memcpy(perspectiveMatrix, localMatrix, sizeof(TransformationMatrix::Matrix4)); + for (i = 0; i < 3; i++) + perspectiveMatrix[i][3] = 0; + perspectiveMatrix[3][3] = 1; + + if (determinant4x4(perspectiveMatrix) == 0) + return false; + + // First, isolate perspective. This is the messiest. + if (localMatrix[0][3] != 0 || localMatrix[1][3] != 0 || + localMatrix[2][3] != 0) { + // rightHandSide is the right hand side of the equation. + Vector4 rightHandSide; + rightHandSide[0] = localMatrix[0][3]; + rightHandSide[1] = localMatrix[1][3]; + rightHandSide[2] = localMatrix[2][3]; + rightHandSide[3] = localMatrix[3][3]; + + // Solve the equation by inverting perspectiveMatrix and multiplying + // rightHandSide by the inverse. (This is the easiest way, not + // necessarily the best.) + TransformationMatrix::Matrix4 inversePerspectiveMatrix, + transposedInversePerspectiveMatrix; + inverse(perspectiveMatrix, inversePerspectiveMatrix); + transposeMatrix4(inversePerspectiveMatrix, + transposedInversePerspectiveMatrix); + + Vector4 perspectivePoint; + v4MulPointByMatrix(rightHandSide, transposedInversePerspectiveMatrix, + perspectivePoint); + + result.perspectiveX = perspectivePoint[0]; + result.perspectiveY = perspectivePoint[1]; + result.perspectiveZ = perspectivePoint[2]; + result.perspectiveW = perspectivePoint[3]; + + // Clear the perspective partition + localMatrix[0][3] = localMatrix[1][3] = localMatrix[2][3] = 0; + localMatrix[3][3] = 1; + } else { + // No perspective. + result.perspectiveX = result.perspectiveY = result.perspectiveZ = 0; + result.perspectiveW = 1; + } + + // Next take care of translation (easy). + result.translateX = localMatrix[3][0]; + localMatrix[3][0] = 0; + result.translateY = localMatrix[3][1]; + localMatrix[3][1] = 0; + result.translateZ = localMatrix[3][2]; + localMatrix[3][2] = 0; + + // Vector4 type and functions need to be added to the common set. + Vector3 row[3], pdum3; + + // Now get scale and shear. + for (i = 0; i < 3; i++) { + row[i][0] = localMatrix[i][0]; + row[i][1] = localMatrix[i][1]; + row[i][2] = localMatrix[i][2]; + } + + // Compute X scale factor and normalize first row. + result.scaleX = v3Length(row[0]); + v3Scale(row[0], 1.0); + + // Compute XY shear factor and make 2nd row orthogonal to 1st. + result.skewXY = v3Dot(row[0], row[1]); + v3Combine(row[1], row[0], row[1], 1.0, -result.skewXY); + + // Now, compute Y scale and normalize 2nd row. + result.scaleY = v3Length(row[1]); + v3Scale(row[1], 1.0); + result.skewXY /= result.scaleY; + + // Compute XZ and YZ shears, orthogonalize 3rd row. + result.skewXZ = v3Dot(row[0], row[2]); + v3Combine(row[2], row[0], row[2], 1.0, -result.skewXZ); + result.skewYZ = v3Dot(row[1], row[2]); + v3Combine(row[2], row[1], row[2], 1.0, -result.skewYZ); + + // Next, get Z scale and normalize 3rd row. + result.scaleZ = v3Length(row[2]); + v3Scale(row[2], 1.0); + result.skewXZ /= result.scaleZ; + result.skewYZ /= result.scaleZ; + + // At this point, the matrix (in rows[]) is orthonormal. + // Check for a coordinate system flip. If the determinant + // is -1, then negate the matrix and the scaling factors. + v3Cross(row[1], row[2], pdum3); + if (v3Dot(row[0], pdum3) < 0) { + result.scaleX *= -1; + result.scaleY *= -1; + result.scaleZ *= -1; - // Now get scale and shear. for (i = 0; i < 3; i++) { - row[i][0] = localMatrix[i][0]; - row[i][1] = localMatrix[i][1]; - row[i][2] = localMatrix[i][2]; + row[i][0] *= -1; + row[i][1] *= -1; + row[i][2] *= -1; } - - // Compute X scale factor and normalize first row. - result.scaleX = v3Length(row[0]); - v3Scale(row[0], 1.0); - - // Compute XY shear factor and make 2nd row orthogonal to 1st. - result.skewXY = v3Dot(row[0], row[1]); - v3Combine(row[1], row[0], row[1], 1.0, -result.skewXY); - - // Now, compute Y scale and normalize 2nd row. - result.scaleY = v3Length(row[1]); - v3Scale(row[1], 1.0); - result.skewXY /= result.scaleY; - - // Compute XZ and YZ shears, orthogonalize 3rd row. - result.skewXZ = v3Dot(row[0], row[2]); - v3Combine(row[2], row[0], row[2], 1.0, -result.skewXZ); - result.skewYZ = v3Dot(row[1], row[2]); - v3Combine(row[2], row[1], row[2], 1.0, -result.skewYZ); - - // Next, get Z scale and normalize 3rd row. - result.scaleZ = v3Length(row[2]); - v3Scale(row[2], 1.0); - result.skewXZ /= result.scaleZ; - result.skewYZ /= result.scaleZ; - - // At this point, the matrix (in rows[]) is orthonormal. - // Check for a coordinate system flip. If the determinant - // is -1, then negate the matrix and the scaling factors. - v3Cross(row[1], row[2], pdum3); - if (v3Dot(row[0], pdum3) < 0) { - - result.scaleX *= -1; - result.scaleY *= -1; - result.scaleZ *= -1; - - for (i = 0; i < 3; i++) { - row[i][0] *= -1; - row[i][1] *= -1; - row[i][2] *= -1; - } - } - - // Now, get the rotations out, as described in the gem. - - // FIXME - Add the ability to return either quaternions (which are - // easier to recompose with) or Euler angles (rx, ry, rz), which - // are easier for authors to deal with. The latter will only be useful - // when we fix https://bugs.webkit.org/show_bug.cgi?id=23799, so I - // will leave the Euler angle code here for now. - - // ret.rotateY = asin(-row[0][2]); - // if (cos(ret.rotateY) != 0) { - // ret.rotateX = atan2(row[1][2], row[2][2]); - // ret.rotateZ = atan2(row[0][1], row[0][0]); - // } else { - // ret.rotateX = atan2(-row[2][0], row[1][1]); - // ret.rotateZ = 0; - // } - - double s, t, x, y, z, w; - - t = row[0][0] + row[1][1] + row[2][2] + 1.0; - - if (t > 1e-4) { - s = 0.5 / std::sqrt(t); - w = 0.25 / s; - x = (row[2][1] - row[1][2]) * s; - y = (row[0][2] - row[2][0]) * s; - z = (row[1][0] - row[0][1]) * s; - } else if (row[0][0] > row[1][1] && row[0][0] > row[2][2]) { - s = std::sqrt(1.0 + row[0][0] - row[1][1] - row[2][2]) * 2.0; // S=4*qx - x = 0.25 * s; - y = (row[0][1] + row[1][0]) / s; - z = (row[0][2] + row[2][0]) / s; - w = (row[2][1] - row[1][2]) / s; - } else if (row[1][1] > row[2][2]) { - s = std::sqrt(1.0 + row[1][1] - row[0][0] - row[2][2]) * 2.0; // S=4*qy - x = (row[0][1] + row[1][0]) / s; - y = 0.25 * s; - z = (row[1][2] + row[2][1]) / s; - w = (row[0][2] - row[2][0]) / s; - } else { - s = std::sqrt(1.0 + row[2][2] - row[0][0] - row[1][1]) * 2.0; // S=4*qz - x = (row[0][2] + row[2][0]) / s; - y = (row[1][2] + row[2][1]) / s; - z = 0.25 * s; - w = (row[1][0] - row[0][1]) / s; - } - - result.quaternionX = x; - result.quaternionY = y; - result.quaternionZ = z; - result.quaternionW = w; - - return true; + } + + // Now, get the rotations out, as described in the gem. + + // FIXME - Add the ability to return either quaternions (which are + // easier to recompose with) or Euler angles (rx, ry, rz), which + // are easier for authors to deal with. The latter will only be useful + // when we fix https://bugs.webkit.org/show_bug.cgi?id=23799, so I + // will leave the Euler angle code here for now. + + // ret.rotateY = asin(-row[0][2]); + // if (cos(ret.rotateY) != 0) { + // ret.rotateX = atan2(row[1][2], row[2][2]); + // ret.rotateZ = atan2(row[0][1], row[0][0]); + // } else { + // ret.rotateX = atan2(-row[2][0], row[1][1]); + // ret.rotateZ = 0; + // } + + double s, t, x, y, z, w; + + t = row[0][0] + row[1][1] + row[2][2] + 1.0; + + if (t > 1e-4) { + s = 0.5 / std::sqrt(t); + w = 0.25 / s; + x = (row[2][1] - row[1][2]) * s; + y = (row[0][2] - row[2][0]) * s; + z = (row[1][0] - row[0][1]) * s; + } else if (row[0][0] > row[1][1] && row[0][0] > row[2][2]) { + s = std::sqrt(1.0 + row[0][0] - row[1][1] - row[2][2]) * 2.0; // S=4*qx + x = 0.25 * s; + y = (row[0][1] + row[1][0]) / s; + z = (row[0][2] + row[2][0]) / s; + w = (row[2][1] - row[1][2]) / s; + } else if (row[1][1] > row[2][2]) { + s = std::sqrt(1.0 + row[1][1] - row[0][0] - row[2][2]) * 2.0; // S=4*qy + x = (row[0][1] + row[1][0]) / s; + y = 0.25 * s; + z = (row[1][2] + row[2][1]) / s; + w = (row[0][2] - row[2][0]) / s; + } else { + s = std::sqrt(1.0 + row[2][2] - row[0][0] - row[1][1]) * 2.0; // S=4*qz + x = (row[0][2] + row[2][0]) / s; + y = (row[1][2] + row[2][1]) / s; + z = 0.25 * s; + w = (row[1][0] - row[0][1]) / s; + } + + result.quaternionX = x; + result.quaternionY = y; + result.quaternionZ = z; + result.quaternionW = w; + + return true; } // Perform a spherical linear interpolation between the two // passed quaternions with 0 <= t <= 1 -static void slerp(double qa[4], const double qb[4], double t) -{ - double ax, ay, az, aw; - double bx, by, bz, bw; - double cx, cy, cz, cw; - double angle; - double th, invth, scale, invscale; - - ax = qa[0]; ay = qa[1]; az = qa[2]; aw = qa[3]; - bx = qb[0]; by = qb[1]; bz = qb[2]; bw = qb[3]; - - angle = ax * bx + ay * by + az * bz + aw * bw; - - if (angle < 0.0) { - ax = -ax; ay = -ay; - az = -az; aw = -aw; - angle = -angle; - } - - if (angle + 1.0 > .05) { - if (1.0 - angle >= .05) { - th = std::acos(angle); - invth = 1.0 / std::sin(th); - scale = std::sin(th * (1.0 - t)) * invth; - invscale = std::sin(th * t) * invth; - } else { - scale = 1.0 - t; - invscale = t; - } +static void slerp(double qa[4], const double qb[4], double t) { + double ax, ay, az, aw; + double bx, by, bz, bw; + double cx, cy, cz, cw; + double angle; + double th, invth, scale, invscale; + + ax = qa[0]; + ay = qa[1]; + az = qa[2]; + aw = qa[3]; + bx = qb[0]; + by = qb[1]; + bz = qb[2]; + bw = qb[3]; + + angle = ax * bx + ay * by + az * bz + aw * bw; + + if (angle < 0.0) { + ax = -ax; + ay = -ay; + az = -az; + aw = -aw; + angle = -angle; + } + + if (angle + 1.0 > .05) { + if (1.0 - angle >= .05) { + th = std::acos(angle); + invth = 1.0 / std::sin(th); + scale = std::sin(th * (1.0 - t)) * invth; + invscale = std::sin(th * t) * invth; } else { - bx = -ay; - by = ax; - bz = -aw; - bw = az; - scale = std::sin(piDouble * (.5 - t)); - invscale = std::sin(piDouble * t); + scale = 1.0 - t; + invscale = t; } - - cx = ax * scale + bx * invscale; - cy = ay * scale + by * invscale; - cz = az * scale + bz * invscale; - cw = aw * scale + bw * invscale; - - qa[0] = cx; qa[1] = cy; qa[2] = cz; qa[3] = cw; + } else { + bx = -ay; + by = ax; + bz = -aw; + bw = az; + scale = std::sin(piDouble * (.5 - t)); + invscale = std::sin(piDouble * t); + } + + cx = ax * scale + bx * invscale; + cy = ay * scale + by * invscale; + cz = az * scale + bz * invscale; + cw = aw * scale + bw * invscale; + + qa[0] = cx; + qa[1] = cy; + qa[2] = cz; + qa[3] = cw; } // End of Supporting Math Functions -TransformationMatrix::TransformationMatrix(const AffineTransform& t) -{ - setMatrix(t.a(), t.b(), t.c(), t.d(), t.e(), t.f()); +TransformationMatrix::TransformationMatrix(const AffineTransform& t) { + setMatrix(t.a(), t.b(), t.c(), t.d(), t.e(), t.f()); } -TransformationMatrix& TransformationMatrix::scale(double s) -{ - return scaleNonUniform(s, s); +TransformationMatrix& TransformationMatrix::scale(double s) { + return scaleNonUniform(s, s); } -TransformationMatrix& TransformationMatrix::rotateFromVector(double x, double y) -{ - return rotate(rad2deg(atan2(y, x))); +TransformationMatrix& TransformationMatrix::rotateFromVector(double x, + double y) { + return rotate(rad2deg(atan2(y, x))); } -TransformationMatrix& TransformationMatrix::flipX() -{ - return scaleNonUniform(-1.0, 1.0); +TransformationMatrix& TransformationMatrix::flipX() { + return scaleNonUniform(-1.0, 1.0); } -TransformationMatrix& TransformationMatrix::flipY() -{ - return scaleNonUniform(1.0, -1.0); +TransformationMatrix& TransformationMatrix::flipY() { + return scaleNonUniform(1.0, -1.0); } -FloatPoint TransformationMatrix::projectPoint(const FloatPoint& p, bool* clamped) const -{ - // This is basically raytracing. We have a point in the destination - // plane with z=0, and we cast a ray parallel to the z-axis from that - // point to find the z-position at which it intersects the z=0 plane - // with the transform applied. Once we have that point we apply the - // inverse transform to find the corresponding point in the source - // space. - // - // Given a plane with normal Pn, and a ray starting at point R0 and - // with direction defined by the vector Rd, we can find the - // intersection point as a distance d from R0 in units of Rd by: - // - // d = -dot (Pn', R0) / dot (Pn', Rd) +FloatPoint TransformationMatrix::projectPoint(const FloatPoint& p, + bool* clamped) const { + // This is basically raytracing. We have a point in the destination + // plane with z=0, and we cast a ray parallel to the z-axis from that + // point to find the z-position at which it intersects the z=0 plane + // with the transform applied. Once we have that point we apply the + // inverse transform to find the corresponding point in the source + // space. + // + // Given a plane with normal Pn, and a ray starting at point R0 and + // with direction defined by the vector Rd, we can find the + // intersection point as a distance d from R0 in units of Rd by: + // + // d = -dot (Pn', R0) / dot (Pn', Rd) + if (clamped) + *clamped = false; + + if (m33() == 0) { + // In this case, the projection plane is parallel to the ray we are trying + // to trace, and there is no well-defined value for the projection. + return FloatPoint(); + } + + double x = p.x(); + double y = p.y(); + double z = -(m13() * x + m23() * y + m43()) / m33(); + + // FIXME: use multVecMatrix() + double outX = x * m11() + y * m21() + z * m31() + m41(); + double outY = x * m12() + y * m22() + z * m32() + m42(); + + double w = x * m14() + y * m24() + z * m34() + m44(); + if (w <= 0) { + // Using int max causes overflow when other code uses the projected point. + // To represent infinity yet reduce the risk of overflow, we use a large but + // not-too-large number here when clamping. + const int largeNumber = 100000000 / kFixedPointDenominator; + outX = copysign(largeNumber, outX); + outY = copysign(largeNumber, outY); if (clamped) - *clamped = false; - - if (m33() == 0) { - // In this case, the projection plane is parallel to the ray we are trying to - // trace, and there is no well-defined value for the projection. - return FloatPoint(); - } - - double x = p.x(); - double y = p.y(); - double z = -(m13() * x + m23() * y + m43()) / m33(); - - // FIXME: use multVecMatrix() - double outX = x * m11() + y * m21() + z * m31() + m41(); - double outY = x * m12() + y * m22() + z * m32() + m42(); - - double w = x * m14() + y * m24() + z * m34() + m44(); - if (w <= 0) { - // Using int max causes overflow when other code uses the projected point. To - // represent infinity yet reduce the risk of overflow, we use a large but - // not-too-large number here when clamping. - const int largeNumber = 100000000 / kFixedPointDenominator; - outX = copysign(largeNumber, outX); - outY = copysign(largeNumber, outY); - if (clamped) - *clamped = true; - } else if (w != 1) { - outX /= w; - outY /= w; - } + *clamped = true; + } else if (w != 1) { + outX /= w; + outY /= w; + } - return FloatPoint(static_cast(outX), static_cast(outY)); + return FloatPoint(static_cast(outX), static_cast(outY)); } -FloatQuad TransformationMatrix::projectQuad(const FloatQuad& q, bool* clamped) const -{ - FloatQuad projectedQuad; +FloatQuad TransformationMatrix::projectQuad(const FloatQuad& q, + bool* clamped) const { + FloatQuad projectedQuad; - bool clamped1 = false; - bool clamped2 = false; - bool clamped3 = false; - bool clamped4 = false; + bool clamped1 = false; + bool clamped2 = false; + bool clamped3 = false; + bool clamped4 = false; - projectedQuad.setP1(projectPoint(q.p1(), &clamped1)); - projectedQuad.setP2(projectPoint(q.p2(), &clamped2)); - projectedQuad.setP3(projectPoint(q.p3(), &clamped3)); - projectedQuad.setP4(projectPoint(q.p4(), &clamped4)); + projectedQuad.setP1(projectPoint(q.p1(), &clamped1)); + projectedQuad.setP2(projectPoint(q.p2(), &clamped2)); + projectedQuad.setP3(projectPoint(q.p3(), &clamped3)); + projectedQuad.setP4(projectPoint(q.p4(), &clamped4)); - if (clamped) - *clamped = clamped1 || clamped2 || clamped3 || clamped4; + if (clamped) + *clamped = clamped1 || clamped2 || clamped3 || clamped4; - // If all points on the quad had w < 0, then the entire quad would not be visible to the projected surface. - bool everythingWasClipped = clamped1 && clamped2 && clamped3 && clamped4; - if (everythingWasClipped) - return FloatQuad(); + // If all points on the quad had w < 0, then the entire quad would not be + // visible to the projected surface. + bool everythingWasClipped = clamped1 && clamped2 && clamped3 && clamped4; + if (everythingWasClipped) + return FloatQuad(); - return projectedQuad; + return projectedQuad; } -static float clampEdgeValue(float f) -{ - ASSERT(!std::isnan(f)); - return std::min(std::max(f, (-LayoutUnit::max() / 2).toFloat()), (LayoutUnit::max() / 2).toFloat()); +static float clampEdgeValue(float f) { + ASSERT(!std::isnan(f)); + return std::min(std::max(f, (-LayoutUnit::max() / 2).toFloat()), + (LayoutUnit::max() / 2).toFloat()); } -LayoutRect TransformationMatrix::clampedBoundsOfProjectedQuad(const FloatQuad& q) const -{ - FloatRect mappedQuadBounds = projectQuad(q).boundingBox(); +LayoutRect TransformationMatrix::clampedBoundsOfProjectedQuad( + const FloatQuad& q) const { + FloatRect mappedQuadBounds = projectQuad(q).boundingBox(); - float left = clampEdgeValue(floorf(mappedQuadBounds.x())); - float top = clampEdgeValue(floorf(mappedQuadBounds.y())); + float left = clampEdgeValue(floorf(mappedQuadBounds.x())); + float top = clampEdgeValue(floorf(mappedQuadBounds.y())); - float right; - if (std::isinf(mappedQuadBounds.x()) && std::isinf(mappedQuadBounds.width())) - right = (LayoutUnit::max() / 2).toFloat(); - else - right = clampEdgeValue(ceilf(mappedQuadBounds.maxX())); + float right; + if (std::isinf(mappedQuadBounds.x()) && std::isinf(mappedQuadBounds.width())) + right = (LayoutUnit::max() / 2).toFloat(); + else + right = clampEdgeValue(ceilf(mappedQuadBounds.maxX())); - float bottom; - if (std::isinf(mappedQuadBounds.y()) && std::isinf(mappedQuadBounds.height())) - bottom = (LayoutUnit::max() / 2).toFloat(); - else - bottom = clampEdgeValue(ceilf(mappedQuadBounds.maxY())); + float bottom; + if (std::isinf(mappedQuadBounds.y()) && std::isinf(mappedQuadBounds.height())) + bottom = (LayoutUnit::max() / 2).toFloat(); + else + bottom = clampEdgeValue(ceilf(mappedQuadBounds.maxY())); - return LayoutRect(LayoutUnit::clamp(left), LayoutUnit::clamp(top), LayoutUnit::clamp(right - left), LayoutUnit::clamp(bottom - top)); + return LayoutRect(LayoutUnit::clamp(left), LayoutUnit::clamp(top), + LayoutUnit::clamp(right - left), + LayoutUnit::clamp(bottom - top)); } -void TransformationMatrix::transformBox(FloatBox& box) const -{ - FloatBox bounds; - bool firstPoint = true; - for (size_t i = 0; i < 2; ++i) { - for (size_t j = 0; j < 2; ++j) { - for (size_t k = 0; k < 2; ++k) { - FloatPoint3D point(box.x(), box.y(), box.z()); - point += FloatPoint3D(i * box.width(), j * box.height(), k * box.depth()); - point = mapPoint(point); - if (firstPoint) { - bounds.setOrigin(point); - firstPoint = false; - } else { - bounds.expandTo(point); - } - } +void TransformationMatrix::transformBox(FloatBox& box) const { + FloatBox bounds; + bool firstPoint = true; + for (size_t i = 0; i < 2; ++i) { + for (size_t j = 0; j < 2; ++j) { + for (size_t k = 0; k < 2; ++k) { + FloatPoint3D point(box.x(), box.y(), box.z()); + point += + FloatPoint3D(i * box.width(), j * box.height(), k * box.depth()); + point = mapPoint(point); + if (firstPoint) { + bounds.setOrigin(point); + firstPoint = false; + } else { + bounds.expandTo(point); } + } } - box = bounds; + } + box = bounds; } -FloatPoint TransformationMatrix::mapPoint(const FloatPoint& p) const -{ - if (isIdentityOrTranslation()) - return FloatPoint(p.x() + static_cast(m_matrix[3][0]), p.y() + static_cast(m_matrix[3][1])); +FloatPoint TransformationMatrix::mapPoint(const FloatPoint& p) const { + if (isIdentityOrTranslation()) + return FloatPoint(p.x() + static_cast(m_matrix[3][0]), + p.y() + static_cast(m_matrix[3][1])); - return internalMapPoint(p); + return internalMapPoint(p); } -FloatPoint3D TransformationMatrix::mapPoint(const FloatPoint3D& p) const -{ - if (isIdentityOrTranslation()) - return FloatPoint3D(p.x() + static_cast(m_matrix[3][0]), - p.y() + static_cast(m_matrix[3][1]), - p.z() + static_cast(m_matrix[3][2])); +FloatPoint3D TransformationMatrix::mapPoint(const FloatPoint3D& p) const { + if (isIdentityOrTranslation()) + return FloatPoint3D(p.x() + static_cast(m_matrix[3][0]), + p.y() + static_cast(m_matrix[3][1]), + p.z() + static_cast(m_matrix[3][2])); - return internalMapPoint(p); + return internalMapPoint(p); } -IntRect TransformationMatrix::mapRect(const IntRect &rect) const -{ - return enclosingIntRect(mapRect(FloatRect(rect))); +IntRect TransformationMatrix::mapRect(const IntRect& rect) const { + return enclosingIntRect(mapRect(FloatRect(rect))); } -LayoutRect TransformationMatrix::mapRect(const LayoutRect& r) const -{ - return enclosingLayoutRect(mapRect(FloatRect(r))); +LayoutRect TransformationMatrix::mapRect(const LayoutRect& r) const { + return enclosingLayoutRect(mapRect(FloatRect(r))); } -FloatRect TransformationMatrix::mapRect(const FloatRect& r) const -{ - if (isIdentityOrTranslation()) { - FloatRect mappedRect(r); - mappedRect.move(static_cast(m_matrix[3][0]), static_cast(m_matrix[3][1])); - return mappedRect; - } +FloatRect TransformationMatrix::mapRect(const FloatRect& r) const { + if (isIdentityOrTranslation()) { + FloatRect mappedRect(r); + mappedRect.move(static_cast(m_matrix[3][0]), + static_cast(m_matrix[3][1])); + return mappedRect; + } - FloatQuad result; + FloatQuad result; - float maxX = r.maxX(); - float maxY = r.maxY(); - result.setP1(internalMapPoint(FloatPoint(r.x(), r.y()))); - result.setP2(internalMapPoint(FloatPoint(maxX, r.y()))); - result.setP3(internalMapPoint(FloatPoint(maxX, maxY))); - result.setP4(internalMapPoint(FloatPoint(r.x(), maxY))); + float maxX = r.maxX(); + float maxY = r.maxY(); + result.setP1(internalMapPoint(FloatPoint(r.x(), r.y()))); + result.setP2(internalMapPoint(FloatPoint(maxX, r.y()))); + result.setP3(internalMapPoint(FloatPoint(maxX, maxY))); + result.setP4(internalMapPoint(FloatPoint(r.x(), maxY))); - return result.boundingBox(); + return result.boundingBox(); } -FloatQuad TransformationMatrix::mapQuad(const FloatQuad& q) const -{ - if (isIdentityOrTranslation()) { - FloatQuad mappedQuad(q); - mappedQuad.move(static_cast(m_matrix[3][0]), static_cast(m_matrix[3][1])); - return mappedQuad; - } - - FloatQuad result; - result.setP1(internalMapPoint(q.p1())); - result.setP2(internalMapPoint(q.p2())); - result.setP3(internalMapPoint(q.p3())); - result.setP4(internalMapPoint(q.p4())); - return result; +FloatQuad TransformationMatrix::mapQuad(const FloatQuad& q) const { + if (isIdentityOrTranslation()) { + FloatQuad mappedQuad(q); + mappedQuad.move(static_cast(m_matrix[3][0]), + static_cast(m_matrix[3][1])); + return mappedQuad; + } + + FloatQuad result; + result.setP1(internalMapPoint(q.p1())); + result.setP2(internalMapPoint(q.p2())); + result.setP3(internalMapPoint(q.p3())); + result.setP4(internalMapPoint(q.p4())); + return result; } -TransformationMatrix& TransformationMatrix::scaleNonUniform(double sx, double sy) -{ - m_matrix[0][0] *= sx; - m_matrix[0][1] *= sx; - m_matrix[0][2] *= sx; - m_matrix[0][3] *= sx; - - m_matrix[1][0] *= sy; - m_matrix[1][1] *= sy; - m_matrix[1][2] *= sy; - m_matrix[1][3] *= sy; - return *this; +TransformationMatrix& TransformationMatrix::scaleNonUniform(double sx, + double sy) { + m_matrix[0][0] *= sx; + m_matrix[0][1] *= sx; + m_matrix[0][2] *= sx; + m_matrix[0][3] *= sx; + + m_matrix[1][0] *= sy; + m_matrix[1][1] *= sy; + m_matrix[1][2] *= sy; + m_matrix[1][3] *= sy; + return *this; } -TransformationMatrix& TransformationMatrix::scale3d(double sx, double sy, double sz) -{ - scaleNonUniform(sx, sy); +TransformationMatrix& TransformationMatrix::scale3d(double sx, + double sy, + double sz) { + scaleNonUniform(sx, sy); - m_matrix[2][0] *= sz; - m_matrix[2][1] *= sz; - m_matrix[2][2] *= sz; - m_matrix[2][3] *= sz; - return *this; + m_matrix[2][0] *= sz; + m_matrix[2][1] *= sz; + m_matrix[2][2] *= sz; + m_matrix[2][3] *= sz; + return *this; } -TransformationMatrix& TransformationMatrix::rotate3d(double x, double y, double z, double angle) -{ - // Normalize the axis of rotation - double length = std::sqrt(x * x + y * y + z * z); - if (length == 0) { - // A direction vector that cannot be normalized, such as [0, 0, 0], will cause the rotation to not be applied. - return *this; - } else if (length != 1) { - x /= length; - y /= length; - z /= length; - } - - // Angles are in degrees. Switch to radians. - angle = deg2rad(angle); - - double sinTheta = std::sin(angle); - double cosTheta = std::cos(angle); - - TransformationMatrix mat; - - // Optimize cases where the axis is along a major axis - if (x == 1.0 && y == 0.0 && z == 0.0) { - mat.m_matrix[0][0] = 1.0; - mat.m_matrix[0][1] = 0.0; - mat.m_matrix[0][2] = 0.0; - mat.m_matrix[1][0] = 0.0; - mat.m_matrix[1][1] = cosTheta; - mat.m_matrix[1][2] = sinTheta; - mat.m_matrix[2][0] = 0.0; - mat.m_matrix[2][1] = -sinTheta; - mat.m_matrix[2][2] = cosTheta; - mat.m_matrix[0][3] = mat.m_matrix[1][3] = mat.m_matrix[2][3] = 0.0; - mat.m_matrix[3][0] = mat.m_matrix[3][1] = mat.m_matrix[3][2] = 0.0; - mat.m_matrix[3][3] = 1.0; - } else if (x == 0.0 && y == 1.0 && z == 0.0) { - mat.m_matrix[0][0] = cosTheta; - mat.m_matrix[0][1] = 0.0; - mat.m_matrix[0][2] = -sinTheta; - mat.m_matrix[1][0] = 0.0; - mat.m_matrix[1][1] = 1.0; - mat.m_matrix[1][2] = 0.0; - mat.m_matrix[2][0] = sinTheta; - mat.m_matrix[2][1] = 0.0; - mat.m_matrix[2][2] = cosTheta; - mat.m_matrix[0][3] = mat.m_matrix[1][3] = mat.m_matrix[2][3] = 0.0; - mat.m_matrix[3][0] = mat.m_matrix[3][1] = mat.m_matrix[3][2] = 0.0; - mat.m_matrix[3][3] = 1.0; - } else if (x == 0.0 && y == 0.0 && z == 1.0) { - mat.m_matrix[0][0] = cosTheta; - mat.m_matrix[0][1] = sinTheta; - mat.m_matrix[0][2] = 0.0; - mat.m_matrix[1][0] = -sinTheta; - mat.m_matrix[1][1] = cosTheta; - mat.m_matrix[1][2] = 0.0; - mat.m_matrix[2][0] = 0.0; - mat.m_matrix[2][1] = 0.0; - mat.m_matrix[2][2] = 1.0; - mat.m_matrix[0][3] = mat.m_matrix[1][3] = mat.m_matrix[2][3] = 0.0; - mat.m_matrix[3][0] = mat.m_matrix[3][1] = mat.m_matrix[3][2] = 0.0; - mat.m_matrix[3][3] = 1.0; - } else { - // This case is the rotation about an arbitrary unit vector. - // - // Formula is adapted from Wikipedia article on Rotation matrix, - // http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle - // - // An alternate resource with the same matrix: http://www.fastgraph.com/makegames/3drotation/ - // - double oneMinusCosTheta = 1 - cosTheta; - mat.m_matrix[0][0] = cosTheta + x * x * oneMinusCosTheta; - mat.m_matrix[0][1] = y * x * oneMinusCosTheta + z * sinTheta; - mat.m_matrix[0][2] = z * x * oneMinusCosTheta - y * sinTheta; - mat.m_matrix[1][0] = x * y * oneMinusCosTheta - z * sinTheta; - mat.m_matrix[1][1] = cosTheta + y * y * oneMinusCosTheta; - mat.m_matrix[1][2] = z * y * oneMinusCosTheta + x * sinTheta; - mat.m_matrix[2][0] = x * z * oneMinusCosTheta + y * sinTheta; - mat.m_matrix[2][1] = y * z * oneMinusCosTheta - x * sinTheta; - mat.m_matrix[2][2] = cosTheta + z * z * oneMinusCosTheta; - mat.m_matrix[0][3] = mat.m_matrix[1][3] = mat.m_matrix[2][3] = 0.0; - mat.m_matrix[3][0] = mat.m_matrix[3][1] = mat.m_matrix[3][2] = 0.0; - mat.m_matrix[3][3] = 1.0; - } - multiply(mat); +TransformationMatrix& TransformationMatrix::rotate3d(double x, + double y, + double z, + double angle) { + // Normalize the axis of rotation + double length = std::sqrt(x * x + y * y + z * z); + if (length == 0) { + // A direction vector that cannot be normalized, such as [0, 0, 0], will + // cause the rotation to not be applied. return *this; -} + } else if (length != 1) { + x /= length; + y /= length; + z /= length; + } -TransformationMatrix& TransformationMatrix::rotate3d(double rx, double ry, double rz) -{ - // Angles are in degrees. Switch to radians. - rx = deg2rad(rx); - ry = deg2rad(ry); - rz = deg2rad(rz); + // Angles are in degrees. Switch to radians. + angle = deg2rad(angle); - TransformationMatrix mat; + double sinTheta = std::sin(angle); + double cosTheta = std::cos(angle); - double sinTheta = std::sin(rz); - double cosTheta = std::cos(rz); + TransformationMatrix mat; - mat.m_matrix[0][0] = cosTheta; - mat.m_matrix[0][1] = sinTheta; + // Optimize cases where the axis is along a major axis + if (x == 1.0 && y == 0.0 && z == 0.0) { + mat.m_matrix[0][0] = 1.0; + mat.m_matrix[0][1] = 0.0; mat.m_matrix[0][2] = 0.0; - mat.m_matrix[1][0] = -sinTheta; + mat.m_matrix[1][0] = 0.0; mat.m_matrix[1][1] = cosTheta; - mat.m_matrix[1][2] = 0.0; + mat.m_matrix[1][2] = sinTheta; mat.m_matrix[2][0] = 0.0; - mat.m_matrix[2][1] = 0.0; - mat.m_matrix[2][2] = 1.0; + mat.m_matrix[2][1] = -sinTheta; + mat.m_matrix[2][2] = cosTheta; mat.m_matrix[0][3] = mat.m_matrix[1][3] = mat.m_matrix[2][3] = 0.0; mat.m_matrix[3][0] = mat.m_matrix[3][1] = mat.m_matrix[3][2] = 0.0; mat.m_matrix[3][3] = 1.0; - - TransformationMatrix rmat(mat); - - sinTheta = std::sin(ry); - cosTheta = std::cos(ry); - + } else if (x == 0.0 && y == 1.0 && z == 0.0) { mat.m_matrix[0][0] = cosTheta; mat.m_matrix[0][1] = 0.0; mat.m_matrix[0][2] = -sinTheta; @@ -884,644 +826,764 @@ TransformationMatrix& TransformationMatrix::rotate3d(double rx, double ry, doubl mat.m_matrix[0][3] = mat.m_matrix[1][3] = mat.m_matrix[2][3] = 0.0; mat.m_matrix[3][0] = mat.m_matrix[3][1] = mat.m_matrix[3][2] = 0.0; mat.m_matrix[3][3] = 1.0; - - rmat.multiply(mat); - - sinTheta = std::sin(rx); - cosTheta = std::cos(rx); - - mat.m_matrix[0][0] = 1.0; - mat.m_matrix[0][1] = 0.0; + } else if (x == 0.0 && y == 0.0 && z == 1.0) { + mat.m_matrix[0][0] = cosTheta; + mat.m_matrix[0][1] = sinTheta; mat.m_matrix[0][2] = 0.0; - mat.m_matrix[1][0] = 0.0; + mat.m_matrix[1][0] = -sinTheta; mat.m_matrix[1][1] = cosTheta; - mat.m_matrix[1][2] = sinTheta; + mat.m_matrix[1][2] = 0.0; mat.m_matrix[2][0] = 0.0; - mat.m_matrix[2][1] = -sinTheta; - mat.m_matrix[2][2] = cosTheta; + mat.m_matrix[2][1] = 0.0; + mat.m_matrix[2][2] = 1.0; mat.m_matrix[0][3] = mat.m_matrix[1][3] = mat.m_matrix[2][3] = 0.0; mat.m_matrix[3][0] = mat.m_matrix[3][1] = mat.m_matrix[3][2] = 0.0; mat.m_matrix[3][3] = 1.0; - - rmat.multiply(mat); - - multiply(rmat); - return *this; + } else { + // This case is the rotation about an arbitrary unit vector. + // + // Formula is adapted from Wikipedia article on Rotation matrix, + // http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle + // + // An alternate resource with the same matrix: + // http://www.fastgraph.com/makegames/3drotation/ + // + double oneMinusCosTheta = 1 - cosTheta; + mat.m_matrix[0][0] = cosTheta + x * x * oneMinusCosTheta; + mat.m_matrix[0][1] = y * x * oneMinusCosTheta + z * sinTheta; + mat.m_matrix[0][2] = z * x * oneMinusCosTheta - y * sinTheta; + mat.m_matrix[1][0] = x * y * oneMinusCosTheta - z * sinTheta; + mat.m_matrix[1][1] = cosTheta + y * y * oneMinusCosTheta; + mat.m_matrix[1][2] = z * y * oneMinusCosTheta + x * sinTheta; + mat.m_matrix[2][0] = x * z * oneMinusCosTheta + y * sinTheta; + mat.m_matrix[2][1] = y * z * oneMinusCosTheta - x * sinTheta; + mat.m_matrix[2][2] = cosTheta + z * z * oneMinusCosTheta; + mat.m_matrix[0][3] = mat.m_matrix[1][3] = mat.m_matrix[2][3] = 0.0; + mat.m_matrix[3][0] = mat.m_matrix[3][1] = mat.m_matrix[3][2] = 0.0; + mat.m_matrix[3][3] = 1.0; + } + multiply(mat); + return *this; } -TransformationMatrix& TransformationMatrix::translate(double tx, double ty) -{ - m_matrix[3][0] += tx * m_matrix[0][0] + ty * m_matrix[1][0]; - m_matrix[3][1] += tx * m_matrix[0][1] + ty * m_matrix[1][1]; - m_matrix[3][2] += tx * m_matrix[0][2] + ty * m_matrix[1][2]; - m_matrix[3][3] += tx * m_matrix[0][3] + ty * m_matrix[1][3]; - return *this; +TransformationMatrix& TransformationMatrix::rotate3d(double rx, + double ry, + double rz) { + // Angles are in degrees. Switch to radians. + rx = deg2rad(rx); + ry = deg2rad(ry); + rz = deg2rad(rz); + + TransformationMatrix mat; + + double sinTheta = std::sin(rz); + double cosTheta = std::cos(rz); + + mat.m_matrix[0][0] = cosTheta; + mat.m_matrix[0][1] = sinTheta; + mat.m_matrix[0][2] = 0.0; + mat.m_matrix[1][0] = -sinTheta; + mat.m_matrix[1][1] = cosTheta; + mat.m_matrix[1][2] = 0.0; + mat.m_matrix[2][0] = 0.0; + mat.m_matrix[2][1] = 0.0; + mat.m_matrix[2][2] = 1.0; + mat.m_matrix[0][3] = mat.m_matrix[1][3] = mat.m_matrix[2][3] = 0.0; + mat.m_matrix[3][0] = mat.m_matrix[3][1] = mat.m_matrix[3][2] = 0.0; + mat.m_matrix[3][3] = 1.0; + + TransformationMatrix rmat(mat); + + sinTheta = std::sin(ry); + cosTheta = std::cos(ry); + + mat.m_matrix[0][0] = cosTheta; + mat.m_matrix[0][1] = 0.0; + mat.m_matrix[0][2] = -sinTheta; + mat.m_matrix[1][0] = 0.0; + mat.m_matrix[1][1] = 1.0; + mat.m_matrix[1][2] = 0.0; + mat.m_matrix[2][0] = sinTheta; + mat.m_matrix[2][1] = 0.0; + mat.m_matrix[2][2] = cosTheta; + mat.m_matrix[0][3] = mat.m_matrix[1][3] = mat.m_matrix[2][3] = 0.0; + mat.m_matrix[3][0] = mat.m_matrix[3][1] = mat.m_matrix[3][2] = 0.0; + mat.m_matrix[3][3] = 1.0; + + rmat.multiply(mat); + + sinTheta = std::sin(rx); + cosTheta = std::cos(rx); + + mat.m_matrix[0][0] = 1.0; + mat.m_matrix[0][1] = 0.0; + mat.m_matrix[0][2] = 0.0; + mat.m_matrix[1][0] = 0.0; + mat.m_matrix[1][1] = cosTheta; + mat.m_matrix[1][2] = sinTheta; + mat.m_matrix[2][0] = 0.0; + mat.m_matrix[2][1] = -sinTheta; + mat.m_matrix[2][2] = cosTheta; + mat.m_matrix[0][3] = mat.m_matrix[1][3] = mat.m_matrix[2][3] = 0.0; + mat.m_matrix[3][0] = mat.m_matrix[3][1] = mat.m_matrix[3][2] = 0.0; + mat.m_matrix[3][3] = 1.0; + + rmat.multiply(mat); + + multiply(rmat); + return *this; } -TransformationMatrix& TransformationMatrix::translate3d(double tx, double ty, double tz) -{ - m_matrix[3][0] += tx * m_matrix[0][0] + ty * m_matrix[1][0] + tz * m_matrix[2][0]; - m_matrix[3][1] += tx * m_matrix[0][1] + ty * m_matrix[1][1] + tz * m_matrix[2][1]; - m_matrix[3][2] += tx * m_matrix[0][2] + ty * m_matrix[1][2] + tz * m_matrix[2][2]; - m_matrix[3][3] += tx * m_matrix[0][3] + ty * m_matrix[1][3] + tz * m_matrix[2][3]; - return *this; +TransformationMatrix& TransformationMatrix::translate(double tx, double ty) { + m_matrix[3][0] += tx * m_matrix[0][0] + ty * m_matrix[1][0]; + m_matrix[3][1] += tx * m_matrix[0][1] + ty * m_matrix[1][1]; + m_matrix[3][2] += tx * m_matrix[0][2] + ty * m_matrix[1][2]; + m_matrix[3][3] += tx * m_matrix[0][3] + ty * m_matrix[1][3]; + return *this; } -TransformationMatrix& TransformationMatrix::translateRight(double tx, double ty) -{ - if (tx != 0) { - m_matrix[0][0] += m_matrix[0][3] * tx; - m_matrix[1][0] += m_matrix[1][3] * tx; - m_matrix[2][0] += m_matrix[2][3] * tx; - m_matrix[3][0] += m_matrix[3][3] * tx; - } - - if (ty != 0) { - m_matrix[0][1] += m_matrix[0][3] * ty; - m_matrix[1][1] += m_matrix[1][3] * ty; - m_matrix[2][1] += m_matrix[2][3] * ty; - m_matrix[3][1] += m_matrix[3][3] * ty; - } - - return *this; +TransformationMatrix& TransformationMatrix::translate3d(double tx, + double ty, + double tz) { + m_matrix[3][0] += + tx * m_matrix[0][0] + ty * m_matrix[1][0] + tz * m_matrix[2][0]; + m_matrix[3][1] += + tx * m_matrix[0][1] + ty * m_matrix[1][1] + tz * m_matrix[2][1]; + m_matrix[3][2] += + tx * m_matrix[0][2] + ty * m_matrix[1][2] + tz * m_matrix[2][2]; + m_matrix[3][3] += + tx * m_matrix[0][3] + ty * m_matrix[1][3] + tz * m_matrix[2][3]; + return *this; } -TransformationMatrix& TransformationMatrix::translateRight3d(double tx, double ty, double tz) -{ - translateRight(tx, ty); - if (tz != 0) { - m_matrix[0][2] += m_matrix[0][3] * tz; - m_matrix[1][2] += m_matrix[1][3] * tz; - m_matrix[2][2] += m_matrix[2][3] * tz; - m_matrix[3][2] += m_matrix[3][3] * tz; - } +TransformationMatrix& TransformationMatrix::translateRight(double tx, + double ty) { + if (tx != 0) { + m_matrix[0][0] += m_matrix[0][3] * tx; + m_matrix[1][0] += m_matrix[1][3] * tx; + m_matrix[2][0] += m_matrix[2][3] * tx; + m_matrix[3][0] += m_matrix[3][3] * tx; + } + + if (ty != 0) { + m_matrix[0][1] += m_matrix[0][3] * ty; + m_matrix[1][1] += m_matrix[1][3] * ty; + m_matrix[2][1] += m_matrix[2][3] * ty; + m_matrix[3][1] += m_matrix[3][3] * ty; + } + + return *this; +} - return *this; +TransformationMatrix& TransformationMatrix::translateRight3d(double tx, + double ty, + double tz) { + translateRight(tx, ty); + if (tz != 0) { + m_matrix[0][2] += m_matrix[0][3] * tz; + m_matrix[1][2] += m_matrix[1][3] * tz; + m_matrix[2][2] += m_matrix[2][3] * tz; + m_matrix[3][2] += m_matrix[3][3] * tz; + } + + return *this; } -TransformationMatrix& TransformationMatrix::skew(double sx, double sy) -{ - // angles are in degrees. Switch to radians - sx = deg2rad(sx); - sy = deg2rad(sy); +TransformationMatrix& TransformationMatrix::skew(double sx, double sy) { + // angles are in degrees. Switch to radians + sx = deg2rad(sx); + sy = deg2rad(sy); - TransformationMatrix mat; - mat.m_matrix[0][1] = std::tan(sy); // note that the y shear goes in the first row - mat.m_matrix[1][0] = std::tan(sx); // and the x shear in the second row + TransformationMatrix mat; + mat.m_matrix[0][1] = + std::tan(sy); // note that the y shear goes in the first row + mat.m_matrix[1][0] = std::tan(sx); // and the x shear in the second row - multiply(mat); - return *this; + multiply(mat); + return *this; } -TransformationMatrix& TransformationMatrix::applyPerspective(double p) -{ - TransformationMatrix mat; - if (p != 0) - mat.m_matrix[2][3] = -1/p; +TransformationMatrix& TransformationMatrix::applyPerspective(double p) { + TransformationMatrix mat; + if (p != 0) + mat.m_matrix[2][3] = -1 / p; - multiply(mat); - return *this; + multiply(mat); + return *this; } -TransformationMatrix TransformationMatrix::rectToRect(const FloatRect& from, const FloatRect& to) -{ - ASSERT(!from.isEmpty()); - return TransformationMatrix(to.width() / from.width(), - 0, 0, - to.height() / from.height(), - to.x() - from.x(), - to.y() - from.y()); +TransformationMatrix TransformationMatrix::rectToRect(const FloatRect& from, + const FloatRect& to) { + ASSERT(!from.isEmpty()); + return TransformationMatrix(to.width() / from.width(), 0, 0, + to.height() / from.height(), to.x() - from.x(), + to.y() - from.y()); } // this = mat * this. -TransformationMatrix& TransformationMatrix::multiply(const TransformationMatrix& mat) -{ +TransformationMatrix& TransformationMatrix::multiply( + const TransformationMatrix& mat) { #if CPU(APPLE_ARMV7S) - double* leftMatrix = &(m_matrix[0][0]); - const double* rightMatrix = &(mat.m_matrix[0][0]); - asm volatile (// First row of leftMatrix. - "mov r3, %[leftMatrix]\n\t" - "vld1.64 { d16-d19 }, [%[leftMatrix], :128]!\n\t" - "vld1.64 { d0-d3}, [%[rightMatrix], :128]!\n\t" - "vmul.f64 d4, d0, d16\n\t" - "vld1.64 { d20-d23 }, [%[leftMatrix], :128]!\n\t" - "vmla.f64 d4, d1, d20\n\t" - "vld1.64 { d24-d27 }, [%[leftMatrix], :128]!\n\t" - "vmla.f64 d4, d2, d24\n\t" - "vld1.64 { d28-d31 }, [%[leftMatrix], :128]!\n\t" - "vmla.f64 d4, d3, d28\n\t" - - "vmul.f64 d5, d0, d17\n\t" - "vmla.f64 d5, d1, d21\n\t" - "vmla.f64 d5, d2, d25\n\t" - "vmla.f64 d5, d3, d29\n\t" - - "vmul.f64 d6, d0, d18\n\t" - "vmla.f64 d6, d1, d22\n\t" - "vmla.f64 d6, d2, d26\n\t" - "vmla.f64 d6, d3, d30\n\t" - - "vmul.f64 d7, d0, d19\n\t" - "vmla.f64 d7, d1, d23\n\t" - "vmla.f64 d7, d2, d27\n\t" - "vmla.f64 d7, d3, d31\n\t" - "vld1.64 { d0-d3}, [%[rightMatrix], :128]!\n\t" - "vst1.64 { d4-d7 }, [r3, :128]!\n\t" - - // Second row of leftMatrix. - "vmul.f64 d4, d0, d16\n\t" - "vmla.f64 d4, d1, d20\n\t" - "vmla.f64 d4, d2, d24\n\t" - "vmla.f64 d4, d3, d28\n\t" - - "vmul.f64 d5, d0, d17\n\t" - "vmla.f64 d5, d1, d21\n\t" - "vmla.f64 d5, d2, d25\n\t" - "vmla.f64 d5, d3, d29\n\t" - - "vmul.f64 d6, d0, d18\n\t" - "vmla.f64 d6, d1, d22\n\t" - "vmla.f64 d6, d2, d26\n\t" - "vmla.f64 d6, d3, d30\n\t" - - "vmul.f64 d7, d0, d19\n\t" - "vmla.f64 d7, d1, d23\n\t" - "vmla.f64 d7, d2, d27\n\t" - "vmla.f64 d7, d3, d31\n\t" - "vld1.64 { d0-d3}, [%[rightMatrix], :128]!\n\t" - "vst1.64 { d4-d7 }, [r3, :128]!\n\t" - - // Third row of leftMatrix. - "vmul.f64 d4, d0, d16\n\t" - "vmla.f64 d4, d1, d20\n\t" - "vmla.f64 d4, d2, d24\n\t" - "vmla.f64 d4, d3, d28\n\t" - - "vmul.f64 d5, d0, d17\n\t" - "vmla.f64 d5, d1, d21\n\t" - "vmla.f64 d5, d2, d25\n\t" - "vmla.f64 d5, d3, d29\n\t" - - "vmul.f64 d6, d0, d18\n\t" - "vmla.f64 d6, d1, d22\n\t" - "vmla.f64 d6, d2, d26\n\t" - "vmla.f64 d6, d3, d30\n\t" - - "vmul.f64 d7, d0, d19\n\t" - "vmla.f64 d7, d1, d23\n\t" - "vmla.f64 d7, d2, d27\n\t" - "vmla.f64 d7, d3, d31\n\t" - "vld1.64 { d0-d3}, [%[rightMatrix], :128]\n\t" - "vst1.64 { d4-d7 }, [r3, :128]!\n\t" - - // Fourth and last row of leftMatrix. - "vmul.f64 d4, d0, d16\n\t" - "vmla.f64 d4, d1, d20\n\t" - "vmla.f64 d4, d2, d24\n\t" - "vmla.f64 d4, d3, d28\n\t" - - "vmul.f64 d5, d0, d17\n\t" - "vmla.f64 d5, d1, d21\n\t" - "vmla.f64 d5, d2, d25\n\t" - "vmla.f64 d5, d3, d29\n\t" - - "vmul.f64 d6, d0, d18\n\t" - "vmla.f64 d6, d1, d22\n\t" - "vmla.f64 d6, d2, d26\n\t" - "vmla.f64 d6, d3, d30\n\t" - - "vmul.f64 d7, d0, d19\n\t" - "vmla.f64 d7, d1, d23\n\t" - "vmla.f64 d7, d2, d27\n\t" - "vmla.f64 d7, d3, d31\n\t" - "vst1.64 { d4-d7 }, [r3, :128]\n\t" - : [leftMatrix]"+r"(leftMatrix), [rightMatrix]"+r"(rightMatrix) - : - : "memory", "r3", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d16", "d17", "d18", "d19", "d20", "d21", "d22", "d23", "d24", "d25", "d26", "d27", "d28", "d29", "d30", "d31"); + double* leftMatrix = &(m_matrix[0][0]); + const double* rightMatrix = &(mat.m_matrix[0][0]); + asm volatile( // First row of leftMatrix. + "mov r3, %[leftMatrix]\n\t" + "vld1.64 { d16-d19 }, [%[leftMatrix], :128]!\n\t" + "vld1.64 { d0-d3}, [%[rightMatrix], :128]!\n\t" + "vmul.f64 d4, d0, d16\n\t" + "vld1.64 { d20-d23 }, [%[leftMatrix], :128]!\n\t" + "vmla.f64 d4, d1, d20\n\t" + "vld1.64 { d24-d27 }, [%[leftMatrix], :128]!\n\t" + "vmla.f64 d4, d2, d24\n\t" + "vld1.64 { d28-d31 }, [%[leftMatrix], :128]!\n\t" + "vmla.f64 d4, d3, d28\n\t" + + "vmul.f64 d5, d0, d17\n\t" + "vmla.f64 d5, d1, d21\n\t" + "vmla.f64 d5, d2, d25\n\t" + "vmla.f64 d5, d3, d29\n\t" + + "vmul.f64 d6, d0, d18\n\t" + "vmla.f64 d6, d1, d22\n\t" + "vmla.f64 d6, d2, d26\n\t" + "vmla.f64 d6, d3, d30\n\t" + + "vmul.f64 d7, d0, d19\n\t" + "vmla.f64 d7, d1, d23\n\t" + "vmla.f64 d7, d2, d27\n\t" + "vmla.f64 d7, d3, d31\n\t" + "vld1.64 { d0-d3}, [%[rightMatrix], :128]!\n\t" + "vst1.64 { d4-d7 }, [r3, :128]!\n\t" + + // Second row of leftMatrix. + "vmul.f64 d4, d0, d16\n\t" + "vmla.f64 d4, d1, d20\n\t" + "vmla.f64 d4, d2, d24\n\t" + "vmla.f64 d4, d3, d28\n\t" + + "vmul.f64 d5, d0, d17\n\t" + "vmla.f64 d5, d1, d21\n\t" + "vmla.f64 d5, d2, d25\n\t" + "vmla.f64 d5, d3, d29\n\t" + + "vmul.f64 d6, d0, d18\n\t" + "vmla.f64 d6, d1, d22\n\t" + "vmla.f64 d6, d2, d26\n\t" + "vmla.f64 d6, d3, d30\n\t" + + "vmul.f64 d7, d0, d19\n\t" + "vmla.f64 d7, d1, d23\n\t" + "vmla.f64 d7, d2, d27\n\t" + "vmla.f64 d7, d3, d31\n\t" + "vld1.64 { d0-d3}, [%[rightMatrix], :128]!\n\t" + "vst1.64 { d4-d7 }, [r3, :128]!\n\t" + + // Third row of leftMatrix. + "vmul.f64 d4, d0, d16\n\t" + "vmla.f64 d4, d1, d20\n\t" + "vmla.f64 d4, d2, d24\n\t" + "vmla.f64 d4, d3, d28\n\t" + + "vmul.f64 d5, d0, d17\n\t" + "vmla.f64 d5, d1, d21\n\t" + "vmla.f64 d5, d2, d25\n\t" + "vmla.f64 d5, d3, d29\n\t" + + "vmul.f64 d6, d0, d18\n\t" + "vmla.f64 d6, d1, d22\n\t" + "vmla.f64 d6, d2, d26\n\t" + "vmla.f64 d6, d3, d30\n\t" + + "vmul.f64 d7, d0, d19\n\t" + "vmla.f64 d7, d1, d23\n\t" + "vmla.f64 d7, d2, d27\n\t" + "vmla.f64 d7, d3, d31\n\t" + "vld1.64 { d0-d3}, [%[rightMatrix], :128]\n\t" + "vst1.64 { d4-d7 }, [r3, :128]!\n\t" + + // Fourth and last row of leftMatrix. + "vmul.f64 d4, d0, d16\n\t" + "vmla.f64 d4, d1, d20\n\t" + "vmla.f64 d4, d2, d24\n\t" + "vmla.f64 d4, d3, d28\n\t" + + "vmul.f64 d5, d0, d17\n\t" + "vmla.f64 d5, d1, d21\n\t" + "vmla.f64 d5, d2, d25\n\t" + "vmla.f64 d5, d3, d29\n\t" + + "vmul.f64 d6, d0, d18\n\t" + "vmla.f64 d6, d1, d22\n\t" + "vmla.f64 d6, d2, d26\n\t" + "vmla.f64 d6, d3, d30\n\t" + + "vmul.f64 d7, d0, d19\n\t" + "vmla.f64 d7, d1, d23\n\t" + "vmla.f64 d7, d2, d27\n\t" + "vmla.f64 d7, d3, d31\n\t" + "vst1.64 { d4-d7 }, [r3, :128]\n\t" + : [leftMatrix] "+r"(leftMatrix), [rightMatrix] "+r"(rightMatrix) + : + : "memory", "r3", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d16", + "d17", "d18", "d19", "d20", "d21", "d22", "d23", "d24", "d25", "d26", + "d27", "d28", "d29", "d30", "d31"); #elif defined(TRANSFORMATION_MATRIX_USE_X86_64_SSE2) - // x86_64 has 16 XMM registers which is enough to do the multiplication fully in registers. - __m128d matrixBlockA = _mm_load_pd(&(m_matrix[0][0])); - __m128d matrixBlockC = _mm_load_pd(&(m_matrix[1][0])); - __m128d matrixBlockE = _mm_load_pd(&(m_matrix[2][0])); - __m128d matrixBlockG = _mm_load_pd(&(m_matrix[3][0])); - - // First row. - __m128d otherMatrixFirstParam = _mm_set1_pd(mat.m_matrix[0][0]); - __m128d otherMatrixSecondParam = _mm_set1_pd(mat.m_matrix[0][1]); - __m128d otherMatrixThirdParam = _mm_set1_pd(mat.m_matrix[0][2]); - __m128d otherMatrixFourthParam = _mm_set1_pd(mat.m_matrix[0][3]); - - // output00 and output01. - __m128d accumulator = _mm_mul_pd(matrixBlockA, otherMatrixFirstParam); - __m128d temp1 = _mm_mul_pd(matrixBlockC, otherMatrixSecondParam); - __m128d temp2 = _mm_mul_pd(matrixBlockE, otherMatrixThirdParam); - __m128d temp3 = _mm_mul_pd(matrixBlockG, otherMatrixFourthParam); - - __m128d matrixBlockB = _mm_load_pd(&(m_matrix[0][2])); - __m128d matrixBlockD = _mm_load_pd(&(m_matrix[1][2])); - __m128d matrixBlockF = _mm_load_pd(&(m_matrix[2][2])); - __m128d matrixBlockH = _mm_load_pd(&(m_matrix[3][2])); - - accumulator = _mm_add_pd(accumulator, temp1); - accumulator = _mm_add_pd(accumulator, temp2); - accumulator = _mm_add_pd(accumulator, temp3); - _mm_store_pd(&m_matrix[0][0], accumulator); - - // output02 and output03. - accumulator = _mm_mul_pd(matrixBlockB, otherMatrixFirstParam); - temp1 = _mm_mul_pd(matrixBlockD, otherMatrixSecondParam); - temp2 = _mm_mul_pd(matrixBlockF, otherMatrixThirdParam); - temp3 = _mm_mul_pd(matrixBlockH, otherMatrixFourthParam); - - accumulator = _mm_add_pd(accumulator, temp1); - accumulator = _mm_add_pd(accumulator, temp2); - accumulator = _mm_add_pd(accumulator, temp3); - _mm_store_pd(&m_matrix[0][2], accumulator); - - // Second row. - otherMatrixFirstParam = _mm_set1_pd(mat.m_matrix[1][0]); - otherMatrixSecondParam = _mm_set1_pd(mat.m_matrix[1][1]); - otherMatrixThirdParam = _mm_set1_pd(mat.m_matrix[1][2]); - otherMatrixFourthParam = _mm_set1_pd(mat.m_matrix[1][3]); - - // output10 and output11. - accumulator = _mm_mul_pd(matrixBlockA, otherMatrixFirstParam); - temp1 = _mm_mul_pd(matrixBlockC, otherMatrixSecondParam); - temp2 = _mm_mul_pd(matrixBlockE, otherMatrixThirdParam); - temp3 = _mm_mul_pd(matrixBlockG, otherMatrixFourthParam); - - accumulator = _mm_add_pd(accumulator, temp1); - accumulator = _mm_add_pd(accumulator, temp2); - accumulator = _mm_add_pd(accumulator, temp3); - _mm_store_pd(&m_matrix[1][0], accumulator); - - // output12 and output13. - accumulator = _mm_mul_pd(matrixBlockB, otherMatrixFirstParam); - temp1 = _mm_mul_pd(matrixBlockD, otherMatrixSecondParam); - temp2 = _mm_mul_pd(matrixBlockF, otherMatrixThirdParam); - temp3 = _mm_mul_pd(matrixBlockH, otherMatrixFourthParam); - - accumulator = _mm_add_pd(accumulator, temp1); - accumulator = _mm_add_pd(accumulator, temp2); - accumulator = _mm_add_pd(accumulator, temp3); - _mm_store_pd(&m_matrix[1][2], accumulator); - - // Third row. - otherMatrixFirstParam = _mm_set1_pd(mat.m_matrix[2][0]); - otherMatrixSecondParam = _mm_set1_pd(mat.m_matrix[2][1]); - otherMatrixThirdParam = _mm_set1_pd(mat.m_matrix[2][2]); - otherMatrixFourthParam = _mm_set1_pd(mat.m_matrix[2][3]); - - // output20 and output21. - accumulator = _mm_mul_pd(matrixBlockA, otherMatrixFirstParam); - temp1 = _mm_mul_pd(matrixBlockC, otherMatrixSecondParam); - temp2 = _mm_mul_pd(matrixBlockE, otherMatrixThirdParam); - temp3 = _mm_mul_pd(matrixBlockG, otherMatrixFourthParam); - - accumulator = _mm_add_pd(accumulator, temp1); - accumulator = _mm_add_pd(accumulator, temp2); - accumulator = _mm_add_pd(accumulator, temp3); - _mm_store_pd(&m_matrix[2][0], accumulator); - - // output22 and output23. - accumulator = _mm_mul_pd(matrixBlockB, otherMatrixFirstParam); - temp1 = _mm_mul_pd(matrixBlockD, otherMatrixSecondParam); - temp2 = _mm_mul_pd(matrixBlockF, otherMatrixThirdParam); - temp3 = _mm_mul_pd(matrixBlockH, otherMatrixFourthParam); - - accumulator = _mm_add_pd(accumulator, temp1); - accumulator = _mm_add_pd(accumulator, temp2); - accumulator = _mm_add_pd(accumulator, temp3); - _mm_store_pd(&m_matrix[2][2], accumulator); - - // Fourth row. - otherMatrixFirstParam = _mm_set1_pd(mat.m_matrix[3][0]); - otherMatrixSecondParam = _mm_set1_pd(mat.m_matrix[3][1]); - otherMatrixThirdParam = _mm_set1_pd(mat.m_matrix[3][2]); - otherMatrixFourthParam = _mm_set1_pd(mat.m_matrix[3][3]); - - // output30 and output31. - accumulator = _mm_mul_pd(matrixBlockA, otherMatrixFirstParam); - temp1 = _mm_mul_pd(matrixBlockC, otherMatrixSecondParam); - temp2 = _mm_mul_pd(matrixBlockE, otherMatrixThirdParam); - temp3 = _mm_mul_pd(matrixBlockG, otherMatrixFourthParam); - - accumulator = _mm_add_pd(accumulator, temp1); - accumulator = _mm_add_pd(accumulator, temp2); - accumulator = _mm_add_pd(accumulator, temp3); - _mm_store_pd(&m_matrix[3][0], accumulator); - - // output32 and output33. - accumulator = _mm_mul_pd(matrixBlockB, otherMatrixFirstParam); - temp1 = _mm_mul_pd(matrixBlockD, otherMatrixSecondParam); - temp2 = _mm_mul_pd(matrixBlockF, otherMatrixThirdParam); - temp3 = _mm_mul_pd(matrixBlockH, otherMatrixFourthParam); - - accumulator = _mm_add_pd(accumulator, temp1); - accumulator = _mm_add_pd(accumulator, temp2); - accumulator = _mm_add_pd(accumulator, temp3); - _mm_store_pd(&m_matrix[3][2], accumulator); + // x86_64 has 16 XMM registers which is enough to do the multiplication fully + // in registers. + __m128d matrixBlockA = _mm_load_pd(&(m_matrix[0][0])); + __m128d matrixBlockC = _mm_load_pd(&(m_matrix[1][0])); + __m128d matrixBlockE = _mm_load_pd(&(m_matrix[2][0])); + __m128d matrixBlockG = _mm_load_pd(&(m_matrix[3][0])); + + // First row. + __m128d otherMatrixFirstParam = _mm_set1_pd(mat.m_matrix[0][0]); + __m128d otherMatrixSecondParam = _mm_set1_pd(mat.m_matrix[0][1]); + __m128d otherMatrixThirdParam = _mm_set1_pd(mat.m_matrix[0][2]); + __m128d otherMatrixFourthParam = _mm_set1_pd(mat.m_matrix[0][3]); + + // output00 and output01. + __m128d accumulator = _mm_mul_pd(matrixBlockA, otherMatrixFirstParam); + __m128d temp1 = _mm_mul_pd(matrixBlockC, otherMatrixSecondParam); + __m128d temp2 = _mm_mul_pd(matrixBlockE, otherMatrixThirdParam); + __m128d temp3 = _mm_mul_pd(matrixBlockG, otherMatrixFourthParam); + + __m128d matrixBlockB = _mm_load_pd(&(m_matrix[0][2])); + __m128d matrixBlockD = _mm_load_pd(&(m_matrix[1][2])); + __m128d matrixBlockF = _mm_load_pd(&(m_matrix[2][2])); + __m128d matrixBlockH = _mm_load_pd(&(m_matrix[3][2])); + + accumulator = _mm_add_pd(accumulator, temp1); + accumulator = _mm_add_pd(accumulator, temp2); + accumulator = _mm_add_pd(accumulator, temp3); + _mm_store_pd(&m_matrix[0][0], accumulator); + + // output02 and output03. + accumulator = _mm_mul_pd(matrixBlockB, otherMatrixFirstParam); + temp1 = _mm_mul_pd(matrixBlockD, otherMatrixSecondParam); + temp2 = _mm_mul_pd(matrixBlockF, otherMatrixThirdParam); + temp3 = _mm_mul_pd(matrixBlockH, otherMatrixFourthParam); + + accumulator = _mm_add_pd(accumulator, temp1); + accumulator = _mm_add_pd(accumulator, temp2); + accumulator = _mm_add_pd(accumulator, temp3); + _mm_store_pd(&m_matrix[0][2], accumulator); + + // Second row. + otherMatrixFirstParam = _mm_set1_pd(mat.m_matrix[1][0]); + otherMatrixSecondParam = _mm_set1_pd(mat.m_matrix[1][1]); + otherMatrixThirdParam = _mm_set1_pd(mat.m_matrix[1][2]); + otherMatrixFourthParam = _mm_set1_pd(mat.m_matrix[1][3]); + + // output10 and output11. + accumulator = _mm_mul_pd(matrixBlockA, otherMatrixFirstParam); + temp1 = _mm_mul_pd(matrixBlockC, otherMatrixSecondParam); + temp2 = _mm_mul_pd(matrixBlockE, otherMatrixThirdParam); + temp3 = _mm_mul_pd(matrixBlockG, otherMatrixFourthParam); + + accumulator = _mm_add_pd(accumulator, temp1); + accumulator = _mm_add_pd(accumulator, temp2); + accumulator = _mm_add_pd(accumulator, temp3); + _mm_store_pd(&m_matrix[1][0], accumulator); + + // output12 and output13. + accumulator = _mm_mul_pd(matrixBlockB, otherMatrixFirstParam); + temp1 = _mm_mul_pd(matrixBlockD, otherMatrixSecondParam); + temp2 = _mm_mul_pd(matrixBlockF, otherMatrixThirdParam); + temp3 = _mm_mul_pd(matrixBlockH, otherMatrixFourthParam); + + accumulator = _mm_add_pd(accumulator, temp1); + accumulator = _mm_add_pd(accumulator, temp2); + accumulator = _mm_add_pd(accumulator, temp3); + _mm_store_pd(&m_matrix[1][2], accumulator); + + // Third row. + otherMatrixFirstParam = _mm_set1_pd(mat.m_matrix[2][0]); + otherMatrixSecondParam = _mm_set1_pd(mat.m_matrix[2][1]); + otherMatrixThirdParam = _mm_set1_pd(mat.m_matrix[2][2]); + otherMatrixFourthParam = _mm_set1_pd(mat.m_matrix[2][3]); + + // output20 and output21. + accumulator = _mm_mul_pd(matrixBlockA, otherMatrixFirstParam); + temp1 = _mm_mul_pd(matrixBlockC, otherMatrixSecondParam); + temp2 = _mm_mul_pd(matrixBlockE, otherMatrixThirdParam); + temp3 = _mm_mul_pd(matrixBlockG, otherMatrixFourthParam); + + accumulator = _mm_add_pd(accumulator, temp1); + accumulator = _mm_add_pd(accumulator, temp2); + accumulator = _mm_add_pd(accumulator, temp3); + _mm_store_pd(&m_matrix[2][0], accumulator); + + // output22 and output23. + accumulator = _mm_mul_pd(matrixBlockB, otherMatrixFirstParam); + temp1 = _mm_mul_pd(matrixBlockD, otherMatrixSecondParam); + temp2 = _mm_mul_pd(matrixBlockF, otherMatrixThirdParam); + temp3 = _mm_mul_pd(matrixBlockH, otherMatrixFourthParam); + + accumulator = _mm_add_pd(accumulator, temp1); + accumulator = _mm_add_pd(accumulator, temp2); + accumulator = _mm_add_pd(accumulator, temp3); + _mm_store_pd(&m_matrix[2][2], accumulator); + + // Fourth row. + otherMatrixFirstParam = _mm_set1_pd(mat.m_matrix[3][0]); + otherMatrixSecondParam = _mm_set1_pd(mat.m_matrix[3][1]); + otherMatrixThirdParam = _mm_set1_pd(mat.m_matrix[3][2]); + otherMatrixFourthParam = _mm_set1_pd(mat.m_matrix[3][3]); + + // output30 and output31. + accumulator = _mm_mul_pd(matrixBlockA, otherMatrixFirstParam); + temp1 = _mm_mul_pd(matrixBlockC, otherMatrixSecondParam); + temp2 = _mm_mul_pd(matrixBlockE, otherMatrixThirdParam); + temp3 = _mm_mul_pd(matrixBlockG, otherMatrixFourthParam); + + accumulator = _mm_add_pd(accumulator, temp1); + accumulator = _mm_add_pd(accumulator, temp2); + accumulator = _mm_add_pd(accumulator, temp3); + _mm_store_pd(&m_matrix[3][0], accumulator); + + // output32 and output33. + accumulator = _mm_mul_pd(matrixBlockB, otherMatrixFirstParam); + temp1 = _mm_mul_pd(matrixBlockD, otherMatrixSecondParam); + temp2 = _mm_mul_pd(matrixBlockF, otherMatrixThirdParam); + temp3 = _mm_mul_pd(matrixBlockH, otherMatrixFourthParam); + + accumulator = _mm_add_pd(accumulator, temp1); + accumulator = _mm_add_pd(accumulator, temp2); + accumulator = _mm_add_pd(accumulator, temp3); + _mm_store_pd(&m_matrix[3][2], accumulator); #else - Matrix4 tmp; - - tmp[0][0] = (mat.m_matrix[0][0] * m_matrix[0][0] + mat.m_matrix[0][1] * m_matrix[1][0] - + mat.m_matrix[0][2] * m_matrix[2][0] + mat.m_matrix[0][3] * m_matrix[3][0]); - tmp[0][1] = (mat.m_matrix[0][0] * m_matrix[0][1] + mat.m_matrix[0][1] * m_matrix[1][1] - + mat.m_matrix[0][2] * m_matrix[2][1] + mat.m_matrix[0][3] * m_matrix[3][1]); - tmp[0][2] = (mat.m_matrix[0][0] * m_matrix[0][2] + mat.m_matrix[0][1] * m_matrix[1][2] - + mat.m_matrix[0][2] * m_matrix[2][2] + mat.m_matrix[0][3] * m_matrix[3][2]); - tmp[0][3] = (mat.m_matrix[0][0] * m_matrix[0][3] + mat.m_matrix[0][1] * m_matrix[1][3] - + mat.m_matrix[0][2] * m_matrix[2][3] + mat.m_matrix[0][3] * m_matrix[3][3]); - - tmp[1][0] = (mat.m_matrix[1][0] * m_matrix[0][0] + mat.m_matrix[1][1] * m_matrix[1][0] - + mat.m_matrix[1][2] * m_matrix[2][0] + mat.m_matrix[1][3] * m_matrix[3][0]); - tmp[1][1] = (mat.m_matrix[1][0] * m_matrix[0][1] + mat.m_matrix[1][1] * m_matrix[1][1] - + mat.m_matrix[1][2] * m_matrix[2][1] + mat.m_matrix[1][3] * m_matrix[3][1]); - tmp[1][2] = (mat.m_matrix[1][0] * m_matrix[0][2] + mat.m_matrix[1][1] * m_matrix[1][2] - + mat.m_matrix[1][2] * m_matrix[2][2] + mat.m_matrix[1][3] * m_matrix[3][2]); - tmp[1][3] = (mat.m_matrix[1][0] * m_matrix[0][3] + mat.m_matrix[1][1] * m_matrix[1][3] - + mat.m_matrix[1][2] * m_matrix[2][3] + mat.m_matrix[1][3] * m_matrix[3][3]); - - tmp[2][0] = (mat.m_matrix[2][0] * m_matrix[0][0] + mat.m_matrix[2][1] * m_matrix[1][0] - + mat.m_matrix[2][2] * m_matrix[2][0] + mat.m_matrix[2][3] * m_matrix[3][0]); - tmp[2][1] = (mat.m_matrix[2][0] * m_matrix[0][1] + mat.m_matrix[2][1] * m_matrix[1][1] - + mat.m_matrix[2][2] * m_matrix[2][1] + mat.m_matrix[2][3] * m_matrix[3][1]); - tmp[2][2] = (mat.m_matrix[2][0] * m_matrix[0][2] + mat.m_matrix[2][1] * m_matrix[1][2] - + mat.m_matrix[2][2] * m_matrix[2][2] + mat.m_matrix[2][3] * m_matrix[3][2]); - tmp[2][3] = (mat.m_matrix[2][0] * m_matrix[0][3] + mat.m_matrix[2][1] * m_matrix[1][3] - + mat.m_matrix[2][2] * m_matrix[2][3] + mat.m_matrix[2][3] * m_matrix[3][3]); - - tmp[3][0] = (mat.m_matrix[3][0] * m_matrix[0][0] + mat.m_matrix[3][1] * m_matrix[1][0] - + mat.m_matrix[3][2] * m_matrix[2][0] + mat.m_matrix[3][3] * m_matrix[3][0]); - tmp[3][1] = (mat.m_matrix[3][0] * m_matrix[0][1] + mat.m_matrix[3][1] * m_matrix[1][1] - + mat.m_matrix[3][2] * m_matrix[2][1] + mat.m_matrix[3][3] * m_matrix[3][1]); - tmp[3][2] = (mat.m_matrix[3][0] * m_matrix[0][2] + mat.m_matrix[3][1] * m_matrix[1][2] - + mat.m_matrix[3][2] * m_matrix[2][2] + mat.m_matrix[3][3] * m_matrix[3][2]); - tmp[3][3] = (mat.m_matrix[3][0] * m_matrix[0][3] + mat.m_matrix[3][1] * m_matrix[1][3] - + mat.m_matrix[3][2] * m_matrix[2][3] + mat.m_matrix[3][3] * m_matrix[3][3]); - - setMatrix(tmp); + Matrix4 tmp; + + tmp[0][0] = (mat.m_matrix[0][0] * m_matrix[0][0] + + mat.m_matrix[0][1] * m_matrix[1][0] + + mat.m_matrix[0][2] * m_matrix[2][0] + + mat.m_matrix[0][3] * m_matrix[3][0]); + tmp[0][1] = (mat.m_matrix[0][0] * m_matrix[0][1] + + mat.m_matrix[0][1] * m_matrix[1][1] + + mat.m_matrix[0][2] * m_matrix[2][1] + + mat.m_matrix[0][3] * m_matrix[3][1]); + tmp[0][2] = (mat.m_matrix[0][0] * m_matrix[0][2] + + mat.m_matrix[0][1] * m_matrix[1][2] + + mat.m_matrix[0][2] * m_matrix[2][2] + + mat.m_matrix[0][3] * m_matrix[3][2]); + tmp[0][3] = (mat.m_matrix[0][0] * m_matrix[0][3] + + mat.m_matrix[0][1] * m_matrix[1][3] + + mat.m_matrix[0][2] * m_matrix[2][3] + + mat.m_matrix[0][3] * m_matrix[3][3]); + + tmp[1][0] = (mat.m_matrix[1][0] * m_matrix[0][0] + + mat.m_matrix[1][1] * m_matrix[1][0] + + mat.m_matrix[1][2] * m_matrix[2][0] + + mat.m_matrix[1][3] * m_matrix[3][0]); + tmp[1][1] = (mat.m_matrix[1][0] * m_matrix[0][1] + + mat.m_matrix[1][1] * m_matrix[1][1] + + mat.m_matrix[1][2] * m_matrix[2][1] + + mat.m_matrix[1][3] * m_matrix[3][1]); + tmp[1][2] = (mat.m_matrix[1][0] * m_matrix[0][2] + + mat.m_matrix[1][1] * m_matrix[1][2] + + mat.m_matrix[1][2] * m_matrix[2][2] + + mat.m_matrix[1][3] * m_matrix[3][2]); + tmp[1][3] = (mat.m_matrix[1][0] * m_matrix[0][3] + + mat.m_matrix[1][1] * m_matrix[1][3] + + mat.m_matrix[1][2] * m_matrix[2][3] + + mat.m_matrix[1][3] * m_matrix[3][3]); + + tmp[2][0] = (mat.m_matrix[2][0] * m_matrix[0][0] + + mat.m_matrix[2][1] * m_matrix[1][0] + + mat.m_matrix[2][2] * m_matrix[2][0] + + mat.m_matrix[2][3] * m_matrix[3][0]); + tmp[2][1] = (mat.m_matrix[2][0] * m_matrix[0][1] + + mat.m_matrix[2][1] * m_matrix[1][1] + + mat.m_matrix[2][2] * m_matrix[2][1] + + mat.m_matrix[2][3] * m_matrix[3][1]); + tmp[2][2] = (mat.m_matrix[2][0] * m_matrix[0][2] + + mat.m_matrix[2][1] * m_matrix[1][2] + + mat.m_matrix[2][2] * m_matrix[2][2] + + mat.m_matrix[2][3] * m_matrix[3][2]); + tmp[2][3] = (mat.m_matrix[2][0] * m_matrix[0][3] + + mat.m_matrix[2][1] * m_matrix[1][3] + + mat.m_matrix[2][2] * m_matrix[2][3] + + mat.m_matrix[2][3] * m_matrix[3][3]); + + tmp[3][0] = (mat.m_matrix[3][0] * m_matrix[0][0] + + mat.m_matrix[3][1] * m_matrix[1][0] + + mat.m_matrix[3][2] * m_matrix[2][0] + + mat.m_matrix[3][3] * m_matrix[3][0]); + tmp[3][1] = (mat.m_matrix[3][0] * m_matrix[0][1] + + mat.m_matrix[3][1] * m_matrix[1][1] + + mat.m_matrix[3][2] * m_matrix[2][1] + + mat.m_matrix[3][3] * m_matrix[3][1]); + tmp[3][2] = (mat.m_matrix[3][0] * m_matrix[0][2] + + mat.m_matrix[3][1] * m_matrix[1][2] + + mat.m_matrix[3][2] * m_matrix[2][2] + + mat.m_matrix[3][3] * m_matrix[3][2]); + tmp[3][3] = (mat.m_matrix[3][0] * m_matrix[0][3] + + mat.m_matrix[3][1] * m_matrix[1][3] + + mat.m_matrix[3][2] * m_matrix[2][3] + + mat.m_matrix[3][3] * m_matrix[3][3]); + + setMatrix(tmp); #endif - return *this; + return *this; } -void TransformationMatrix::multVecMatrix(double x, double y, double& resultX, double& resultY) const -{ - resultX = m_matrix[3][0] + x * m_matrix[0][0] + y * m_matrix[1][0]; - resultY = m_matrix[3][1] + x * m_matrix[0][1] + y * m_matrix[1][1]; - double w = m_matrix[3][3] + x * m_matrix[0][3] + y * m_matrix[1][3]; - if (w != 1 && w != 0) { - resultX /= w; - resultY /= w; - } +void TransformationMatrix::multVecMatrix(double x, + double y, + double& resultX, + double& resultY) const { + resultX = m_matrix[3][0] + x * m_matrix[0][0] + y * m_matrix[1][0]; + resultY = m_matrix[3][1] + x * m_matrix[0][1] + y * m_matrix[1][1]; + double w = m_matrix[3][3] + x * m_matrix[0][3] + y * m_matrix[1][3]; + if (w != 1 && w != 0) { + resultX /= w; + resultY /= w; + } } -void TransformationMatrix::multVecMatrix(double x, double y, double z, double& resultX, double& resultY, double& resultZ) const -{ - resultX = m_matrix[3][0] + x * m_matrix[0][0] + y * m_matrix[1][0] + z * m_matrix[2][0]; - resultY = m_matrix[3][1] + x * m_matrix[0][1] + y * m_matrix[1][1] + z * m_matrix[2][1]; - resultZ = m_matrix[3][2] + x * m_matrix[0][2] + y * m_matrix[1][2] + z * m_matrix[2][2]; - double w = m_matrix[3][3] + x * m_matrix[0][3] + y * m_matrix[1][3] + z * m_matrix[2][3]; - if (w != 1 && w != 0) { - resultX /= w; - resultY /= w; - resultZ /= w; - } +void TransformationMatrix::multVecMatrix(double x, + double y, + double z, + double& resultX, + double& resultY, + double& resultZ) const { + resultX = m_matrix[3][0] + x * m_matrix[0][0] + y * m_matrix[1][0] + + z * m_matrix[2][0]; + resultY = m_matrix[3][1] + x * m_matrix[0][1] + y * m_matrix[1][1] + + z * m_matrix[2][1]; + resultZ = m_matrix[3][2] + x * m_matrix[0][2] + y * m_matrix[1][2] + + z * m_matrix[2][2]; + double w = m_matrix[3][3] + x * m_matrix[0][3] + y * m_matrix[1][3] + + z * m_matrix[2][3]; + if (w != 1 && w != 0) { + resultX /= w; + resultY /= w; + resultZ /= w; + } } -bool TransformationMatrix::isInvertible() const -{ - if (isIdentityOrTranslation()) - return true; +bool TransformationMatrix::isInvertible() const { + if (isIdentityOrTranslation()) + return true; - double det = blink::determinant4x4(m_matrix); + double det = blink::determinant4x4(m_matrix); - if (fabs(det) < SMALL_NUMBER) - return false; + if (fabs(det) < SMALL_NUMBER) + return false; - return true; + return true; } -TransformationMatrix TransformationMatrix::inverse() const -{ - if (isIdentityOrTranslation()) { - // identity matrix - if (m_matrix[3][0] == 0 && m_matrix[3][1] == 0 && m_matrix[3][2] == 0) - return TransformationMatrix(); - - // translation - return TransformationMatrix(1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - -m_matrix[3][0], -m_matrix[3][1], -m_matrix[3][2], 1); - } +TransformationMatrix TransformationMatrix::inverse() const { + if (isIdentityOrTranslation()) { + // identity matrix + if (m_matrix[3][0] == 0 && m_matrix[3][1] == 0 && m_matrix[3][2] == 0) + return TransformationMatrix(); + + // translation + return TransformationMatrix(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, + -m_matrix[3][0], -m_matrix[3][1], + -m_matrix[3][2], 1); + } - TransformationMatrix invMat; - bool inverted = blink::inverse(m_matrix, invMat.m_matrix); - if (!inverted) - return TransformationMatrix(); + TransformationMatrix invMat; + bool inverted = blink::inverse(m_matrix, invMat.m_matrix); + if (!inverted) + return TransformationMatrix(); - return invMat; + return invMat; } -void TransformationMatrix::makeAffine() -{ - m_matrix[0][2] = 0; - m_matrix[0][3] = 0; +void TransformationMatrix::makeAffine() { + m_matrix[0][2] = 0; + m_matrix[0][3] = 0; - m_matrix[1][2] = 0; - m_matrix[1][3] = 0; + m_matrix[1][2] = 0; + m_matrix[1][3] = 0; - m_matrix[2][0] = 0; - m_matrix[2][1] = 0; - m_matrix[2][2] = 1; - m_matrix[2][3] = 0; + m_matrix[2][0] = 0; + m_matrix[2][1] = 0; + m_matrix[2][2] = 1; + m_matrix[2][3] = 0; - m_matrix[3][2] = 0; - m_matrix[3][3] = 1; + m_matrix[3][2] = 0; + m_matrix[3][3] = 1; } -AffineTransform TransformationMatrix::toAffineTransform() const -{ - return AffineTransform(m_matrix[0][0], m_matrix[0][1], m_matrix[1][0], - m_matrix[1][1], m_matrix[3][0], m_matrix[3][1]); +AffineTransform TransformationMatrix::toAffineTransform() const { + return AffineTransform(m_matrix[0][0], m_matrix[0][1], m_matrix[1][0], + m_matrix[1][1], m_matrix[3][0], m_matrix[3][1]); } -static inline void blendFloat(double& from, double to, double progress) -{ - if (from != to) - from = from + (to - from) * progress; +static inline void blendFloat(double& from, double to, double progress) { + if (from != to) + from = from + (to - from) * progress; } -void TransformationMatrix::blend(const TransformationMatrix& from, double progress) -{ - if (from.isIdentity() && isIdentity()) - return; - - // decompose - DecomposedType fromDecomp; - DecomposedType toDecomp; - if (!from.decompose(fromDecomp) || !decompose(toDecomp)) { - if (progress < 0.5) - *this = from; - return; - } - - // interpolate - blendFloat(fromDecomp.scaleX, toDecomp.scaleX, progress); - blendFloat(fromDecomp.scaleY, toDecomp.scaleY, progress); - blendFloat(fromDecomp.scaleZ, toDecomp.scaleZ, progress); - blendFloat(fromDecomp.skewXY, toDecomp.skewXY, progress); - blendFloat(fromDecomp.skewXZ, toDecomp.skewXZ, progress); - blendFloat(fromDecomp.skewYZ, toDecomp.skewYZ, progress); - blendFloat(fromDecomp.translateX, toDecomp.translateX, progress); - blendFloat(fromDecomp.translateY, toDecomp.translateY, progress); - blendFloat(fromDecomp.translateZ, toDecomp.translateZ, progress); - blendFloat(fromDecomp.perspectiveX, toDecomp.perspectiveX, progress); - blendFloat(fromDecomp.perspectiveY, toDecomp.perspectiveY, progress); - blendFloat(fromDecomp.perspectiveZ, toDecomp.perspectiveZ, progress); - blendFloat(fromDecomp.perspectiveW, toDecomp.perspectiveW, progress); - - slerp(&fromDecomp.quaternionX, &toDecomp.quaternionX, progress); - - // recompose - recompose(fromDecomp); +void TransformationMatrix::blend(const TransformationMatrix& from, + double progress) { + if (from.isIdentity() && isIdentity()) + return; + + // decompose + DecomposedType fromDecomp; + DecomposedType toDecomp; + if (!from.decompose(fromDecomp) || !decompose(toDecomp)) { + if (progress < 0.5) + *this = from; + return; + } + + // interpolate + blendFloat(fromDecomp.scaleX, toDecomp.scaleX, progress); + blendFloat(fromDecomp.scaleY, toDecomp.scaleY, progress); + blendFloat(fromDecomp.scaleZ, toDecomp.scaleZ, progress); + blendFloat(fromDecomp.skewXY, toDecomp.skewXY, progress); + blendFloat(fromDecomp.skewXZ, toDecomp.skewXZ, progress); + blendFloat(fromDecomp.skewYZ, toDecomp.skewYZ, progress); + blendFloat(fromDecomp.translateX, toDecomp.translateX, progress); + blendFloat(fromDecomp.translateY, toDecomp.translateY, progress); + blendFloat(fromDecomp.translateZ, toDecomp.translateZ, progress); + blendFloat(fromDecomp.perspectiveX, toDecomp.perspectiveX, progress); + blendFloat(fromDecomp.perspectiveY, toDecomp.perspectiveY, progress); + blendFloat(fromDecomp.perspectiveZ, toDecomp.perspectiveZ, progress); + blendFloat(fromDecomp.perspectiveW, toDecomp.perspectiveW, progress); + + slerp(&fromDecomp.quaternionX, &toDecomp.quaternionX, progress); + + // recompose + recompose(fromDecomp); } -bool TransformationMatrix::decompose(DecomposedType& decomp) const -{ - if (isIdentity()) { - memset(&decomp, 0, sizeof(decomp)); - decomp.perspectiveW = 1; - decomp.scaleX = 1; - decomp.scaleY = 1; - decomp.scaleZ = 1; - } - - if (!blink::decompose(m_matrix, decomp)) - return false; - return true; +bool TransformationMatrix::decompose(DecomposedType& decomp) const { + if (isIdentity()) { + memset(&decomp, 0, sizeof(decomp)); + decomp.perspectiveW = 1; + decomp.scaleX = 1; + decomp.scaleY = 1; + decomp.scaleZ = 1; + } + + if (!blink::decompose(m_matrix, decomp)) + return false; + return true; } -void TransformationMatrix::recompose(const DecomposedType& decomp) -{ - makeIdentity(); - - // first apply perspective - m_matrix[0][3] = decomp.perspectiveX; - m_matrix[1][3] = decomp.perspectiveY; - m_matrix[2][3] = decomp.perspectiveZ; - m_matrix[3][3] = decomp.perspectiveW; - - // now translate - translate3d(decomp.translateX, decomp.translateY, decomp.translateZ); - - // apply rotation - double xx = decomp.quaternionX * decomp.quaternionX; - double xy = decomp.quaternionX * decomp.quaternionY; - double xz = decomp.quaternionX * decomp.quaternionZ; - double xw = decomp.quaternionX * decomp.quaternionW; - double yy = decomp.quaternionY * decomp.quaternionY; - double yz = decomp.quaternionY * decomp.quaternionZ; - double yw = decomp.quaternionY * decomp.quaternionW; - double zz = decomp.quaternionZ * decomp.quaternionZ; - double zw = decomp.quaternionZ * decomp.quaternionW; - - // Construct a composite rotation matrix from the quaternion values - TransformationMatrix rotationMatrix(1 - 2 * (yy + zz), 2 * (xy - zw), 2 * (xz + yw), 0, - 2 * (xy + zw), 1 - 2 * (xx + zz), 2 * (yz - xw), 0, - 2 * (xz - yw), 2 * (yz + xw), 1 - 2 * (xx + yy), 0, - 0, 0, 0, 1); - - multiply(rotationMatrix); - - // now apply skew - if (decomp.skewYZ) { - TransformationMatrix tmp; - tmp.setM32(decomp.skewYZ); - multiply(tmp); - } - - if (decomp.skewXZ) { - TransformationMatrix tmp; - tmp.setM31(decomp.skewXZ); - multiply(tmp); - } - - if (decomp.skewXY) { - TransformationMatrix tmp; - tmp.setM21(decomp.skewXY); - multiply(tmp); - } - - // finally, apply scale - scale3d(decomp.scaleX, decomp.scaleY, decomp.scaleZ); +void TransformationMatrix::recompose(const DecomposedType& decomp) { + makeIdentity(); + + // first apply perspective + m_matrix[0][3] = decomp.perspectiveX; + m_matrix[1][3] = decomp.perspectiveY; + m_matrix[2][3] = decomp.perspectiveZ; + m_matrix[3][3] = decomp.perspectiveW; + + // now translate + translate3d(decomp.translateX, decomp.translateY, decomp.translateZ); + + // apply rotation + double xx = decomp.quaternionX * decomp.quaternionX; + double xy = decomp.quaternionX * decomp.quaternionY; + double xz = decomp.quaternionX * decomp.quaternionZ; + double xw = decomp.quaternionX * decomp.quaternionW; + double yy = decomp.quaternionY * decomp.quaternionY; + double yz = decomp.quaternionY * decomp.quaternionZ; + double yw = decomp.quaternionY * decomp.quaternionW; + double zz = decomp.quaternionZ * decomp.quaternionZ; + double zw = decomp.quaternionZ * decomp.quaternionW; + + // Construct a composite rotation matrix from the quaternion values + TransformationMatrix rotationMatrix( + 1 - 2 * (yy + zz), 2 * (xy - zw), 2 * (xz + yw), 0, 2 * (xy + zw), + 1 - 2 * (xx + zz), 2 * (yz - xw), 0, 2 * (xz - yw), 2 * (yz + xw), + 1 - 2 * (xx + yy), 0, 0, 0, 0, 1); + + multiply(rotationMatrix); + + // now apply skew + if (decomp.skewYZ) { + TransformationMatrix tmp; + tmp.setM32(decomp.skewYZ); + multiply(tmp); + } + + if (decomp.skewXZ) { + TransformationMatrix tmp; + tmp.setM31(decomp.skewXZ); + multiply(tmp); + } + + if (decomp.skewXY) { + TransformationMatrix tmp; + tmp.setM21(decomp.skewXY); + multiply(tmp); + } + + // finally, apply scale + scale3d(decomp.scaleX, decomp.scaleY, decomp.scaleZ); } -bool TransformationMatrix::isIntegerTranslation() const -{ - if (!isIdentityOrTranslation()) - return false; +bool TransformationMatrix::isIntegerTranslation() const { + if (!isIdentityOrTranslation()) + return false; - // Check for translate Z. - if (m_matrix[3][2]) - return false; + // Check for translate Z. + if (m_matrix[3][2]) + return false; - // Check for non-integer translate X/Y. - if (static_cast(m_matrix[3][0]) != m_matrix[3][0] || static_cast(m_matrix[3][1]) != m_matrix[3][1]) - return false; + // Check for non-integer translate X/Y. + if (static_cast(m_matrix[3][0]) != m_matrix[3][0] || + static_cast(m_matrix[3][1]) != m_matrix[3][1]) + return false; - return true; + return true; } -TransformationMatrix TransformationMatrix::to2dTransform() const -{ - return TransformationMatrix(m_matrix[0][0], m_matrix[0][1], 0, m_matrix[0][3], - m_matrix[1][0], m_matrix[1][1], 0, m_matrix[1][3], - 0, 0, 1, 0, - m_matrix[3][0], m_matrix[3][1], 0, m_matrix[3][3]); +TransformationMatrix TransformationMatrix::to2dTransform() const { + return TransformationMatrix(m_matrix[0][0], m_matrix[0][1], 0, m_matrix[0][3], + m_matrix[1][0], m_matrix[1][1], 0, m_matrix[1][3], + 0, 0, 1, 0, m_matrix[3][0], m_matrix[3][1], 0, + m_matrix[3][3]); } -void TransformationMatrix::toColumnMajorFloatArray(FloatMatrix4& result) const -{ - result[0] = m11(); - result[1] = m12(); - result[2] = m13(); - result[3] = m14(); - result[4] = m21(); - result[5] = m22(); - result[6] = m23(); - result[7] = m24(); - result[8] = m31(); - result[9] = m32(); - result[10] = m33(); - result[11] = m34(); - result[12] = m41(); - result[13] = m42(); - result[14] = m43(); - result[15] = m44(); +void TransformationMatrix::toColumnMajorFloatArray(FloatMatrix4& result) const { + result[0] = m11(); + result[1] = m12(); + result[2] = m13(); + result[3] = m14(); + result[4] = m21(); + result[5] = m22(); + result[6] = m23(); + result[7] = m24(); + result[8] = m31(); + result[9] = m32(); + result[10] = m33(); + result[11] = m34(); + result[12] = m41(); + result[13] = m42(); + result[14] = m43(); + result[15] = m44(); } -SkMatrix44 TransformationMatrix::toSkMatrix44(const TransformationMatrix& matrix) -{ - SkMatrix44 ret(SkMatrix44::kUninitialized_Constructor); - ret.setDouble(0, 0, matrix.m11()); - ret.setDouble(0, 1, matrix.m21()); - ret.setDouble(0, 2, matrix.m31()); - ret.setDouble(0, 3, matrix.m41()); - ret.setDouble(1, 0, matrix.m12()); - ret.setDouble(1, 1, matrix.m22()); - ret.setDouble(1, 2, matrix.m32()); - ret.setDouble(1, 3, matrix.m42()); - ret.setDouble(2, 0, matrix.m13()); - ret.setDouble(2, 1, matrix.m23()); - ret.setDouble(2, 2, matrix.m33()); - ret.setDouble(2, 3, matrix.m43()); - ret.setDouble(3, 0, matrix.m14()); - ret.setDouble(3, 1, matrix.m24()); - ret.setDouble(3, 2, matrix.m34()); - ret.setDouble(3, 3, matrix.m44()); - return ret; +SkMatrix44 TransformationMatrix::toSkMatrix44( + const TransformationMatrix& matrix) { + SkMatrix44 ret(SkMatrix44::kUninitialized_Constructor); + ret.setDouble(0, 0, matrix.m11()); + ret.setDouble(0, 1, matrix.m21()); + ret.setDouble(0, 2, matrix.m31()); + ret.setDouble(0, 3, matrix.m41()); + ret.setDouble(1, 0, matrix.m12()); + ret.setDouble(1, 1, matrix.m22()); + ret.setDouble(1, 2, matrix.m32()); + ret.setDouble(1, 3, matrix.m42()); + ret.setDouble(2, 0, matrix.m13()); + ret.setDouble(2, 1, matrix.m23()); + ret.setDouble(2, 2, matrix.m33()); + ret.setDouble(2, 3, matrix.m43()); + ret.setDouble(3, 0, matrix.m14()); + ret.setDouble(3, 1, matrix.m24()); + ret.setDouble(3, 2, matrix.m34()); + ret.setDouble(3, 3, matrix.m44()); + return ret; } -} +} // namespace blink diff --git a/sky/engine/platform/transforms/TransformationMatrix.h b/sky/engine/platform/transforms/TransformationMatrix.h index 79d38c72cae94..d3dabe73d8b30 100644 --- a/sky/engine/platform/transforms/TransformationMatrix.h +++ b/sky/engine/platform/transforms/TransformationMatrix.h @@ -26,7 +26,7 @@ #ifndef SKY_ENGINE_PLATFORM_TRANSFORMS_TRANSFORMATIONMATRIX_H_ #define SKY_ENGINE_PLATFORM_TRANSFORMS_TRANSFORMATIONMATRIX_H_ -#include //for memcpy +#include //for memcpy #include "flutter/sky/engine/platform/geometry/FloatPoint.h" #include "flutter/sky/engine/platform/geometry/FloatPoint3D.h" #include "flutter/sky/engine/platform/geometry/IntPoint.h" @@ -47,310 +47,369 @@ class FloatBox; #endif class PLATFORM_EXPORT TransformationMatrix { - WTF_MAKE_FAST_ALLOCATED; -public: + WTF_MAKE_FAST_ALLOCATED; + public: #if CPU(APPLE_ARMV7S) || defined(TRANSFORMATION_MATRIX_USE_X86_64_SSE2) - typedef double Matrix4[4][4] __attribute__((aligned (16))); + typedef double Matrix4[4][4] __attribute__((aligned(16))); #else - typedef double Matrix4[4][4]; + typedef double Matrix4[4][4]; #endif - TransformationMatrix() { makeIdentity(); } - TransformationMatrix(const AffineTransform& t); - TransformationMatrix(const TransformationMatrix& t) { *this = t; } - TransformationMatrix(double a, double b, double c, double d, double e, double f) { setMatrix(a, b, c, d, e, f); } - TransformationMatrix(double m11, double m12, double m13, double m14, - double m21, double m22, double m23, double m24, - double m31, double m32, double m33, double m34, - double m41, double m42, double m43, double m44) - { - setMatrix(m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44); - } - - void setMatrix(double a, double b, double c, double d, double e, double f) - { - m_matrix[0][0] = a; m_matrix[0][1] = b; m_matrix[0][2] = 0; m_matrix[0][3] = 0; - m_matrix[1][0] = c; m_matrix[1][1] = d; m_matrix[1][2] = 0; m_matrix[1][3] = 0; - m_matrix[2][0] = 0; m_matrix[2][1] = 0; m_matrix[2][2] = 1; m_matrix[2][3] = 0; - m_matrix[3][0] = e; m_matrix[3][1] = f; m_matrix[3][2] = 0; m_matrix[3][3] = 1; - } - - void setMatrix(double m11, double m12, double m13, double m14, - double m21, double m22, double m23, double m24, - double m31, double m32, double m33, double m34, - double m41, double m42, double m43, double m44) - { - m_matrix[0][0] = m11; m_matrix[0][1] = m12; m_matrix[0][2] = m13; m_matrix[0][3] = m14; - m_matrix[1][0] = m21; m_matrix[1][1] = m22; m_matrix[1][2] = m23; m_matrix[1][3] = m24; - m_matrix[2][0] = m31; m_matrix[2][1] = m32; m_matrix[2][2] = m33; m_matrix[2][3] = m34; - m_matrix[3][0] = m41; m_matrix[3][1] = m42; m_matrix[3][2] = m43; m_matrix[3][3] = m44; - } - - TransformationMatrix& operator =(const TransformationMatrix &t) - { - setMatrix(t.m_matrix); - return *this; - } - - TransformationMatrix& makeIdentity() - { - setMatrix(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); - return *this; - } - - bool isIdentity() const - { - return m_matrix[0][0] == 1 && m_matrix[0][1] == 0 && m_matrix[0][2] == 0 && m_matrix[0][3] == 0 && - m_matrix[1][0] == 0 && m_matrix[1][1] == 1 && m_matrix[1][2] == 0 && m_matrix[1][3] == 0 && - m_matrix[2][0] == 0 && m_matrix[2][1] == 0 && m_matrix[2][2] == 1 && m_matrix[2][3] == 0 && - m_matrix[3][0] == 0 && m_matrix[3][1] == 0 && m_matrix[3][2] == 0 && m_matrix[3][3] == 1; - } - - // This form preserves the double math from input to output - void map(double x, double y, double& x2, double& y2) const { multVecMatrix(x, y, x2, y2); } - - // Map a 3D point through the transform, returning a 3D point. - FloatPoint3D mapPoint(const FloatPoint3D&) const; - - // Map a 2D point through the transform, returning a 2D point. - // Note that this ignores the z component, effectively projecting the point into the z=0 plane. - FloatPoint mapPoint(const FloatPoint&) const; - - // Like the version above, except that it rounds the mapped point to the nearest integer value. - IntPoint mapPoint(const IntPoint& p) const - { - return roundedIntPoint(mapPoint(FloatPoint(p))); - } - - // If the matrix has 3D components, the z component of the result is - // dropped, effectively projecting the rect into the z=0 plane - FloatRect mapRect(const FloatRect&) const; - - // Rounds the resulting mapped rectangle out. This is helpful for bounding - // box computations but may not be what is wanted in other contexts. - IntRect mapRect(const IntRect&) const; - LayoutRect mapRect(const LayoutRect&) const; - - // If the matrix has 3D components, the z component of the result is - // dropped, effectively projecting the quad into the z=0 plane - FloatQuad mapQuad(const FloatQuad&) const; - - // Map a point on the z=0 plane into a point on - // the plane with with the transform applied, by extending - // a ray perpendicular to the source plane and computing - // the local x,y position of the point where that ray intersects - // with the destination plane. - FloatPoint projectPoint(const FloatPoint&, bool* clamped = 0) const; - // Projects the four corners of the quad - FloatQuad projectQuad(const FloatQuad&, bool* clamped = 0) const; - // Projects the four corners of the quad and takes a bounding box, - // while sanitizing values created when the w component is negative. - LayoutRect clampedBoundsOfProjectedQuad(const FloatQuad&) const; - - void transformBox(FloatBox&) const; - - double m11() const { return m_matrix[0][0]; } - void setM11(double f) { m_matrix[0][0] = f; } - double m12() const { return m_matrix[0][1]; } - void setM12(double f) { m_matrix[0][1] = f; } - double m13() const { return m_matrix[0][2]; } - void setM13(double f) { m_matrix[0][2] = f; } - double m14() const { return m_matrix[0][3]; } - void setM14(double f) { m_matrix[0][3] = f; } - double m21() const { return m_matrix[1][0]; } - void setM21(double f) { m_matrix[1][0] = f; } - double m22() const { return m_matrix[1][1]; } - void setM22(double f) { m_matrix[1][1] = f; } - double m23() const { return m_matrix[1][2]; } - void setM23(double f) { m_matrix[1][2] = f; } - double m24() const { return m_matrix[1][3]; } - void setM24(double f) { m_matrix[1][3] = f; } - double m31() const { return m_matrix[2][0]; } - void setM31(double f) { m_matrix[2][0] = f; } - double m32() const { return m_matrix[2][1]; } - void setM32(double f) { m_matrix[2][1] = f; } - double m33() const { return m_matrix[2][2]; } - void setM33(double f) { m_matrix[2][2] = f; } - double m34() const { return m_matrix[2][3]; } - void setM34(double f) { m_matrix[2][3] = f; } - double m41() const { return m_matrix[3][0]; } - void setM41(double f) { m_matrix[3][0] = f; } - double m42() const { return m_matrix[3][1]; } - void setM42(double f) { m_matrix[3][1] = f; } - double m43() const { return m_matrix[3][2]; } - void setM43(double f) { m_matrix[3][2] = f; } - double m44() const { return m_matrix[3][3]; } - void setM44(double f) { m_matrix[3][3] = f; } - - double a() const { return m_matrix[0][0]; } - void setA(double a) { m_matrix[0][0] = a; } - - double b() const { return m_matrix[0][1]; } - void setB(double b) { m_matrix[0][1] = b; } - - double c() const { return m_matrix[1][0]; } - void setC(double c) { m_matrix[1][0] = c; } - - double d() const { return m_matrix[1][1]; } - void setD(double d) { m_matrix[1][1] = d; } - - double e() const { return m_matrix[3][0]; } - void setE(double e) { m_matrix[3][0] = e; } - - double f() const { return m_matrix[3][1]; } - void setF(double f) { m_matrix[3][1] = f; } - - // this = mat * this. - TransformationMatrix& multiply(const TransformationMatrix&); - - TransformationMatrix& scale(double); - TransformationMatrix& scaleNonUniform(double sx, double sy); - TransformationMatrix& scale3d(double sx, double sy, double sz); - - TransformationMatrix& rotate(double d) { return rotate3d(0, 0, d); } - TransformationMatrix& rotateFromVector(double x, double y); - TransformationMatrix& rotate3d(double rx, double ry, double rz); - - // The vector (x,y,z) is normalized if it's not already. A vector of - // (0,0,0) uses a vector of (0,0,1). - TransformationMatrix& rotate3d(double x, double y, double z, double angle); - - TransformationMatrix& translate(double tx, double ty); - TransformationMatrix& translate3d(double tx, double ty, double tz); - - // translation added with a post-multiply - TransformationMatrix& translateRight(double tx, double ty); - TransformationMatrix& translateRight3d(double tx, double ty, double tz); - - TransformationMatrix& flipX(); - TransformationMatrix& flipY(); - TransformationMatrix& skew(double angleX, double angleY); - TransformationMatrix& skewX(double angle) { return skew(angle, 0); } - TransformationMatrix& skewY(double angle) { return skew(0, angle); } - - TransformationMatrix& applyPerspective(double p); - bool hasPerspective() const { return m_matrix[2][3] != 0.0f; } - - // returns a transformation that maps a rect to a rect - static TransformationMatrix rectToRect(const FloatRect&, const FloatRect&); - - bool isInvertible() const; - - // This method returns the identity matrix if it is not invertible. - // Use isInvertible() before calling this if you need to know. - TransformationMatrix inverse() const; - - // decompose the matrix into its component parts - typedef struct { - double scaleX, scaleY, scaleZ; - double skewXY, skewXZ, skewYZ; - double quaternionX, quaternionY, quaternionZ, quaternionW; - double translateX, translateY, translateZ; - double perspectiveX, perspectiveY, perspectiveZ, perspectiveW; - } DecomposedType; - - bool decompose(DecomposedType& decomp) const; - void recompose(const DecomposedType& decomp); - - void blend(const TransformationMatrix& from, double progress); - - bool isAffine() const - { - return (m13() == 0 && m14() == 0 && m23() == 0 && m24() == 0 && - m31() == 0 && m32() == 0 && m33() == 1 && m34() == 0 && m43() == 0 && m44() == 1); - } - - // Throw away the non-affine parts of the matrix (lossy!) - void makeAffine(); - - AffineTransform toAffineTransform() const; - - bool operator==(const TransformationMatrix& m2) const - { - return (m_matrix[0][0] == m2.m_matrix[0][0] && - m_matrix[0][1] == m2.m_matrix[0][1] && - m_matrix[0][2] == m2.m_matrix[0][2] && - m_matrix[0][3] == m2.m_matrix[0][3] && - m_matrix[1][0] == m2.m_matrix[1][0] && - m_matrix[1][1] == m2.m_matrix[1][1] && - m_matrix[1][2] == m2.m_matrix[1][2] && - m_matrix[1][3] == m2.m_matrix[1][3] && - m_matrix[2][0] == m2.m_matrix[2][0] && - m_matrix[2][1] == m2.m_matrix[2][1] && - m_matrix[2][2] == m2.m_matrix[2][2] && - m_matrix[2][3] == m2.m_matrix[2][3] && - m_matrix[3][0] == m2.m_matrix[3][0] && - m_matrix[3][1] == m2.m_matrix[3][1] && - m_matrix[3][2] == m2.m_matrix[3][2] && - m_matrix[3][3] == m2.m_matrix[3][3]); - } - - bool operator!=(const TransformationMatrix& other) const { return !(*this == other); } - - // *this = *this * t - TransformationMatrix& operator*=(const TransformationMatrix& t) - { - return multiply(t); - } - - // result = *this * t - TransformationMatrix operator*(const TransformationMatrix& t) const - { - TransformationMatrix result = *this; - result.multiply(t); - return result; - } - - bool isIdentityOrTranslation() const - { - return m_matrix[0][0] == 1 && m_matrix[0][1] == 0 && m_matrix[0][2] == 0 && m_matrix[0][3] == 0 - && m_matrix[1][0] == 0 && m_matrix[1][1] == 1 && m_matrix[1][2] == 0 && m_matrix[1][3] == 0 - && m_matrix[2][0] == 0 && m_matrix[2][1] == 0 && m_matrix[2][2] == 1 && m_matrix[2][3] == 0 - && m_matrix[3][3] == 1; - } - - bool isIntegerTranslation() const; - - // This method returns the matrix without 3D components. - TransformationMatrix to2dTransform() const; - - typedef float FloatMatrix4[16]; - void toColumnMajorFloatArray(FloatMatrix4& result) const; - - static SkMatrix44 toSkMatrix44(const TransformationMatrix&); - -private: - // multiply passed 2D point by matrix (assume z=0) - void multVecMatrix(double x, double y, double& dstX, double& dstY) const; - FloatPoint internalMapPoint(const FloatPoint& sourcePoint) const - { - double resultX; - double resultY; - multVecMatrix(sourcePoint.x(), sourcePoint.y(), resultX, resultY); - return FloatPoint(static_cast(resultX), static_cast(resultY)); - } - - // multiply passed 3D point by matrix - void multVecMatrix(double x, double y, double z, double& dstX, double& dstY, double& dstZ) const; - FloatPoint3D internalMapPoint(const FloatPoint3D& sourcePoint) const - { - double resultX; - double resultY; - double resultZ; - multVecMatrix(sourcePoint.x(), sourcePoint.y(), sourcePoint.z(), resultX, resultY, resultZ); - return FloatPoint3D(static_cast(resultX), static_cast(resultY), static_cast(resultZ)); - } - - void setMatrix(const Matrix4 m) - { - if (m && m != m_matrix) - memcpy(m_matrix, m, sizeof(Matrix4)); - } - - Matrix4 m_matrix; + TransformationMatrix() { makeIdentity(); } + TransformationMatrix(const AffineTransform& t); + TransformationMatrix(const TransformationMatrix& t) { *this = t; } + TransformationMatrix(double a, + double b, + double c, + double d, + double e, + double f) { + setMatrix(a, b, c, d, e, f); + } + TransformationMatrix(double m11, + double m12, + double m13, + double m14, + double m21, + double m22, + double m23, + double m24, + double m31, + double m32, + double m33, + double m34, + double m41, + double m42, + double m43, + double m44) { + setMatrix(m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, + m42, m43, m44); + } + + void setMatrix(double a, double b, double c, double d, double e, double f) { + m_matrix[0][0] = a; + m_matrix[0][1] = b; + m_matrix[0][2] = 0; + m_matrix[0][3] = 0; + m_matrix[1][0] = c; + m_matrix[1][1] = d; + m_matrix[1][2] = 0; + m_matrix[1][3] = 0; + m_matrix[2][0] = 0; + m_matrix[2][1] = 0; + m_matrix[2][2] = 1; + m_matrix[2][3] = 0; + m_matrix[3][0] = e; + m_matrix[3][1] = f; + m_matrix[3][2] = 0; + m_matrix[3][3] = 1; + } + + void setMatrix(double m11, + double m12, + double m13, + double m14, + double m21, + double m22, + double m23, + double m24, + double m31, + double m32, + double m33, + double m34, + double m41, + double m42, + double m43, + double m44) { + m_matrix[0][0] = m11; + m_matrix[0][1] = m12; + m_matrix[0][2] = m13; + m_matrix[0][3] = m14; + m_matrix[1][0] = m21; + m_matrix[1][1] = m22; + m_matrix[1][2] = m23; + m_matrix[1][3] = m24; + m_matrix[2][0] = m31; + m_matrix[2][1] = m32; + m_matrix[2][2] = m33; + m_matrix[2][3] = m34; + m_matrix[3][0] = m41; + m_matrix[3][1] = m42; + m_matrix[3][2] = m43; + m_matrix[3][3] = m44; + } + + TransformationMatrix& operator=(const TransformationMatrix& t) { + setMatrix(t.m_matrix); + return *this; + } + + TransformationMatrix& makeIdentity() { + setMatrix(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); + return *this; + } + + bool isIdentity() const { + return m_matrix[0][0] == 1 && m_matrix[0][1] == 0 && m_matrix[0][2] == 0 && + m_matrix[0][3] == 0 && m_matrix[1][0] == 0 && m_matrix[1][1] == 1 && + m_matrix[1][2] == 0 && m_matrix[1][3] == 0 && m_matrix[2][0] == 0 && + m_matrix[2][1] == 0 && m_matrix[2][2] == 1 && m_matrix[2][3] == 0 && + m_matrix[3][0] == 0 && m_matrix[3][1] == 0 && m_matrix[3][2] == 0 && + m_matrix[3][3] == 1; + } + + // This form preserves the double math from input to output + void map(double x, double y, double& x2, double& y2) const { + multVecMatrix(x, y, x2, y2); + } + + // Map a 3D point through the transform, returning a 3D point. + FloatPoint3D mapPoint(const FloatPoint3D&) const; + + // Map a 2D point through the transform, returning a 2D point. + // Note that this ignores the z component, effectively projecting the point + // into the z=0 plane. + FloatPoint mapPoint(const FloatPoint&) const; + + // Like the version above, except that it rounds the mapped point to the + // nearest integer value. + IntPoint mapPoint(const IntPoint& p) const { + return roundedIntPoint(mapPoint(FloatPoint(p))); + } + + // If the matrix has 3D components, the z component of the result is + // dropped, effectively projecting the rect into the z=0 plane + FloatRect mapRect(const FloatRect&) const; + + // Rounds the resulting mapped rectangle out. This is helpful for bounding + // box computations but may not be what is wanted in other contexts. + IntRect mapRect(const IntRect&) const; + LayoutRect mapRect(const LayoutRect&) const; + + // If the matrix has 3D components, the z component of the result is + // dropped, effectively projecting the quad into the z=0 plane + FloatQuad mapQuad(const FloatQuad&) const; + + // Map a point on the z=0 plane into a point on + // the plane with with the transform applied, by extending + // a ray perpendicular to the source plane and computing + // the local x,y position of the point where that ray intersects + // with the destination plane. + FloatPoint projectPoint(const FloatPoint&, bool* clamped = 0) const; + // Projects the four corners of the quad + FloatQuad projectQuad(const FloatQuad&, bool* clamped = 0) const; + // Projects the four corners of the quad and takes a bounding box, + // while sanitizing values created when the w component is negative. + LayoutRect clampedBoundsOfProjectedQuad(const FloatQuad&) const; + + void transformBox(FloatBox&) const; + + double m11() const { return m_matrix[0][0]; } + void setM11(double f) { m_matrix[0][0] = f; } + double m12() const { return m_matrix[0][1]; } + void setM12(double f) { m_matrix[0][1] = f; } + double m13() const { return m_matrix[0][2]; } + void setM13(double f) { m_matrix[0][2] = f; } + double m14() const { return m_matrix[0][3]; } + void setM14(double f) { m_matrix[0][3] = f; } + double m21() const { return m_matrix[1][0]; } + void setM21(double f) { m_matrix[1][0] = f; } + double m22() const { return m_matrix[1][1]; } + void setM22(double f) { m_matrix[1][1] = f; } + double m23() const { return m_matrix[1][2]; } + void setM23(double f) { m_matrix[1][2] = f; } + double m24() const { return m_matrix[1][3]; } + void setM24(double f) { m_matrix[1][3] = f; } + double m31() const { return m_matrix[2][0]; } + void setM31(double f) { m_matrix[2][0] = f; } + double m32() const { return m_matrix[2][1]; } + void setM32(double f) { m_matrix[2][1] = f; } + double m33() const { return m_matrix[2][2]; } + void setM33(double f) { m_matrix[2][2] = f; } + double m34() const { return m_matrix[2][3]; } + void setM34(double f) { m_matrix[2][3] = f; } + double m41() const { return m_matrix[3][0]; } + void setM41(double f) { m_matrix[3][0] = f; } + double m42() const { return m_matrix[3][1]; } + void setM42(double f) { m_matrix[3][1] = f; } + double m43() const { return m_matrix[3][2]; } + void setM43(double f) { m_matrix[3][2] = f; } + double m44() const { return m_matrix[3][3]; } + void setM44(double f) { m_matrix[3][3] = f; } + + double a() const { return m_matrix[0][0]; } + void setA(double a) { m_matrix[0][0] = a; } + + double b() const { return m_matrix[0][1]; } + void setB(double b) { m_matrix[0][1] = b; } + + double c() const { return m_matrix[1][0]; } + void setC(double c) { m_matrix[1][0] = c; } + + double d() const { return m_matrix[1][1]; } + void setD(double d) { m_matrix[1][1] = d; } + + double e() const { return m_matrix[3][0]; } + void setE(double e) { m_matrix[3][0] = e; } + + double f() const { return m_matrix[3][1]; } + void setF(double f) { m_matrix[3][1] = f; } + + // this = mat * this. + TransformationMatrix& multiply(const TransformationMatrix&); + + TransformationMatrix& scale(double); + TransformationMatrix& scaleNonUniform(double sx, double sy); + TransformationMatrix& scale3d(double sx, double sy, double sz); + + TransformationMatrix& rotate(double d) { return rotate3d(0, 0, d); } + TransformationMatrix& rotateFromVector(double x, double y); + TransformationMatrix& rotate3d(double rx, double ry, double rz); + + // The vector (x,y,z) is normalized if it's not already. A vector of + // (0,0,0) uses a vector of (0,0,1). + TransformationMatrix& rotate3d(double x, double y, double z, double angle); + + TransformationMatrix& translate(double tx, double ty); + TransformationMatrix& translate3d(double tx, double ty, double tz); + + // translation added with a post-multiply + TransformationMatrix& translateRight(double tx, double ty); + TransformationMatrix& translateRight3d(double tx, double ty, double tz); + + TransformationMatrix& flipX(); + TransformationMatrix& flipY(); + TransformationMatrix& skew(double angleX, double angleY); + TransformationMatrix& skewX(double angle) { return skew(angle, 0); } + TransformationMatrix& skewY(double angle) { return skew(0, angle); } + + TransformationMatrix& applyPerspective(double p); + bool hasPerspective() const { return m_matrix[2][3] != 0.0f; } + + // returns a transformation that maps a rect to a rect + static TransformationMatrix rectToRect(const FloatRect&, const FloatRect&); + + bool isInvertible() const; + + // This method returns the identity matrix if it is not invertible. + // Use isInvertible() before calling this if you need to know. + TransformationMatrix inverse() const; + + // decompose the matrix into its component parts + typedef struct { + double scaleX, scaleY, scaleZ; + double skewXY, skewXZ, skewYZ; + double quaternionX, quaternionY, quaternionZ, quaternionW; + double translateX, translateY, translateZ; + double perspectiveX, perspectiveY, perspectiveZ, perspectiveW; + } DecomposedType; + + bool decompose(DecomposedType& decomp) const; + void recompose(const DecomposedType& decomp); + + void blend(const TransformationMatrix& from, double progress); + + bool isAffine() const { + return (m13() == 0 && m14() == 0 && m23() == 0 && m24() == 0 && + m31() == 0 && m32() == 0 && m33() == 1 && m34() == 0 && + m43() == 0 && m44() == 1); + } + + // Throw away the non-affine parts of the matrix (lossy!) + void makeAffine(); + + AffineTransform toAffineTransform() const; + + bool operator==(const TransformationMatrix& m2) const { + return (m_matrix[0][0] == m2.m_matrix[0][0] && + m_matrix[0][1] == m2.m_matrix[0][1] && + m_matrix[0][2] == m2.m_matrix[0][2] && + m_matrix[0][3] == m2.m_matrix[0][3] && + m_matrix[1][0] == m2.m_matrix[1][0] && + m_matrix[1][1] == m2.m_matrix[1][1] && + m_matrix[1][2] == m2.m_matrix[1][2] && + m_matrix[1][3] == m2.m_matrix[1][3] && + m_matrix[2][0] == m2.m_matrix[2][0] && + m_matrix[2][1] == m2.m_matrix[2][1] && + m_matrix[2][2] == m2.m_matrix[2][2] && + m_matrix[2][3] == m2.m_matrix[2][3] && + m_matrix[3][0] == m2.m_matrix[3][0] && + m_matrix[3][1] == m2.m_matrix[3][1] && + m_matrix[3][2] == m2.m_matrix[3][2] && + m_matrix[3][3] == m2.m_matrix[3][3]); + } + + bool operator!=(const TransformationMatrix& other) const { + return !(*this == other); + } + + // *this = *this * t + TransformationMatrix& operator*=(const TransformationMatrix& t) { + return multiply(t); + } + + // result = *this * t + TransformationMatrix operator*(const TransformationMatrix& t) const { + TransformationMatrix result = *this; + result.multiply(t); + return result; + } + + bool isIdentityOrTranslation() const { + return m_matrix[0][0] == 1 && m_matrix[0][1] == 0 && m_matrix[0][2] == 0 && + m_matrix[0][3] == 0 && m_matrix[1][0] == 0 && m_matrix[1][1] == 1 && + m_matrix[1][2] == 0 && m_matrix[1][3] == 0 && m_matrix[2][0] == 0 && + m_matrix[2][1] == 0 && m_matrix[2][2] == 1 && m_matrix[2][3] == 0 && + m_matrix[3][3] == 1; + } + + bool isIntegerTranslation() const; + + // This method returns the matrix without 3D components. + TransformationMatrix to2dTransform() const; + + typedef float FloatMatrix4[16]; + void toColumnMajorFloatArray(FloatMatrix4& result) const; + + static SkMatrix44 toSkMatrix44(const TransformationMatrix&); + + private: + // multiply passed 2D point by matrix (assume z=0) + void multVecMatrix(double x, double y, double& dstX, double& dstY) const; + FloatPoint internalMapPoint(const FloatPoint& sourcePoint) const { + double resultX; + double resultY; + multVecMatrix(sourcePoint.x(), sourcePoint.y(), resultX, resultY); + return FloatPoint(static_cast(resultX), static_cast(resultY)); + } + + // multiply passed 3D point by matrix + void multVecMatrix(double x, + double y, + double z, + double& dstX, + double& dstY, + double& dstZ) const; + FloatPoint3D internalMapPoint(const FloatPoint3D& sourcePoint) const { + double resultX; + double resultY; + double resultZ; + multVecMatrix(sourcePoint.x(), sourcePoint.y(), sourcePoint.z(), resultX, + resultY, resultZ); + return FloatPoint3D(static_cast(resultX), + static_cast(resultY), + static_cast(resultZ)); + } + + void setMatrix(const Matrix4 m) { + if (m && m != m_matrix) + memcpy(m_matrix, m, sizeof(Matrix4)); + } + + Matrix4 m_matrix; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_TRANSFORMS_TRANSFORMATIONMATRIX_H_ diff --git a/sky/engine/platform/transforms/TransformationMatrixTest.cpp b/sky/engine/platform/transforms/TransformationMatrixTest.cpp index 3e8639ffed8ed..b487a19b71b30 100644 --- a/sky/engine/platform/transforms/TransformationMatrixTest.cpp +++ b/sky/engine/platform/transforms/TransformationMatrixTest.cpp @@ -9,19 +9,19 @@ using namespace blink; namespace { -TEST(TransformationMatrixTest, NonInvertableBlendTest) -{ - TransformationMatrix from; - TransformationMatrix to(2.7133590938, 0.0, 0.0, 0.0, 0.0, 2.4645137761, 0.0, 0.0, 0.0, 0.0, 0.00, 0.01, 0.02, 0.03, 0.04, 0.05); - TransformationMatrix result; +TEST(TransformationMatrixTest, NonInvertableBlendTest) { + TransformationMatrix from; + TransformationMatrix to(2.7133590938, 0.0, 0.0, 0.0, 0.0, 2.4645137761, 0.0, + 0.0, 0.0, 0.0, 0.00, 0.01, 0.02, 0.03, 0.04, 0.05); + TransformationMatrix result; - result = to; - result.blend(from, 0.25); - EXPECT_TRUE(result == from); + result = to; + result.blend(from, 0.25); + EXPECT_TRUE(result == from); - result = to; - result.blend(from, 0.75); - EXPECT_TRUE(result == to); + result = to; + result.blend(from, 0.75); + EXPECT_TRUE(result == to); } -} // namespace +} // namespace diff --git a/sky/engine/platform/transforms/TranslateTransformOperation.cpp b/sky/engine/platform/transforms/TranslateTransformOperation.cpp index 1850b421a529c..d41a0b0d00f06 100644 --- a/sky/engine/platform/transforms/TranslateTransformOperation.cpp +++ b/sky/engine/platform/transforms/TranslateTransformOperation.cpp @@ -23,29 +23,36 @@ namespace blink { -PassRefPtr TranslateTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity) -{ - if (from && !from->canBlendWith(*this)) - return this; +PassRefPtr TranslateTransformOperation::blend( + const TransformOperation* from, + double progress, + bool blendToIdentity) { + if (from && !from->canBlendWith(*this)) + return this; - const Length zeroLength(0, Fixed); - if (blendToIdentity) - return TranslateTransformOperation::create(zeroLength.blend(m_x, progress, ValueRangeAll), zeroLength.blend(m_y, progress, ValueRangeAll), blink::blend(0., m_z, progress), m_type); + const Length zeroLength(0, Fixed); + if (blendToIdentity) + return TranslateTransformOperation::create( + zeroLength.blend(m_x, progress, ValueRangeAll), + zeroLength.blend(m_y, progress, ValueRangeAll), + blink::blend(0., m_z, progress), m_type); - const TranslateTransformOperation* fromOp = static_cast(from); - Length fromX = fromOp ? fromOp->m_x : zeroLength; - Length fromY = fromOp ? fromOp->m_y : zeroLength; - double fromZ = fromOp ? fromOp->m_z : 0; - return TranslateTransformOperation::create(m_x.blend(fromX, progress, ValueRangeAll), m_y.blend(fromY, progress, ValueRangeAll), blink::blend(fromZ, m_z, progress), m_type); + const TranslateTransformOperation* fromOp = + static_cast(from); + Length fromX = fromOp ? fromOp->m_x : zeroLength; + Length fromY = fromOp ? fromOp->m_y : zeroLength; + double fromZ = fromOp ? fromOp->m_z : 0; + return TranslateTransformOperation::create( + m_x.blend(fromX, progress, ValueRangeAll), + m_y.blend(fromY, progress, ValueRangeAll), + blink::blend(fromZ, m_z, progress), m_type); } -bool TranslateTransformOperation::canBlendWith(const TransformOperation& other) const -{ - return other.type() == Translate - || other.type() == TranslateX - || other.type() == TranslateY - || other.type() == TranslateZ - || other.type() == Translate3D; +bool TranslateTransformOperation::canBlendWith( + const TransformOperation& other) const { + return other.type() == Translate || other.type() == TranslateX || + other.type() == TranslateY || other.type() == TranslateZ || + other.type() == Translate3D; } -} // namespace blink +} // namespace blink diff --git a/sky/engine/platform/transforms/TranslateTransformOperation.h b/sky/engine/platform/transforms/TranslateTransformOperation.h index 6fb6d8cfa195b..022ca8f80cc7c 100644 --- a/sky/engine/platform/transforms/TranslateTransformOperation.h +++ b/sky/engine/platform/transforms/TranslateTransformOperation.h @@ -32,66 +32,77 @@ namespace blink { class PLATFORM_EXPORT TranslateTransformOperation : public TransformOperation { -public: - static PassRefPtr create(const Length& tx, const Length& ty, OperationType type) - { - return adoptRef(new TranslateTransformOperation(tx, ty, 0, type)); - } - - static PassRefPtr create(const Length& tx, const Length& ty, double tz, OperationType type) - { - return adoptRef(new TranslateTransformOperation(tx, ty, tz, type)); - } - - virtual bool canBlendWith(const TransformOperation& other) const; - - double x(const FloatSize& borderBoxSize) const { return floatValueForLength(m_x, borderBoxSize.width()); } - double y(const FloatSize& borderBoxSize) const { return floatValueForLength(m_y, borderBoxSize.height()); } - - Length x() const { return m_x; } - Length y() const { return m_y; } - double z() const { return m_z; } - -private: - virtual bool isIdentity() const override { return !floatValueForLength(m_x, 1) && !floatValueForLength(m_y, 1) && !m_z; } - - virtual OperationType type() const override { return m_type; } - - virtual bool operator==(const TransformOperation& o) const override - { - if (!isSameType(o)) - return false; - const TranslateTransformOperation* t = static_cast(&o); - return m_x == t->m_x && m_y == t->m_y && m_z == t->m_z; - } - - virtual void apply(TransformationMatrix& transform, const FloatSize& borderBoxSize) const override - { - transform.translate3d(x(borderBoxSize), y(borderBoxSize), z()); - } - - virtual PassRefPtr blend(const TransformOperation* from, double progress, bool blendToIdentity = false) override; - - virtual bool dependsOnBoxSize() const override - { - return m_x.isPercent() || m_y.isPercent(); - } - - TranslateTransformOperation(const Length& tx, const Length& ty, double tz, OperationType type) - : m_x(tx) - , m_y(ty) - , m_z(tz) - , m_type(type) - { - ASSERT(type == TranslateX || type == TranslateY || type == TranslateZ || type == Translate || type == Translate3D); - } - - Length m_x; - Length m_y; - double m_z; - OperationType m_type; + public: + static PassRefPtr create(const Length& tx, + const Length& ty, + OperationType type) { + return adoptRef(new TranslateTransformOperation(tx, ty, 0, type)); + } + + static PassRefPtr create(const Length& tx, + const Length& ty, + double tz, + OperationType type) { + return adoptRef(new TranslateTransformOperation(tx, ty, tz, type)); + } + + virtual bool canBlendWith(const TransformOperation& other) const; + + double x(const FloatSize& borderBoxSize) const { + return floatValueForLength(m_x, borderBoxSize.width()); + } + double y(const FloatSize& borderBoxSize) const { + return floatValueForLength(m_y, borderBoxSize.height()); + } + + Length x() const { return m_x; } + Length y() const { return m_y; } + double z() const { return m_z; } + + private: + virtual bool isIdentity() const override { + return !floatValueForLength(m_x, 1) && !floatValueForLength(m_y, 1) && !m_z; + } + + virtual OperationType type() const override { return m_type; } + + virtual bool operator==(const TransformOperation& o) const override { + if (!isSameType(o)) + return false; + const TranslateTransformOperation* t = + static_cast(&o); + return m_x == t->m_x && m_y == t->m_y && m_z == t->m_z; + } + + virtual void apply(TransformationMatrix& transform, + const FloatSize& borderBoxSize) const override { + transform.translate3d(x(borderBoxSize), y(borderBoxSize), z()); + } + + virtual PassRefPtr blend( + const TransformOperation* from, + double progress, + bool blendToIdentity = false) override; + + virtual bool dependsOnBoxSize() const override { + return m_x.isPercent() || m_y.isPercent(); + } + + TranslateTransformOperation(const Length& tx, + const Length& ty, + double tz, + OperationType type) + : m_x(tx), m_y(ty), m_z(tz), m_type(type) { + ASSERT(type == TranslateX || type == TranslateY || type == TranslateZ || + type == Translate || type == Translate3D); + } + + Length m_x; + Length m_y; + double m_z; + OperationType m_type; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PLATFORM_TRANSFORMS_TRANSLATETRANSFORMOPERATION_H_ diff --git a/sky/engine/public/platform/WebBlendMode.h b/sky/engine/public/platform/WebBlendMode.h index 39e686c277905..0aed97402dd36 100644 --- a/sky/engine/public/platform/WebBlendMode.h +++ b/sky/engine/public/platform/WebBlendMode.h @@ -36,24 +36,24 @@ namespace blink { enum WebBlendMode { - WebBlendModeNormal, - WebBlendModeMultiply, - WebBlendModeScreen, - WebBlendModeOverlay, - WebBlendModeDarken, - WebBlendModeLighten, - WebBlendModeColorDodge, - WebBlendModeColorBurn, - WebBlendModeHardLight, - WebBlendModeSofxlight, - WebBlendModeDifference, - WebBlendModeExclusion, - WebBlendModeHue, - WebBlendModeSaturation, - WebBlendModeColor, - WebBlendModeLuminosity + WebBlendModeNormal, + WebBlendModeMultiply, + WebBlendModeScreen, + WebBlendModeOverlay, + WebBlendModeDarken, + WebBlendModeLighten, + WebBlendModeColorDodge, + WebBlendModeColorBurn, + WebBlendModeHardLight, + WebBlendModeSofxlight, + WebBlendModeDifference, + WebBlendModeExclusion, + WebBlendModeHue, + WebBlendModeSaturation, + WebBlendModeColor, + WebBlendModeLuminosity }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PUBLIC_PLATFORM_WEBBLENDMODE_H_ diff --git a/sky/engine/public/platform/WebCommon.h b/sky/engine/public/platform/WebCommon.h index d531e7a5bab79..ae1d890aefcb9 100644 --- a/sky/engine/public/platform/WebCommon.h +++ b/sky/engine/public/platform/WebCommon.h @@ -44,21 +44,20 @@ #endif #if defined(COMPONENT_BUILD) - #define BLINK_EXPORT __attribute__((visibility("default"))) - #define BLINK_PLATFORM_EXPORT __attribute__((visibility("default"))) - #define BLINK_COMMON_EXPORT __attribute__((visibility("default"))) -#else // defined(COMPONENT_BUILD) - #define BLINK_EXPORT - #define BLINK_PLATFORM_EXPORT - #define BLINK_COMMON_EXPORT +#define BLINK_EXPORT __attribute__((visibility("default"))) +#define BLINK_PLATFORM_EXPORT __attribute__((visibility("default"))) +#define BLINK_COMMON_EXPORT __attribute__((visibility("default"))) +#else // defined(COMPONENT_BUILD) +#define BLINK_EXPORT +#define BLINK_PLATFORM_EXPORT +#define BLINK_COMMON_EXPORT #endif - // ----------------------------------------------------------------------------- // Basic types -#include // For size_t -#include // For int32_t +#include // For size_t +#include // For int32_t namespace blink { @@ -74,18 +73,23 @@ typedef unsigned char WebLChar; // ----------------------------------------------------------------------------- // Assertions -BLINK_COMMON_EXPORT void failedAssertion(const char* file, int line, const char* function, const char* assertion); +BLINK_COMMON_EXPORT void failedAssertion(const char* file, + int line, + const char* function, + const char* assertion); -} // namespace blink +} // namespace blink -// Ideally, only use inside the public directory but outside of INSIDE_BLINK blocks. (Otherwise use WTF's ASSERT.) +// Ideally, only use inside the public directory but outside of INSIDE_BLINK +// blocks. (Otherwise use WTF's ASSERT.) #if defined(NDEBUG) #define BLINK_ASSERT(assertion) ((void)0) #else -#define BLINK_ASSERT(assertion) do { \ - if (!(assertion)) \ - failedAssertion(__FILE__, __LINE__, __FUNCTION__, #assertion); \ -} while (0) +#define BLINK_ASSERT(assertion) \ + do { \ + if (!(assertion)) \ + failedAssertion(__FILE__, __LINE__, __FUNCTION__, #assertion); \ + } while (0) #endif #define BLINK_ASSERT_NOT_REACHED() BLINK_ASSERT(0) diff --git a/sky/engine/public/platform/WebDiscardableMemory.h b/sky/engine/public/platform/WebDiscardableMemory.h index 4fae462f9494c..5feb541228ed4 100644 --- a/sky/engine/public/platform/WebDiscardableMemory.h +++ b/sky/engine/public/platform/WebDiscardableMemory.h @@ -47,29 +47,29 @@ namespace blink { // mem->unlock(); // class WebDiscardableMemory { -public: - // Must not be called while locked. - virtual ~WebDiscardableMemory() { } + public: + // Must not be called while locked. + virtual ~WebDiscardableMemory() {} - // Locks the memory, prevent it from being discarded. Once locked. you may - // obtain a pointer to that memory using the data() method. - // - // lock() may return false, indicating that the underlying memory was - // discarded and that the lock failed. In this case, the - // WebDiscardableMemory is effectively dead. - // - // Nested calls to lock are not allowed. - virtual bool lock() = 0; + // Locks the memory, prevent it from being discarded. Once locked. you may + // obtain a pointer to that memory using the data() method. + // + // lock() may return false, indicating that the underlying memory was + // discarded and that the lock failed. In this case, the + // WebDiscardableMemory is effectively dead. + // + // Nested calls to lock are not allowed. + virtual bool lock() = 0; - // Returns the current pointer for the discardable memory. This call is ONLY - // valid when the discardable memory object is locked. - virtual void* data() = 0; + // Returns the current pointer for the discardable memory. This call is ONLY + // valid when the discardable memory object is locked. + virtual void* data() = 0; - // Unlock the memory so that it can be purged by the system. Must be called - // after every successful lock call. - virtual void unlock() = 0; + // Unlock the memory so that it can be purged by the system. Must be called + // after every successful lock call. + virtual void unlock() = 0; }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_PUBLIC_PLATFORM_WEBDISCARDABLEMEMORY_H_ diff --git a/sky/engine/wtf/ASCIICType.h b/sky/engine/wtf/ASCIICType.h index a9487ddb21771..18237bc8a948e 100644 --- a/sky/engine/wtf/ASCIICType.h +++ b/sky/engine/wtf/ASCIICType.h @@ -33,54 +33,56 @@ // The behavior of many of the functions in the header is dependent // on the current locale. But in the WebKit project, all uses of those functions -// are in code processing something that's not locale-specific. These equivalents -// for some of the functions are named more explicitly, not dependent -// on the C library locale, and we should also optimize them as needed. +// are in code processing something that's not locale-specific. These +// equivalents for some of the functions are named more explicitly, +// not dependent on the C library locale, and we should also optimize them as +// needed. -// All functions return false or leave the character unchanged if passed a character -// that is outside the range 0-7F. So they can be used on Unicode strings or -// characters if the intent is to do processing only if the character is ASCII. +// All functions return false or leave the character unchanged if passed a +// character that is outside the range 0-7F. So they can be used on Unicode +// strings or characters if the intent is to do processing only if the character +// is ASCII. namespace WTF { -template inline bool isASCII(CharType c) -{ - return !(c & ~0x7F); +template +inline bool isASCII(CharType c) { + return !(c & ~0x7F); } -template inline bool isASCIIAlpha(CharType c) -{ - return (c | 0x20) >= 'a' && (c | 0x20) <= 'z'; +template +inline bool isASCIIAlpha(CharType c) { + return (c | 0x20) >= 'a' && (c | 0x20) <= 'z'; } -template inline bool isASCIIDigit(CharType c) -{ - return c >= '0' && c <= '9'; +template +inline bool isASCIIDigit(CharType c) { + return c >= '0' && c <= '9'; } -template inline bool isASCIIAlphanumeric(CharType c) -{ - return isASCIIDigit(c) || isASCIIAlpha(c); +template +inline bool isASCIIAlphanumeric(CharType c) { + return isASCIIDigit(c) || isASCIIAlpha(c); } -template inline bool isASCIIHexDigit(CharType c) -{ - return isASCIIDigit(c) || ((c | 0x20) >= 'a' && (c | 0x20) <= 'f'); +template +inline bool isASCIIHexDigit(CharType c) { + return isASCIIDigit(c) || ((c | 0x20) >= 'a' && (c | 0x20) <= 'f'); } -template inline bool isASCIILower(CharType c) -{ - return c >= 'a' && c <= 'z'; +template +inline bool isASCIILower(CharType c) { + return c >= 'a' && c <= 'z'; } -template inline bool isASCIIOctalDigit(CharType c) -{ - return (c >= '0') & (c <= '7'); +template +inline bool isASCIIOctalDigit(CharType c) { + return (c >= '0') & (c <= '7'); } -template inline bool isASCIIPrintable(CharType c) -{ - return c >= ' ' && c <= '~'; +template +inline bool isASCIIPrintable(CharType c) { + return c >= ' ' && c <= '~'; } /* @@ -96,77 +98,78 @@ template inline bool isASCIIPrintable(CharType c) 0C \f 0 0B \v 0 */ -template inline bool isASCIISpace(CharType c) -{ - return c <= ' ' && (c == ' ' || (c <= 0xD && c >= 0x9)); +template +inline bool isASCIISpace(CharType c) { + return c <= ' ' && (c == ' ' || (c <= 0xD && c >= 0x9)); } -template inline bool isASCIIUpper(CharType c) -{ - return c >= 'A' && c <= 'Z'; +template +inline bool isASCIIUpper(CharType c) { + return c >= 'A' && c <= 'Z'; } -template inline CharType toASCIILower(CharType c) -{ +template +inline CharType toASCIILower(CharType c) { #if defined(_MSC_FULL_VER) && _MSC_FULL_VER == 170060610 - // Make a workaround for VS2012 update 3 optimizer bug, remove once VS2012 fix it. - return (c >= 'A' && c <= 'Z') ? c + 0x20 : c; + // Make a workaround for VS2012 update 3 optimizer bug, remove once VS2012 fix + // it. + return (c >= 'A' && c <= 'Z') ? c + 0x20 : c; #else - return c | ((c >= 'A' && c <= 'Z') << 5); + return c | ((c >= 'A' && c <= 'Z') << 5); #endif } -template inline CharType toASCIILowerUnchecked(CharType character) -{ - // This function can be used for comparing any input character - // to a lowercase English character. The isASCIIAlphaCaselessEqual - // below should be used for regular comparison of ASCII alpha - // characters, but switch statements in CSS tokenizer require - // direct use of this function. - return character | 0x20; +template +inline CharType toASCIILowerUnchecked(CharType character) { + // This function can be used for comparing any input character + // to a lowercase English character. The isASCIIAlphaCaselessEqual + // below should be used for regular comparison of ASCII alpha + // characters, but switch statements in CSS tokenizer require + // direct use of this function. + return character | 0x20; } -template inline CharType toASCIIUpper(CharType c) -{ - return c & ~((c >= 'a' && c <= 'z') << 5); +template +inline CharType toASCIIUpper(CharType c) { + return c & ~((c >= 'a' && c <= 'z') << 5); } -template inline int toASCIIHexValue(CharType c) -{ - ASSERT(isASCIIHexDigit(c)); - return c < 'A' ? c - '0' : (c - 'A' + 10) & 0xF; +template +inline int toASCIIHexValue(CharType c) { + ASSERT(isASCIIHexDigit(c)); + return c < 'A' ? c - '0' : (c - 'A' + 10) & 0xF; } -template inline int toASCIIHexValue(CharType upperValue, CharType lowerValue) -{ - ASSERT(isASCIIHexDigit(upperValue) && isASCIIHexDigit(lowerValue)); - return ((toASCIIHexValue(upperValue) << 4) & 0xF0) | toASCIIHexValue(lowerValue); +template +inline int toASCIIHexValue(CharType upperValue, CharType lowerValue) { + ASSERT(isASCIIHexDigit(upperValue) && isASCIIHexDigit(lowerValue)); + return ((toASCIIHexValue(upperValue) << 4) & 0xF0) | + toASCIIHexValue(lowerValue); } -inline char lowerNibbleToASCIIHexDigit(char c) -{ - char nibble = c & 0xF; - return nibble < 10 ? '0' + nibble : 'A' + nibble - 10; +inline char lowerNibbleToASCIIHexDigit(char c) { + char nibble = c & 0xF; + return nibble < 10 ? '0' + nibble : 'A' + nibble - 10; } -inline char upperNibbleToASCIIHexDigit(char c) -{ - char nibble = (c >> 4) & 0xF; - return nibble < 10 ? '0' + nibble : 'A' + nibble - 10; +inline char upperNibbleToASCIIHexDigit(char c) { + char nibble = (c >> 4) & 0xF; + return nibble < 10 ? '0' + nibble : 'A' + nibble - 10; } -template inline bool isASCIIAlphaCaselessEqual(CharType cssCharacter, char character) -{ - // This function compares a (preferrably) constant ASCII - // lowercase letter to any input character. - ASSERT(character >= 'a' && character <= 'z'); - return LIKELY(toASCIILowerUnchecked(cssCharacter) == character); +template +inline bool isASCIIAlphaCaselessEqual(CharType cssCharacter, char character) { + // This function compares a (preferrably) constant ASCII + // lowercase letter to any input character. + ASSERT(character >= 'a' && character <= 'z'); + return LIKELY(toASCIILowerUnchecked(cssCharacter) == character); } -} +} // namespace WTF using WTF::isASCII; using WTF::isASCIIAlpha; +using WTF::isASCIIAlphaCaselessEqual; using WTF::isASCIIAlphanumeric; using WTF::isASCIIDigit; using WTF::isASCIIHexDigit; @@ -175,12 +178,11 @@ using WTF::isASCIIOctalDigit; using WTF::isASCIIPrintable; using WTF::isASCIISpace; using WTF::isASCIIUpper; +using WTF::lowerNibbleToASCIIHexDigit; using WTF::toASCIIHexValue; using WTF::toASCIILower; using WTF::toASCIILowerUnchecked; using WTF::toASCIIUpper; -using WTF::lowerNibbleToASCIIHexDigit; using WTF::upperNibbleToASCIIHexDigit; -using WTF::isASCIIAlphaCaselessEqual; #endif // SKY_ENGINE_WTF_ASCIICTYPE_H_ diff --git a/sky/engine/wtf/AddressSpaceRandomization.cpp b/sky/engine/wtf/AddressSpaceRandomization.cpp index 1abc271c64713..8f03f0929eeba 100644 --- a/sky/engine/wtf/AddressSpaceRandomization.cpp +++ b/sky/engine/wtf/AddressSpaceRandomization.cpp @@ -14,83 +14,80 @@ namespace { // This is the same PRNG as used by tcmalloc for mapping address randomness; // see http://burtleburtle.net/bob/rand/smallprng.html struct ranctx { - int lock; - bool initialized; - uint32_t a; - uint32_t b; - uint32_t c; - uint32_t d; + int lock; + bool initialized; + uint32_t a; + uint32_t b; + uint32_t c; + uint32_t d; }; #define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k)))) -uint32_t ranvalInternal(ranctx* x) -{ - uint32_t e = x->a - rot(x->b, 27); - x->a = x->b ^ rot(x->c, 17); - x->b = x->c + x->d; - x->c = x->d + e; - x->d = e + x->a; - return x->d; +uint32_t ranvalInternal(ranctx* x) { + uint32_t e = x->a - rot(x->b, 27); + x->a = x->b ^ rot(x->c, 17); + x->b = x->c + x->d; + x->c = x->d + e; + x->d = e + x->a; + return x->d; } #undef rot -uint32_t ranval(ranctx* x) -{ - spinLockLock(&x->lock); - if (UNLIKELY(!x->initialized)) { - x->initialized = true; - char c; - uint32_t seed = static_cast(reinterpret_cast(&c)); - x->a = 0xf1ea5eed; - x->b = x->c = x->d = seed; - for (int i = 0; i < 20; ++i) { - (void) ranvalInternal(x); - } +uint32_t ranval(ranctx* x) { + spinLockLock(&x->lock); + if (UNLIKELY(!x->initialized)) { + x->initialized = true; + char c; + uint32_t seed = static_cast(reinterpret_cast(&c)); + x->a = 0xf1ea5eed; + x->b = x->c = x->d = seed; + for (int i = 0; i < 20; ++i) { + (void)ranvalInternal(x); } - uint32_t ret = ranvalInternal(x); - spinLockUnlock(&x->lock); - return ret; + } + uint32_t ret = ranvalInternal(x); + spinLockUnlock(&x->lock); + return ret; } static struct ranctx s_ranctx; -} +} // namespace // Calculates a random preferred mapping address. In calculating an // address, we balance good ASLR against not fragmenting the address // space too badly. -void* getRandomPageBase() -{ - uintptr_t random; - random = static_cast(ranval(&s_ranctx)); +void* getRandomPageBase() { + uintptr_t random; + random = static_cast(ranval(&s_ranctx)); #if CPU(X86_64) - random <<= 32UL; - random |= static_cast(ranval(&s_ranctx)); - // This address mask gives a low liklihood of address space collisions. - // We handle the situation gracefully if there is a collision. + random <<= 32UL; + random |= static_cast(ranval(&s_ranctx)); + // This address mask gives a low liklihood of address space collisions. + // We handle the situation gracefully if there is a collision. #if OS(WIN) - // 64-bit Windows has a bizarrely small 8TB user address space. - // Allocates in the 1-5TB region. - random &= 0x3ffffffffffUL; - random += 0x10000000000UL; + // 64-bit Windows has a bizarrely small 8TB user address space. + // Allocates in the 1-5TB region. + random &= 0x3ffffffffffUL; + random += 0x10000000000UL; #else - // Linux and OS X support the full 47-bit user space of x64 processors. - random &= 0x3fffffffffffUL; + // Linux and OS X support the full 47-bit user space of x64 processors. + random &= 0x3fffffffffffUL; #endif #elif CPU(ARM64) - // ARM64 on Linux has 39-bit user space. - random &= 0x3fffffffffUL; - random += 0x1000000000UL; -#else // !CPU(X86_64) && !CPU(ARM64) - // This is a good range on Windows, Linux and Mac. - // Allocates in the 0.5-1.5GB region. - random &= 0x3fffffff; - random += 0x20000000; -#endif // CPU(X86_64) - random &= kPageAllocationGranularityBaseMask; - return reinterpret_cast(random); + // ARM64 on Linux has 39-bit user space. + random &= 0x3fffffffffUL; + random += 0x1000000000UL; +#else // !CPU(X86_64) && !CPU(ARM64) + // This is a good range on Windows, Linux and Mac. + // Allocates in the 0.5-1.5GB region. + random &= 0x3fffffff; + random += 0x20000000; +#endif // CPU(X86_64) + random &= kPageAllocationGranularityBaseMask; + return reinterpret_cast(random); } -} +} // namespace WTF diff --git a/sky/engine/wtf/AddressSpaceRandomization.h b/sky/engine/wtf/AddressSpaceRandomization.h index 313db031c2e0f..87770656b8704 100644 --- a/sky/engine/wtf/AddressSpaceRandomization.h +++ b/sky/engine/wtf/AddressSpaceRandomization.h @@ -14,6 +14,6 @@ namespace WTF { // space too badly. WTF_EXPORT void* getRandomPageBase(); -} +} // namespace WTF #endif // SKY_ENGINE_WTF_ADDRESSSPACERANDOMIZATION_H_ diff --git a/sky/engine/wtf/Alignment.h b/sky/engine/wtf/Alignment.h index bbbb7cf76b265..2750a2bde59ea 100644 --- a/sky/engine/wtf/Alignment.h +++ b/sky/engine/wtf/Alignment.h @@ -21,47 +21,69 @@ #ifndef SKY_ENGINE_WTF_ALIGNMENT_H_ #define SKY_ENGINE_WTF_ALIGNMENT_H_ +#include #include #include -#include #include "flutter/sky/engine/wtf/Compiler.h" namespace WTF { #if COMPILER(GCC) - #define WTF_ALIGN_OF(type) __alignof__(type) - #define WTF_ALIGNED(variable_type, variable, n) variable_type variable __attribute__((__aligned__(n))) +#define WTF_ALIGN_OF(type) __alignof__(type) +#define WTF_ALIGNED(variable_type, variable, n) \ + variable_type variable __attribute__((__aligned__(n))) #else - #error WTF_ALIGN macros need alignment control. +#error WTF_ALIGN macros need alignment control. #endif #if COMPILER(GCC) - typedef char __attribute__((__may_alias__)) AlignedBufferChar; +typedef char __attribute__((__may_alias__)) AlignedBufferChar; #else - typedef char AlignedBufferChar; +typedef char AlignedBufferChar; #endif - template struct AlignedBuffer; - template struct AlignedBuffer { AlignedBufferChar buffer[size]; }; - template struct AlignedBuffer { WTF_ALIGNED(AlignedBufferChar, buffer[size], 2); }; - template struct AlignedBuffer { WTF_ALIGNED(AlignedBufferChar, buffer[size], 4); }; - template struct AlignedBuffer { WTF_ALIGNED(AlignedBufferChar, buffer[size], 8); }; - template struct AlignedBuffer { WTF_ALIGNED(AlignedBufferChar, buffer[size], 16); }; - template struct AlignedBuffer { WTF_ALIGNED(AlignedBufferChar, buffer[size], 32); }; - template struct AlignedBuffer { WTF_ALIGNED(AlignedBufferChar, buffer[size], 64); }; +template +struct AlignedBuffer; +template +struct AlignedBuffer { + AlignedBufferChar buffer[size]; +}; +template +struct AlignedBuffer { + WTF_ALIGNED(AlignedBufferChar, buffer[size], 2); +}; +template +struct AlignedBuffer { + WTF_ALIGNED(AlignedBufferChar, buffer[size], 4); +}; +template +struct AlignedBuffer { + WTF_ALIGNED(AlignedBufferChar, buffer[size], 8); +}; +template +struct AlignedBuffer { + WTF_ALIGNED(AlignedBufferChar, buffer[size], 16); +}; +template +struct AlignedBuffer { + WTF_ALIGNED(AlignedBufferChar, buffer[size], 32); +}; +template +struct AlignedBuffer { + WTF_ALIGNED(AlignedBufferChar, buffer[size], 64); +}; - template - void swap(AlignedBuffer& a, AlignedBuffer& b) - { - for (size_t i = 0; i < size; ++i) - std::swap(a.buffer[i], b.buffer[i]); - } +template +void swap(AlignedBuffer& a, + AlignedBuffer& b) { + for (size_t i = 0; i < size; ++i) + std::swap(a.buffer[i], b.buffer[i]); +} - template - inline bool isAlignedTo(const void* pointer) - { - return !(reinterpret_cast(pointer) & mask); - } +template +inline bool isAlignedTo(const void* pointer) { + return !(reinterpret_cast(pointer) & mask); } +} // namespace WTF #endif // SKY_ENGINE_WTF_ALIGNMENT_H_ diff --git a/sky/engine/wtf/Assertions.cpp b/sky/engine/wtf/Assertions.cpp index 7a8faefc87344..77aba28d3cbe5 100644 --- a/sky/engine/wtf/Assertions.cpp +++ b/sky/engine/wtf/Assertions.cpp @@ -27,7 +27,8 @@ // The vprintf_stderr_common function triggers this error in the Mac build. // Feel free to remove this pragma if this file builds on Mac. -// According to http://gcc.gnu.org/onlinedocs/gcc-4.2.1/gcc/Diagnostic-Pragmas.html#Diagnostic-Pragmas +// According to +// http://gcc.gnu.org/onlinedocs/gcc-4.2.1/gcc/Diagnostic-Pragmas.html#Diagnostic-Pragmas // we need to place this directive before any data or functions are defined. #pragma GCC diagnostic ignored "-Wmissing-format-attribute" @@ -60,12 +61,11 @@ extern "C" { WTF_ATTRIBUTE_PRINTF(1, 0) -static void vprintf_stderr_common(const char* format, va_list args) -{ +static void vprintf_stderr_common(const char* format, va_list args) { #if OS(ANDROID) - __android_log_vprint(ANDROID_LOG_WARN, "flutter", format, args); + __android_log_vprint(ANDROID_LOG_WARN, "flutter", format, args); #else - vfprintf(stderr, format, args); + vfprintf(stderr, format, args); #endif } @@ -74,32 +74,34 @@ static void vprintf_stderr_common(const char* format, va_list args) #pragma GCC diagnostic ignored "-Wformat-nonliteral" #endif -static void vprintf_stderr_with_prefix(const char* prefix, const char* format, va_list args) -{ - size_t prefixLength = strlen(prefix); - size_t formatLength = strlen(format); - OwnPtr formatWithPrefix = adoptArrayPtr(new char[prefixLength + formatLength + 1]); - memcpy(formatWithPrefix.get(), prefix, prefixLength); - memcpy(formatWithPrefix.get() + prefixLength, format, formatLength); - formatWithPrefix[prefixLength + formatLength] = 0; - - vprintf_stderr_common(formatWithPrefix.get(), args); +static void vprintf_stderr_with_prefix(const char* prefix, + const char* format, + va_list args) { + size_t prefixLength = strlen(prefix); + size_t formatLength = strlen(format); + OwnPtr formatWithPrefix = + adoptArrayPtr(new char[prefixLength + formatLength + 1]); + memcpy(formatWithPrefix.get(), prefix, prefixLength); + memcpy(formatWithPrefix.get() + prefixLength, format, formatLength); + formatWithPrefix[prefixLength + formatLength] = 0; + + vprintf_stderr_common(formatWithPrefix.get(), args); } -static void vprintf_stderr_with_trailing_newline(const char* format, va_list args) -{ - size_t formatLength = strlen(format); - if (formatLength && format[formatLength - 1] == '\n') { - vprintf_stderr_common(format, args); - return; - } +static void vprintf_stderr_with_trailing_newline(const char* format, + va_list args) { + size_t formatLength = strlen(format); + if (formatLength && format[formatLength - 1] == '\n') { + vprintf_stderr_common(format, args); + return; + } - OwnPtr formatWithNewline = adoptArrayPtr(new char[formatLength + 2]); - memcpy(formatWithNewline.get(), format, formatLength); - formatWithNewline[formatLength] = '\n'; - formatWithNewline[formatLength + 1] = 0; + OwnPtr formatWithNewline = adoptArrayPtr(new char[formatLength + 2]); + memcpy(formatWithNewline.get(), format, formatLength); + formatWithNewline[formatLength] = '\n'; + formatWithNewline[formatLength + 1] = 0; - vprintf_stderr_common(formatWithNewline.get(), args); + vprintf_stderr_common(formatWithNewline.get(), args); } #if COMPILER(CLANG) || COMPILER(GCC) @@ -107,102 +109,117 @@ static void vprintf_stderr_with_trailing_newline(const char* format, va_list arg #endif WTF_ATTRIBUTE_PRINTF(1, 2) -static void printf_stderr_common(const char* format, ...) -{ - va_list args; - va_start(args, format); - vprintf_stderr_common(format, args); - va_end(args); +static void printf_stderr_common(const char* format, ...) { + va_list args; + va_start(args, format); + vprintf_stderr_common(format, args); + va_end(args); } -static void printCallSite(const char* file, int line, const char* function) -{ - // By using this format, which matches the format used by MSVC for compiler errors, developers - // using Visual Studio can double-click the file/line number in the Output Window to have the - // editor navigate to that line of code. It seems fine for other developers, too. - printf_stderr_common("%s(%d) : %s\n", file, line, function); +static void printCallSite(const char* file, int line, const char* function) { + // By using this format, which matches the format used by MSVC for compiler + // errors, developers using Visual Studio can double-click the file/line + // number in the Output Window to have the editor navigate to that line of + // code. It seems fine for other developers, too. + printf_stderr_common("%s(%d) : %s\n", file, line, function); } -void WTFReportAssertionFailure(const char* file, int line, const char* function, const char* assertion) -{ - if (assertion) - printf_stderr_common("ASSERTION FAILED: %s\n", assertion); - else - printf_stderr_common("SHOULD NEVER BE REACHED\n"); - printCallSite(file, line, function); +void WTFReportAssertionFailure(const char* file, + int line, + const char* function, + const char* assertion) { + if (assertion) + printf_stderr_common("ASSERTION FAILED: %s\n", assertion); + else + printf_stderr_common("SHOULD NEVER BE REACHED\n"); + printCallSite(file, line, function); } -void WTFReportAssertionFailureWithMessage(const char* file, int line, const char* function, const char* assertion, const char* format, ...) -{ - va_list args; - va_start(args, format); - vprintf_stderr_with_prefix("ASSERTION FAILED: ", format, args); - va_end(args); - printf_stderr_common("\n%s\n", assertion); - printCallSite(file, line, function); +void WTFReportAssertionFailureWithMessage(const char* file, + int line, + const char* function, + const char* assertion, + const char* format, + ...) { + va_list args; + va_start(args, format); + vprintf_stderr_with_prefix("ASSERTION FAILED: ", format, args); + va_end(args); + printf_stderr_common("\n%s\n", assertion); + printCallSite(file, line, function); } -void WTFReportArgumentAssertionFailure(const char* file, int line, const char* function, const char* argName, const char* assertion) -{ - printf_stderr_common("ARGUMENT BAD: %s, %s\n", argName, assertion); - printCallSite(file, line, function); +void WTFReportArgumentAssertionFailure(const char* file, + int line, + const char* function, + const char* argName, + const char* assertion) { + printf_stderr_common("ARGUMENT BAD: %s, %s\n", argName, assertion); + printCallSite(file, line, function); } -void WTFReportBacktrace() -{ - glue::PrintStackTrace(); +void WTFReportBacktrace() { + glue::PrintStackTrace(); } -void WTFReportFatalError(const char* file, int line, const char* function, const char* format, ...) -{ - va_list args; - va_start(args, format); - vprintf_stderr_with_prefix("FATAL ERROR: ", format, args); - va_end(args); - printf_stderr_common("\n"); - printCallSite(file, line, function); +void WTFReportFatalError(const char* file, + int line, + const char* function, + const char* format, + ...) { + va_list args; + va_start(args, format); + vprintf_stderr_with_prefix("FATAL ERROR: ", format, args); + va_end(args); + printf_stderr_common("\n"); + printCallSite(file, line, function); } -void WTFReportError(const char* file, int line, const char* function, const char* format, ...) -{ - va_list args; - va_start(args, format); - vprintf_stderr_with_prefix("ERROR: ", format, args); - va_end(args); - printf_stderr_common("\n"); - printCallSite(file, line, function); +void WTFReportError(const char* file, + int line, + const char* function, + const char* format, + ...) { + va_list args; + va_start(args, format); + vprintf_stderr_with_prefix("ERROR: ", format, args); + va_end(args); + printf_stderr_common("\n"); + printCallSite(file, line, function); } -void WTFLog(WTFLogChannel* channel, const char* format, ...) -{ - if (channel->state != WTFLogChannelOn) - return; +void WTFLog(WTFLogChannel* channel, const char* format, ...) { + if (channel->state != WTFLogChannelOn) + return; - va_list args; - va_start(args, format); - vprintf_stderr_with_trailing_newline(format, args); - va_end(args); + va_list args; + va_start(args, format); + vprintf_stderr_with_trailing_newline(format, args); + va_end(args); } -void WTFLogVerbose(const char* file, int line, const char* function, WTFLogChannel* channel, const char* format, ...) -{ - if (channel->state != WTFLogChannelOn) - return; - - va_list args; - va_start(args, format); - vprintf_stderr_with_trailing_newline(format, args); - va_end(args); - - printCallSite(file, line, function); +void WTFLogVerbose(const char* file, + int line, + const char* function, + WTFLogChannel* channel, + const char* format, + ...) { + if (channel->state != WTFLogChannelOn) + return; + + va_list args; + va_start(args, format); + vprintf_stderr_with_trailing_newline(format, args); + va_end(args); + + printCallSite(file, line, function); } -void WTFLogAlways(const char* format, ...) -{ - va_list args; - va_start(args, format); - vprintf_stderr_with_trailing_newline(format, args); - va_end(args); +void WTFLogAlways(const char* format, ...) { + va_list args; + va_start(args, format); + vprintf_stderr_with_trailing_newline(format, args); + va_end(args); } -} // extern "C" +} // extern "C" diff --git a/sky/engine/wtf/Assertions.h b/sky/engine/wtf/Assertions.h index 1fcd6082b2da8..1870452cc8f79 100644 --- a/sky/engine/wtf/Assertions.h +++ b/sky/engine/wtf/Assertions.h @@ -80,16 +80,20 @@ #define LOG_DISABLED !ENABLE(ASSERT) #endif -/* WTF logging functions can process %@ in the format string to log a NSObject* but the printf format attribute - emits a warning when %@ is used in the format string. Until is resolved we can't include - the attribute when being used from Objective-C code in case it decides to use %@. */ +/* WTF logging functions can process %@ in the format string to log a NSObject* + but the printf format attribute emits a warning when %@ is used in the format + string. Until is resolved we can't include the + attribute when being used from Objective-C code in case it decides to use %@. + */ #if COMPILER(GCC) && !defined(__OBJC__) -#define WTF_ATTRIBUTE_PRINTF(formatStringArgument, extraArguments) __attribute__((__format__(printf, formatStringArgument, extraArguments))) +#define WTF_ATTRIBUTE_PRINTF(formatStringArgument, extraArguments) \ + __attribute__((__format__(printf, formatStringArgument, extraArguments))) #else #define WTF_ATTRIBUTE_PRINTF(formatStringArgument, extraArguments) #endif -/* These helper functions are always declared, but not necessarily always defined if the corresponding function is disabled. */ +/* These helper functions are always declared, but not necessarily always + * defined if the corresponding function is disabled. */ #ifdef __cplusplus extern "C" { @@ -98,17 +102,45 @@ extern "C" { typedef enum { WTFLogChannelOff, WTFLogChannelOn } WTFLogChannelState; typedef struct { - WTFLogChannelState state; + WTFLogChannelState state; } WTFLogChannel; -WTF_EXPORT void WTFReportAssertionFailure(const char* file, int line, const char* function, const char* assertion); -WTF_EXPORT void WTFReportAssertionFailureWithMessage(const char* file, int line, const char* function, const char* assertion, const char* format, ...) WTF_ATTRIBUTE_PRINTF(5, 6); -WTF_EXPORT void WTFReportArgumentAssertionFailure(const char* file, int line, const char* function, const char* argName, const char* assertion); -WTF_EXPORT void WTFReportFatalError(const char* file, int line, const char* function, const char* format, ...) WTF_ATTRIBUTE_PRINTF(4, 5); -WTF_EXPORT void WTFReportError(const char* file, int line, const char* function, const char* format, ...) WTF_ATTRIBUTE_PRINTF(4, 5); -WTF_EXPORT void WTFLog(WTFLogChannel*, const char* format, ...) WTF_ATTRIBUTE_PRINTF(2, 3); -WTF_EXPORT void WTFLogVerbose(const char* file, int line, const char* function, WTFLogChannel*, const char* format, ...) WTF_ATTRIBUTE_PRINTF(5, 6); -WTF_EXPORT void WTFLogAlways(const char* format, ...) WTF_ATTRIBUTE_PRINTF(1, 2); +WTF_EXPORT void WTFReportAssertionFailure(const char* file, + int line, + const char* function, + const char* assertion); +WTF_EXPORT void WTFReportAssertionFailureWithMessage(const char* file, + int line, + const char* function, + const char* assertion, + const char* format, + ...) + WTF_ATTRIBUTE_PRINTF(5, 6); +WTF_EXPORT void WTFReportArgumentAssertionFailure(const char* file, + int line, + const char* function, + const char* argName, + const char* assertion); +WTF_EXPORT void WTFReportFatalError(const char* file, + int line, + const char* function, + const char* format, + ...) WTF_ATTRIBUTE_PRINTF(4, 5); +WTF_EXPORT void WTFReportError(const char* file, + int line, + const char* function, + const char* format, + ...) WTF_ATTRIBUTE_PRINTF(4, 5); +WTF_EXPORT void WTFLog(WTFLogChannel*, const char* format, ...) + WTF_ATTRIBUTE_PRINTF(2, 3); +WTF_EXPORT void WTFLogVerbose(const char* file, + int line, + const char* function, + WTFLogChannel*, + const char* format, + ...) WTF_ATTRIBUTE_PRINTF(5, 6); +WTF_EXPORT void WTFLogAlways(const char* format, ...) + WTF_ATTRIBUTE_PRINTF(1, 2); WTF_EXPORT void WTFReportBacktrace(); @@ -116,28 +148,29 @@ WTF_EXPORT void WTFReportBacktrace(); } #endif -/* IMMEDIATE_CRASH() - Like CRASH() below but crashes in the fastest, simplest possible way with no attempt at logging. */ +/* IMMEDIATE_CRASH() - Like CRASH() below but crashes in the fastest, simplest + * possible way with no attempt at logging. */ #ifndef IMMEDIATE_CRASH #if COMPILER(GCC) #define IMMEDIATE_CRASH() __builtin_trap() #else -#define IMMEDIATE_CRASH() ((void(*)())0)() +#define IMMEDIATE_CRASH() ((void (*)())0)() #endif #endif -/* CRASH() - Raises a fatal error resulting in program termination and triggering either the debugger or the crash reporter. +/* CRASH() - Raises a fatal error resulting in program termination and + triggering either the debugger or the crash reporter. Use CRASH() in response to known, unrecoverable errors like out-of-memory. Macro is enabled in both debug and release mode. - To test for unknown errors and verify assumptions, use ASSERT instead, to avoid impacting performance in release builds. + To test for unknown errors and verify assumptions, use ASSERT instead, to + avoid impacting performance in release builds. Signals are ignored by the crash reporter on OS X so we must do better. */ #ifndef CRASH #define CRASH() \ - (WTFReportBacktrace(), \ - (*(int*)0xfbadbeef = 0), \ - IMMEDIATE_CRASH()) + (WTFReportBacktrace(), (*(int*)0xfbadbeef = 0), IMMEDIATE_CRASH()) #endif #if COMPILER(CLANG) @@ -156,9 +189,10 @@ WTF_EXPORT void WTFReportBacktrace(); #else -#define BACKTRACE() do { \ +#define BACKTRACE() \ + do { \ WTFReportBacktrace(); \ -} while(false) + } while (false) #endif @@ -168,28 +202,30 @@ WTF_EXPORT void WTFReportBacktrace(); Expressions inside them are evaluated in debug builds only. */ #if OS(WIN) -/* FIXME: Change to use something other than ASSERT to avoid this conflict with the underlying platform */ +/* FIXME: Change to use something other than ASSERT to avoid this conflict with + * the underlying platform */ #undef ASSERT #endif #if ENABLE(ASSERT) -#define ASSERT(assertion) \ - (!(assertion) ? \ - (WTFReportAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #assertion), \ - CRASH()) : \ - (void)0) +#define ASSERT(assertion) \ + (!(assertion) ? (WTFReportAssertionFailure(__FILE__, __LINE__, \ + WTF_PRETTY_FUNCTION, #assertion), \ + CRASH()) \ + : (void)0) -#define ASSERT_AT(assertion, file, line, function) \ - (!(assertion) ? \ - (WTFReportAssertionFailure(file, line, function, #assertion), \ - CRASH()) : \ - (void)0) +#define ASSERT_AT(assertion, file, line, function) \ + (!(assertion) \ + ? (WTFReportAssertionFailure(file, line, function, #assertion), \ + CRASH()) \ + : (void)0) -#define ASSERT_NOT_REACHED() do { \ +#define ASSERT_NOT_REACHED() \ + do { \ WTFReportAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, 0); \ - CRASH(); \ -} while (0) + CRASH(); \ + } while (0) #define ASSERT_UNUSED(variable, assertion) ASSERT(assertion) @@ -216,18 +252,20 @@ WTF_EXPORT void WTFReportBacktrace(); */ #ifdef ADDRESS_SANITIZER -#define ASSERT_WITH_SECURITY_IMPLICATION(assertion) \ - (!(assertion) ? \ - (WTFReportAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #assertion), \ - CRASH()) : \ - (void)0) +#define ASSERT_WITH_SECURITY_IMPLICATION(assertion) \ + (!(assertion) ? (WTFReportAssertionFailure(__FILE__, __LINE__, \ + WTF_PRETTY_FUNCTION, #assertion), \ + CRASH()) \ + : (void)0) -#define RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(assertion) ASSERT_WITH_SECURITY_IMPLICATION(assertion) +#define RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(assertion) \ + ASSERT_WITH_SECURITY_IMPLICATION(assertion) #else #define ASSERT_WITH_SECURITY_IMPLICATION(assertion) ASSERT(assertion) -#define RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(assertion) RELEASE_ASSERT(assertion) +#define RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(assertion) \ + RELEASE_ASSERT(assertion) #endif @@ -244,12 +282,14 @@ WTF_EXPORT void WTFReportBacktrace(); #if ASSERT_MSG_DISABLED #define ASSERT_WITH_MESSAGE(assertion, ...) ((void)0) #else -#define ASSERT_WITH_MESSAGE(assertion, ...) do \ - if (!(assertion)) { \ - WTFReportAssertionFailureWithMessage(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #assertion, __VA_ARGS__); \ - CRASH(); \ - } \ -while (0) +#define ASSERT_WITH_MESSAGE(assertion, ...) \ + do \ + if (!(assertion)) { \ + WTFReportAssertionFailureWithMessage( \ + __FILE__, __LINE__, WTF_PRETTY_FUNCTION, #assertion, __VA_ARGS__); \ + CRASH(); \ + } \ + while (0) #endif /* ASSERT_WITH_MESSAGE_UNUSED */ @@ -257,12 +297,14 @@ while (0) #if ASSERT_MSG_DISABLED #define ASSERT_WITH_MESSAGE_UNUSED(variable, assertion, ...) ((void)variable) #else -#define ASSERT_WITH_MESSAGE_UNUSED(variable, assertion, ...) do \ - if (!(assertion)) { \ - WTFReportAssertionFailureWithMessage(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #assertion, __VA_ARGS__); \ - CRASH(); \ - } \ -while (0) +#define ASSERT_WITH_MESSAGE_UNUSED(variable, assertion, ...) \ + do \ + if (!(assertion)) { \ + WTFReportAssertionFailureWithMessage( \ + __FILE__, __LINE__, WTF_PRETTY_FUNCTION, #assertion, __VA_ARGS__); \ + CRASH(); \ + } \ + while (0) #endif /* ASSERT_ARG */ @@ -273,12 +315,14 @@ while (0) #else -#define ASSERT_ARG(argName, assertion) do \ - if (!(assertion)) { \ - WTFReportArgumentAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #argName, #assertion); \ - CRASH(); \ - } \ -while (0) +#define ASSERT_ARG(argName, assertion) \ + do \ + if (!(assertion)) { \ + WTFReportArgumentAssertionFailure( \ + __FILE__, __LINE__, WTF_PRETTY_FUNCTION, #argName, #assertion); \ + CRASH(); \ + } \ + while (0) #endif @@ -292,10 +336,11 @@ while (0) #if FATAL_DISABLED #define FATAL(...) ((void)0) #else -#define FATAL(...) do { \ +#define FATAL(...) \ + do { \ WTFReportFatalError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, __VA_ARGS__); \ - CRASH(); \ -} while (0) + CRASH(); \ + } while (0) #endif /* WTF_LOG_ERROR */ @@ -303,7 +348,8 @@ while (0) #if ERROR_DISABLED #define WTF_LOG_ERROR(...) ((void)0) #else -#define WTF_LOG_ERROR(...) WTFReportError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, __VA_ARGS__) +#define WTF_LOG_ERROR(...) \ + WTFReportError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, __VA_ARGS__) #endif /* WTF_LOG */ @@ -311,9 +357,12 @@ while (0) #if LOG_DISABLED #define WTF_LOG(channel, ...) ((void)0) #else -#define WTF_LOG(channel, ...) WTFLog(&JOIN_LOG_CHANNEL_WITH_PREFIX(LOG_CHANNEL_PREFIX, channel), __VA_ARGS__) -#define JOIN_LOG_CHANNEL_WITH_PREFIX(prefix, channel) JOIN_LOG_CHANNEL_WITH_PREFIX_LEVEL_2(prefix, channel) -#define JOIN_LOG_CHANNEL_WITH_PREFIX_LEVEL_2(prefix, channel) prefix ## channel +#define WTF_LOG(channel, ...) \ + WTFLog(&JOIN_LOG_CHANNEL_WITH_PREFIX(LOG_CHANNEL_PREFIX, channel), \ + __VA_ARGS__) +#define JOIN_LOG_CHANNEL_WITH_PREFIX(prefix, channel) \ + JOIN_LOG_CHANNEL_WITH_PREFIX_LEVEL_2(prefix, channel) +#define JOIN_LOG_CHANNEL_WITH_PREFIX_LEVEL_2(prefix, channel) prefix##channel #endif /* UNREACHABLE_FOR_PLATFORM */ @@ -323,9 +372,8 @@ while (0) a function. Hence it uses macro naming convention. */ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wmissing-noreturn" -static inline void UNREACHABLE_FOR_PLATFORM() -{ - ASSERT_NOT_REACHED(); +static inline void UNREACHABLE_FOR_PLATFORM() { + ASSERT_NOT_REACHED(); } #pragma clang diagnostic pop #else @@ -342,57 +390,77 @@ static inline void UNREACHABLE_FOR_PLATFORM() #if ENABLE(ASSERT) #define RELEASE_ASSERT(assertion) ASSERT(assertion) -#define RELEASE_ASSERT_WITH_MESSAGE(assertion, ...) ASSERT_WITH_MESSAGE(assertion, __VA_ARGS__) +#define RELEASE_ASSERT_WITH_MESSAGE(assertion, ...) \ + ASSERT_WITH_MESSAGE(assertion, __VA_ARGS__) #define RELEASE_ASSERT_NOT_REACHED() ASSERT_NOT_REACHED() #else -#define RELEASE_ASSERT(assertion) (UNLIKELY(!(assertion)) ? (IMMEDIATE_CRASH()) : (void)0) +#define RELEASE_ASSERT(assertion) \ + (UNLIKELY(!(assertion)) ? (IMMEDIATE_CRASH()) : (void)0) #define RELEASE_ASSERT_WITH_MESSAGE(assertion, ...) RELEASE_ASSERT(assertion) #define RELEASE_ASSERT_NOT_REACHED() IMMEDIATE_CRASH() #endif /* DEFINE_COMPARISON_OPERATORS_WITH_REFERENCES */ -// Allow equality comparisons of Objects by reference or pointer, interchangeably. -// This can be only used on types whose equality makes no other sense than pointer equality. -#define DEFINE_COMPARISON_OPERATORS_WITH_REFERENCES(thisType) \ - inline bool operator==(const thisType& a, const thisType& b) { return &a == &b; } \ - inline bool operator==(const thisType& a, const thisType* b) { return &a == b; } \ - inline bool operator==(const thisType* a, const thisType& b) { return a == &b; } \ - inline bool operator!=(const thisType& a, const thisType& b) { return !(a == b); } \ - inline bool operator!=(const thisType& a, const thisType* b) { return !(a == b); } \ - inline bool operator!=(const thisType* a, const thisType& b) { return !(a == b); } - -#define DEFINE_COMPARISON_OPERATORS_WITH_REFERENCES_REFCOUNTED(thisType) \ - DEFINE_COMPARISON_OPERATORS_WITH_REFERENCES(thisType) \ - inline bool operator==(const PassRefPtr& a, const thisType& b) { return a.get() == &b; } \ - inline bool operator==(const thisType& a, const PassRefPtr& b) { return &a == b.get(); } \ - inline bool operator!=(const PassRefPtr& a, const thisType& b) { return !(a == b); } \ - inline bool operator!=(const thisType& a, const PassRefPtr& b) { return !(a == b); } +// Allow equality comparisons of Objects by reference or pointer, +// interchangeably. This can be only used on types whose equality makes no other +// sense than pointer equality. +#define DEFINE_COMPARISON_OPERATORS_WITH_REFERENCES(thisType) \ + inline bool operator==(const thisType& a, const thisType& b) { \ + return &a == &b; \ + } \ + inline bool operator==(const thisType& a, const thisType* b) { \ + return &a == b; \ + } \ + inline bool operator==(const thisType* a, const thisType& b) { \ + return a == &b; \ + } \ + inline bool operator!=(const thisType& a, const thisType& b) { \ + return !(a == b); \ + } \ + inline bool operator!=(const thisType& a, const thisType* b) { \ + return !(a == b); \ + } \ + inline bool operator!=(const thisType* a, const thisType& b) { \ + return !(a == b); \ + } + +#define DEFINE_COMPARISON_OPERATORS_WITH_REFERENCES_REFCOUNTED(thisType) \ + DEFINE_COMPARISON_OPERATORS_WITH_REFERENCES(thisType) \ + inline bool operator==(const PassRefPtr& a, const thisType& b) { \ + return a.get() == &b; \ + } \ + inline bool operator==(const thisType& a, const PassRefPtr& b) { \ + return &a == b.get(); \ + } \ + inline bool operator!=(const PassRefPtr& a, const thisType& b) { \ + return !(a == b); \ + } \ + inline bool operator!=(const thisType& a, const PassRefPtr& b) { \ + return !(a == b); \ + } /* DEFINE_TYPE_CASTS */ -#define DEFINE_TYPE_CASTS(thisType, argumentType, argumentName, pointerPredicate, referencePredicate) \ -inline thisType* to##thisType(argumentType* argumentName) \ -{ \ +#define DEFINE_TYPE_CASTS(thisType, argumentType, argumentName, \ + pointerPredicate, referencePredicate) \ + inline thisType* to##thisType(argumentType* argumentName) { \ ASSERT_WITH_SECURITY_IMPLICATION(!argumentName || (pointerPredicate)); \ - return static_cast(argumentName); \ -} \ -inline const thisType* to##thisType(const argumentType* argumentName) \ -{ \ + return static_cast(argumentName); \ + } \ + inline const thisType* to##thisType(const argumentType* argumentName) { \ ASSERT_WITH_SECURITY_IMPLICATION(!argumentName || (pointerPredicate)); \ - return static_cast(argumentName); \ -} \ -inline thisType& to##thisType(argumentType& argumentName) \ -{ \ - ASSERT_WITH_SECURITY_IMPLICATION(referencePredicate); \ - return static_cast(argumentName); \ -} \ -inline const thisType& to##thisType(const argumentType& argumentName) \ -{ \ - ASSERT_WITH_SECURITY_IMPLICATION(referencePredicate); \ - return static_cast(argumentName); \ -} \ -void to##thisType(const thisType*); \ -void to##thisType(const thisType&) + return static_cast(argumentName); \ + } \ + inline thisType& to##thisType(argumentType& argumentName) { \ + ASSERT_WITH_SECURITY_IMPLICATION(referencePredicate); \ + return static_cast(argumentName); \ + } \ + inline const thisType& to##thisType(const argumentType& argumentName) { \ + ASSERT_WITH_SECURITY_IMPLICATION(referencePredicate); \ + return static_cast(argumentName); \ + } \ + void to##thisType(const thisType*); \ + void to##thisType(const thisType&) #endif // SKY_ENGINE_WTF_ASSERTIONS_H_ diff --git a/sky/engine/wtf/Atomics.h b/sky/engine/wtf/Atomics.h index c6cfc7cfb3b75..b105f70ae89d0 100644 --- a/sky/engine/wtf/Atomics.h +++ b/sky/engine/wtf/Atomics.h @@ -42,48 +42,56 @@ namespace WTF { // atomicAdd returns the result of the addition. -ALWAYS_INLINE int atomicAdd(int volatile* addend, int increment) { return __sync_add_and_fetch(addend, increment); } +ALWAYS_INLINE int atomicAdd(int volatile* addend, int increment) { + return __sync_add_and_fetch(addend, increment); +} // atomicSubtract returns the result of the subtraction. -ALWAYS_INLINE int atomicSubtract(int volatile* addend, int decrement) { return __sync_sub_and_fetch(addend, decrement); } +ALWAYS_INLINE int atomicSubtract(int volatile* addend, int decrement) { + return __sync_sub_and_fetch(addend, decrement); +} -ALWAYS_INLINE int atomicIncrement(int volatile* addend) { return atomicAdd(addend, 1); } -ALWAYS_INLINE int atomicDecrement(int volatile* addend) { return atomicSubtract(addend, 1); } +ALWAYS_INLINE int atomicIncrement(int volatile* addend) { + return atomicAdd(addend, 1); +} +ALWAYS_INLINE int atomicDecrement(int volatile* addend) { + return atomicSubtract(addend, 1); +} -ALWAYS_INLINE int64_t atomicIncrement(int64_t volatile* addend) { return __sync_add_and_fetch(addend, 1); } -ALWAYS_INLINE int64_t atomicDecrement(int64_t volatile* addend) { return __sync_sub_and_fetch(addend, 1); } +ALWAYS_INLINE int64_t atomicIncrement(int64_t volatile* addend) { + return __sync_add_and_fetch(addend, 1); +} +ALWAYS_INLINE int64_t atomicDecrement(int64_t volatile* addend) { + return __sync_sub_and_fetch(addend, 1); +} -ALWAYS_INLINE int atomicTestAndSetToOne(int volatile* ptr) -{ - int ret = __sync_lock_test_and_set(ptr, 1); - ASSERT(!ret || ret == 1); - return ret; +ALWAYS_INLINE int atomicTestAndSetToOne(int volatile* ptr) { + int ret = __sync_lock_test_and_set(ptr, 1); + ASSERT(!ret || ret == 1); + return ret; } -ALWAYS_INLINE void atomicSetOneToZero(int volatile* ptr) -{ - ASSERT(*ptr == 1); - __sync_lock_release(ptr); +ALWAYS_INLINE void atomicSetOneToZero(int volatile* ptr) { + ASSERT(*ptr == 1); + __sync_lock_release(ptr); } #if defined(THREAD_SANITIZER) -ALWAYS_INLINE void releaseStore(volatile int* ptr, int value) -{ - __tsan_atomic32_store(ptr, value, __tsan_memory_order_release); +ALWAYS_INLINE void releaseStore(volatile int* ptr, int value) { + __tsan_atomic32_store(ptr, value, __tsan_memory_order_release); } -ALWAYS_INLINE int acquireLoad(volatile const int* ptr) -{ - return __tsan_atomic32_load(ptr, __tsan_memory_order_acquire); +ALWAYS_INLINE int acquireLoad(volatile const int* ptr) { + return __tsan_atomic32_load(ptr, __tsan_memory_order_acquire); } -ALWAYS_INLINE void releaseStore(volatile unsigned* ptr, unsigned value) -{ - __tsan_atomic32_store(reinterpret_cast(ptr), static_cast(value), __tsan_memory_order_release); +ALWAYS_INLINE void releaseStore(volatile unsigned* ptr, unsigned value) { + __tsan_atomic32_store(reinterpret_cast(ptr), + static_cast(value), __tsan_memory_order_release); } -ALWAYS_INLINE unsigned acquireLoad(volatile const unsigned* ptr) -{ - return static_cast(__tsan_atomic32_load(reinterpret_cast(ptr), __tsan_memory_order_acquire)); +ALWAYS_INLINE unsigned acquireLoad(volatile const unsigned* ptr) { + return static_cast(__tsan_atomic32_load( + reinterpret_cast(ptr), __tsan_memory_order_acquire)); } #else @@ -94,11 +102,10 @@ ALWAYS_INLINE unsigned acquireLoad(volatile const unsigned* ptr) // On ARM __sync_synchronize generates dmb which is very expensive on single // core devices which don't actually need it. Avoid the cost by calling into // kuser_memory_barrier helper. -inline void memoryBarrier() -{ - // Note: This is a function call, which is also an implicit compiler barrier. - typedef void (*KernelMemoryBarrierFunc)(); - ((KernelMemoryBarrierFunc)0xffff0fa0)(); +inline void memoryBarrier() { + // Note: This is a function call, which is also an implicit compiler barrier. + typedef void (*KernelMemoryBarrierFunc)(); + ((KernelMemoryBarrierFunc)0xffff0fa0)(); } #define MEMORY_BARRIER() memoryBarrier() #else @@ -106,45 +113,41 @@ inline void memoryBarrier() #define MEMORY_BARRIER() __sync_synchronize() #endif -ALWAYS_INLINE void releaseStore(volatile int* ptr, int value) -{ - MEMORY_BARRIER(); - *ptr = value; +ALWAYS_INLINE void releaseStore(volatile int* ptr, int value) { + MEMORY_BARRIER(); + *ptr = value; } -ALWAYS_INLINE int acquireLoad(volatile const int* ptr) -{ - int value = *ptr; - MEMORY_BARRIER(); - return value; +ALWAYS_INLINE int acquireLoad(volatile const int* ptr) { + int value = *ptr; + MEMORY_BARRIER(); + return value; } -ALWAYS_INLINE void releaseStore(volatile unsigned* ptr, unsigned value) -{ - MEMORY_BARRIER(); - *ptr = value; +ALWAYS_INLINE void releaseStore(volatile unsigned* ptr, unsigned value) { + MEMORY_BARRIER(); + *ptr = value; } -ALWAYS_INLINE unsigned acquireLoad(volatile const unsigned* ptr) -{ - unsigned value = *ptr; - MEMORY_BARRIER(); - return value; +ALWAYS_INLINE unsigned acquireLoad(volatile const unsigned* ptr) { + unsigned value = *ptr; + MEMORY_BARRIER(); + return value; } #undef MEMORY_BARRIER #endif -} // namespace WTF +} // namespace WTF +using WTF::acquireLoad; using WTF::atomicAdd; -using WTF::atomicSubtract; using WTF::atomicDecrement; using WTF::atomicIncrement; -using WTF::atomicTestAndSetToOne; using WTF::atomicSetOneToZero; -using WTF::acquireLoad; +using WTF::atomicSubtract; +using WTF::atomicTestAndSetToOne; using WTF::releaseStore; #endif // SKY_ENGINE_WTF_ATOMICS_H_ diff --git a/sky/engine/wtf/BitwiseOperations.h b/sky/engine/wtf/BitwiseOperations.h index fcd4db322ac14..259d3329ae5fa 100644 --- a/sky/engine/wtf/BitwiseOperations.h +++ b/sky/engine/wtf/BitwiseOperations.h @@ -51,28 +51,30 @@ namespace WTF { // though nascent processor clz instructions have defined behaviour for 0. // We could drop to raw __asm__ to do better, but we'll avoid doing that unless // we see proof that we need to. -ALWAYS_INLINE uint32_t countLeadingZeros32(uint32_t x) -{ - return LIKELY(x) ? __builtin_clz(x) : 32; +ALWAYS_INLINE uint32_t countLeadingZeros32(uint32_t x) { + return LIKELY(x) ? __builtin_clz(x) : 32; } -ALWAYS_INLINE uint64_t countLeadingZeros64(uint64_t x) -{ - return LIKELY(x) ? __builtin_clzll(x) : 64; +ALWAYS_INLINE uint64_t countLeadingZeros64(uint64_t x) { + return LIKELY(x) ? __builtin_clzll(x) : 64; } #endif #if CPU(64BIT) -ALWAYS_INLINE size_t countLeadingZerosSizet(size_t x) { return countLeadingZeros64(x); } +ALWAYS_INLINE size_t countLeadingZerosSizet(size_t x) { + return countLeadingZeros64(x); +} #else -ALWAYS_INLINE size_t countLeadingZerosSizet(size_t x) { return countLeadingZeros32(x); } +ALWAYS_INLINE size_t countLeadingZerosSizet(size_t x) { + return countLeadingZeros32(x); +} #endif -} // namespace WTF +} // namespace WTF #endif // SKY_ENGINE_WTF_BITWISEOPERATIONS_H_ diff --git a/sky/engine/wtf/ByteOrder.h b/sky/engine/wtf/ByteOrder.h index d16485d2da0ae..dbe6242786a6a 100644 --- a/sky/engine/wtf/ByteOrder.h +++ b/sky/engine/wtf/ByteOrder.h @@ -1,32 +1,32 @@ /* -* Copyright (C) 2012 Google Inc. All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are -* met: -* -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above -* copyright notice, this list of conditions and the following disclaimer -* in the documentation and/or other materials provided with the -* distribution. -* * Neither the name of Google Inc. nor the names of its -* contributors may be used to endorse or promote products derived from -* this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ + * Copyright (C) 2012 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ #ifndef SKY_ENGINE_WTF_BYTEORDER_H_ #define SKY_ENGINE_WTF_BYTEORDER_H_ diff --git a/sky/engine/wtf/ByteSwap.h b/sky/engine/wtf/ByteSwap.h index 69cc3b598612a..e598c24b7f4a1 100644 --- a/sky/engine/wtf/ByteSwap.h +++ b/sky/engine/wtf/ByteSwap.h @@ -38,27 +38,41 @@ namespace WTF { -inline uint32_t wswap32(uint32_t x) { return ((x & 0xffff0000) >> 16) | ((x & 0x0000ffff) << 16); } +inline uint32_t wswap32(uint32_t x) { + return ((x & 0xffff0000) >> 16) | ((x & 0x0000ffff) << 16); +} -ALWAYS_INLINE uint64_t bswap64(uint64_t x) { return __builtin_bswap64(x); } -ALWAYS_INLINE uint32_t bswap32(uint32_t x) { return __builtin_bswap32(x); } +ALWAYS_INLINE uint64_t bswap64(uint64_t x) { + return __builtin_bswap64(x); +} +ALWAYS_INLINE uint32_t bswap32(uint32_t x) { + return __builtin_bswap32(x); +} // GCC 4.6 lacks __builtin_bswap16. Newer versions have it but we support 4.6. #if COMPILER(CLANG) -ALWAYS_INLINE uint16_t bswap16(uint16_t x) { return __builtin_bswap16(x); } +ALWAYS_INLINE uint16_t bswap16(uint16_t x) { + return __builtin_bswap16(x); +} #else -inline uint16_t bswap16(uint16_t x) { return ((x & 0xff00) >> 8) | ((x & 0x00ff) << 8); } +inline uint16_t bswap16(uint16_t x) { + return ((x & 0xff00) >> 8) | ((x & 0x00ff) << 8); +} #endif #if CPU(64BIT) -ALWAYS_INLINE size_t bswapuintptrt(size_t x) { return bswap64(x); } +ALWAYS_INLINE size_t bswapuintptrt(size_t x) { + return bswap64(x); +} #else -ALWAYS_INLINE size_t bswapuintptrt(size_t x) { return bswap32(x); } +ALWAYS_INLINE size_t bswapuintptrt(size_t x) { + return bswap32(x); +} #endif -} // namespace WTF +} // namespace WTF #endif // SKY_ENGINE_WTF_BYTESWAP_H_ diff --git a/sky/engine/wtf/CPU.h b/sky/engine/wtf/CPU.h index 99267245bba03..6fcf18799e570 100644 --- a/sky/engine/wtf/CPU.h +++ b/sky/engine/wtf/CPU.h @@ -32,7 +32,8 @@ #include "flutter/sky/engine/wtf/Compiler.h" /* CPU() - the target CPU architecture */ -#define CPU(WTF_FEATURE) (defined WTF_CPU_##WTF_FEATURE && WTF_CPU_##WTF_FEATURE) +#define CPU(WTF_FEATURE) \ + (defined WTF_CPU_##WTF_FEATURE && WTF_CPU_##WTF_FEATURE) /* ==== CPU() - the target CPU architecture ==== */ @@ -40,69 +41,54 @@ /* This defines CPU(32BIT) or CPU(64BIT), as appropriate. */ /* CPU(X86) - i386 / x86 32-bit */ -#if defined(__i386__) \ - || defined(i386) \ - || defined(_M_IX86) \ - || defined(_X86_) \ - || defined(__THW_INTEL) +#if defined(__i386__) || defined(i386) || defined(_M_IX86) || \ + defined(_X86_) || defined(__THW_INTEL) #define WTF_CPU_X86 1 #endif /* CPU(X86_64) - AMD64 / Intel64 / x86_64 64-bit */ -#if defined(__x86_64__) \ - || defined(_M_X64) +#if defined(__x86_64__) || defined(_M_X64) #define WTF_CPU_X86_64 1 #define WTF_CPU_64BIT 1 #endif /* CPU(ARM) - ARM, any version*/ -#define WTF_ARM_ARCH_AT_LEAST(N) (CPU(ARM) && defined(WTF_ARM_ARCH_VERSION) && WTF_ARM_ARCH_VERSION >= N) +#define WTF_ARM_ARCH_AT_LEAST(N) \ + (CPU(ARM) && defined(WTF_ARM_ARCH_VERSION) && WTF_ARM_ARCH_VERSION >= N) -#if defined(arm) \ - || defined(__arm__) \ - || defined(ARM) \ - || defined(_ARM_) +#if defined(arm) || defined(__arm__) || defined(ARM) || defined(_ARM_) #define WTF_CPU_ARM 1 #if defined(__ARMEB__) #define WTF_CPU_BIG_ENDIAN 1 -#elif !defined(__ARM_EABI__) \ - && !defined(__EABI__) \ - && !defined(__VFP_FP__) \ - && !defined(ANDROID) +#elif !defined(__ARM_EABI__) && !defined(__EABI__) && !defined(__VFP_FP__) && \ + !defined(ANDROID) #define WTF_CPU_MIDDLE_ENDIAN 1 #endif /* Set WTF_ARM_ARCH_VERSION */ -#if defined(__ARM_ARCH_4__) \ - || defined(__ARM_ARCH_4T__) \ - || defined(__MARM_ARMV4__) +#if defined(__ARM_ARCH_4__) || defined(__ARM_ARCH_4T__) || \ + defined(__MARM_ARMV4__) #define WTF_ARM_ARCH_VERSION 4 -#elif defined(__ARM_ARCH_5__) \ - || defined(__ARM_ARCH_5T__) \ - || defined(__MARM_ARMV5__) +#elif defined(__ARM_ARCH_5__) || defined(__ARM_ARCH_5T__) || \ + defined(__MARM_ARMV5__) #define WTF_ARM_ARCH_VERSION 5 -#elif defined(__ARM_ARCH_5E__) \ - || defined(__ARM_ARCH_5TE__) \ - || defined(__ARM_ARCH_5TEJ__) +#elif defined(__ARM_ARCH_5E__) || defined(__ARM_ARCH_5TE__) || \ + defined(__ARM_ARCH_5TEJ__) #define WTF_ARM_ARCH_VERSION 5 -#elif defined(__ARM_ARCH_6__) \ - || defined(__ARM_ARCH_6J__) \ - || defined(__ARM_ARCH_6K__) \ - || defined(__ARM_ARCH_6Z__) \ - || defined(__ARM_ARCH_6ZK__) \ - || defined(__ARM_ARCH_6T2__) \ - || defined(__ARMV6__) +#elif defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || \ + defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) || \ + defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) || \ + defined(__ARMV6__) #define WTF_ARM_ARCH_VERSION 6 -#elif defined(__ARM_ARCH_7A__) \ - || defined(__ARM_ARCH_7R__) \ - || defined(__ARM_ARCH_7S__) +#elif defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) || \ + defined(__ARM_ARCH_7S__) #define WTF_ARM_ARCH_VERSION 7 /* MSVC sets _M_ARM */ @@ -114,44 +100,37 @@ #endif /* Set WTF_THUMB_ARCH_VERSION */ -#if defined(__ARM_ARCH_4T__) +#if defined(__ARM_ARCH_4T__) #define WTF_THUMB_ARCH_VERSION 1 -#elif defined(__ARM_ARCH_5T__) \ - || defined(__ARM_ARCH_5TE__) \ - || defined(__ARM_ARCH_5TEJ__) +#elif defined(__ARM_ARCH_5T__) || defined(__ARM_ARCH_5TE__) || \ + defined(__ARM_ARCH_5TEJ__) #define WTF_THUMB_ARCH_VERSION 2 -#elif defined(__ARM_ARCH_6J__) \ - || defined(__ARM_ARCH_6K__) \ - || defined(__ARM_ARCH_6Z__) \ - || defined(__ARM_ARCH_6ZK__) \ - || defined(__ARM_ARCH_6M__) +#elif defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) || \ + defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || \ + defined(__ARM_ARCH_6M__) #define WTF_THUMB_ARCH_VERSION 3 -#elif defined(__ARM_ARCH_6T2__) \ - || defined(__ARM_ARCH_7__) \ - || defined(__ARM_ARCH_7A__) \ - || defined(__ARM_ARCH_7M__) \ - || defined(__ARM_ARCH_7R__) \ - || defined(__ARM_ARCH_7S__) +#elif defined(__ARM_ARCH_6T2__) || defined(__ARM_ARCH_7__) || \ + defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7M__) || \ + defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7S__) #define WTF_THUMB_ARCH_VERSION 4 #else #define WTF_THUMB_ARCH_VERSION 0 #endif - /* CPU(ARM_THUMB2) - Thumb2 instruction set is available */ #if !defined(WTF_CPU_ARM_THUMB2) -# if defined(thumb2) || defined(__thumb2__) \ - || ((defined(__thumb) || defined(__thumb__)) && WTF_THUMB_ARCH_VERSION == 4) -# define WTF_CPU_ARM_THUMB2 1 -# elif WTF_ARM_ARCH_AT_LEAST(4) -# define WTF_CPU_ARM_THUMB2 0 -# else -# error "Unsupported ARM architecture" -# endif +#if defined(thumb2) || defined(__thumb2__) || \ + ((defined(__thumb) || defined(__thumb__)) && WTF_THUMB_ARCH_VERSION == 4) +#define WTF_CPU_ARM_THUMB2 1 +#elif WTF_ARM_ARCH_AT_LEAST(4) +#define WTF_CPU_ARM_THUMB2 0 +#else +#error "Unsupported ARM architecture" +#endif #endif /* !defined(WTF_CPU_ARM_THUMB2) */ #if defined(__ARM_NEON__) && !defined(WTF_CPU_ARM_NEON) diff --git a/sky/engine/wtf/CheckedArithmetic.h b/sky/engine/wtf/CheckedArithmetic.h index ee49abb3970b8..02aa093567ee0 100644 --- a/sky/engine/wtf/CheckedArithmetic.h +++ b/sky/engine/wtf/CheckedArithmetic.h @@ -36,8 +36,8 @@ /* Checked * * This class provides a mechanism to perform overflow-safe integer arithmetic - * without having to manually ensure that you have all the required bounds checks - * directly in your code. + * without having to manually ensure that you have all the required bounds + * checks directly in your code. * * There are two modes of operation: * - The default is Checked, and crashes at the point @@ -67,647 +67,666 @@ namespace WTF { -ENUM_CLASS(CheckedState) -{ - DidOverflow, - DidNotOverflow -} ENUM_CLASS_END(CheckedState); +ENUM_CLASS(CheckedState){DidOverflow, + DidNotOverflow} ENUM_CLASS_END(CheckedState); class CrashOnOverflow { -protected: - NO_RETURN_DUE_TO_CRASH void overflowed() - { - CRASH(); - } + protected: + NO_RETURN_DUE_TO_CRASH void overflowed() { CRASH(); } - void clearOverflow() { } + void clearOverflow() {} -public: - bool hasOverflowed() const { return false; } + public: + bool hasOverflowed() const { return false; } }; class RecordOverflow { -protected: - RecordOverflow() - : m_overflowed(false) - { - } + protected: + RecordOverflow() : m_overflowed(false) {} - void overflowed() - { - m_overflowed = true; - } + void overflowed() { m_overflowed = true; } - void clearOverflow() - { - m_overflowed = false; - } + void clearOverflow() { m_overflowed = false; } -public: - bool hasOverflowed() const { return m_overflowed; } + public: + bool hasOverflowed() const { return m_overflowed; } -private: - unsigned char m_overflowed; + private: + unsigned char m_overflowed; }; -template class Checked; -template struct RemoveChecked; -template struct RemoveChecked >; - -template ::is_signed, bool sourceSigned = std::numeric_limits::is_signed> struct BoundsChecker; -template struct BoundsChecker { - static bool inBounds(Source value) - { - // Same signedness so implicit type conversion will always increase precision - // to widest type - return value <= std::numeric_limits::max(); - } +template +class Checked; +template +struct RemoveChecked; +template +struct RemoveChecked>; + +template ::is_signed, + bool sourceSigned = std::numeric_limits::is_signed> +struct BoundsChecker; +template +struct BoundsChecker { + static bool inBounds(Source value) { + // Same signedness so implicit type conversion will always increase + // precision to widest type + return value <= std::numeric_limits::max(); + } }; -template struct BoundsChecker { - static bool inBounds(Source value) - { - // Same signedness so implicit type conversion will always increase precision - // to widest type - return std::numeric_limits::min() <= value && value <= std::numeric_limits::max(); - } +template +struct BoundsChecker { + static bool inBounds(Source value) { + // Same signedness so implicit type conversion will always increase + // precision to widest type + return std::numeric_limits::min() <= value && + value <= std::numeric_limits::max(); + } }; -template struct BoundsChecker { - static bool inBounds(Source value) - { - // Target is unsigned so any value less than zero is clearly unsafe - if (value < 0) - return false; - // If our (unsigned) Target is the same or greater width we can - // convert value to type Target without losing precision - if (sizeof(Target) >= sizeof(Source)) - return static_cast(value) <= std::numeric_limits::max(); - // The signed Source type has greater precision than the target so - // max(Target) -> Source will widen. - return value <= static_cast(std::numeric_limits::max()); - } +template +struct BoundsChecker { + static bool inBounds(Source value) { + // Target is unsigned so any value less than zero is clearly unsafe + if (value < 0) + return false; + // If our (unsigned) Target is the same or greater width we can + // convert value to type Target without losing precision + if (sizeof(Target) >= sizeof(Source)) + return static_cast(value) <= std::numeric_limits::max(); + // The signed Source type has greater precision than the target so + // max(Target) -> Source will widen. + return value <= static_cast(std::numeric_limits::max()); + } }; -template struct BoundsChecker { - static bool inBounds(Source value) - { - // Signed target with an unsigned source - if (sizeof(Target) <= sizeof(Source)) - return value <= static_cast(std::numeric_limits::max()); - // Target is Wider than Source so we're guaranteed to fit any value in - // unsigned Source - return true; - } +template +struct BoundsChecker { + static bool inBounds(Source value) { + // Signed target with an unsigned source + if (sizeof(Target) <= sizeof(Source)) + return value <= static_cast(std::numeric_limits::max()); + // Target is Wider than Source so we're guaranteed to fit any value in + // unsigned Source + return true; + } }; -template ::value || (sizeof(Target) > sizeof(Source)) > struct BoundsCheckElider; -template struct BoundsCheckElider { - static bool inBounds(Source) { return true; } -}; -template struct BoundsCheckElider : public BoundsChecker { +template ::value || + (sizeof(Target) > sizeof(Source))> +struct BoundsCheckElider; +template +struct BoundsCheckElider { + static bool inBounds(Source) { return true; } }; +template +struct BoundsCheckElider + : public BoundsChecker {}; -template static inline bool isInBounds(Source value) -{ - return BoundsCheckElider::inBounds(value); +template +static inline bool isInBounds(Source value) { + return BoundsCheckElider::inBounds(value); } -template struct RemoveChecked { - typedef T CleanType; - static const CleanType DefaultValue = 0; +template +struct RemoveChecked { + typedef T CleanType; + static const CleanType DefaultValue = 0; }; -template struct RemoveChecked > { - typedef typename RemoveChecked::CleanType CleanType; - static const CleanType DefaultValue = 0; +template +struct RemoveChecked> { + typedef typename RemoveChecked::CleanType CleanType; + static const CleanType DefaultValue = 0; }; -template struct RemoveChecked > { - typedef typename RemoveChecked::CleanType CleanType; - static const CleanType DefaultValue = 0; +template +struct RemoveChecked> { + typedef typename RemoveChecked::CleanType CleanType; + static const CleanType DefaultValue = 0; }; // The ResultBase and SignednessSelector are used to workaround typeof not being // available in MSVC -template sizeof(V)), bool sameSize = (sizeof(U) == sizeof(V))> struct ResultBase; -template struct ResultBase { - typedef U ResultType; +template sizeof(V)), + bool sameSize = (sizeof(U) == sizeof(V))> +struct ResultBase; +template +struct ResultBase { + typedef U ResultType; }; -template struct ResultBase { - typedef V ResultType; +template +struct ResultBase { + typedef V ResultType; }; -template struct ResultBase { - typedef U ResultType; +template +struct ResultBase { + typedef U ResultType; }; -template ::is_signed, bool vIsSigned = std::numeric_limits::is_signed> struct SignednessSelector; -template struct SignednessSelector { - typedef U ResultType; +template ::is_signed, + bool vIsSigned = std::numeric_limits::is_signed> +struct SignednessSelector; +template +struct SignednessSelector { + typedef U ResultType; }; -template struct SignednessSelector { - typedef U ResultType; +template +struct SignednessSelector { + typedef U ResultType; }; -template struct SignednessSelector { - typedef V ResultType; +template +struct SignednessSelector { + typedef V ResultType; }; -template struct SignednessSelector { - typedef U ResultType; +template +struct SignednessSelector { + typedef U ResultType; }; -template struct ResultBase { - typedef typename SignednessSelector::ResultType ResultType; +template +struct ResultBase { + typedef typename SignednessSelector::ResultType ResultType; }; -template struct Result : ResultBase::CleanType, typename RemoveChecked::CleanType> { +template +struct Result : ResultBase::CleanType, + typename RemoveChecked::CleanType> {}; + +template ::ResultType, + bool lhsSigned = std::numeric_limits::is_signed, + bool rhsSigned = std::numeric_limits::is_signed> +struct ArithmeticOperations; + +template +struct ArithmeticOperations { + // LHS and RHS are signed types + + // Helper function + static inline bool signsMatch(LHS lhs, RHS rhs) { return (lhs ^ rhs) >= 0; } + + static inline bool add(LHS lhs, + RHS rhs, + ResultType& result) WARN_UNUSED_RETURN { + if (signsMatch(lhs, rhs)) { + if (lhs >= 0) { + if ((std::numeric_limits::max() - rhs) < lhs) + return false; + } else { + ResultType temp = lhs - std::numeric_limits::min(); + if (rhs < -temp) + return false; + } + } // if the signs do not match this operation can't overflow + result = lhs + rhs; + return true; + } + + static inline bool sub(LHS lhs, + RHS rhs, + ResultType& result) WARN_UNUSED_RETURN { + if (!signsMatch(lhs, rhs)) { + if (lhs >= 0) { + if (lhs > std::numeric_limits::max() + rhs) + return false; + } else { + if (rhs > std::numeric_limits::max() + lhs) + return false; + } + } // if the signs match this operation can't overflow + result = lhs - rhs; + return true; + } + + static inline bool multiply(LHS lhs, + RHS rhs, + ResultType& result) WARN_UNUSED_RETURN { + if (signsMatch(lhs, rhs)) { + if (lhs >= 0) { + if (lhs && (std::numeric_limits::max() / lhs) < rhs) + return false; + } else { + if (static_cast(lhs) == + std::numeric_limits::min() || + static_cast(rhs) == + std::numeric_limits::min()) + return false; + if ((std::numeric_limits::max() / -lhs) < -rhs) + return false; + } + } else { + if (lhs < 0) { + if (rhs && lhs < (std::numeric_limits::min() / rhs)) + return false; + } else { + if (lhs && rhs < (std::numeric_limits::min() / lhs)) + return false; + } + } + result = lhs * rhs; + return true; + } + + static inline bool equals(LHS lhs, RHS rhs) { return lhs == rhs; } }; -template ::ResultType, - bool lhsSigned = std::numeric_limits::is_signed, bool rhsSigned = std::numeric_limits::is_signed> struct ArithmeticOperations; - -template struct ArithmeticOperations { - // LHS and RHS are signed types - - // Helper function - static inline bool signsMatch(LHS lhs, RHS rhs) - { - return (lhs ^ rhs) >= 0; - } - - static inline bool add(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN - { - if (signsMatch(lhs, rhs)) { - if (lhs >= 0) { - if ((std::numeric_limits::max() - rhs) < lhs) - return false; - } else { - ResultType temp = lhs - std::numeric_limits::min(); - if (rhs < -temp) - return false; - } - } // if the signs do not match this operation can't overflow - result = lhs + rhs; - return true; - } - - static inline bool sub(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN - { - if (!signsMatch(lhs, rhs)) { - if (lhs >= 0) { - if (lhs > std::numeric_limits::max() + rhs) - return false; - } else { - if (rhs > std::numeric_limits::max() + lhs) - return false; - } - } // if the signs match this operation can't overflow - result = lhs - rhs; - return true; - } - - static inline bool multiply(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN - { - if (signsMatch(lhs, rhs)) { - if (lhs >= 0) { - if (lhs && (std::numeric_limits::max() / lhs) < rhs) - return false; - } else { - if (static_cast(lhs) == std::numeric_limits::min() || static_cast(rhs) == std::numeric_limits::min()) - return false; - if ((std::numeric_limits::max() / -lhs) < -rhs) - return false; - } - } else { - if (lhs < 0) { - if (rhs && lhs < (std::numeric_limits::min() / rhs)) - return false; - } else { - if (lhs && rhs < (std::numeric_limits::min() / lhs)) - return false; - } - } - result = lhs * rhs; - return true; - } - - static inline bool equals(LHS lhs, RHS rhs) { return lhs == rhs; } - +template +struct ArithmeticOperations { + // LHS and RHS are unsigned types so bounds checks are nice and easy + static inline bool add(LHS lhs, + RHS rhs, + ResultType& result) WARN_UNUSED_RETURN { + ResultType temp = lhs + rhs; + if (temp < lhs) + return false; + result = temp; + return true; + } + + static inline bool sub(LHS lhs, + RHS rhs, + ResultType& result) WARN_UNUSED_RETURN { + ResultType temp = lhs - rhs; + if (temp > lhs) + return false; + result = temp; + return true; + } + + static inline bool multiply(LHS lhs, + RHS rhs, + ResultType& result) WARN_UNUSED_RETURN { + if (!lhs || !rhs) { + result = 0; + return true; + } + if (std::numeric_limits::max() / lhs < rhs) + return false; + result = lhs * rhs; + return true; + } + + static inline bool equals(LHS lhs, RHS rhs) { return lhs == rhs; } }; -template struct ArithmeticOperations { - // LHS and RHS are unsigned types so bounds checks are nice and easy - static inline bool add(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN - { - ResultType temp = lhs + rhs; - if (temp < lhs) - return false; - result = temp; - return true; - } - - static inline bool sub(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN - { - ResultType temp = lhs - rhs; - if (temp > lhs) - return false; - result = temp; - return true; - } - - static inline bool multiply(LHS lhs, RHS rhs, ResultType& result) WARN_UNUSED_RETURN - { - if (!lhs || !rhs) { - result = 0; - return true; - } - if (std::numeric_limits::max() / lhs < rhs) - return false; - result = lhs * rhs; - return true; - } - - static inline bool equals(LHS lhs, RHS rhs) { return lhs == rhs; } - +template +struct ArithmeticOperations { + static inline bool add(int64_t lhs, int64_t rhs, ResultType& result) { + int64_t temp = lhs + rhs; + if (temp < std::numeric_limits::min()) + return false; + if (temp > std::numeric_limits::max()) + return false; + result = static_cast(temp); + return true; + } + + static inline bool sub(int64_t lhs, int64_t rhs, ResultType& result) { + int64_t temp = lhs - rhs; + if (temp < std::numeric_limits::min()) + return false; + if (temp > std::numeric_limits::max()) + return false; + result = static_cast(temp); + return true; + } + + static inline bool multiply(int64_t lhs, int64_t rhs, ResultType& result) { + int64_t temp = lhs * rhs; + if (temp < std::numeric_limits::min()) + return false; + if (temp > std::numeric_limits::max()) + return false; + result = static_cast(temp); + return true; + } + + static inline bool equals(int lhs, unsigned rhs) { + return static_cast(lhs) == static_cast(rhs); + } }; -template struct ArithmeticOperations { - static inline bool add(int64_t lhs, int64_t rhs, ResultType& result) - { - int64_t temp = lhs + rhs; - if (temp < std::numeric_limits::min()) - return false; - if (temp > std::numeric_limits::max()) - return false; - result = static_cast(temp); - return true; - } - - static inline bool sub(int64_t lhs, int64_t rhs, ResultType& result) - { - int64_t temp = lhs - rhs; - if (temp < std::numeric_limits::min()) - return false; - if (temp > std::numeric_limits::max()) - return false; - result = static_cast(temp); - return true; - } - - static inline bool multiply(int64_t lhs, int64_t rhs, ResultType& result) - { - int64_t temp = lhs * rhs; - if (temp < std::numeric_limits::min()) - return false; - if (temp > std::numeric_limits::max()) - return false; - result = static_cast(temp); - return true; - } - - static inline bool equals(int lhs, unsigned rhs) - { - return static_cast(lhs) == static_cast(rhs); - } +template +struct ArithmeticOperations { + static inline bool add(int64_t lhs, int64_t rhs, ResultType& result) { + return ArithmeticOperations::add(rhs, lhs, + result); + } + + static inline bool sub(int64_t lhs, int64_t rhs, ResultType& result) { + return ArithmeticOperations::sub(lhs, rhs, + result); + } + + static inline bool multiply(int64_t lhs, int64_t rhs, ResultType& result) { + return ArithmeticOperations::multiply(rhs, lhs, + result); + } + + static inline bool equals(unsigned lhs, int rhs) { + return ArithmeticOperations::equals(rhs, lhs); + } }; -template struct ArithmeticOperations { - static inline bool add(int64_t lhs, int64_t rhs, ResultType& result) - { - return ArithmeticOperations::add(rhs, lhs, result); - } - - static inline bool sub(int64_t lhs, int64_t rhs, ResultType& result) - { - return ArithmeticOperations::sub(lhs, rhs, result); - } - - static inline bool multiply(int64_t lhs, int64_t rhs, ResultType& result) - { - return ArithmeticOperations::multiply(rhs, lhs, result); - } - - static inline bool equals(unsigned lhs, int rhs) - { - return ArithmeticOperations::equals(rhs, lhs); - } -}; - -template static inline bool safeAdd(U lhs, V rhs, R& result) -{ - return ArithmeticOperations::add(lhs, rhs, result); +template +static inline bool safeAdd(U lhs, V rhs, R& result) { + return ArithmeticOperations::add(lhs, rhs, result); } -template static inline bool safeSub(U lhs, V rhs, R& result) -{ - return ArithmeticOperations::sub(lhs, rhs, result); +template +static inline bool safeSub(U lhs, V rhs, R& result) { + return ArithmeticOperations::sub(lhs, rhs, result); } -template static inline bool safeMultiply(U lhs, V rhs, R& result) -{ - return ArithmeticOperations::multiply(lhs, rhs, result); +template +static inline bool safeMultiply(U lhs, V rhs, R& result) { + return ArithmeticOperations::multiply(lhs, rhs, result); } -template static inline bool safeEquals(U lhs, V rhs) -{ - return ArithmeticOperations::equals(lhs, rhs); +template +static inline bool safeEquals(U lhs, V rhs) { + return ArithmeticOperations::equals(lhs, rhs); } enum ResultOverflowedTag { ResultOverflowed }; -template class Checked : public OverflowHandler { -public: - template friend class Checked; - Checked() - : m_value(0) - { - } - - Checked(ResultOverflowedTag) - : m_value(0) - { - this->overflowed(); - } - - template Checked(U value) - { - if (!isInBounds(value)) - this->overflowed(); - m_value = static_cast(value); - } - - template Checked(const Checked& rhs) - : m_value(rhs.m_value) - { - if (rhs.hasOverflowed()) - this->overflowed(); - } - - template Checked(const Checked& rhs) - : OverflowHandler(rhs) - { - if (!isInBounds(rhs.m_value)) - this->overflowed(); - m_value = static_cast(rhs.m_value); - } - - template Checked(const Checked& rhs) - { - if (rhs.hasOverflowed()) - this->overflowed(); - if (!isInBounds(rhs.m_value)) - this->overflowed(); - m_value = static_cast(rhs.m_value); - } - - const Checked& operator=(Checked rhs) - { - this->clearOverflow(); - if (rhs.hasOverflowed()) - this->overflowed(); - m_value = static_cast(rhs.m_value); - return *this; - } - - template const Checked& operator=(U value) - { - return *this = Checked(value); - } - - template const Checked& operator=(const Checked& rhs) - { - return *this = Checked(rhs); - } - - // prefix - const Checked& operator++() - { - if (m_value == std::numeric_limits::max()) - this->overflowed(); - m_value++; - return *this; - } - - const Checked& operator--() - { - if (m_value == std::numeric_limits::min()) - this->overflowed(); - m_value--; - return *this; - } - - // postfix operators - const Checked operator++(int) - { - if (m_value == std::numeric_limits::max()) - this->overflowed(); - return Checked(m_value++); - } - - const Checked operator--(int) - { - if (m_value == std::numeric_limits::min()) - this->overflowed(); - return Checked(m_value--); - } - - // Boolean operators - bool operator!() const - { - if (this->hasOverflowed()) - CRASH(); - return !m_value; - } - - typedef void* (Checked::*UnspecifiedBoolType); - operator UnspecifiedBoolType*() const - { - if (this->hasOverflowed()) - CRASH(); - return (m_value) ? reinterpret_cast(1) : 0; - } - - // Value accessors. unsafeGet() will crash if there's been an overflow. - T unsafeGet() const - { - if (this->hasOverflowed()) - CRASH(); - return m_value; - } - - inline CheckedState safeGet(T& value) const WARN_UNUSED_RETURN - { - value = m_value; - if (this->hasOverflowed()) - return CheckedState::DidOverflow; - return CheckedState::DidNotOverflow; - } - - // Mutating assignment - template const Checked operator+=(U rhs) - { - if (!safeAdd(m_value, rhs, m_value)) - this->overflowed(); - return *this; - } - - template const Checked operator-=(U rhs) - { - if (!safeSub(m_value, rhs, m_value)) - this->overflowed(); - return *this; - } - - template const Checked operator*=(U rhs) - { - if (!safeMultiply(m_value, rhs, m_value)) - this->overflowed(); - return *this; - } - - const Checked operator*=(double rhs) - { - double result = rhs * m_value; - // Handle +/- infinity and NaN - if (!(std::numeric_limits::min() <= result && std::numeric_limits::max() >= result)) - this->overflowed(); - m_value = (T)result; - return *this; - } - - const Checked operator*=(float rhs) - { - return *this *= (double)rhs; - } - - template const Checked operator+=(Checked rhs) - { - if (rhs.hasOverflowed()) - this->overflowed(); - return *this += rhs.m_value; - } - - template const Checked operator-=(Checked rhs) - { - if (rhs.hasOverflowed()) - this->overflowed(); - return *this -= rhs.m_value; - } - - template const Checked operator*=(Checked rhs) - { - if (rhs.hasOverflowed()) - this->overflowed(); - return *this *= rhs.m_value; - } - - // Equality comparisons - template bool operator==(Checked rhs) - { - return unsafeGet() == rhs.unsafeGet(); - } - - template bool operator==(U rhs) - { - if (this->hasOverflowed()) - this->overflowed(); - return safeEquals(m_value, rhs); - } - - template const Checked operator==(Checked rhs) - { - return unsafeGet() == Checked(rhs.unsafeGet()); - } - - template bool operator!=(U rhs) - { - return !(*this == rhs); - } - -private: - // Disallow implicit conversion of floating point to integer types - Checked(float); - Checked(double); - void operator=(float); - void operator=(double); - void operator+=(float); - void operator+=(double); - void operator-=(float); - void operator-=(double); - T m_value; +template +class Checked : public OverflowHandler { + public: + template + friend class Checked; + Checked() : m_value(0) {} + + Checked(ResultOverflowedTag) : m_value(0) { this->overflowed(); } + + template + Checked(U value) { + if (!isInBounds(value)) + this->overflowed(); + m_value = static_cast(value); + } + + template + Checked(const Checked& rhs) : m_value(rhs.m_value) { + if (rhs.hasOverflowed()) + this->overflowed(); + } + + template + Checked(const Checked& rhs) : OverflowHandler(rhs) { + if (!isInBounds(rhs.m_value)) + this->overflowed(); + m_value = static_cast(rhs.m_value); + } + + template + Checked(const Checked& rhs) { + if (rhs.hasOverflowed()) + this->overflowed(); + if (!isInBounds(rhs.m_value)) + this->overflowed(); + m_value = static_cast(rhs.m_value); + } + + const Checked& operator=(Checked rhs) { + this->clearOverflow(); + if (rhs.hasOverflowed()) + this->overflowed(); + m_value = static_cast(rhs.m_value); + return *this; + } + + template + const Checked& operator=(U value) { + return *this = Checked(value); + } + + template + const Checked& operator=(const Checked& rhs) { + return *this = Checked(rhs); + } + + // prefix + const Checked& operator++() { + if (m_value == std::numeric_limits::max()) + this->overflowed(); + m_value++; + return *this; + } + + const Checked& operator--() { + if (m_value == std::numeric_limits::min()) + this->overflowed(); + m_value--; + return *this; + } + + // postfix operators + const Checked operator++(int) { + if (m_value == std::numeric_limits::max()) + this->overflowed(); + return Checked(m_value++); + } + + const Checked operator--(int) { + if (m_value == std::numeric_limits::min()) + this->overflowed(); + return Checked(m_value--); + } + + // Boolean operators + bool operator!() const { + if (this->hasOverflowed()) + CRASH(); + return !m_value; + } + + typedef void*(Checked::*UnspecifiedBoolType); + operator UnspecifiedBoolType*() const { + if (this->hasOverflowed()) + CRASH(); + return (m_value) ? reinterpret_cast(1) : 0; + } + + // Value accessors. unsafeGet() will crash if there's been an overflow. + T unsafeGet() const { + if (this->hasOverflowed()) + CRASH(); + return m_value; + } + + inline CheckedState safeGet(T& value) const WARN_UNUSED_RETURN { + value = m_value; + if (this->hasOverflowed()) + return CheckedState::DidOverflow; + return CheckedState::DidNotOverflow; + } + + // Mutating assignment + template + const Checked operator+=(U rhs) { + if (!safeAdd(m_value, rhs, m_value)) + this->overflowed(); + return *this; + } + + template + const Checked operator-=(U rhs) { + if (!safeSub(m_value, rhs, m_value)) + this->overflowed(); + return *this; + } + + template + const Checked operator*=(U rhs) { + if (!safeMultiply(m_value, rhs, m_value)) + this->overflowed(); + return *this; + } + + const Checked operator*=(double rhs) { + double result = rhs * m_value; + // Handle +/- infinity and NaN + if (!(std::numeric_limits::min() <= result && + std::numeric_limits::max() >= result)) + this->overflowed(); + m_value = (T)result; + return *this; + } + + const Checked operator*=(float rhs) { return *this *= (double)rhs; } + + template + const Checked operator+=(Checked rhs) { + if (rhs.hasOverflowed()) + this->overflowed(); + return *this += rhs.m_value; + } + + template + const Checked operator-=(Checked rhs) { + if (rhs.hasOverflowed()) + this->overflowed(); + return *this -= rhs.m_value; + } + + template + const Checked operator*=(Checked rhs) { + if (rhs.hasOverflowed()) + this->overflowed(); + return *this *= rhs.m_value; + } + + // Equality comparisons + template + bool operator==(Checked rhs) { + return unsafeGet() == rhs.unsafeGet(); + } + + template + bool operator==(U rhs) { + if (this->hasOverflowed()) + this->overflowed(); + return safeEquals(m_value, rhs); + } + + template + const Checked operator==(Checked rhs) { + return unsafeGet() == Checked(rhs.unsafeGet()); + } + + template + bool operator!=(U rhs) { + return !(*this == rhs); + } + + private: + // Disallow implicit conversion of floating point to integer types + Checked(float); + Checked(double); + void operator=(float); + void operator=(double); + void operator+=(float); + void operator+=(double); + void operator-=(float); + void operator-=(double); + T m_value; }; -template static inline Checked::ResultType, OverflowHandler> operator+(Checked lhs, Checked rhs) -{ - U x = 0; - V y = 0; - bool overflowed = lhs.safeGet(x) == CheckedState::DidOverflow || rhs.safeGet(y) == CheckedState::DidOverflow; - typename Result::ResultType result = 0; - overflowed |= !safeAdd(x, y, result); - if (overflowed) - return ResultOverflowed; - return result; +template +static inline Checked::ResultType, OverflowHandler> +operator+(Checked lhs, Checked rhs) { + U x = 0; + V y = 0; + bool overflowed = lhs.safeGet(x) == CheckedState::DidOverflow || + rhs.safeGet(y) == CheckedState::DidOverflow; + typename Result::ResultType result = 0; + overflowed |= !safeAdd(x, y, result); + if (overflowed) + return ResultOverflowed; + return result; } -template static inline Checked::ResultType, OverflowHandler> operator-(Checked lhs, Checked rhs) -{ - U x = 0; - V y = 0; - bool overflowed = lhs.safeGet(x) == CheckedState::DidOverflow || rhs.safeGet(y) == CheckedState::DidOverflow; - typename Result::ResultType result = 0; - overflowed |= !safeSub(x, y, result); - if (overflowed) - return ResultOverflowed; - return result; +template +static inline Checked::ResultType, OverflowHandler> +operator-(Checked lhs, Checked rhs) { + U x = 0; + V y = 0; + bool overflowed = lhs.safeGet(x) == CheckedState::DidOverflow || + rhs.safeGet(y) == CheckedState::DidOverflow; + typename Result::ResultType result = 0; + overflowed |= !safeSub(x, y, result); + if (overflowed) + return ResultOverflowed; + return result; } -template static inline Checked::ResultType, OverflowHandler> operator*(Checked lhs, Checked rhs) -{ - U x = 0; - V y = 0; - bool overflowed = lhs.safeGet(x) == CheckedState::DidOverflow || rhs.safeGet(y) == CheckedState::DidOverflow; - typename Result::ResultType result = 0; - overflowed |= !safeMultiply(x, y, result); - if (overflowed) - return ResultOverflowed; - return result; +template +static inline Checked::ResultType, OverflowHandler> +operator*(Checked lhs, Checked rhs) { + U x = 0; + V y = 0; + bool overflowed = lhs.safeGet(x) == CheckedState::DidOverflow || + rhs.safeGet(y) == CheckedState::DidOverflow; + typename Result::ResultType result = 0; + overflowed |= !safeMultiply(x, y, result); + if (overflowed) + return ResultOverflowed; + return result; } -template static inline Checked::ResultType, OverflowHandler> operator+(Checked lhs, V rhs) -{ - return lhs + Checked(rhs); +template +static inline Checked::ResultType, OverflowHandler> +operator+(Checked lhs, V rhs) { + return lhs + Checked(rhs); } -template static inline Checked::ResultType, OverflowHandler> operator-(Checked lhs, V rhs) -{ - return lhs - Checked(rhs); +template +static inline Checked::ResultType, OverflowHandler> +operator-(Checked lhs, V rhs) { + return lhs - Checked(rhs); } -template static inline Checked::ResultType, OverflowHandler> operator*(Checked lhs, V rhs) -{ - return lhs * Checked(rhs); +template +static inline Checked::ResultType, OverflowHandler> +operator*(Checked lhs, V rhs) { + return lhs * Checked(rhs); } -template static inline Checked::ResultType, OverflowHandler> operator+(U lhs, Checked rhs) -{ - return Checked(lhs) + rhs; +template +static inline Checked::ResultType, OverflowHandler> +operator+(U lhs, Checked rhs) { + return Checked(lhs) + rhs; } -template static inline Checked::ResultType, OverflowHandler> operator-(U lhs, Checked rhs) -{ - return Checked(lhs) - rhs; +template +static inline Checked::ResultType, OverflowHandler> +operator-(U lhs, Checked rhs) { + return Checked(lhs) - rhs; } -template static inline Checked::ResultType, OverflowHandler> operator*(U lhs, Checked rhs) -{ - return Checked(lhs) * rhs; +template +static inline Checked::ResultType, OverflowHandler> +operator*(U lhs, Checked rhs) { + return Checked(lhs) * rhs; } -} +} // namespace WTF using WTF::Checked; using WTF::CheckedState; diff --git a/sky/engine/wtf/CheckedArithmeticTest.cpp b/sky/engine/wtf/CheckedArithmeticTest.cpp index f20e92460c6c3..e015fd5f4cfbb 100644 --- a/sky/engine/wtf/CheckedArithmeticTest.cpp +++ b/sky/engine/wtf/CheckedArithmeticTest.cpp @@ -23,128 +23,199 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ - #include #include "flutter/sky/engine/wtf/CheckedArithmetic.h" namespace { -#define CheckedArithmeticTest(type, coerceLiteral, MixedSignednessTest) \ - TEST(WTF, Checked_##type) \ - { \ - Checked value; \ - EXPECT_EQ(coerceLiteral(0), value.unsafeGet()); \ - EXPECT_EQ(std::numeric_limits::max(), (value + std::numeric_limits::max()).unsafeGet()); \ - EXPECT_EQ(std::numeric_limits::max(), (std::numeric_limits::max() + value).unsafeGet()); \ - EXPECT_EQ(std::numeric_limits::min(), (value + std::numeric_limits::min()).unsafeGet()); \ - EXPECT_EQ(std::numeric_limits::min(), (std::numeric_limits::min() + value).unsafeGet()); \ - EXPECT_EQ(coerceLiteral(0), (value * coerceLiteral(0)).unsafeGet()); \ - EXPECT_EQ(coerceLiteral(0), (coerceLiteral(0) * value).unsafeGet()); \ - EXPECT_EQ(coerceLiteral(0), (value * value).unsafeGet()); \ - EXPECT_EQ(coerceLiteral(0), (value - coerceLiteral(0)).unsafeGet()); \ - EXPECT_EQ(coerceLiteral(0), (coerceLiteral(0) - value).unsafeGet()); \ - EXPECT_EQ(coerceLiteral(0), (value - value).unsafeGet()); \ - EXPECT_EQ(coerceLiteral(0), (value++).unsafeGet()); \ - EXPECT_EQ(coerceLiteral(1), (value--).unsafeGet()); \ - EXPECT_EQ(coerceLiteral(1), (++value).unsafeGet()); \ - EXPECT_EQ(coerceLiteral(0), (--value).unsafeGet()); \ - EXPECT_EQ(coerceLiteral(10), (value += coerceLiteral(10)).unsafeGet()); \ - EXPECT_EQ(coerceLiteral(10), value.unsafeGet()); \ - EXPECT_EQ(coerceLiteral(100), (value *= coerceLiteral(10)).unsafeGet()); \ - EXPECT_EQ(coerceLiteral(100), value.unsafeGet()); \ - EXPECT_EQ(coerceLiteral(0), (value -= coerceLiteral(100)).unsafeGet()); \ - EXPECT_EQ(coerceLiteral(0), value.unsafeGet()); \ - value = 10; \ - EXPECT_EQ(coerceLiteral(10), value.unsafeGet()); \ - EXPECT_EQ(coerceLiteral(0), (value - coerceLiteral(10)).unsafeGet()); \ - EXPECT_EQ(coerceLiteral(10), value.unsafeGet()); \ - value = std::numeric_limits::min(); \ - EXPECT_EQ(true, (Checked(value - coerceLiteral(1))).hasOverflowed()); \ - EXPECT_EQ(true, !((value--).hasOverflowed())); \ - EXPECT_EQ(true, value.hasOverflowed()); \ - value = std::numeric_limits::max(); \ - EXPECT_EQ(true, !value.hasOverflowed()); \ - EXPECT_EQ(true, (Checked(value + coerceLiteral(1))).hasOverflowed()); \ - EXPECT_EQ(true, !(value++).hasOverflowed()); \ - EXPECT_EQ(true, value.hasOverflowed()); \ - value = std::numeric_limits::max(); \ - EXPECT_EQ(true, (value += coerceLiteral(1)).hasOverflowed()); \ - EXPECT_EQ(true, value.hasOverflowed()); \ - value = 10; \ - type _value = 0; \ - EXPECT_EQ(true, CheckedState::DidNotOverflow == (value * Checked(0)).safeGet(_value)); \ - _value = 0; \ - EXPECT_EQ(true, CheckedState::DidNotOverflow == (Checked(0) * value).safeGet(_value)); \ - _value = 0; \ - EXPECT_EQ(true, CheckedState::DidOverflow == (value * Checked(std::numeric_limits::max())).safeGet(_value)); \ - _value = 0; \ - EXPECT_EQ(true, CheckedState::DidOverflow == (Checked(std::numeric_limits::max()) * value).safeGet(_value)); \ - value = 0; \ - _value = 0; \ - EXPECT_EQ(true, CheckedState::DidNotOverflow == (value * Checked(std::numeric_limits::max())).safeGet(_value)); \ - _value = 0; \ - EXPECT_EQ(true, CheckedState::DidNotOverflow == (Checked(std::numeric_limits::max()) * value).safeGet(_value)); \ - value = 1; \ - _value = 0; \ - EXPECT_EQ(true, CheckedState::DidNotOverflow == (value * Checked(std::numeric_limits::max())).safeGet(_value)); \ - _value = 0; \ - EXPECT_EQ(true, CheckedState::DidNotOverflow == (Checked(std::numeric_limits::max()) * value).safeGet(_value)); \ - _value = 0; \ - value = 0; \ - EXPECT_EQ(true, CheckedState::DidNotOverflow == (value * Checked(std::numeric_limits::max())).safeGet(_value)); \ - _value = 0; \ - EXPECT_EQ(true, CheckedState::DidNotOverflow == (Checked(std::numeric_limits::max()) * (type)0).safeGet(_value)); \ - _value = 0; \ - value = 1; \ - EXPECT_EQ(true, CheckedState::DidNotOverflow == (value * Checked(std::numeric_limits::max())).safeGet(_value)); \ - _value = 0; \ - EXPECT_EQ(true, CheckedState::DidNotOverflow == (Checked(std::numeric_limits::max()) * (type)1).safeGet(_value)); \ - _value = 0; \ - value = 2; \ - EXPECT_EQ(true, CheckedState::DidOverflow == (value * Checked(std::numeric_limits::max())).safeGet(_value)); \ - _value = 0; \ - EXPECT_EQ(true, CheckedState::DidOverflow == (Checked(std::numeric_limits::max()) * (type)2).safeGet(_value)); \ - value = 10; \ - EXPECT_EQ(true, (value * Checked(std::numeric_limits::max())).hasOverflowed()); \ - MixedSignednessTest(EXPECT_EQ(coerceLiteral(0), (value + -10).unsafeGet())); \ - MixedSignednessTest(EXPECT_EQ(0U, (value - 10U).unsafeGet())); \ - MixedSignednessTest(EXPECT_EQ(coerceLiteral(0), (-10 + value).unsafeGet())); \ - MixedSignednessTest(EXPECT_EQ(0U, (10U - value).unsafeGet())); \ - value = std::numeric_limits::min(); \ - MixedSignednessTest(EXPECT_EQ(true, (Checked(value - 1)).hasOverflowed())); \ - MixedSignednessTest(EXPECT_EQ(true, !(value--).hasOverflowed())); \ - MixedSignednessTest(EXPECT_EQ(true, value.hasOverflowed())); \ - value = std::numeric_limits::max(); \ - MixedSignednessTest(EXPECT_EQ(true, !value.hasOverflowed())); \ - MixedSignednessTest(EXPECT_EQ(true, (Checked(value + 1)).hasOverflowed())); \ - MixedSignednessTest(EXPECT_EQ(true, !(value++).hasOverflowed())); \ - MixedSignednessTest(EXPECT_EQ(true, value.hasOverflowed())); \ - value = std::numeric_limits::max(); \ - MixedSignednessTest(EXPECT_EQ(true, (value += 1).hasOverflowed())); \ - MixedSignednessTest(EXPECT_EQ(true, value.hasOverflowed())); \ - value = std::numeric_limits::min(); \ - MixedSignednessTest(EXPECT_EQ(true, (value - 1U).hasOverflowed())); \ - MixedSignednessTest(EXPECT_EQ(true, !(value--).hasOverflowed())); \ - MixedSignednessTest(EXPECT_EQ(true, value.hasOverflowed())); \ - value = std::numeric_limits::max(); \ - MixedSignednessTest(EXPECT_EQ(true, !value.hasOverflowed())); \ - MixedSignednessTest(EXPECT_EQ(true, (Checked(value + 1U)).hasOverflowed())); \ - MixedSignednessTest(EXPECT_EQ(true, !(value++).hasOverflowed())); \ - MixedSignednessTest(EXPECT_EQ(true, value.hasOverflowed())); \ - value = std::numeric_limits::max(); \ - MixedSignednessTest(EXPECT_EQ(true, (value += 1U).hasOverflowed())); \ - MixedSignednessTest(EXPECT_EQ(true, value.hasOverflowed())); \ - } +#define CheckedArithmeticTest(type, coerceLiteral, MixedSignednessTest) \ + TEST(WTF, Checked_##type) { \ + Checked value; \ + EXPECT_EQ(coerceLiteral(0), value.unsafeGet()); \ + EXPECT_EQ(std::numeric_limits::max(), \ + (value + std::numeric_limits::max()).unsafeGet()); \ + EXPECT_EQ(std::numeric_limits::max(), \ + (std::numeric_limits::max() + value).unsafeGet()); \ + EXPECT_EQ(std::numeric_limits::min(), \ + (value + std::numeric_limits::min()).unsafeGet()); \ + EXPECT_EQ(std::numeric_limits::min(), \ + (std::numeric_limits::min() + value).unsafeGet()); \ + EXPECT_EQ(coerceLiteral(0), (value * coerceLiteral(0)).unsafeGet()); \ + EXPECT_EQ(coerceLiteral(0), (coerceLiteral(0) * value).unsafeGet()); \ + EXPECT_EQ(coerceLiteral(0), (value * value).unsafeGet()); \ + EXPECT_EQ(coerceLiteral(0), (value - coerceLiteral(0)).unsafeGet()); \ + EXPECT_EQ(coerceLiteral(0), (coerceLiteral(0) - value).unsafeGet()); \ + EXPECT_EQ(coerceLiteral(0), (value - value).unsafeGet()); \ + EXPECT_EQ(coerceLiteral(0), (value++).unsafeGet()); \ + EXPECT_EQ(coerceLiteral(1), (value--).unsafeGet()); \ + EXPECT_EQ(coerceLiteral(1), (++value).unsafeGet()); \ + EXPECT_EQ(coerceLiteral(0), (--value).unsafeGet()); \ + EXPECT_EQ(coerceLiteral(10), (value += coerceLiteral(10)).unsafeGet()); \ + EXPECT_EQ(coerceLiteral(10), value.unsafeGet()); \ + EXPECT_EQ(coerceLiteral(100), (value *= coerceLiteral(10)).unsafeGet()); \ + EXPECT_EQ(coerceLiteral(100), value.unsafeGet()); \ + EXPECT_EQ(coerceLiteral(0), (value -= coerceLiteral(100)).unsafeGet()); \ + EXPECT_EQ(coerceLiteral(0), value.unsafeGet()); \ + value = 10; \ + EXPECT_EQ(coerceLiteral(10), value.unsafeGet()); \ + EXPECT_EQ(coerceLiteral(0), (value - coerceLiteral(10)).unsafeGet()); \ + EXPECT_EQ(coerceLiteral(10), value.unsafeGet()); \ + value = std::numeric_limits::min(); \ + EXPECT_EQ(true, (Checked(value - coerceLiteral(1))) \ + .hasOverflowed()); \ + EXPECT_EQ(true, !((value--).hasOverflowed())); \ + EXPECT_EQ(true, value.hasOverflowed()); \ + value = std::numeric_limits::max(); \ + EXPECT_EQ(true, !value.hasOverflowed()); \ + EXPECT_EQ(true, (Checked(value + coerceLiteral(1))) \ + .hasOverflowed()); \ + EXPECT_EQ(true, !(value++).hasOverflowed()); \ + EXPECT_EQ(true, value.hasOverflowed()); \ + value = std::numeric_limits::max(); \ + EXPECT_EQ(true, (value += coerceLiteral(1)).hasOverflowed()); \ + EXPECT_EQ(true, value.hasOverflowed()); \ + value = 10; \ + type _value = 0; \ + EXPECT_EQ(true, \ + CheckedState::DidNotOverflow == \ + (value * Checked(0)).safeGet(_value)); \ + _value = 0; \ + EXPECT_EQ(true, \ + CheckedState::DidNotOverflow == \ + (Checked(0) * value).safeGet(_value)); \ + _value = 0; \ + EXPECT_EQ(true, CheckedState::DidOverflow == \ + (value * Checked( \ + std::numeric_limits::max())) \ + .safeGet(_value)); \ + _value = 0; \ + EXPECT_EQ( \ + true, \ + CheckedState::DidOverflow == \ + (Checked(std::numeric_limits::max()) * \ + value) \ + .safeGet(_value)); \ + value = 0; \ + _value = 0; \ + EXPECT_EQ(true, CheckedState::DidNotOverflow == \ + (value * Checked( \ + std::numeric_limits::max())) \ + .safeGet(_value)); \ + _value = 0; \ + EXPECT_EQ( \ + true, \ + CheckedState::DidNotOverflow == \ + (Checked(std::numeric_limits::max()) * \ + value) \ + .safeGet(_value)); \ + value = 1; \ + _value = 0; \ + EXPECT_EQ(true, CheckedState::DidNotOverflow == \ + (value * Checked( \ + std::numeric_limits::max())) \ + .safeGet(_value)); \ + _value = 0; \ + EXPECT_EQ( \ + true, \ + CheckedState::DidNotOverflow == \ + (Checked(std::numeric_limits::max()) * \ + value) \ + .safeGet(_value)); \ + _value = 0; \ + value = 0; \ + EXPECT_EQ(true, CheckedState::DidNotOverflow == \ + (value * Checked( \ + std::numeric_limits::max())) \ + .safeGet(_value)); \ + _value = 0; \ + EXPECT_EQ( \ + true, \ + CheckedState::DidNotOverflow == \ + (Checked(std::numeric_limits::max()) * \ + (type)0) \ + .safeGet(_value)); \ + _value = 0; \ + value = 1; \ + EXPECT_EQ(true, CheckedState::DidNotOverflow == \ + (value * Checked( \ + std::numeric_limits::max())) \ + .safeGet(_value)); \ + _value = 0; \ + EXPECT_EQ( \ + true, \ + CheckedState::DidNotOverflow == \ + (Checked(std::numeric_limits::max()) * \ + (type)1) \ + .safeGet(_value)); \ + _value = 0; \ + value = 2; \ + EXPECT_EQ(true, CheckedState::DidOverflow == \ + (value * Checked( \ + std::numeric_limits::max())) \ + .safeGet(_value)); \ + _value = 0; \ + EXPECT_EQ( \ + true, \ + CheckedState::DidOverflow == \ + (Checked(std::numeric_limits::max()) * \ + (type)2) \ + .safeGet(_value)); \ + value = 10; \ + EXPECT_EQ(true, (value * Checked( \ + std::numeric_limits::max())) \ + .hasOverflowed()); \ + MixedSignednessTest( \ + EXPECT_EQ(coerceLiteral(0), (value + -10).unsafeGet())); \ + MixedSignednessTest(EXPECT_EQ(0U, (value - 10U).unsafeGet())); \ + MixedSignednessTest( \ + EXPECT_EQ(coerceLiteral(0), (-10 + value).unsafeGet())); \ + MixedSignednessTest(EXPECT_EQ(0U, (10U - value).unsafeGet())); \ + value = std::numeric_limits::min(); \ + MixedSignednessTest(EXPECT_EQ( \ + true, (Checked(value - 1)).hasOverflowed())); \ + MixedSignednessTest(EXPECT_EQ(true, !(value--).hasOverflowed())); \ + MixedSignednessTest(EXPECT_EQ(true, value.hasOverflowed())); \ + value = std::numeric_limits::max(); \ + MixedSignednessTest(EXPECT_EQ(true, !value.hasOverflowed())); \ + MixedSignednessTest(EXPECT_EQ( \ + true, (Checked(value + 1)).hasOverflowed())); \ + MixedSignednessTest(EXPECT_EQ(true, !(value++).hasOverflowed())); \ + MixedSignednessTest(EXPECT_EQ(true, value.hasOverflowed())); \ + value = std::numeric_limits::max(); \ + MixedSignednessTest(EXPECT_EQ(true, (value += 1).hasOverflowed())); \ + MixedSignednessTest(EXPECT_EQ(true, value.hasOverflowed())); \ + value = std::numeric_limits::min(); \ + MixedSignednessTest(EXPECT_EQ(true, (value - 1U).hasOverflowed())); \ + MixedSignednessTest(EXPECT_EQ(true, !(value--).hasOverflowed())); \ + MixedSignednessTest(EXPECT_EQ(true, value.hasOverflowed())); \ + value = std::numeric_limits::max(); \ + MixedSignednessTest(EXPECT_EQ(true, !value.hasOverflowed())); \ + MixedSignednessTest(EXPECT_EQ( \ + true, (Checked(value + 1U)).hasOverflowed())); \ + MixedSignednessTest(EXPECT_EQ(true, !(value++).hasOverflowed())); \ + MixedSignednessTest(EXPECT_EQ(true, value.hasOverflowed())); \ + value = std::numeric_limits::max(); \ + MixedSignednessTest(EXPECT_EQ(true, (value += 1U).hasOverflowed())); \ + MixedSignednessTest(EXPECT_EQ(true, value.hasOverflowed())); \ + } #define CoerceLiteralToUnsigned(x) x##U #define CoerceLiteralNop(x) x #define AllowMixedSignednessTest(x) x #define IgnoreMixedSignednessTest(x) CheckedArithmeticTest(int8_t, CoerceLiteralNop, IgnoreMixedSignednessTest) -CheckedArithmeticTest(int16_t, CoerceLiteralNop, IgnoreMixedSignednessTest) -CheckedArithmeticTest(int32_t, CoerceLiteralNop, AllowMixedSignednessTest) -CheckedArithmeticTest(uint32_t, CoerceLiteralToUnsigned, AllowMixedSignednessTest) -CheckedArithmeticTest(int64_t, CoerceLiteralNop, IgnoreMixedSignednessTest) -CheckedArithmeticTest(uint64_t, CoerceLiteralToUnsigned, IgnoreMixedSignednessTest) + CheckedArithmeticTest(int16_t, CoerceLiteralNop, IgnoreMixedSignednessTest) + CheckedArithmeticTest(int32_t, + CoerceLiteralNop, + AllowMixedSignednessTest) + CheckedArithmeticTest(uint32_t, + CoerceLiteralToUnsigned, + AllowMixedSignednessTest) + CheckedArithmeticTest(int64_t, + CoerceLiteralNop, + IgnoreMixedSignednessTest) + CheckedArithmeticTest(uint64_t, + CoerceLiteralToUnsigned, + IgnoreMixedSignednessTest) -} // namespace +} // namespace diff --git a/sky/engine/wtf/Compiler.h b/sky/engine/wtf/Compiler.h index ef673af7a67e5..539ad600c7d3d 100644 --- a/sky/engine/wtf/Compiler.h +++ b/sky/engine/wtf/Compiler.h @@ -27,7 +27,8 @@ #define SKY_ENGINE_WTF_COMPILER_H_ /* COMPILER() - the compiler being used to build the project */ -#define COMPILER(WTF_FEATURE) (defined WTF_COMPILER_##WTF_FEATURE && WTF_COMPILER_##WTF_FEATURE) +#define COMPILER(WTF_FEATURE) \ + (defined WTF_COMPILER_##WTF_FEATURE && WTF_COMPILER_##WTF_FEATURE) /* ==== COMPILER() - the compiler being used to build the project ==== */ @@ -45,16 +46,18 @@ /* COMPILER(GCC) - GNU Compiler Collection */ #if defined(__GNUC__) #define WTF_COMPILER_GCC 1 -#define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) -#define GCC_VERSION_AT_LEAST(major, minor, patch) (GCC_VERSION >= (major * 10000 + minor * 100 + patch)) +#define GCC_VERSION \ + (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) +#define GCC_VERSION_AT_LEAST(major, minor, patch) \ + (GCC_VERSION >= (major * 10000 + minor * 100 + patch)) #else -/* Define this for !GCC compilers, just so we can write things like GCC_VERSION_AT_LEAST(4, 1, 0). */ +/* Define this for !GCC compilers, just so we can write things like + * GCC_VERSION_AT_LEAST(4, 1, 0). */ #define GCC_VERSION_AT_LEAST(major, minor, patch) 0 #endif /* ==== Compiler features ==== */ - /* ALWAYS_INLINE */ #ifndef ALWAYS_INLINE @@ -65,7 +68,6 @@ #endif #endif - /* NEVER_INLINE */ #ifndef NEVER_INLINE @@ -76,7 +78,6 @@ #endif #endif - /* UNLIKELY */ #ifndef UNLIKELY @@ -87,7 +88,6 @@ #endif #endif - /* LIKELY */ #ifndef LIKELY @@ -98,10 +98,8 @@ #endif #endif - /* NO_RETURN */ - #ifndef NO_RETURN #if COMPILER(GCC) #define NO_RETURN __attribute((__noreturn__)) @@ -110,11 +108,10 @@ #endif #endif - /* WARN_UNUSED_RETURN */ #if COMPILER(GCC) -#define WARN_UNUSED_RETURN __attribute__ ((warn_unused_result)) +#define WARN_UNUSED_RETURN __attribute__((warn_unused_result)) #else #define WARN_UNUSED_RETURN #endif @@ -127,7 +124,6 @@ #define ALLOW_UNUSED #endif - /* REFERENCED_FROM_ASM */ #ifndef REFERENCED_FROM_ASM diff --git a/sky/engine/wtf/DataLog.cpp b/sky/engine/wtf/DataLog.cpp index 9747cc57f38bd..867bfb99a28b0 100644 --- a/sky/engine/wtf/DataLog.cpp +++ b/sky/engine/wtf/DataLog.cpp @@ -32,10 +32,12 @@ #define DATA_LOG_TO_FILE 0 -// Uncomment to force logging to the given file regardless of what the environment variable says. Note that -// we will append "..txt" where is the PID. +// Uncomment to force logging to the given file regardless of what the +// environment variable says. Note that we will append "..txt" where +// is the PID. -// This path won't work on Windows, make sure to change to something like C:\\Users\\\\log.txt. +// This path won't work on Windows, make sure to change to something like +// C:\\Users\\\\log.txt. #define DATA_LOG_FILENAME "/tmp/WTFLog" namespace WTF { @@ -46,63 +48,60 @@ static pthread_once_t initializeLogFileOnceKey = PTHREAD_ONCE_INIT; static FilePrintStream* file; -static void initializeLogFileOnce() -{ +static void initializeLogFileOnce() { #if DATA_LOG_TO_FILE #ifdef DATA_LOG_FILENAME - const char* filename = DATA_LOG_FILENAME; + const char* filename = DATA_LOG_FILENAME; #else - const char* filename = getenv("WTF_DATA_LOG_FILENAME"); + const char* filename = getenv("WTF_DATA_LOG_FILENAME"); #endif - char actualFilename[1024]; + char actualFilename[1024]; - snprintf(actualFilename, sizeof(actualFilename), "%s.%d.txt", filename, getpid()); + snprintf(actualFilename, sizeof(actualFilename), "%s.%d.txt", filename, + getpid()); - if (filename) { - file = FilePrintStream::open(actualFilename, "w").leakPtr(); - if (!file) - fprintf(stderr, "Warning: Could not open log file %s for writing.\n", actualFilename); - } -#endif // DATA_LOG_TO_FILE + if (filename) { + file = FilePrintStream::open(actualFilename, "w").leakPtr(); if (!file) - file = new FilePrintStream(stderr, FilePrintStream::Borrow); - - setvbuf(file->file(), 0, _IONBF, 0); // Prefer unbuffered output, so that we get a full log upon crash or deadlock. + fprintf(stderr, "Warning: Could not open log file %s for writing.\n", + actualFilename); + } +#endif // DATA_LOG_TO_FILE + if (!file) + file = new FilePrintStream(stderr, FilePrintStream::Borrow); + + setvbuf(file->file(), 0, _IONBF, 0); // Prefer unbuffered output, so that we + // get a full log upon crash or + // deadlock. } -static void initializeLogFile() -{ +static void initializeLogFile() { #if USE(PTHREADS) - pthread_once(&initializeLogFileOnceKey, initializeLogFileOnce); + pthread_once(&initializeLogFileOnceKey, initializeLogFileOnce); #else - if (!file) - initializeLogFileOnce(); + if (!file) + initializeLogFileOnce(); #endif } -FilePrintStream& dataFile() -{ - initializeLogFile(); - return *file; +FilePrintStream& dataFile() { + initializeLogFile(); + return *file; } -void dataLogFV(const char* format, va_list argList) -{ - dataFile().vprintf(format, argList); +void dataLogFV(const char* format, va_list argList) { + dataFile().vprintf(format, argList); } -void dataLogF(const char* format, ...) -{ - va_list argList; - va_start(argList, format); - dataLogFV(format, argList); - va_end(argList); +void dataLogF(const char* format, ...) { + va_list argList; + va_start(argList, format); + dataLogFV(format, argList); + va_end(argList); } -void dataLogFString(const char* str) -{ - dataFile().printf("%s", str); +void dataLogFString(const char* str) { + dataFile().printf("%s", str); } -} // namespace WTF - +} // namespace WTF diff --git a/sky/engine/wtf/DataLog.h b/sky/engine/wtf/DataLog.h index 2b9024f759537..aa056cc78b228 100644 --- a/sky/engine/wtf/DataLog.h +++ b/sky/engine/wtf/DataLog.h @@ -40,89 +40,224 @@ void dataLogFV(const char* format, va_list) WTF_ATTRIBUTE_PRINTF(1, 0); void dataLogF(const char* format, ...) WTF_ATTRIBUTE_PRINTF(1, 2); void dataLogFString(const char*); -template -void dataLog(const T& value) -{ - dataFile().print(value); +template +void dataLog(const T& value) { + dataFile().print(value); } -template -void dataLog(const T1& value1, const T2& value2) -{ - dataFile().print(value1, value2); +template +void dataLog(const T1& value1, const T2& value2) { + dataFile().print(value1, value2); } -template -void dataLog(const T1& value1, const T2& value2, const T3& value3) -{ - dataFile().print(value1, value2, value3); +template +void dataLog(const T1& value1, const T2& value2, const T3& value3) { + dataFile().print(value1, value2, value3); } -template -void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4) -{ - dataFile().print(value1, value2, value3, value4); +template +void dataLog(const T1& value1, + const T2& value2, + const T3& value3, + const T4& value4) { + dataFile().print(value1, value2, value3, value4); } -template -void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5) -{ - dataFile().print(value1, value2, value3, value4, value5); +template +void dataLog(const T1& value1, + const T2& value2, + const T3& value3, + const T4& value4, + const T5& value5) { + dataFile().print(value1, value2, value3, value4, value5); } -template -void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6) -{ - dataFile().print(value1, value2, value3, value4, value5, value6); +template +void dataLog(const T1& value1, + const T2& value2, + const T3& value3, + const T4& value4, + const T5& value5, + const T6& value6) { + dataFile().print(value1, value2, value3, value4, value5, value6); } -template -void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7) -{ - dataFile().print(value1, value2, value3, value4, value5, value6, value7); +template +void dataLog(const T1& value1, + const T2& value2, + const T3& value3, + const T4& value4, + const T5& value5, + const T6& value6, + const T7& value7) { + dataFile().print(value1, value2, value3, value4, value5, value6, value7); } -template -void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8) -{ - dataFile().print(value1, value2, value3, value4, value5, value6, value7, value8); +template +void dataLog(const T1& value1, + const T2& value2, + const T3& value3, + const T4& value4, + const T5& value5, + const T6& value6, + const T7& value7, + const T8& value8) { + dataFile().print(value1, value2, value3, value4, value5, value6, value7, + value8); } -template -void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9) -{ - dataFile().print(value1, value2, value3, value4, value5, value6, value7, value8, value9); +template +void dataLog(const T1& value1, + const T2& value2, + const T3& value3, + const T4& value4, + const T5& value5, + const T6& value6, + const T7& value7, + const T8& value8, + const T9& value9) { + dataFile().print(value1, value2, value3, value4, value5, value6, value7, + value8, value9); } -template -void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9, const T10& value10) -{ - dataFile().print(value1, value2, value3, value4, value5, value6, value7, value8, value9, value10); +template +void dataLog(const T1& value1, + const T2& value2, + const T3& value3, + const T4& value4, + const T5& value5, + const T6& value6, + const T7& value7, + const T8& value8, + const T9& value9, + const T10& value10) { + dataFile().print(value1, value2, value3, value4, value5, value6, value7, + value8, value9, value10); } -template -void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9, const T10& value10, const T11& value11) -{ - dataFile().print(value1, value2, value3, value4, value5, value6, value7, value8, value9, value10, value11); +template +void dataLog(const T1& value1, + const T2& value2, + const T3& value3, + const T4& value4, + const T5& value5, + const T6& value6, + const T7& value7, + const T8& value8, + const T9& value9, + const T10& value10, + const T11& value11) { + dataFile().print(value1, value2, value3, value4, value5, value6, value7, + value8, value9, value10, value11); } -template -void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9, const T10& value10, const T11& value11, const T12& value12) -{ - dataFile().print(value1, value2, value3, value4, value5, value6, value7, value8, value9, value10, value11, value12); +template +void dataLog(const T1& value1, + const T2& value2, + const T3& value3, + const T4& value4, + const T5& value5, + const T6& value6, + const T7& value7, + const T8& value8, + const T9& value9, + const T10& value10, + const T11& value11, + const T12& value12) { + dataFile().print(value1, value2, value3, value4, value5, value6, value7, + value8, value9, value10, value11, value12); } -template -void dataLog(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9, const T10& value10, const T11& value11, const T12& value12, const T13& value13) -{ - dataFile().print(value1, value2, value3, value4, value5, value6, value7, value8, value9, value10, value11, value12, value13); +template +void dataLog(const T1& value1, + const T2& value2, + const T3& value3, + const T4& value4, + const T5& value5, + const T6& value6, + const T7& value7, + const T8& value8, + const T9& value9, + const T10& value10, + const T11& value11, + const T12& value12, + const T13& value13) { + dataFile().print(value1, value2, value3, value4, value5, value6, value7, + value8, value9, value10, value11, value12, value13); } -} // namespace WTF +} // namespace WTF using WTF::dataLog; using WTF::dataLogF; using WTF::dataLogFString; #endif // SKY_ENGINE_WTF_DATALOG_H_ - diff --git a/sky/engine/wtf/DefaultAllocator.cpp b/sky/engine/wtf/DefaultAllocator.cpp index 96d74f8513dc4..313887f90d49a 100644 --- a/sky/engine/wtf/DefaultAllocator.cpp +++ b/sky/engine/wtf/DefaultAllocator.cpp @@ -34,14 +34,12 @@ namespace WTF { -void* DefaultAllocator::backingAllocate(size_t size) -{ - return partitionAllocGeneric(Partitions::getBufferPartition(), size); +void* DefaultAllocator::backingAllocate(size_t size) { + return partitionAllocGeneric(Partitions::getBufferPartition(), size); } -void DefaultAllocator::backingFree(void* address) -{ - partitionFreeGeneric(Partitions::getBufferPartition(), address); +void DefaultAllocator::backingFree(void* address) { + partitionFreeGeneric(Partitions::getBufferPartition(), address); } -} // namespace WTF +} // namespace WTF diff --git a/sky/engine/wtf/DefaultAllocator.h b/sky/engine/wtf/DefaultAllocator.h index 9398bfdd2a9ee..6593da24e7036 100644 --- a/sky/engine/wtf/DefaultAllocator.h +++ b/sky/engine/wtf/DefaultAllocator.h @@ -47,144 +47,121 @@ namespace WTF { class DefaultAllocatorDummyVisitor; class DefaultAllocatorQuantizer { -public: - template - static size_t quantizedSize(size_t count) - { - RELEASE_ASSERT(count <= kMaxUnquantizedAllocation / sizeof(T)); - return partitionAllocActualSize(Partitions::getBufferPartition(), count * sizeof(T)); - } - static const size_t kMaxUnquantizedAllocation = kGenericMaxDirectMapped; + public: + template + static size_t quantizedSize(size_t count) { + RELEASE_ASSERT(count <= kMaxUnquantizedAllocation / sizeof(T)); + return partitionAllocActualSize(Partitions::getBufferPartition(), + count * sizeof(T)); + } + static const size_t kMaxUnquantizedAllocation = kGenericMaxDirectMapped; }; class DefaultAllocator { -public: - typedef DefaultAllocatorQuantizer Quantizer; - static const bool isGarbageCollected = false; - template - struct VectorBackingHelper { - typedef void Type; - }; - template - struct HashTableBackingHelper { - typedef void Type; - }; - template - static Return backingMalloc(size_t size) - { - return reinterpret_cast(backingAllocate(size)); - } - template - static Return zeroedBackingMalloc(size_t size) - { - void* result = backingAllocate(size); - memset(result, 0, size); - return reinterpret_cast(result); - } - template - static Return malloc(size_t size) - { - return reinterpret_cast(fastMalloc(size)); - } - WTF_EXPORT static void backingFree(void* address); - static void free(void* address) - { - fastFree(address); - } - template - static void* newArray(size_t bytes) - { - return malloc(bytes); - } - static void - deleteArray(void* ptr) - { - free(ptr); // Not the system free, the one from this class. - } - - static bool isAllocationAllowed() { return true; } - - static void markNoTracing(...) - { - ASSERT_NOT_REACHED(); - } - - static void registerDelayedMarkNoTracing(...) - { - ASSERT_NOT_REACHED(); - } - - static void registerWeakMembers(...) - { - ASSERT_NOT_REACHED(); - } - - static void registerWeakTable(...) - { - ASSERT_NOT_REACHED(); - } + public: + typedef DefaultAllocatorQuantizer Quantizer; + static const bool isGarbageCollected = false; + template + struct VectorBackingHelper { + typedef void Type; + }; + template + struct HashTableBackingHelper { + typedef void Type; + }; + template + static Return backingMalloc(size_t size) { + return reinterpret_cast(backingAllocate(size)); + } + template + static Return zeroedBackingMalloc(size_t size) { + void* result = backingAllocate(size); + memset(result, 0, size); + return reinterpret_cast(result); + } + template + static Return malloc(size_t size) { + return reinterpret_cast(fastMalloc(size)); + } + WTF_EXPORT static void backingFree(void* address); + static void free(void* address) { fastFree(address); } + template + static void* newArray(size_t bytes) { + return malloc(bytes); + } + static void deleteArray(void* ptr) { + free(ptr); // Not the system free, the one from this class. + } + + static bool isAllocationAllowed() { return true; } + + static void markNoTracing(...) { ASSERT_NOT_REACHED(); } + + static void registerDelayedMarkNoTracing(...) { ASSERT_NOT_REACHED(); } + + static void registerWeakMembers(...) { ASSERT_NOT_REACHED(); } + + static void registerWeakTable(...) { ASSERT_NOT_REACHED(); } #if ENABLE(ASSERT) - static bool weakTableRegistered(...) - { - ASSERT_NOT_REACHED(); - return false; - } + static bool weakTableRegistered(...) { + ASSERT_NOT_REACHED(); + return false; + } #endif - template - static void trace(...) - { - ASSERT_NOT_REACHED(); - } + template + static void trace(...) { + ASSERT_NOT_REACHED(); + } - template - struct OtherType { - typedef T* Type; - }; + template + struct OtherType { + typedef T* Type; + }; - template - static T& getOther(T* other) - { - return *other; - } + template + static T& getOther(T* other) { + return *other; + } - static void enterNoAllocationScope() { } - static void leaveNoAllocationScope() { } + static void enterNoAllocationScope() {} + static void leaveNoAllocationScope() {} -private: - WTF_EXPORT static void* backingAllocate(size_t); + private: + WTF_EXPORT static void* backingAllocate(size_t); }; // The Windows compiler seems to be very eager to instantiate things it won't // need, so unless we have this class we get compile errors. class DefaultAllocatorDummyVisitor { -public: - template inline bool isAlive(T obj) - { - ASSERT_NOT_REACHED(); - return false; - } + public: + template + inline bool isAlive(T obj) { + ASSERT_NOT_REACHED(); + return false; + } }; -} // namespace WTF - -#define WTF_USE_ALLOCATOR(ClassName, Allocator) \ -public: \ - void* operator new(size_t size) \ - { \ - return Allocator::template malloc(size); \ - } \ - void operator delete(void* p) { Allocator::free(p); } \ - void* operator new[](size_t size) { return Allocator::template newArray(size); } \ - void operator delete[](void* p) { Allocator::deleteArray(p); } \ - void* operator new(size_t, NotNullTag, void* location) \ - { \ - ASSERT(location); \ - return location; \ - } \ -private: \ -typedef int __thisIsHereToForceASemicolonAfterThisMacro +} // namespace WTF + +#define WTF_USE_ALLOCATOR(ClassName, Allocator) \ + public: \ + void* operator new(size_t size) { \ + return Allocator::template malloc(size); \ + } \ + void operator delete(void* p) { Allocator::free(p); } \ + void* operator new[](size_t size) { \ + return Allocator::template newArray(size); \ + } \ + void operator delete[](void* p) { Allocator::deleteArray(p); } \ + void* operator new(size_t, NotNullTag, void* location) { \ + ASSERT(location); \ + return location; \ + } \ + \ + private: \ + typedef int __thisIsHereToForceASemicolonAfterThisMacro using WTF::DefaultAllocator; diff --git a/sky/engine/wtf/Deque.h b/sky/engine/wtf/Deque.h index 499934411f6ba..5b85864122bc9 100644 --- a/sky/engine/wtf/Deque.h +++ b/sky/engine/wtf/Deque.h @@ -38,479 +38,532 @@ #include "flutter/sky/engine/wtf/Vector.h" namespace WTF { - template class DequeIteratorBase; - template class DequeIterator; - template class DequeConstIterator; - - template - class Deque : public VectorDestructorBase, T, (inlineCapacity > 0), Allocator::isGarbageCollected> { - WTF_USE_ALLOCATOR(Deque, Allocator); - public: - typedef DequeIterator iterator; - typedef DequeConstIterator const_iterator; - typedef std::reverse_iterator reverse_iterator; - typedef std::reverse_iterator const_reverse_iterator; - typedef PassTraits Pass; - typedef typename PassTraits::PassType PassType; - - Deque(); - Deque(const Deque&); - // FIXME: Doesn't work if there is an inline buffer, due to crbug.com/360572 - Deque& operator=(const Deque&); - - void finalize(); - - // We hard wire the inlineCapacity to zero here, due to crbug.com/360572 - void swap(Deque&); - - size_t size() const { return m_start <= m_end ? m_end - m_start : m_end + m_buffer.capacity() - m_start; } - bool isEmpty() const { return m_start == m_end; } - - iterator begin() { return iterator(this, m_start); } - iterator end() { return iterator(this, m_end); } - const_iterator begin() const { return const_iterator(this, m_start); } - const_iterator end() const { return const_iterator(this, m_end); } - reverse_iterator rbegin() { return reverse_iterator(end()); } - reverse_iterator rend() { return reverse_iterator(begin()); } - const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } - const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } - - T& first() { ASSERT(m_start != m_end); return m_buffer.buffer()[m_start]; } - const T& first() const { ASSERT(m_start != m_end); return m_buffer.buffer()[m_start]; } - PassType takeFirst(); - - T& last() { ASSERT(m_start != m_end); return *(--end()); } - const T& last() const { ASSERT(m_start != m_end); return *(--end()); } - PassType takeLast(); - - T& at(size_t i) - { - RELEASE_ASSERT(i < size()); - size_t right = m_buffer.capacity() - m_start; - return i < right ? m_buffer.buffer()[m_start + i] : m_buffer.buffer()[i - right]; - } - const T& at(size_t i) const - { - RELEASE_ASSERT(i < size()); - size_t right = m_buffer.capacity() - m_start; - return i < right ? m_buffer.buffer()[m_start + i] : m_buffer.buffer()[i - right]; - } - - T& operator[](size_t i) { return at(i); } - const T& operator[](size_t i) const { return at(i); } - - template void append(const U&); - template void prepend(const U&); - void removeFirst(); - void removeLast(); - void remove(iterator&); - void remove(const_iterator&); - - void clear(); - - template - iterator findIf(Predicate&); - - private: - friend class DequeIteratorBase; - - typedef VectorBuffer Buffer; - typedef VectorTypeOperations TypeOperations; - typedef DequeIteratorBase IteratorBase; - - void remove(size_t position); - void destroyAll(); - void expandCapacityIfNeeded(); - void expandCapacity(); - - Buffer m_buffer; - unsigned m_start; - unsigned m_end; - }; - - template - class DequeIteratorBase { - protected: - DequeIteratorBase(); - DequeIteratorBase(const Deque*, size_t); - DequeIteratorBase(const DequeIteratorBase&); - DequeIteratorBase& operator=(const DequeIteratorBase&); - ~DequeIteratorBase(); - - void assign(const DequeIteratorBase& other) { *this = other; } - - void increment(); - void decrement(); - - T* before() const; - T* after() const; - - bool isEqual(const DequeIteratorBase&) const; - - private: - Deque* m_deque; - unsigned m_index; - - friend class Deque; - }; - - template - class DequeIterator : public DequeIteratorBase { - private: - typedef DequeIteratorBase Base; - typedef DequeIterator Iterator; - - public: - typedef ptrdiff_t difference_type; - typedef T value_type; - typedef T* pointer; - typedef T& reference; - typedef std::bidirectional_iterator_tag iterator_category; - - DequeIterator(Deque* deque, size_t index) : Base(deque, index) { } - - DequeIterator(const Iterator& other) : Base(other) { } - DequeIterator& operator=(const Iterator& other) { Base::assign(other); return *this; } - - T& operator*() const { return *Base::after(); } - T* operator->() const { return Base::after(); } - - bool operator==(const Iterator& other) const { return Base::isEqual(other); } - bool operator!=(const Iterator& other) const { return !Base::isEqual(other); } - - Iterator& operator++() { Base::increment(); return *this; } - // postfix ++ intentionally omitted - Iterator& operator--() { Base::decrement(); return *this; } - // postfix -- intentionally omitted - }; - - template - class DequeConstIterator : public DequeIteratorBase { - private: - typedef DequeIteratorBase Base; - typedef DequeConstIterator Iterator; - typedef DequeIterator NonConstIterator; - - public: - typedef ptrdiff_t difference_type; - typedef T value_type; - typedef const T* pointer; - typedef const T& reference; - typedef std::bidirectional_iterator_tag iterator_category; - - DequeConstIterator(const Deque* deque, size_t index) : Base(deque, index) { } - - DequeConstIterator(const Iterator& other) : Base(other) { } - DequeConstIterator(const NonConstIterator& other) : Base(other) { } - DequeConstIterator& operator=(const Iterator& other) { Base::assign(other); return *this; } - DequeConstIterator& operator=(const NonConstIterator& other) { Base::assign(other); return *this; } - - const T& operator*() const { return *Base::after(); } - const T* operator->() const { return Base::after(); } - - bool operator==(const Iterator& other) const { return Base::isEqual(other); } - bool operator!=(const Iterator& other) const { return !Base::isEqual(other); } - - Iterator& operator++() { Base::increment(); return *this; } - // postfix ++ intentionally omitted - Iterator& operator--() { Base::decrement(); return *this; } - // postfix -- intentionally omitted - }; - - template - inline Deque::Deque() - : m_start(0) - , m_end(0) - { - } - - template - inline Deque::Deque(const Deque& other) - : m_buffer(other.m_buffer.capacity()) - , m_start(other.m_start) - , m_end(other.m_end) - { - const T* otherBuffer = other.m_buffer.buffer(); - if (m_start <= m_end) - TypeOperations::uninitializedCopy(otherBuffer + m_start, otherBuffer + m_end, m_buffer.buffer() + m_start); - else { - TypeOperations::uninitializedCopy(otherBuffer, otherBuffer + m_end, m_buffer.buffer()); - TypeOperations::uninitializedCopy(otherBuffer + m_start, otherBuffer + m_buffer.capacity(), m_buffer.buffer() + m_start); - } - } - - template - inline Deque& Deque::operator=(const Deque& other) - { - Deque copy(other); - swap(copy); - return *this; - } - - template - inline void Deque::destroyAll() - { - if (m_start <= m_end) { - TypeOperations::destruct(m_buffer.buffer() + m_start, m_buffer.buffer() + m_end); - } else { - TypeOperations::destruct(m_buffer.buffer(), m_buffer.buffer() + m_end); - TypeOperations::destruct(m_buffer.buffer() + m_start, m_buffer.buffer() + m_buffer.capacity()); - } - } - - // Off-GC-heap deques: Destructor should be called. - // On-GC-heap deques: Destructor should be called for inline buffers - // (if any) but destructor shouldn't be called for vector backing since - // it is managed by the traced GC heap. - template - inline void Deque::finalize() - { - if (!inlineCapacity && !m_buffer.buffer()) - return; - if (!isEmpty() && !(Allocator::isGarbageCollected && m_buffer.hasOutOfLineBuffer())) - destroyAll(); - - m_buffer.destruct(); - } - - // FIXME: Doesn't work if there is an inline buffer, due to crbug.com/360572 - template - inline void Deque::swap(Deque& other) - { - std::swap(m_start, other.m_start); - std::swap(m_end, other.m_end); - m_buffer.swapVectorBuffer(other.m_buffer); - } - - template - inline void Deque::clear() - { - destroyAll(); - m_start = 0; - m_end = 0; - m_buffer.deallocateBuffer(m_buffer.buffer()); - m_buffer.resetBufferPointer(); - } - - template - template - inline DequeIterator Deque::findIf(Predicate& predicate) - { - iterator end_iterator = end(); - for (iterator it = begin(); it != end_iterator; ++it) { - if (predicate(*it)) - return it; - } - return end_iterator; - } - - template - inline void Deque::expandCapacityIfNeeded() - { - if (m_start) { - if (m_end + 1 != m_start) - return; - } else if (m_end) { - if (m_end != m_buffer.capacity() - 1) - return; - } else if (m_buffer.capacity()) - return; - - expandCapacity(); - } - - template - void Deque::expandCapacity() - { - size_t oldCapacity = m_buffer.capacity(); - T* oldBuffer = m_buffer.buffer(); - m_buffer.allocateBuffer(std::max(static_cast(16), oldCapacity + oldCapacity / 4 + 1)); - if (m_start <= m_end) - TypeOperations::move(oldBuffer + m_start, oldBuffer + m_end, m_buffer.buffer() + m_start); - else { - TypeOperations::move(oldBuffer, oldBuffer + m_end, m_buffer.buffer()); - size_t newStart = m_buffer.capacity() - (oldCapacity - m_start); - TypeOperations::move(oldBuffer + m_start, oldBuffer + oldCapacity, m_buffer.buffer() + newStart); - m_start = newStart; - } - m_buffer.deallocateBuffer(oldBuffer); - } - - template - inline typename Deque::PassType Deque::takeFirst() - { - T oldFirst = Pass::transfer(first()); - removeFirst(); - return Pass::transfer(oldFirst); - } - - template - inline typename Deque::PassType Deque::takeLast() - { - T oldLast = Pass::transfer(last()); - removeLast(); - return Pass::transfer(oldLast); - } - - template template - inline void Deque::append(const U& value) - { - expandCapacityIfNeeded(); - new (NotNull, &m_buffer.buffer()[m_end]) T(value); - if (m_end == m_buffer.capacity() - 1) - m_end = 0; - else - ++m_end; - } - - template template - inline void Deque::prepend(const U& value) - { - expandCapacityIfNeeded(); - if (!m_start) - m_start = m_buffer.capacity() - 1; - else - --m_start; - new (NotNull, &m_buffer.buffer()[m_start]) T(value); - } - - template - inline void Deque::removeFirst() - { - ASSERT(!isEmpty()); - TypeOperations::destruct(&m_buffer.buffer()[m_start], &m_buffer.buffer()[m_start + 1]); - if (m_start == m_buffer.capacity() - 1) - m_start = 0; - else - ++m_start; - } - - template - inline void Deque::removeLast() - { - ASSERT(!isEmpty()); - if (!m_end) - m_end = m_buffer.capacity() - 1; - else - --m_end; - TypeOperations::destruct(&m_buffer.buffer()[m_end], &m_buffer.buffer()[m_end + 1]); - } - - template - inline void Deque::remove(iterator& it) - { - remove(it.m_index); - } - - template - inline void Deque::remove(const_iterator& it) - { - remove(it.m_index); - } - - template - inline void Deque::remove(size_t position) - { - if (position == m_end) - return; - - T* buffer = m_buffer.buffer(); - TypeOperations::destruct(&buffer[position], &buffer[position + 1]); - - // Find which segment of the circular buffer contained the remove element, and only move elements in that part. - if (position >= m_start) { - TypeOperations::moveOverlapping(buffer + m_start, buffer + position, buffer + m_start + 1); - m_start = (m_start + 1) % m_buffer.capacity(); - } else { - TypeOperations::moveOverlapping(buffer + position + 1, buffer + m_end, buffer + position); - m_end = (m_end - 1 + m_buffer.capacity()) % m_buffer.capacity(); - } - } - - template - inline DequeIteratorBase::DequeIteratorBase() - : m_deque(0) - { - } - - template - inline DequeIteratorBase::DequeIteratorBase(const Deque* deque, size_t index) - : m_deque(const_cast*>(deque)) - , m_index(index) - { - } - - template - inline DequeIteratorBase::DequeIteratorBase(const DequeIteratorBase& other) - : m_deque(other.m_deque) - , m_index(other.m_index) - { - } - - template - inline DequeIteratorBase& DequeIteratorBase::operator=(const DequeIteratorBase& other) - { - m_deque = other.m_deque; - m_index = other.m_index; - return *this; - } - - template - inline DequeIteratorBase::~DequeIteratorBase() - { - } - - template - inline bool DequeIteratorBase::isEqual(const DequeIteratorBase& other) const - { - return m_index == other.m_index; - } - - template - inline void DequeIteratorBase::increment() - { - ASSERT(m_index != m_deque->m_end); - ASSERT(m_deque->m_buffer.capacity()); - if (m_index == m_deque->m_buffer.capacity() - 1) - m_index = 0; - else - ++m_index; - } - - template - inline void DequeIteratorBase::decrement() - { - ASSERT(m_index != m_deque->m_start); - ASSERT(m_deque->m_buffer.capacity()); - if (!m_index) - m_index = m_deque->m_buffer.capacity() - 1; - else - --m_index; - } - - template - inline T* DequeIteratorBase::after() const - { - ASSERT(m_index != m_deque->m_end); - return &m_deque->m_buffer.buffer()[m_index]; - } - - template - inline T* DequeIteratorBase::before() const - { - ASSERT(m_index != m_deque->m_start); - if (!m_index) - return &m_deque->m_buffer.buffer()[m_deque->m_buffer.capacity() - 1]; - return &m_deque->m_buffer.buffer()[m_index - 1]; - } - - template - inline void swap(Deque& a, Deque& b) - { - a.swap(b); - } - -} // namespace WTF +template +class DequeIteratorBase; +template +class DequeIterator; +template +class DequeConstIterator; + +template +class Deque : public VectorDestructorBase, + T, + (inlineCapacity > 0), + Allocator::isGarbageCollected> { + WTF_USE_ALLOCATOR(Deque, Allocator); + + public: + typedef DequeIterator iterator; + typedef DequeConstIterator const_iterator; + typedef std::reverse_iterator reverse_iterator; + typedef std::reverse_iterator const_reverse_iterator; + typedef PassTraits Pass; + typedef typename PassTraits::PassType PassType; + + Deque(); + Deque(const Deque&); + // FIXME: Doesn't work if there is an inline buffer, due to crbug.com/360572 + Deque& operator=(const Deque&); + + void finalize(); + + // We hard wire the inlineCapacity to zero here, due to crbug.com/360572 + void swap(Deque&); + + size_t size() const { + return m_start <= m_end ? m_end - m_start + : m_end + m_buffer.capacity() - m_start; + } + bool isEmpty() const { return m_start == m_end; } + + iterator begin() { return iterator(this, m_start); } + iterator end() { return iterator(this, m_end); } + const_iterator begin() const { return const_iterator(this, m_start); } + const_iterator end() const { return const_iterator(this, m_end); } + reverse_iterator rbegin() { return reverse_iterator(end()); } + reverse_iterator rend() { return reverse_iterator(begin()); } + const_reverse_iterator rbegin() const { + return const_reverse_iterator(end()); + } + const_reverse_iterator rend() const { + return const_reverse_iterator(begin()); + } + + T& first() { + ASSERT(m_start != m_end); + return m_buffer.buffer()[m_start]; + } + const T& first() const { + ASSERT(m_start != m_end); + return m_buffer.buffer()[m_start]; + } + PassType takeFirst(); + + T& last() { + ASSERT(m_start != m_end); + return *(--end()); + } + const T& last() const { + ASSERT(m_start != m_end); + return *(--end()); + } + PassType takeLast(); + + T& at(size_t i) { + RELEASE_ASSERT(i < size()); + size_t right = m_buffer.capacity() - m_start; + return i < right ? m_buffer.buffer()[m_start + i] + : m_buffer.buffer()[i - right]; + } + const T& at(size_t i) const { + RELEASE_ASSERT(i < size()); + size_t right = m_buffer.capacity() - m_start; + return i < right ? m_buffer.buffer()[m_start + i] + : m_buffer.buffer()[i - right]; + } + + T& operator[](size_t i) { return at(i); } + const T& operator[](size_t i) const { return at(i); } + + template + void append(const U&); + template + void prepend(const U&); + void removeFirst(); + void removeLast(); + void remove(iterator&); + void remove(const_iterator&); + + void clear(); + + template + iterator findIf(Predicate&); + + private: + friend class DequeIteratorBase; + + typedef VectorBuffer Buffer; + typedef VectorTypeOperations TypeOperations; + typedef DequeIteratorBase IteratorBase; + + void remove(size_t position); + void destroyAll(); + void expandCapacityIfNeeded(); + void expandCapacity(); + + Buffer m_buffer; + unsigned m_start; + unsigned m_end; +}; + +template +class DequeIteratorBase { + protected: + DequeIteratorBase(); + DequeIteratorBase(const Deque*, size_t); + DequeIteratorBase(const DequeIteratorBase&); + DequeIteratorBase& operator=( + const DequeIteratorBase&); + ~DequeIteratorBase(); + + void assign(const DequeIteratorBase& other) { *this = other; } + + void increment(); + void decrement(); + + T* before() const; + T* after() const; + + bool isEqual(const DequeIteratorBase&) const; + + private: + Deque* m_deque; + unsigned m_index; + + friend class Deque; +}; + +template +class DequeIterator : public DequeIteratorBase { + private: + typedef DequeIteratorBase Base; + typedef DequeIterator Iterator; + + public: + typedef ptrdiff_t difference_type; + typedef T value_type; + typedef T* pointer; + typedef T& reference; + typedef std::bidirectional_iterator_tag iterator_category; + + DequeIterator(Deque* deque, size_t index) + : Base(deque, index) {} + + DequeIterator(const Iterator& other) : Base(other) {} + DequeIterator& operator=(const Iterator& other) { + Base::assign(other); + return *this; + } + + T& operator*() const { return *Base::after(); } + T* operator->() const { return Base::after(); } + + bool operator==(const Iterator& other) const { return Base::isEqual(other); } + bool operator!=(const Iterator& other) const { return !Base::isEqual(other); } + + Iterator& operator++() { + Base::increment(); + return *this; + } + // postfix ++ intentionally omitted + Iterator& operator--() { + Base::decrement(); + return *this; + } + // postfix -- intentionally omitted +}; + +template +class DequeConstIterator + : public DequeIteratorBase { + private: + typedef DequeIteratorBase Base; + typedef DequeConstIterator Iterator; + typedef DequeIterator NonConstIterator; + + public: + typedef ptrdiff_t difference_type; + typedef T value_type; + typedef const T* pointer; + typedef const T& reference; + typedef std::bidirectional_iterator_tag iterator_category; + + DequeConstIterator(const Deque* deque, + size_t index) + : Base(deque, index) {} + + DequeConstIterator(const Iterator& other) : Base(other) {} + DequeConstIterator(const NonConstIterator& other) : Base(other) {} + DequeConstIterator& operator=(const Iterator& other) { + Base::assign(other); + return *this; + } + DequeConstIterator& operator=(const NonConstIterator& other) { + Base::assign(other); + return *this; + } + + const T& operator*() const { return *Base::after(); } + const T* operator->() const { return Base::after(); } + + bool operator==(const Iterator& other) const { return Base::isEqual(other); } + bool operator!=(const Iterator& other) const { return !Base::isEqual(other); } + + Iterator& operator++() { + Base::increment(); + return *this; + } + // postfix ++ intentionally omitted + Iterator& operator--() { + Base::decrement(); + return *this; + } + // postfix -- intentionally omitted +}; + +template +inline Deque::Deque() : m_start(0), m_end(0) {} + +template +inline Deque::Deque( + const Deque& other) + : m_buffer(other.m_buffer.capacity()), + m_start(other.m_start), + m_end(other.m_end) { + const T* otherBuffer = other.m_buffer.buffer(); + if (m_start <= m_end) + TypeOperations::uninitializedCopy(otherBuffer + m_start, + otherBuffer + m_end, + m_buffer.buffer() + m_start); + else { + TypeOperations::uninitializedCopy(otherBuffer, otherBuffer + m_end, + m_buffer.buffer()); + TypeOperations::uninitializedCopy(otherBuffer + m_start, + otherBuffer + m_buffer.capacity(), + m_buffer.buffer() + m_start); + } +} + +template +inline Deque& Deque::operator=( + const Deque& other) { + Deque copy(other); + swap(copy); + return *this; +} + +template +inline void Deque::destroyAll() { + if (m_start <= m_end) { + TypeOperations::destruct(m_buffer.buffer() + m_start, + m_buffer.buffer() + m_end); + } else { + TypeOperations::destruct(m_buffer.buffer(), m_buffer.buffer() + m_end); + TypeOperations::destruct(m_buffer.buffer() + m_start, + m_buffer.buffer() + m_buffer.capacity()); + } +} + +// Off-GC-heap deques: Destructor should be called. +// On-GC-heap deques: Destructor should be called for inline buffers +// (if any) but destructor shouldn't be called for vector backing since +// it is managed by the traced GC heap. +template +inline void Deque::finalize() { + if (!inlineCapacity && !m_buffer.buffer()) + return; + if (!isEmpty() && + !(Allocator::isGarbageCollected && m_buffer.hasOutOfLineBuffer())) + destroyAll(); + + m_buffer.destruct(); +} + +// FIXME: Doesn't work if there is an inline buffer, due to crbug.com/360572 +template +inline void Deque::swap( + Deque& other) { + std::swap(m_start, other.m_start); + std::swap(m_end, other.m_end); + m_buffer.swapVectorBuffer(other.m_buffer); +} + +template +inline void Deque::clear() { + destroyAll(); + m_start = 0; + m_end = 0; + m_buffer.deallocateBuffer(m_buffer.buffer()); + m_buffer.resetBufferPointer(); +} + +template +template +inline DequeIterator +Deque::findIf(Predicate& predicate) { + iterator end_iterator = end(); + for (iterator it = begin(); it != end_iterator; ++it) { + if (predicate(*it)) + return it; + } + return end_iterator; +} + +template +inline void Deque::expandCapacityIfNeeded() { + if (m_start) { + if (m_end + 1 != m_start) + return; + } else if (m_end) { + if (m_end != m_buffer.capacity() - 1) + return; + } else if (m_buffer.capacity()) + return; + + expandCapacity(); +} + +template +void Deque::expandCapacity() { + size_t oldCapacity = m_buffer.capacity(); + T* oldBuffer = m_buffer.buffer(); + m_buffer.allocateBuffer( + std::max(static_cast(16), oldCapacity + oldCapacity / 4 + 1)); + if (m_start <= m_end) + TypeOperations::move(oldBuffer + m_start, oldBuffer + m_end, + m_buffer.buffer() + m_start); + else { + TypeOperations::move(oldBuffer, oldBuffer + m_end, m_buffer.buffer()); + size_t newStart = m_buffer.capacity() - (oldCapacity - m_start); + TypeOperations::move(oldBuffer + m_start, oldBuffer + oldCapacity, + m_buffer.buffer() + newStart); + m_start = newStart; + } + m_buffer.deallocateBuffer(oldBuffer); +} + +template +inline typename Deque::PassType +Deque::takeFirst() { + T oldFirst = Pass::transfer(first()); + removeFirst(); + return Pass::transfer(oldFirst); +} + +template +inline typename Deque::PassType +Deque::takeLast() { + T oldLast = Pass::transfer(last()); + removeLast(); + return Pass::transfer(oldLast); +} + +template +template +inline void Deque::append(const U& value) { + expandCapacityIfNeeded(); + new (NotNull, &m_buffer.buffer()[m_end]) T(value); + if (m_end == m_buffer.capacity() - 1) + m_end = 0; + else + ++m_end; +} + +template +template +inline void Deque::prepend(const U& value) { + expandCapacityIfNeeded(); + if (!m_start) + m_start = m_buffer.capacity() - 1; + else + --m_start; + new (NotNull, &m_buffer.buffer()[m_start]) T(value); +} + +template +inline void Deque::removeFirst() { + ASSERT(!isEmpty()); + TypeOperations::destruct(&m_buffer.buffer()[m_start], + &m_buffer.buffer()[m_start + 1]); + if (m_start == m_buffer.capacity() - 1) + m_start = 0; + else + ++m_start; +} + +template +inline void Deque::removeLast() { + ASSERT(!isEmpty()); + if (!m_end) + m_end = m_buffer.capacity() - 1; + else + --m_end; + TypeOperations::destruct(&m_buffer.buffer()[m_end], + &m_buffer.buffer()[m_end + 1]); +} + +template +inline void Deque::remove(iterator& it) { + remove(it.m_index); +} + +template +inline void Deque::remove(const_iterator& it) { + remove(it.m_index); +} + +template +inline void Deque::remove(size_t position) { + if (position == m_end) + return; + + T* buffer = m_buffer.buffer(); + TypeOperations::destruct(&buffer[position], &buffer[position + 1]); + + // Find which segment of the circular buffer contained the remove element, and + // only move elements in that part. + if (position >= m_start) { + TypeOperations::moveOverlapping(buffer + m_start, buffer + position, + buffer + m_start + 1); + m_start = (m_start + 1) % m_buffer.capacity(); + } else { + TypeOperations::moveOverlapping(buffer + position + 1, buffer + m_end, + buffer + position); + m_end = (m_end - 1 + m_buffer.capacity()) % m_buffer.capacity(); + } +} + +template +inline DequeIteratorBase::DequeIteratorBase() + : m_deque(0) {} + +template +inline DequeIteratorBase::DequeIteratorBase( + const Deque* deque, + size_t index) + : m_deque(const_cast*>(deque)), + m_index(index) {} + +template +inline DequeIteratorBase::DequeIteratorBase( + const DequeIteratorBase& other) + : m_deque(other.m_deque), m_index(other.m_index) {} + +template +inline DequeIteratorBase& +DequeIteratorBase::operator=( + const DequeIteratorBase& other) { + m_deque = other.m_deque; + m_index = other.m_index; + return *this; +} + +template +inline DequeIteratorBase::~DequeIteratorBase() {} + +template +inline bool DequeIteratorBase::isEqual( + const DequeIteratorBase& other) const { + return m_index == other.m_index; +} + +template +inline void DequeIteratorBase::increment() { + ASSERT(m_index != m_deque->m_end); + ASSERT(m_deque->m_buffer.capacity()); + if (m_index == m_deque->m_buffer.capacity() - 1) + m_index = 0; + else + ++m_index; +} + +template +inline void DequeIteratorBase::decrement() { + ASSERT(m_index != m_deque->m_start); + ASSERT(m_deque->m_buffer.capacity()); + if (!m_index) + m_index = m_deque->m_buffer.capacity() - 1; + else + --m_index; +} + +template +inline T* DequeIteratorBase::after() const { + ASSERT(m_index != m_deque->m_end); + return &m_deque->m_buffer.buffer()[m_index]; +} + +template +inline T* DequeIteratorBase::before() const { + ASSERT(m_index != m_deque->m_start); + if (!m_index) + return &m_deque->m_buffer.buffer()[m_deque->m_buffer.capacity() - 1]; + return &m_deque->m_buffer.buffer()[m_index - 1]; +} + +template +inline void swap(Deque& a, + Deque& b) { + a.swap(b); +} + +} // namespace WTF using WTF::Deque; diff --git a/sky/engine/wtf/DequeTest.cpp b/sky/engine/wtf/DequeTest.cpp index cad1ad7549157..fedb77a9d3bfc 100644 --- a/sky/engine/wtf/DequeTest.cpp +++ b/sky/engine/wtf/DequeTest.cpp @@ -23,7 +23,6 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ - #include "flutter/sky/engine/wtf/Deque.h" #include @@ -33,283 +32,272 @@ namespace { -TEST(DequeTest, Basic) -{ - Deque intDeque; - EXPECT_TRUE(intDeque.isEmpty()); - EXPECT_EQ(0ul, intDeque.size()); +TEST(DequeTest, Basic) { + Deque intDeque; + EXPECT_TRUE(intDeque.isEmpty()); + EXPECT_EQ(0ul, intDeque.size()); } -void checkNumberSequence(Deque& deque, int from, int to, bool increment) -{ - Deque::iterator it = increment ? deque.begin() : deque.end(); - size_t index = increment ? 0 : deque.size(); - int step = from < to ? 1 : -1; - for (int i = from; i != to + step; i += step) { - if (!increment) { - --it; - --index; - } - - EXPECT_EQ(i, *it); - EXPECT_EQ(i, deque[index]); - - if (increment) { - ++it; - ++index; - } +void checkNumberSequence(Deque& deque, int from, int to, bool increment) { + Deque::iterator it = increment ? deque.begin() : deque.end(); + size_t index = increment ? 0 : deque.size(); + int step = from < to ? 1 : -1; + for (int i = from; i != to + step; i += step) { + if (!increment) { + --it; + --index; } - EXPECT_EQ(increment ? deque.end() : deque.begin(), it); - EXPECT_EQ(increment ? deque.size() : 0, index); -} -void checkNumberSequenceReverse(Deque& deque, int from, int to, bool increment) -{ - Deque::reverse_iterator it = increment ? deque.rbegin() : deque.rend(); - size_t index = increment ? 0 : deque.size(); - int step = from < to ? 1 : -1; - for (int i = from; i != to + step; i += step) { - if (!increment) { - --it; - --index; - } - - EXPECT_EQ(i, *it); - EXPECT_EQ(i, deque.at(deque.size() - 1 - index)); - - if (increment) { - ++it; - ++index; - } + EXPECT_EQ(i, *it); + EXPECT_EQ(i, deque[index]); + + if (increment) { + ++it; + ++index; } - EXPECT_EQ(increment ? deque.rend() : deque.rbegin(), it); - EXPECT_EQ(increment ? deque.size() : 0, index); + } + EXPECT_EQ(increment ? deque.end() : deque.begin(), it); + EXPECT_EQ(increment ? deque.size() : 0, index); } -TEST(DequeTest, Reverse) -{ - Deque intDeque; - intDeque.append(10); - intDeque.append(11); - intDeque.append(12); - intDeque.append(13); - - checkNumberSequence(intDeque, 10, 13, true); - checkNumberSequence(intDeque, 13, 10, false); - checkNumberSequenceReverse(intDeque, 13, 10, true); - checkNumberSequenceReverse(intDeque, 10, 13, false); - - intDeque.append(14); - intDeque.append(15); - EXPECT_EQ(10, intDeque.takeFirst()); - EXPECT_EQ(15, intDeque.takeLast()); - checkNumberSequence(intDeque, 11, 14, true); - checkNumberSequence(intDeque, 14, 11, false); - checkNumberSequenceReverse(intDeque, 14, 11, true); - checkNumberSequenceReverse(intDeque, 11, 14, false); - - for (int i = 15; i < 200; ++i) - intDeque.append(i); - checkNumberSequence(intDeque, 11, 199, true); - checkNumberSequence(intDeque, 199, 11, false); - checkNumberSequenceReverse(intDeque, 199, 11, true); - checkNumberSequenceReverse(intDeque, 11, 199, false); - - for (int i = 0; i < 180; ++i) { - EXPECT_EQ(i + 11, intDeque[0]); - EXPECT_EQ(i + 11, intDeque.takeFirst()); +void checkNumberSequenceReverse(Deque& deque, + int from, + int to, + bool increment) { + Deque::reverse_iterator it = increment ? deque.rbegin() : deque.rend(); + size_t index = increment ? 0 : deque.size(); + int step = from < to ? 1 : -1; + for (int i = from; i != to + step; i += step) { + if (!increment) { + --it; + --index; } - checkNumberSequence(intDeque, 191, 199, true); - checkNumberSequence(intDeque, 199, 191, false); - checkNumberSequenceReverse(intDeque, 199, 191, true); - checkNumberSequenceReverse(intDeque, 191, 199, false); - - Deque intDeque2; - swap(intDeque, intDeque2); - - checkNumberSequence(intDeque2, 191, 199, true); - checkNumberSequence(intDeque2, 199, 191, false); - checkNumberSequenceReverse(intDeque2, 199, 191, true); - checkNumberSequenceReverse(intDeque2, 191, 199, false); - - intDeque.swap(intDeque2); - checkNumberSequence(intDeque, 191, 199, true); - checkNumberSequence(intDeque, 199, 191, false); - checkNumberSequenceReverse(intDeque, 199, 191, true); - checkNumberSequenceReverse(intDeque, 191, 199, false); + EXPECT_EQ(i, *it); + EXPECT_EQ(i, deque.at(deque.size() - 1 - index)); - intDeque.swap(intDeque2); + if (increment) { + ++it; + ++index; + } + } + EXPECT_EQ(increment ? deque.rend() : deque.rbegin(), it); + EXPECT_EQ(increment ? deque.size() : 0, index); +} - checkNumberSequence(intDeque2, 191, 199, true); - checkNumberSequence(intDeque2, 199, 191, false); - checkNumberSequenceReverse(intDeque2, 199, 191, true); - checkNumberSequenceReverse(intDeque2, 191, 199, false); +TEST(DequeTest, Reverse) { + Deque intDeque; + intDeque.append(10); + intDeque.append(11); + intDeque.append(12); + intDeque.append(13); + + checkNumberSequence(intDeque, 10, 13, true); + checkNumberSequence(intDeque, 13, 10, false); + checkNumberSequenceReverse(intDeque, 13, 10, true); + checkNumberSequenceReverse(intDeque, 10, 13, false); + + intDeque.append(14); + intDeque.append(15); + EXPECT_EQ(10, intDeque.takeFirst()); + EXPECT_EQ(15, intDeque.takeLast()); + checkNumberSequence(intDeque, 11, 14, true); + checkNumberSequence(intDeque, 14, 11, false); + checkNumberSequenceReverse(intDeque, 14, 11, true); + checkNumberSequenceReverse(intDeque, 11, 14, false); + + for (int i = 15; i < 200; ++i) + intDeque.append(i); + checkNumberSequence(intDeque, 11, 199, true); + checkNumberSequence(intDeque, 199, 11, false); + checkNumberSequenceReverse(intDeque, 199, 11, true); + checkNumberSequenceReverse(intDeque, 11, 199, false); + + for (int i = 0; i < 180; ++i) { + EXPECT_EQ(i + 11, intDeque[0]); + EXPECT_EQ(i + 11, intDeque.takeFirst()); + } + checkNumberSequence(intDeque, 191, 199, true); + checkNumberSequence(intDeque, 199, 191, false); + checkNumberSequenceReverse(intDeque, 199, 191, true); + checkNumberSequenceReverse(intDeque, 191, 199, false); + + Deque intDeque2; + swap(intDeque, intDeque2); + + checkNumberSequence(intDeque2, 191, 199, true); + checkNumberSequence(intDeque2, 199, 191, false); + checkNumberSequenceReverse(intDeque2, 199, 191, true); + checkNumberSequenceReverse(intDeque2, 191, 199, false); + + intDeque.swap(intDeque2); + + checkNumberSequence(intDeque, 191, 199, true); + checkNumberSequence(intDeque, 199, 191, false); + checkNumberSequenceReverse(intDeque, 199, 191, true); + checkNumberSequenceReverse(intDeque, 191, 199, false); + + intDeque.swap(intDeque2); + + checkNumberSequence(intDeque2, 191, 199, true); + checkNumberSequence(intDeque2, 199, 191, false); + checkNumberSequenceReverse(intDeque2, 199, 191, true); + checkNumberSequenceReverse(intDeque2, 191, 199, false); } class DestructCounter { -public: - explicit DestructCounter(int i, int* destructNumber) - : m_i(i) - , m_destructNumber(destructNumber) - { } - - ~DestructCounter() { ++(*m_destructNumber); } - int get() const { return m_i; } - -private: - int m_i; - int* m_destructNumber; + public: + explicit DestructCounter(int i, int* destructNumber) + : m_i(i), m_destructNumber(destructNumber) {} + + ~DestructCounter() { ++(*m_destructNumber); } + int get() const { return m_i; } + + private: + int m_i; + int* m_destructNumber; }; -typedef WTF::Deque > OwnPtrDeque; - -TEST(DequeTest, OwnPtr) -{ - int destructNumber = 0; - OwnPtrDeque deque; - deque.append(adoptPtr(new DestructCounter(0, &destructNumber))); - deque.append(adoptPtr(new DestructCounter(1, &destructNumber))); - EXPECT_EQ(2u, deque.size()); - - OwnPtr& counter0 = deque.first(); - EXPECT_EQ(0, counter0->get()); - int counter1 = deque.last()->get(); - EXPECT_EQ(1, counter1); - EXPECT_EQ(0, destructNumber); - - size_t index = 0; - for (OwnPtrDeque::iterator iter = deque.begin(); iter != deque.end(); ++iter) { - OwnPtr& refCounter = *iter; - EXPECT_EQ(index, static_cast(refCounter->get())); - EXPECT_EQ(index, static_cast((*refCounter).get())); - index++; - } - EXPECT_EQ(0, destructNumber); - - OwnPtrDeque::iterator it = deque.begin(); - for (index = 0; index < deque.size(); ++index) { - OwnPtr& refCounter = *it; - EXPECT_EQ(index, static_cast(refCounter->get())); - index++; - ++it; - } - EXPECT_EQ(0, destructNumber); - - EXPECT_EQ(0, deque.first()->get()); - deque.removeFirst(); - EXPECT_EQ(1, deque.first()->get()); - EXPECT_EQ(1u, deque.size()); - EXPECT_EQ(1, destructNumber); - - OwnPtr ownCounter1 = deque.first().release(); - deque.removeFirst(); - EXPECT_EQ(counter1, ownCounter1->get()); - EXPECT_EQ(0u, deque.size()); - EXPECT_EQ(1, destructNumber); - - ownCounter1.clear(); - EXPECT_EQ(2, destructNumber); - - size_t count = 1025; - destructNumber = 0; - for (size_t i = 0; i < count; ++i) - deque.prepend(adoptPtr(new DestructCounter(i, &destructNumber))); - - // Deque relocation must not destruct OwnPtr element. - EXPECT_EQ(0, destructNumber); - EXPECT_EQ(count, deque.size()); - - OwnPtrDeque copyDeque; - deque.swap(copyDeque); - EXPECT_EQ(0, destructNumber); - EXPECT_EQ(count, copyDeque.size()); - EXPECT_EQ(0u, deque.size()); - - copyDeque.clear(); - EXPECT_EQ(count, static_cast(destructNumber)); +typedef WTF::Deque> OwnPtrDeque; + +TEST(DequeTest, OwnPtr) { + int destructNumber = 0; + OwnPtrDeque deque; + deque.append(adoptPtr(new DestructCounter(0, &destructNumber))); + deque.append(adoptPtr(new DestructCounter(1, &destructNumber))); + EXPECT_EQ(2u, deque.size()); + + OwnPtr& counter0 = deque.first(); + EXPECT_EQ(0, counter0->get()); + int counter1 = deque.last()->get(); + EXPECT_EQ(1, counter1); + EXPECT_EQ(0, destructNumber); + + size_t index = 0; + for (OwnPtrDeque::iterator iter = deque.begin(); iter != deque.end(); + ++iter) { + OwnPtr& refCounter = *iter; + EXPECT_EQ(index, static_cast(refCounter->get())); + EXPECT_EQ(index, static_cast((*refCounter).get())); + index++; + } + EXPECT_EQ(0, destructNumber); + + OwnPtrDeque::iterator it = deque.begin(); + for (index = 0; index < deque.size(); ++index) { + OwnPtr& refCounter = *it; + EXPECT_EQ(index, static_cast(refCounter->get())); + index++; + ++it; + } + EXPECT_EQ(0, destructNumber); + + EXPECT_EQ(0, deque.first()->get()); + deque.removeFirst(); + EXPECT_EQ(1, deque.first()->get()); + EXPECT_EQ(1u, deque.size()); + EXPECT_EQ(1, destructNumber); + + OwnPtr ownCounter1 = deque.first().release(); + deque.removeFirst(); + EXPECT_EQ(counter1, ownCounter1->get()); + EXPECT_EQ(0u, deque.size()); + EXPECT_EQ(1, destructNumber); + + ownCounter1.clear(); + EXPECT_EQ(2, destructNumber); + + size_t count = 1025; + destructNumber = 0; + for (size_t i = 0; i < count; ++i) + deque.prepend(adoptPtr(new DestructCounter(i, &destructNumber))); + + // Deque relocation must not destruct OwnPtr element. + EXPECT_EQ(0, destructNumber); + EXPECT_EQ(count, deque.size()); + + OwnPtrDeque copyDeque; + deque.swap(copyDeque); + EXPECT_EQ(0, destructNumber); + EXPECT_EQ(count, copyDeque.size()); + EXPECT_EQ(0u, deque.size()); + + copyDeque.clear(); + EXPECT_EQ(count, static_cast(destructNumber)); } // WrappedInt class will fail if it was memmoved or memcpyed. static HashSet constructedWrappedInts; class WrappedInt { -public: - WrappedInt(int i = 0) - : m_originalThisPtr(this) - , m_i(i) - { - constructedWrappedInts.add(this); - } - - WrappedInt(const WrappedInt& other) - : m_originalThisPtr(this) - , m_i(other.m_i) - { - constructedWrappedInts.add(this); - } - - WrappedInt& operator=(const WrappedInt& other) - { - m_i = other.m_i; - return *this; - } - - ~WrappedInt() - { - EXPECT_EQ(m_originalThisPtr, this); - EXPECT_TRUE(constructedWrappedInts.contains(this)); - constructedWrappedInts.remove(this); - } - - int get() const { return m_i; } - -private: - void* m_originalThisPtr; - int m_i; + public: + WrappedInt(int i = 0) : m_originalThisPtr(this), m_i(i) { + constructedWrappedInts.add(this); + } + + WrappedInt(const WrappedInt& other) + : m_originalThisPtr(this), m_i(other.m_i) { + constructedWrappedInts.add(this); + } + + WrappedInt& operator=(const WrappedInt& other) { + m_i = other.m_i; + return *this; + } + + ~WrappedInt() { + EXPECT_EQ(m_originalThisPtr, this); + EXPECT_TRUE(constructedWrappedInts.contains(this)); + constructedWrappedInts.remove(this); + } + + int get() const { return m_i; } + + private: + void* m_originalThisPtr; + int m_i; }; -TEST(DequeTest, SwapWithoutInlineCapacity) -{ - Deque dequeA; - dequeA.append(WrappedInt(1)); - Deque dequeB; - dequeB.append(WrappedInt(2)); +TEST(DequeTest, SwapWithoutInlineCapacity) { + Deque dequeA; + dequeA.append(WrappedInt(1)); + Deque dequeB; + dequeB.append(WrappedInt(2)); - ASSERT_EQ(dequeA.size(), dequeB.size()); - dequeA.swap(dequeB); + ASSERT_EQ(dequeA.size(), dequeB.size()); + dequeA.swap(dequeB); - ASSERT_EQ(1u, dequeA.size()); - EXPECT_EQ(2, dequeA.first().get()); - ASSERT_EQ(1u, dequeB.size()); - EXPECT_EQ(1, dequeB.first().get()); + ASSERT_EQ(1u, dequeA.size()); + EXPECT_EQ(2, dequeA.first().get()); + ASSERT_EQ(1u, dequeB.size()); + EXPECT_EQ(1, dequeB.first().get()); - dequeA.append(WrappedInt(3)); + dequeA.append(WrappedInt(3)); - ASSERT_GT(dequeA.size(), dequeB.size()); - dequeA.swap(dequeB); + ASSERT_GT(dequeA.size(), dequeB.size()); + dequeA.swap(dequeB); - ASSERT_EQ(1u, dequeA.size()); - EXPECT_EQ(1, dequeA.first().get()); - ASSERT_EQ(2u, dequeB.size()); - EXPECT_EQ(2, dequeB.first().get()); + ASSERT_EQ(1u, dequeA.size()); + EXPECT_EQ(1, dequeA.first().get()); + ASSERT_EQ(2u, dequeB.size()); + EXPECT_EQ(2, dequeB.first().get()); - ASSERT_LT(dequeA.size(), dequeB.size()); - dequeA.swap(dequeB); + ASSERT_LT(dequeA.size(), dequeB.size()); + dequeA.swap(dequeB); - ASSERT_EQ(2u, dequeA.size()); - EXPECT_EQ(2, dequeA.first().get()); - ASSERT_EQ(1u, dequeB.size()); - EXPECT_EQ(1, dequeB.first().get()); + ASSERT_EQ(2u, dequeA.size()); + EXPECT_EQ(2, dequeA.first().get()); + ASSERT_EQ(1u, dequeB.size()); + EXPECT_EQ(1, dequeB.first().get()); - dequeA.append(WrappedInt(4)); - dequeA.swap(dequeB); + dequeA.append(WrappedInt(4)); + dequeA.swap(dequeB); - ASSERT_EQ(1u, dequeA.size()); - EXPECT_EQ(1, dequeA.first().get()); - ASSERT_EQ(3u, dequeB.size()); - EXPECT_EQ(2, dequeB.first().get()); + ASSERT_EQ(1u, dequeA.size()); + EXPECT_EQ(1, dequeA.first().get()); + ASSERT_EQ(3u, dequeB.size()); + EXPECT_EQ(2, dequeB.first().get()); - dequeB.swap(dequeA); + dequeB.swap(dequeA); } -} // namespace +} // namespace diff --git a/sky/engine/wtf/DoubleBufferedDeque.h b/sky/engine/wtf/DoubleBufferedDeque.h index 637c6c55edfb0..9ebe68ae757c3 100644 --- a/sky/engine/wtf/DoubleBufferedDeque.h +++ b/sky/engine/wtf/DoubleBufferedDeque.h @@ -10,37 +10,32 @@ namespace WTF { -// A helper class for managing double buffered deques, typically where the client locks when appending or swapping. -template class DoubleBufferedDeque { - WTF_MAKE_NONCOPYABLE(DoubleBufferedDeque); -public: - DoubleBufferedDeque() - : m_activeIndex(0) { } - - void append(const T& value) - { - m_queue[m_activeIndex].append(value); - } - - bool isEmpty() const - { - return m_queue[m_activeIndex].isEmpty(); - } - - Deque& swapBuffers() - { - int oldIndex = m_activeIndex; - m_activeIndex ^= 1; - ASSERT(m_queue[m_activeIndex].isEmpty()); - return m_queue[oldIndex]; - } - -private: - Deque m_queue[2]; - int m_activeIndex; +// A helper class for managing double buffered deques, typically where the +// client locks when appending or swapping. +template +class DoubleBufferedDeque { + WTF_MAKE_NONCOPYABLE(DoubleBufferedDeque); + + public: + DoubleBufferedDeque() : m_activeIndex(0) {} + + void append(const T& value) { m_queue[m_activeIndex].append(value); } + + bool isEmpty() const { return m_queue[m_activeIndex].isEmpty(); } + + Deque& swapBuffers() { + int oldIndex = m_activeIndex; + m_activeIndex ^= 1; + ASSERT(m_queue[m_activeIndex].isEmpty()); + return m_queue[oldIndex]; + } + + private: + Deque m_queue[2]; + int m_activeIndex; }; -} // namespace WTF +} // namespace WTF using WTF::DoubleBufferedDeque; diff --git a/sky/engine/wtf/DoubleBufferedDequeTest.cpp b/sky/engine/wtf/DoubleBufferedDequeTest.cpp index ea6f4b51f54d0..9aeb1ba02e740 100644 --- a/sky/engine/wtf/DoubleBufferedDequeTest.cpp +++ b/sky/engine/wtf/DoubleBufferedDequeTest.cpp @@ -10,43 +10,40 @@ namespace { typedef testing::Test DoubleBufferedDequeTest; -TEST(DoubleBufferedDequeTest, TestIsEmpty) -{ - DoubleBufferedDeque queue; +TEST(DoubleBufferedDequeTest, TestIsEmpty) { + DoubleBufferedDeque queue; - EXPECT_TRUE(queue.isEmpty()); - queue.append(1); - EXPECT_FALSE(queue.isEmpty()); + EXPECT_TRUE(queue.isEmpty()); + queue.append(1); + EXPECT_FALSE(queue.isEmpty()); } -TEST(DoubleBufferedDequeTest, TestIsEmptyAfterSwapBuffers) -{ - DoubleBufferedDeque queue; - queue.append(1); +TEST(DoubleBufferedDequeTest, TestIsEmptyAfterSwapBuffers) { + DoubleBufferedDeque queue; + queue.append(1); - queue.swapBuffers(); - EXPECT_TRUE(queue.isEmpty()); + queue.swapBuffers(); + EXPECT_TRUE(queue.isEmpty()); } -TEST(DoubleBufferedDequeTest, TestDoubleBuffering) -{ - DoubleBufferedDeque queue; - queue.append(1); - queue.append(10); - queue.append(100); - - { - Deque& deque = queue.swapBuffers(); - EXPECT_EQ(1, deque.takeFirst()); - EXPECT_EQ(10, deque.takeFirst()); - EXPECT_EQ(100, deque.takeFirst()); - } - queue.append(2); - - EXPECT_EQ(2, queue.swapBuffers().takeFirst()); - queue.append(3); - - EXPECT_EQ(3, queue.swapBuffers().takeFirst()); +TEST(DoubleBufferedDequeTest, TestDoubleBuffering) { + DoubleBufferedDeque queue; + queue.append(1); + queue.append(10); + queue.append(100); + + { + Deque& deque = queue.swapBuffers(); + EXPECT_EQ(1, deque.takeFirst()); + EXPECT_EQ(10, deque.takeFirst()); + EXPECT_EQ(100, deque.takeFirst()); + } + queue.append(2); + + EXPECT_EQ(2, queue.swapBuffers().takeFirst()); + queue.append(3); + + EXPECT_EQ(3, queue.swapBuffers().takeFirst()); } -} // namespace +} // namespace diff --git a/sky/engine/wtf/DoublyLinkedList.h b/sky/engine/wtf/DoublyLinkedList.h index f3a410600e83f..dc830734f940c 100644 --- a/sky/engine/wtf/DoublyLinkedList.h +++ b/sky/engine/wtf/DoublyLinkedList.h @@ -29,166 +29,165 @@ namespace WTF { // This class allows nodes to share code without dictating data member layout. -template class DoublyLinkedListNode { -public: - DoublyLinkedListNode(); +template +class DoublyLinkedListNode { + public: + DoublyLinkedListNode(); - void setPrev(T*); - void setNext(T*); + void setPrev(T*); + void setNext(T*); - T* prev() const; - T* next() const; + T* prev() const; + T* next() const; }; -template inline DoublyLinkedListNode::DoublyLinkedListNode() -{ - setPrev(0); - setNext(0); +template +inline DoublyLinkedListNode::DoublyLinkedListNode() { + setPrev(0); + setNext(0); } -template inline void DoublyLinkedListNode::setPrev(T* prev) -{ - static_cast(this)->m_prev = prev; +template +inline void DoublyLinkedListNode::setPrev(T* prev) { + static_cast(this)->m_prev = prev; } -template inline void DoublyLinkedListNode::setNext(T* next) -{ - static_cast(this)->m_next = next; +template +inline void DoublyLinkedListNode::setNext(T* next) { + static_cast(this)->m_next = next; } -template inline T* DoublyLinkedListNode::prev() const -{ - return static_cast(this)->m_prev; +template +inline T* DoublyLinkedListNode::prev() const { + return static_cast(this)->m_prev; } -template inline T* DoublyLinkedListNode::next() const -{ - return static_cast(this)->m_next; +template +inline T* DoublyLinkedListNode::next() const { + return static_cast(this)->m_next; } -template class DoublyLinkedList { -public: - DoublyLinkedList(); +template +class DoublyLinkedList { + public: + DoublyLinkedList(); - bool isEmpty() const; - size_t size() const; // This is O(n). - void clear(); + bool isEmpty() const; + size_t size() const; // This is O(n). + void clear(); - T* head() const; - T* removeHead(); + T* head() const; + T* removeHead(); - T* tail() const; + T* tail() const; - void push(T*); - void append(T*); - void remove(T*); + void push(T*); + void append(T*); + void remove(T*); -private: - T* m_head; - T* m_tail; + private: + T* m_head; + T* m_tail; }; -template inline DoublyLinkedList::DoublyLinkedList() - : m_head(0) - , m_tail(0) -{ -} +template +inline DoublyLinkedList::DoublyLinkedList() : m_head(0), m_tail(0) {} -template inline bool DoublyLinkedList::isEmpty() const -{ - return !m_head; +template +inline bool DoublyLinkedList::isEmpty() const { + return !m_head; } -template inline size_t DoublyLinkedList::size() const -{ - size_t size = 0; - for (T* node = m_head; node; node = node->next()) - ++size; - return size; +template +inline size_t DoublyLinkedList::size() const { + size_t size = 0; + for (T* node = m_head; node; node = node->next()) + ++size; + return size; } -template inline void DoublyLinkedList::clear() -{ - m_head = 0; - m_tail = 0; +template +inline void DoublyLinkedList::clear() { + m_head = 0; + m_tail = 0; } -template inline T* DoublyLinkedList::head() const -{ - return m_head; +template +inline T* DoublyLinkedList::head() const { + return m_head; } -template inline T* DoublyLinkedList::tail() const -{ - return m_tail; +template +inline T* DoublyLinkedList::tail() const { + return m_tail; } -template inline void DoublyLinkedList::push(T* node) -{ - if (!m_head) { - ASSERT(!m_tail); - m_head = node; - m_tail = node; - node->setPrev(0); - node->setNext(0); - return; - } - - ASSERT(m_tail); - m_head->setPrev(node); - node->setNext(m_head); - node->setPrev(0); +template +inline void DoublyLinkedList::push(T* node) { + if (!m_head) { + ASSERT(!m_tail); m_head = node; + m_tail = node; + node->setPrev(0); + node->setNext(0); + return; + } + + ASSERT(m_tail); + m_head->setPrev(node); + node->setNext(m_head); + node->setPrev(0); + m_head = node; } -template inline void DoublyLinkedList::append(T* node) -{ - if (!m_tail) { - ASSERT(!m_head); - m_head = node; - m_tail = node; - node->setPrev(0); - node->setNext(0); - return; - } - - ASSERT(m_head); - m_tail->setNext(node); - node->setPrev(m_tail); - node->setNext(0); +template +inline void DoublyLinkedList::append(T* node) { + if (!m_tail) { + ASSERT(!m_head); + m_head = node; m_tail = node; + node->setPrev(0); + node->setNext(0); + return; + } + + ASSERT(m_head); + m_tail->setNext(node); + node->setPrev(m_tail); + node->setNext(0); + m_tail = node; } -template inline void DoublyLinkedList::remove(T* node) -{ - if (node->prev()) { - ASSERT(node != m_head); - node->prev()->setNext(node->next()); - } else { - ASSERT(node == m_head); - m_head = node->next(); - } - - if (node->next()) { - ASSERT(node != m_tail); - node->next()->setPrev(node->prev()); - } else { - ASSERT(node == m_tail); - m_tail = node->prev(); - } +template +inline void DoublyLinkedList::remove(T* node) { + if (node->prev()) { + ASSERT(node != m_head); + node->prev()->setNext(node->next()); + } else { + ASSERT(node == m_head); + m_head = node->next(); + } + + if (node->next()) { + ASSERT(node != m_tail); + node->next()->setPrev(node->prev()); + } else { + ASSERT(node == m_tail); + m_tail = node->prev(); + } } -template inline T* DoublyLinkedList::removeHead() -{ - T* node = head(); - if (node) - remove(node); - return node; +template +inline T* DoublyLinkedList::removeHead() { + T* node = head(); + if (node) + remove(node); + return node; } -} // namespace WTF +} // namespace WTF -using WTF::DoublyLinkedListNode; using WTF::DoublyLinkedList; +using WTF::DoublyLinkedListNode; #endif // SKY_ENGINE_WTF_DOUBLYLINKEDLIST_H_ diff --git a/sky/engine/wtf/DynamicAnnotations.cpp b/sky/engine/wtf/DynamicAnnotations.cpp index f7574ede7e19e..76b5439b04df0 100644 --- a/sky/engine/wtf/DynamicAnnotations.cpp +++ b/sky/engine/wtf/DynamicAnnotations.cpp @@ -24,7 +24,6 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #include "flutter/sky/engine/wtf/DynamicAnnotations.h" #if USE(DYNAMIC_ANNOTATIONS) && !USE(DYNAMIC_ANNOTATIONS_NOIMPL) @@ -33,28 +32,29 @@ // This makes all Annotate* functions different, which prevents the linker from // folding them. #ifdef __COUNTER__ -#define DYNAMIC_ANNOTATIONS_IMPL \ - volatile short lineno = (__LINE__ << 8) + __COUNTER__; \ - (void)lineno; +#define DYNAMIC_ANNOTATIONS_IMPL \ + volatile short lineno = (__LINE__ << 8) + __COUNTER__; \ + (void)lineno; #else -#define DYNAMIC_ANNOTATIONS_IMPL \ - volatile short lineno = (__LINE__ << 8); \ - (void)lineno; +#define DYNAMIC_ANNOTATIONS_IMPL \ + volatile short lineno = (__LINE__ << 8); \ + (void)lineno; #endif -void WTFAnnotateBenignRaceSized(const char*, int, const volatile void*, long, const char*) -{ - DYNAMIC_ANNOTATIONS_IMPL +void WTFAnnotateBenignRaceSized(const char*, + int, + const volatile void*, + long, + const char*) { + DYNAMIC_ANNOTATIONS_IMPL } -void WTFAnnotateHappensBefore(const char*, int, const volatile void*) -{ - DYNAMIC_ANNOTATIONS_IMPL +void WTFAnnotateHappensBefore(const char*, int, const volatile void*) { + DYNAMIC_ANNOTATIONS_IMPL } -void WTFAnnotateHappensAfter(const char*, int, const volatile void*) -{ - DYNAMIC_ANNOTATIONS_IMPL +void WTFAnnotateHappensAfter(const char*, int, const volatile void*) { + DYNAMIC_ANNOTATIONS_IMPL } -#endif // USE(DYNAMIC_ANNOTATIONS) && !USE(DYNAMIC_ANNOTATIONS_NOIMPL) +#endif // USE(DYNAMIC_ANNOTATIONS) && !USE(DYNAMIC_ANNOTATIONS_NOIMPL) diff --git a/sky/engine/wtf/DynamicAnnotations.h b/sky/engine/wtf/DynamicAnnotations.h index 9dc76c2639e2b..8913dc8d89ce4 100644 --- a/sky/engine/wtf/DynamicAnnotations.h +++ b/sky/engine/wtf/DynamicAnnotations.h @@ -45,16 +45,21 @@ * The dynamic analysis tools can intercept these functions and replace them * with their own implementations. * - * See http://code.google.com/p/data-race-test/wiki/DynamicAnnotations for more information. + * See http://code.google.com/p/data-race-test/wiki/DynamicAnnotations for more + * information. */ #include "flutter/sky/engine/wtf/OperatingSystem.h" #include "flutter/sky/engine/wtf/WTFExport.h" #if USE(DYNAMIC_ANNOTATIONS) -/* Tell data race detector that we're not interested in reports on the given address range. */ -#define WTF_ANNOTATE_BENIGN_RACE_SIZED(address, size, description) WTFAnnotateBenignRaceSized(__FILE__, __LINE__, address, size, description) -#define WTF_ANNOTATE_BENIGN_RACE(pointer, description) WTFAnnotateBenignRaceSized(__FILE__, __LINE__, pointer, sizeof(*(pointer)), description) +/* Tell data race detector that we're not interested in reports on the given + * address range. */ +#define WTF_ANNOTATE_BENIGN_RACE_SIZED(address, size, description) \ + WTFAnnotateBenignRaceSized(__FILE__, __LINE__, address, size, description) +#define WTF_ANNOTATE_BENIGN_RACE(pointer, description) \ + WTFAnnotateBenignRaceSized(__FILE__, __LINE__, pointer, sizeof(*(pointer)), \ + description) /* Annotations for user-defined synchronization mechanisms. * These annotations can be used to define happens-before arcs in user-defined @@ -73,27 +78,37 @@ * return false; * } */ -#define WTF_ANNOTATE_HAPPENS_BEFORE(address) WTFAnnotateHappensBefore(__FILE__, __LINE__, address) -#define WTF_ANNOTATE_HAPPENS_AFTER(address) WTFAnnotateHappensAfter(__FILE__, __LINE__, address) +#define WTF_ANNOTATE_HAPPENS_BEFORE(address) \ + WTFAnnotateHappensBefore(__FILE__, __LINE__, address) +#define WTF_ANNOTATE_HAPPENS_AFTER(address) \ + WTFAnnotateHappensAfter(__FILE__, __LINE__, address) #ifdef __cplusplus extern "C" { #endif /* Don't use these directly, use the above macros instead. */ -WTF_EXPORT void WTFAnnotateBenignRaceSized(const char* file, int line, const volatile void* memory, long size, const char* description); -WTF_EXPORT void WTFAnnotateHappensBefore(const char* file, int line, const volatile void* address); -WTF_EXPORT void WTFAnnotateHappensAfter(const char* file, int line, const volatile void* address); +WTF_EXPORT void WTFAnnotateBenignRaceSized(const char* file, + int line, + const volatile void* memory, + long size, + const char* description); +WTF_EXPORT void WTFAnnotateHappensBefore(const char* file, + int line, + const volatile void* address); +WTF_EXPORT void WTFAnnotateHappensAfter(const char* file, + int line, + const volatile void* address); #ifdef __cplusplus -} // extern "C" +} // extern "C" #endif -#else // USE(DYNAMIC_ANNOTATIONS) +#else // USE(DYNAMIC_ANNOTATIONS) /* These macros are empty when dynamic annotations are not enabled so you can * use them without affecting the performance of release binaries. */ #define WTF_ANNOTATE_BENIGN_RACE_SIZED(address, size, description) #define WTF_ANNOTATE_BENIGN_RACE(pointer, description) #define WTF_ANNOTATE_HAPPENS_BEFORE(address) #define WTF_ANNOTATE_HAPPENS_AFTER(address) -#endif // USE(DYNAMIC_ANNOTATIONS) +#endif // USE(DYNAMIC_ANNOTATIONS) #endif // SKY_ENGINE_WTF_DYNAMICANNOTATIONS_H_ diff --git a/sky/engine/wtf/EnumClass.h b/sky/engine/wtf/EnumClass.h index 0859ac15721ab..c36ca5108d2ed 100644 --- a/sky/engine/wtf/EnumClass.h +++ b/sky/engine/wtf/EnumClass.h @@ -50,15 +50,13 @@ namespace WTF { // ValueN // } ENUM_CLASS_END(MyEnums); // -// The ENUM_CLASS macros will use C++11's enum class if the compiler supports it. -// Otherwise, it will use the EnumClass template below. +// The ENUM_CLASS macros will use C++11's enum class if the compiler supports +// it. Otherwise, it will use the EnumClass template below. - -#define ENUM_CLASS(__enumName) \ - enum class __enumName +#define ENUM_CLASS(__enumName) enum class __enumName #define ENUM_CLASS_END(__enumName) -} // namespace WTF +} // namespace WTF #endif // SKY_ENGINE_WTF_ENUMCLASS_H_ diff --git a/sky/engine/wtf/FastAllocBase.h b/sky/engine/wtf/FastAllocBase.h index e475e07776d86..cadcffa1558ea 100644 --- a/sky/engine/wtf/FastAllocBase.h +++ b/sky/engine/wtf/FastAllocBase.h @@ -1,5 +1,6 @@ /* - * Copyright (C) 2008, 2009 Paul Pedriana . All rights reserved. + * Copyright (C) 2008, 2009 Paul Pedriana . + * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -29,7 +30,8 @@ #ifndef SKY_ENGINE_WTF_FASTALLOCBASE_H_ #define SKY_ENGINE_WTF_FASTALLOCBASE_H_ -// Provides customizable overrides of fastMalloc/fastFree and operator new/delete +// Provides customizable overrides of fastMalloc/fastFree and operator +// new/delete // // Provided functionality: // Macro: WTF_MAKE_FAST_ALLOCATED @@ -51,36 +53,24 @@ #include "flutter/sky/engine/wtf/FastMalloc.h" #include "flutter/sky/engine/wtf/StdLibExtras.h" -#define WTF_MAKE_FAST_ALLOCATED \ -public: \ - void* operator new(size_t, void* p) { return p; } \ - void* operator new[](size_t, void* p) { return p; } \ - \ - void* operator new(size_t size) \ - { \ - return ::WTF::fastMalloc(size); \ - } \ - \ - void operator delete(void* p) \ - { \ - ::WTF::fastFree(p); \ - } \ - \ - void* operator new[](size_t size) \ - { \ - return ::WTF::fastMalloc(size); \ - } \ - \ - void operator delete[](void* p) \ - { \ - ::WTF::fastFree(p); \ - } \ - void* operator new(size_t, NotNullTag, void* location) \ - { \ - ASSERT(location); \ - return location; \ - } \ -private: \ -typedef int __thisIsHereToForceASemicolonAfterThisMacro +#define WTF_MAKE_FAST_ALLOCATED \ + public: \ + void* operator new(size_t, void* p) { return p; } \ + void* operator new[](size_t, void* p) { return p; } \ + \ + void* operator new(size_t size) { return ::WTF::fastMalloc(size); } \ + \ + void operator delete(void* p) { ::WTF::fastFree(p); } \ + \ + void* operator new[](size_t size) { return ::WTF::fastMalloc(size); } \ + \ + void operator delete[](void* p) { ::WTF::fastFree(p); } \ + void* operator new(size_t, NotNullTag, void* location) { \ + ASSERT(location); \ + return location; \ + } \ + \ + private: \ + typedef int __thisIsHereToForceASemicolonAfterThisMacro #endif // SKY_ENGINE_WTF_FASTALLOCBASE_H_ diff --git a/sky/engine/wtf/FastMalloc.cpp b/sky/engine/wtf/FastMalloc.cpp index 971005b620f4d..71b582c3c8811 100644 --- a/sky/engine/wtf/FastMalloc.cpp +++ b/sky/engine/wtf/FastMalloc.cpp @@ -1,6 +1,7 @@ // Copyright (c) 2005, 2007, Google Inc. // All rights reserved. -// Copyright (C) 2005, 2006, 2007, 2008, 2009, 2011 Apple Inc. All rights reserved. +// Copyright (C) 2005, 2006, 2007, 2008, 2009, 2011 Apple Inc. +// All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are @@ -40,47 +41,41 @@ static PartitionAllocatorGeneric gPartition; static int gLock = 0; static bool gInitialized = false; -void* fastZeroedMalloc(size_t n) -{ - void* result = fastMalloc(n); - memset(result, 0, n); - return result; +void* fastZeroedMalloc(size_t n) { + void* result = fastMalloc(n); + memset(result, 0, n); + return result; } -char* fastStrDup(const char* src) -{ - size_t len = strlen(src) + 1; - char* dup = static_cast(fastMalloc(len)); - memcpy(dup, src, len); - return dup; +char* fastStrDup(const char* src) { + size_t len = strlen(src) + 1; + char* dup = static_cast(fastMalloc(len)); + memcpy(dup, src, len); + return dup; } -void fastMallocShutdown() -{ - gPartition.shutdown(); +void fastMallocShutdown() { + gPartition.shutdown(); } -void* fastMalloc(size_t n) -{ - if (UNLIKELY(!gInitialized)) { - spinLockLock(&gLock); - if (!gInitialized) { - gInitialized = true; - gPartition.init(); - } - spinLockUnlock(&gLock); +void* fastMalloc(size_t n) { + if (UNLIKELY(!gInitialized)) { + spinLockLock(&gLock); + if (!gInitialized) { + gInitialized = true; + gPartition.init(); } - return partitionAllocGeneric(gPartition.root(), n); + spinLockUnlock(&gLock); + } + return partitionAllocGeneric(gPartition.root(), n); } -void fastFree(void* p) -{ - partitionFreeGeneric(gPartition.root(), p); +void fastFree(void* p) { + partitionFreeGeneric(gPartition.root(), p); } -void* fastRealloc(void* p, size_t n) -{ - return partitionReallocGeneric(gPartition.root(), p, n); +void* fastRealloc(void* p, size_t n) { + return partitionReallocGeneric(gPartition.root(), p, n); } -} // namespace WTF +} // namespace WTF diff --git a/sky/engine/wtf/FastMalloc.h b/sky/engine/wtf/FastMalloc.h index c87054040aef9..7574553aade0a 100644 --- a/sky/engine/wtf/FastMalloc.h +++ b/sky/engine/wtf/FastMalloc.h @@ -38,7 +38,7 @@ WTF_EXPORT char* fastStrDup(const char*); WTF_EXPORT void fastFree(void*); -} // namespace WTF +} // namespace WTF using WTF::fastFree; using WTF::fastMalloc; diff --git a/sky/engine/wtf/FilePrintStream.cpp b/sky/engine/wtf/FilePrintStream.cpp index a788cedfd2196..0b0ee6102d955 100644 --- a/sky/engine/wtf/FilePrintStream.cpp +++ b/sky/engine/wtf/FilePrintStream.cpp @@ -28,36 +28,29 @@ namespace WTF { FilePrintStream::FilePrintStream(FILE* file, AdoptionMode adoptionMode) - : m_file(file) - , m_adoptionMode(adoptionMode) -{ -} + : m_file(file), m_adoptionMode(adoptionMode) {} -FilePrintStream::~FilePrintStream() -{ - if (m_adoptionMode == Borrow) - return; - fclose(m_file); +FilePrintStream::~FilePrintStream() { + if (m_adoptionMode == Borrow) + return; + fclose(m_file); } -PassOwnPtr FilePrintStream::open(const char* filename, const char* mode) -{ - FILE* file = fopen(filename, mode); - if (!file) - return PassOwnPtr(); +PassOwnPtr FilePrintStream::open(const char* filename, + const char* mode) { + FILE* file = fopen(filename, mode); + if (!file) + return PassOwnPtr(); - return adoptPtr(new FilePrintStream(file)); + return adoptPtr(new FilePrintStream(file)); } -void FilePrintStream::vprintf(const char* format, va_list argList) -{ - vfprintf(m_file, format, argList); +void FilePrintStream::vprintf(const char* format, va_list argList) { + vfprintf(m_file, format, argList); } -void FilePrintStream::flush() -{ - fflush(m_file); +void FilePrintStream::flush() { + fflush(m_file); } -} // namespace WTF - +} // namespace WTF diff --git a/sky/engine/wtf/FilePrintStream.h b/sky/engine/wtf/FilePrintStream.h index bf3045779a295..5d26469e197ba 100644 --- a/sky/engine/wtf/FilePrintStream.h +++ b/sky/engine/wtf/FilePrintStream.h @@ -33,30 +33,28 @@ namespace WTF { class WTF_EXPORT FilePrintStream final : public PrintStream { -public: - enum AdoptionMode { - Adopt, - Borrow - }; + public: + enum AdoptionMode { Adopt, Borrow }; - FilePrintStream(FILE*, AdoptionMode = Adopt); - virtual ~FilePrintStream(); + FilePrintStream(FILE*, AdoptionMode = Adopt); + virtual ~FilePrintStream(); - static PassOwnPtr open(const char* filename, const char* mode); + static PassOwnPtr open(const char* filename, + const char* mode); - FILE* file() { return m_file; } + FILE* file() { return m_file; } - virtual void vprintf(const char* format, va_list) override WTF_ATTRIBUTE_PRINTF(2, 0); - virtual void flush() override; + virtual void vprintf(const char* format, va_list) override + WTF_ATTRIBUTE_PRINTF(2, 0); + virtual void flush() override; -private: - FILE* m_file; - AdoptionMode m_adoptionMode; + private: + FILE* m_file; + AdoptionMode m_adoptionMode; }; -} // namespace WTF +} // namespace WTF using WTF::FilePrintStream; #endif // SKY_ENGINE_WTF_FILEPRINTSTREAM_H_ - diff --git a/sky/engine/wtf/Forward.h b/sky/engine/wtf/Forward.h index ef53768c4bf36..38eb2805f83b0 100644 --- a/sky/engine/wtf/Forward.h +++ b/sky/engine/wtf/Forward.h @@ -24,22 +24,29 @@ #include namespace WTF { - template class Function; - template class OwnPtr; - template class PassOwnPtr; - template class PassRefPtr; - template class RefPtr; - template class Vector; +template +class Function; +template +class OwnPtr; +template +class PassOwnPtr; +template +class PassRefPtr; +template +class RefPtr; +template +class Vector; - class AtomicString; - class CString; - template - class SizeSpecificPartitionAllocator; - class String; - template class StringBuffer; - class StringBuilder; - class StringImpl; -} +class AtomicString; +class CString; +template +class SizeSpecificPartitionAllocator; +class String; +template +class StringBuffer; +class StringBuilder; +class StringImpl; +} // namespace WTF using WTF::Function; using WTF::OwnPtr; diff --git a/sky/engine/wtf/GetPtr.h b/sky/engine/wtf/GetPtr.h index efb849ddbb0b6..72f323380b0c2 100644 --- a/sky/engine/wtf/GetPtr.h +++ b/sky/engine/wtf/GetPtr.h @@ -23,16 +23,16 @@ namespace WTF { - template inline T* getPtr(T* p) - { - return p; - } +template +inline T* getPtr(T* p) { + return p; +} - template inline T* getPtr(T& p) - { - return &p; - } +template +inline T* getPtr(T& p) { + return &p; +} -} // namespace WTF +} // namespace WTF #endif // SKY_ENGINE_WTF_GETPTR_H_ diff --git a/sky/engine/wtf/HashCountedSet.h b/sky/engine/wtf/HashCountedSet.h index e9d6d1d5f6b83..0272523bc9ea7 100644 --- a/sky/engine/wtf/HashCountedSet.h +++ b/sky/engine/wtf/HashCountedSet.h @@ -27,125 +27,141 @@ namespace WTF { - // An unordered hash set that keeps track of how many times you added an - // item to the set. The iterators have fields ->key and ->value that return - // the set members and their counts, respectively. - template< - typename Value, - typename HashFunctions = typename DefaultHash::Hash, - typename Traits = HashTraits, - typename Allocator = DefaultAllocator > class HashCountedSet { - WTF_USE_ALLOCATOR(HashCountedSet, Allocator); - private: - typedef HashMap, Allocator> ImplType; - public: - typedef Value ValueType; - typedef typename ImplType::iterator iterator; - typedef typename ImplType::const_iterator const_iterator; - typedef typename ImplType::AddResult AddResult; - - HashCountedSet() {} - - void swap(HashCountedSet& other) { m_impl.swap(other.m_impl); } - - unsigned size() const { return m_impl.size(); } - unsigned capacity() const { return m_impl.capacity(); } - bool isEmpty() const { return m_impl.isEmpty(); } - - // Iterators iterate over pairs of values (called key) and counts (called value). - iterator begin() { return m_impl.begin(); } - iterator end() { return m_impl.end(); } - const_iterator begin() const { return m_impl.begin(); } - const_iterator end() const { return m_impl.end(); } - - iterator find(const ValueType& value) { return m_impl.find(value); } - const_iterator find(const ValueType& value) const { return m_impl.find(value); } - bool contains(const ValueType& value ) const { return m_impl.contains(value); } - unsigned count(const ValueType& value ) const { return m_impl.get(value); } - - // Increases the count if an equal value is already present - // the return value is a pair of an iterator to the new value's - // location, and a bool that is true if an new entry was added. - AddResult add(const ValueType&); - - // Reduces the count of the value, and removes it if count - // goes down to zero, returns true if the value is removed. - bool remove(const ValueType& value) { return remove(find(value)); } - bool remove(iterator); - - // Removes the value, regardless of its count. - void removeAll(const ValueType& value) { removeAll(find(value)); } - void removeAll(iterator); - - // Clears the whole set. - void clear() { m_impl.clear(); } - - private: - ImplType m_impl; - }; - - template - inline typename HashCountedSet::AddResult HashCountedSet::add(const ValueType& value) - { - AddResult result = m_impl.add(value, 0); - ++result.storedValue->value; - return result; - } - - template - inline bool HashCountedSet::remove(iterator it) - { - if (it == end()) - return false; - - unsigned oldVal = it->value; - ASSERT(oldVal); - unsigned newVal = oldVal - 1; - if (newVal) { - it->value = newVal; - return false; - } - - m_impl.remove(it); - return true; - } - - template - inline void HashCountedSet::removeAll(iterator it) - { - if (it == end()) - return; - - m_impl.remove(it); - } - - template - inline void copyToVector(const HashCountedSet& collection, VectorType& vector) - { - typedef typename HashCountedSet::const_iterator iterator; - - vector.resize(collection.size()); - - iterator it = collection.begin(); - iterator end = collection.end(); - for (unsigned i = 0; it != end; ++it, ++i) - vector[i] = *it; - } - - template - inline void copyToVector(const HashCountedSet& collection, Vector& vector) - { - typedef typename HashCountedSet::const_iterator iterator; - - vector.resize(collection.size()); - - iterator it = collection.begin(); - iterator end = collection.end(); - for (unsigned i = 0; it != end; ++it, ++i) - vector[i] = (*it).key; - } - -} // namespace WTF +// An unordered hash set that keeps track of how many times you added an +// item to the set. The iterators have fields ->key and ->value that return +// the set members and their counts, respectively. +template ::Hash, + typename Traits = HashTraits, + typename Allocator = DefaultAllocator> +class HashCountedSet { + WTF_USE_ALLOCATOR(HashCountedSet, Allocator); + + private: + typedef HashMap, + Allocator> + ImplType; + + public: + typedef Value ValueType; + typedef typename ImplType::iterator iterator; + typedef typename ImplType::const_iterator const_iterator; + typedef typename ImplType::AddResult AddResult; + + HashCountedSet() {} + + void swap(HashCountedSet& other) { m_impl.swap(other.m_impl); } + + unsigned size() const { return m_impl.size(); } + unsigned capacity() const { return m_impl.capacity(); } + bool isEmpty() const { return m_impl.isEmpty(); } + + // Iterators iterate over pairs of values (called key) and counts (called + // value). + iterator begin() { return m_impl.begin(); } + iterator end() { return m_impl.end(); } + const_iterator begin() const { return m_impl.begin(); } + const_iterator end() const { return m_impl.end(); } + + iterator find(const ValueType& value) { return m_impl.find(value); } + const_iterator find(const ValueType& value) const { + return m_impl.find(value); + } + bool contains(const ValueType& value) const { return m_impl.contains(value); } + unsigned count(const ValueType& value) const { return m_impl.get(value); } + + // Increases the count if an equal value is already present + // the return value is a pair of an iterator to the new value's + // location, and a bool that is true if an new entry was added. + AddResult add(const ValueType&); + + // Reduces the count of the value, and removes it if count + // goes down to zero, returns true if the value is removed. + bool remove(const ValueType& value) { return remove(find(value)); } + bool remove(iterator); + + // Removes the value, regardless of its count. + void removeAll(const ValueType& value) { removeAll(find(value)); } + void removeAll(iterator); + + // Clears the whole set. + void clear() { m_impl.clear(); } + + private: + ImplType m_impl; +}; + +template +inline typename HashCountedSet::AddResult +HashCountedSet::add(const ValueType& value) { + AddResult result = m_impl.add(value, 0); + ++result.storedValue->value; + return result; +} + +template +inline bool HashCountedSet::remove(iterator it) { + if (it == end()) + return false; + + unsigned oldVal = it->value; + ASSERT(oldVal); + unsigned newVal = oldVal - 1; + if (newVal) { + it->value = newVal; + return false; + } + + m_impl.remove(it); + return true; +} + +template +inline void HashCountedSet::removeAll(iterator it) { + if (it == end()) + return; + + m_impl.remove(it); +} + +template +inline void copyToVector(const HashCountedSet& collection, + VectorType& vector) { + typedef typename HashCountedSet::const_iterator iterator; + + vector.resize(collection.size()); + + iterator it = collection.begin(); + iterator end = collection.end(); + for (unsigned i = 0; it != end; ++it, ++i) + vector[i] = *it; +} + +template +inline void copyToVector( + const HashCountedSet& collection, + Vector& vector) { + typedef typename HashCountedSet::const_iterator iterator; + + vector.resize(collection.size()); + + iterator it = collection.begin(); + iterator end = collection.end(); + for (unsigned i = 0; it != end; ++it, ++i) + vector[i] = (*it).key; +} + +} // namespace WTF using WTF::HashCountedSet; diff --git a/sky/engine/wtf/HashFunctions.h b/sky/engine/wtf/HashFunctions.h index 0f20885ad974c..054bf7614ac29 100644 --- a/sky/engine/wtf/HashFunctions.h +++ b/sky/engine/wtf/HashFunctions.h @@ -28,211 +28,340 @@ namespace WTF { - template struct IntTypes; - template<> struct IntTypes<1> { typedef int8_t SignedType; typedef uint8_t UnsignedType; }; - template<> struct IntTypes<2> { typedef int16_t SignedType; typedef uint16_t UnsignedType; }; - template<> struct IntTypes<4> { typedef int32_t SignedType; typedef uint32_t UnsignedType; }; - template<> struct IntTypes<8> { typedef int64_t SignedType; typedef uint64_t UnsignedType; }; - - // integer hash function - - // Thomas Wang's 32 Bit Mix Function: http://www.cris.com/~Ttwang/tech/inthash.htm - inline unsigned intHash(uint8_t key8) - { - unsigned key = key8; - key += ~(key << 15); - key ^= (key >> 10); - key += (key << 3); - key ^= (key >> 6); - key += ~(key << 11); - key ^= (key >> 16); - return key; - } - - // Thomas Wang's 32 Bit Mix Function: http://www.cris.com/~Ttwang/tech/inthash.htm - inline unsigned intHash(uint16_t key16) - { - unsigned key = key16; - key += ~(key << 15); - key ^= (key >> 10); - key += (key << 3); - key ^= (key >> 6); - key += ~(key << 11); - key ^= (key >> 16); - return key; - } - - // Thomas Wang's 32 Bit Mix Function: http://www.cris.com/~Ttwang/tech/inthash.htm - inline unsigned intHash(uint32_t key) - { - key += ~(key << 15); - key ^= (key >> 10); - key += (key << 3); - key ^= (key >> 6); - key += ~(key << 11); - key ^= (key >> 16); - return key; - } - - // Thomas Wang's 64 bit Mix Function: http://www.cris.com/~Ttwang/tech/inthash.htm - inline unsigned intHash(uint64_t key) - { - key += ~(key << 32); - key ^= (key >> 22); - key += ~(key << 13); - key ^= (key >> 8); - key += (key << 3); - key ^= (key >> 15); - key += ~(key << 27); - key ^= (key >> 31); - return static_cast(key); - } - - // Compound integer hash method: http://opendatastructures.org/versions/edition-0.1d/ods-java/node33.html#SECTION00832000000000000000 - inline unsigned pairIntHash(unsigned key1, unsigned key2) - { - unsigned shortRandom1 = 277951225; // A random 32-bit value. - unsigned shortRandom2 = 95187966; // A random 32-bit value. - uint64_t longRandom = 19248658165952622LL; // A random 64-bit value. - - uint64_t product = longRandom * (shortRandom1 * key1 + shortRandom2 * key2); - unsigned highBits = static_cast(product >> (sizeof(uint64_t) - sizeof(unsigned))); - return highBits; - } - - template struct IntHash { - static unsigned hash(T key) { return intHash(static_cast::UnsignedType>(key)); } - static bool equal(T a, T b) { return a == b; } - static const bool safeToCompareToEmptyOrDeleted = true; - }; - - template struct FloatHash { - typedef typename IntTypes::UnsignedType Bits; - static unsigned hash(T key) - { - return intHash(bitwise_cast(key)); - } - static bool equal(T a, T b) - { - return bitwise_cast(a) == bitwise_cast(b); - } - static const bool safeToCompareToEmptyOrDeleted = true; - }; - - // pointer identity hash function - - template struct PtrHash { - static unsigned hash(T key) - { - return IntHash::hash(reinterpret_cast(key)); - } - static bool equal(T a, T b) { return a == b; } - static bool equal(std::nullptr_t, T b) { return b == 0; } - static bool equal(T a, std::nullptr_t) { return a == 0; } - static const bool safeToCompareToEmptyOrDeleted = true; - }; - template struct PtrHash > : PtrHash { - using PtrHash::hash; - static unsigned hash(const RefPtr

& key) { return hash(key.get()); } - static unsigned hash(const PassRefPtr

& key) { return hash(key.get()); } - using PtrHash::equal; - static bool equal(const RefPtr

& a, const RefPtr

& b) { return a == b; } - static bool equal(P* a, const RefPtr

& b) { return a == b; } - static bool equal(const RefPtr

& a, P* b) { return a == b; } - static bool equal(const RefPtr

& a, const PassRefPtr

& b) { return a == b; } - }; - template struct PtrHash > : PtrHash { - using PtrHash::hash; - static unsigned hash(const RawPtr

& key) { return hash(key.get()); } - using PtrHash::equal; - static bool equal(const RawPtr

& a, const RawPtr

& b) { return a == b; } - static bool equal(P* a, const RawPtr

& b) { return a == b; } - static bool equal(const RawPtr

& a, P* b) { return a == b; } - }; - template struct PtrHash > : PtrHash { - using PtrHash::hash; - static unsigned hash(const OwnPtr

& key) { return hash(key.get()); } - static unsigned hash(const PassOwnPtr

& key) { return hash(key.get()); } - - static bool equal(const OwnPtr

& a, const OwnPtr

& b) - { - return a.get() == b.get(); - } - static bool equal(const OwnPtr

& a, P* b) { return a == b; } - static bool equal(const OwnPtr

& a, const PassOwnPtr

& b) - { - return a.get() == b.get(); - } - }; - - // default hash function for each type - - template struct DefaultHash; - - template struct PairHash { - static unsigned hash(const std::pair& p) - { - return pairIntHash(DefaultHash::Hash::hash(p.first), DefaultHash::Hash::hash(p.second)); - } - static bool equal(const std::pair& a, const std::pair& b) - { - return DefaultHash::Hash::equal(a.first, b.first) && DefaultHash::Hash::equal(a.second, b.second); - } - static const bool safeToCompareToEmptyOrDeleted = DefaultHash::Hash::safeToCompareToEmptyOrDeleted - && DefaultHash::Hash::safeToCompareToEmptyOrDeleted; - }; - - template struct IntPairHash { - static unsigned hash(const std::pair& p) { return pairIntHash(p.first, p.second); } - static bool equal(const std::pair& a, const std::pair& b) { return PairHash::equal(a, b); } - static const bool safeToCompareToEmptyOrDeleted = PairHash::safeToCompareToEmptyOrDeleted; - }; - - // make IntHash the default hash function for many integer types - - template<> struct DefaultHash { typedef IntHash Hash; }; - template<> struct DefaultHash { typedef IntHash Hash; }; - template<> struct DefaultHash { typedef IntHash Hash; }; - template<> struct DefaultHash { typedef IntHash Hash; }; - template<> struct DefaultHash { typedef IntHash Hash; }; - template<> struct DefaultHash { typedef IntHash Hash; }; - template<> struct DefaultHash { typedef IntHash Hash; }; - template<> struct DefaultHash { typedef IntHash Hash; }; - template<> struct DefaultHash { typedef IntHash Hash; }; - template<> struct DefaultHash { typedef FloatHash Hash; }; - template<> struct DefaultHash { typedef FloatHash Hash; }; - - // make PtrHash the default hash function for pointer types that don't specialize - - template struct DefaultHash { typedef PtrHash Hash; }; - template struct DefaultHash > { typedef PtrHash > Hash; }; - template struct DefaultHash > { typedef PtrHash > Hash; }; - template struct DefaultHash > { typedef PtrHash > Hash; }; - - // make IntPairHash the default hash function for pairs of (at most) 32-bit integers. - - template<> struct DefaultHash > { typedef IntPairHash Hash; }; - template<> struct DefaultHash > { typedef IntPairHash Hash; }; - template<> struct DefaultHash > { typedef IntPairHash Hash; }; - template<> struct DefaultHash > { typedef IntPairHash Hash; }; - template<> struct DefaultHash > { typedef IntPairHash Hash; }; - template<> struct DefaultHash > { typedef IntPairHash Hash; }; - template<> struct DefaultHash > { typedef IntPairHash Hash; }; - template<> struct DefaultHash > { typedef IntPairHash Hash; }; - template<> struct DefaultHash > { typedef IntPairHash Hash; }; - template<> struct DefaultHash > { typedef IntPairHash Hash; }; - template<> struct DefaultHash > { typedef IntPairHash Hash; }; - template<> struct DefaultHash > { typedef IntPairHash Hash; }; - template<> struct DefaultHash > { typedef IntPairHash Hash; }; - template<> struct DefaultHash > { typedef IntPairHash Hash; }; - template<> struct DefaultHash > { typedef IntPairHash Hash; }; - template<> struct DefaultHash > { typedef IntPairHash Hash; }; - - // make PairHash the default hash function for pairs of arbitrary values. - - template struct DefaultHash > { typedef PairHash Hash; }; - -} // namespace WTF +template +struct IntTypes; +template <> +struct IntTypes<1> { + typedef int8_t SignedType; + typedef uint8_t UnsignedType; +}; +template <> +struct IntTypes<2> { + typedef int16_t SignedType; + typedef uint16_t UnsignedType; +}; +template <> +struct IntTypes<4> { + typedef int32_t SignedType; + typedef uint32_t UnsignedType; +}; +template <> +struct IntTypes<8> { + typedef int64_t SignedType; + typedef uint64_t UnsignedType; +}; + +// integer hash function + +// Thomas Wang's 32 Bit Mix Function: +// http://www.cris.com/~Ttwang/tech/inthash.htm +inline unsigned intHash(uint8_t key8) { + unsigned key = key8; + key += ~(key << 15); + key ^= (key >> 10); + key += (key << 3); + key ^= (key >> 6); + key += ~(key << 11); + key ^= (key >> 16); + return key; +} + +// Thomas Wang's 32 Bit Mix Function: +// http://www.cris.com/~Ttwang/tech/inthash.htm +inline unsigned intHash(uint16_t key16) { + unsigned key = key16; + key += ~(key << 15); + key ^= (key >> 10); + key += (key << 3); + key ^= (key >> 6); + key += ~(key << 11); + key ^= (key >> 16); + return key; +} + +// Thomas Wang's 32 Bit Mix Function: +// http://www.cris.com/~Ttwang/tech/inthash.htm +inline unsigned intHash(uint32_t key) { + key += ~(key << 15); + key ^= (key >> 10); + key += (key << 3); + key ^= (key >> 6); + key += ~(key << 11); + key ^= (key >> 16); + return key; +} + +// Thomas Wang's 64 bit Mix Function: +// http://www.cris.com/~Ttwang/tech/inthash.htm +inline unsigned intHash(uint64_t key) { + key += ~(key << 32); + key ^= (key >> 22); + key += ~(key << 13); + key ^= (key >> 8); + key += (key << 3); + key ^= (key >> 15); + key += ~(key << 27); + key ^= (key >> 31); + return static_cast(key); +} + +// Compound integer hash method: +// http://opendatastructures.org/versions/edition-0.1d/ods-java/node33.html#SECTION00832000000000000000 +inline unsigned pairIntHash(unsigned key1, unsigned key2) { + unsigned shortRandom1 = 277951225; // A random 32-bit value. + unsigned shortRandom2 = 95187966; // A random 32-bit value. + uint64_t longRandom = 19248658165952622LL; // A random 64-bit value. + + uint64_t product = longRandom * (shortRandom1 * key1 + shortRandom2 * key2); + unsigned highBits = + static_cast(product >> (sizeof(uint64_t) - sizeof(unsigned))); + return highBits; +} + +template +struct IntHash { + static unsigned hash(T key) { + return intHash( + static_cast::UnsignedType>(key)); + } + static bool equal(T a, T b) { return a == b; } + static const bool safeToCompareToEmptyOrDeleted = true; +}; + +template +struct FloatHash { + typedef typename IntTypes::UnsignedType Bits; + static unsigned hash(T key) { return intHash(bitwise_cast(key)); } + static bool equal(T a, T b) { + return bitwise_cast(a) == bitwise_cast(b); + } + static const bool safeToCompareToEmptyOrDeleted = true; +}; + +// pointer identity hash function + +template +struct PtrHash { + static unsigned hash(T key) { + return IntHash::hash(reinterpret_cast(key)); + } + static bool equal(T a, T b) { return a == b; } + static bool equal(std::nullptr_t, T b) { return b == 0; } + static bool equal(T a, std::nullptr_t) { return a == 0; } + static const bool safeToCompareToEmptyOrDeleted = true; +}; +template +struct PtrHash> : PtrHash { + using PtrHash::hash; + static unsigned hash(const RefPtr

& key) { return hash(key.get()); } + static unsigned hash(const PassRefPtr

& key) { return hash(key.get()); } + using PtrHash::equal; + static bool equal(const RefPtr

& a, const RefPtr

& b) { return a == b; } + static bool equal(P* a, const RefPtr

& b) { return a == b; } + static bool equal(const RefPtr

& a, P* b) { return a == b; } + static bool equal(const RefPtr

& a, const PassRefPtr

& b) { + return a == b; + } +}; +template +struct PtrHash> : PtrHash { + using PtrHash::hash; + static unsigned hash(const RawPtr

& key) { return hash(key.get()); } + using PtrHash::equal; + static bool equal(const RawPtr

& a, const RawPtr

& b) { return a == b; } + static bool equal(P* a, const RawPtr

& b) { return a == b; } + static bool equal(const RawPtr

& a, P* b) { return a == b; } +}; +template +struct PtrHash> : PtrHash { + using PtrHash::hash; + static unsigned hash(const OwnPtr

& key) { return hash(key.get()); } + static unsigned hash(const PassOwnPtr

& key) { return hash(key.get()); } + + static bool equal(const OwnPtr

& a, const OwnPtr

& b) { + return a.get() == b.get(); + } + static bool equal(const OwnPtr

& a, P* b) { return a == b; } + static bool equal(const OwnPtr

& a, const PassOwnPtr

& b) { + return a.get() == b.get(); + } +}; + +// default hash function for each type + +template +struct DefaultHash; + +template +struct PairHash { + static unsigned hash(const std::pair& p) { + return pairIntHash(DefaultHash::Hash::hash(p.first), + DefaultHash::Hash::hash(p.second)); + } + static bool equal(const std::pair& a, const std::pair& b) { + return DefaultHash::Hash::equal(a.first, b.first) && + DefaultHash::Hash::equal(a.second, b.second); + } + static const bool safeToCompareToEmptyOrDeleted = + DefaultHash::Hash::safeToCompareToEmptyOrDeleted && + DefaultHash::Hash::safeToCompareToEmptyOrDeleted; +}; + +template +struct IntPairHash { + static unsigned hash(const std::pair& p) { + return pairIntHash(p.first, p.second); + } + static bool equal(const std::pair& a, const std::pair& b) { + return PairHash::equal(a, b); + } + static const bool safeToCompareToEmptyOrDeleted = + PairHash::safeToCompareToEmptyOrDeleted; +}; + +// make IntHash the default hash function for many integer types + +template <> +struct DefaultHash { + typedef IntHash Hash; +}; +template <> +struct DefaultHash { + typedef IntHash Hash; +}; +template <> +struct DefaultHash { + typedef IntHash Hash; +}; +template <> +struct DefaultHash { + typedef IntHash Hash; +}; +template <> +struct DefaultHash { + typedef IntHash Hash; +}; +template <> +struct DefaultHash { + typedef IntHash Hash; +}; +template <> +struct DefaultHash { + typedef IntHash Hash; +}; +template <> +struct DefaultHash { + typedef IntHash Hash; +}; +template <> +struct DefaultHash { + typedef IntHash Hash; +}; +template <> +struct DefaultHash { + typedef FloatHash Hash; +}; +template <> +struct DefaultHash { + typedef FloatHash Hash; +}; + +// make PtrHash the default hash function for pointer types that don't +// specialize + +template +struct DefaultHash { + typedef PtrHash Hash; +}; +template +struct DefaultHash> { + typedef PtrHash> Hash; +}; +template +struct DefaultHash> { + typedef PtrHash> Hash; +}; +template +struct DefaultHash> { + typedef PtrHash> Hash; +}; + +// make IntPairHash the default hash function for pairs of (at most) 32-bit +// integers. + +template <> +struct DefaultHash> { + typedef IntPairHash Hash; +}; +template <> +struct DefaultHash> { + typedef IntPairHash Hash; +}; +template <> +struct DefaultHash> { + typedef IntPairHash Hash; +}; +template <> +struct DefaultHash> { + typedef IntPairHash Hash; +}; +template <> +struct DefaultHash> { + typedef IntPairHash Hash; +}; +template <> +struct DefaultHash> { + typedef IntPairHash Hash; +}; +template <> +struct DefaultHash> { + typedef IntPairHash Hash; +}; +template <> +struct DefaultHash> { + typedef IntPairHash Hash; +}; +template <> +struct DefaultHash> { + typedef IntPairHash Hash; +}; +template <> +struct DefaultHash> { + typedef IntPairHash Hash; +}; +template <> +struct DefaultHash> { + typedef IntPairHash Hash; +}; +template <> +struct DefaultHash> { + typedef IntPairHash Hash; +}; +template <> +struct DefaultHash> { + typedef IntPairHash Hash; +}; +template <> +struct DefaultHash> { + typedef IntPairHash Hash; +}; +template <> +struct DefaultHash> { + typedef IntPairHash Hash; +}; +template <> +struct DefaultHash> { + typedef IntPairHash Hash; +}; + +// make PairHash the default hash function for pairs of arbitrary values. + +template +struct DefaultHash> { + typedef PairHash Hash; +}; + +} // namespace WTF using WTF::DefaultHash; using WTF::IntHash; diff --git a/sky/engine/wtf/HashIterators.h b/sky/engine/wtf/HashIterators.h index 709343aee9ba8..a6f4f9cf6bb34 100644 --- a/sky/engine/wtf/HashIterators.h +++ b/sky/engine/wtf/HashIterators.h @@ -28,191 +28,238 @@ namespace WTF { - template struct HashTableConstKeysIterator; - template struct HashTableConstValuesIterator; - template struct HashTableKeysIterator; - template struct HashTableValuesIterator; - - template struct HashTableConstIteratorAdapter > { - private: - typedef KeyValuePair ValueType; - public: - typedef HashTableConstKeysIterator Keys; - typedef HashTableConstValuesIterator Values; - - HashTableConstIteratorAdapter() {} - HashTableConstIteratorAdapter(const typename HashTableType::const_iterator& impl) : m_impl(impl) {} - - const ValueType* get() const { return (const ValueType*)m_impl.get(); } - const ValueType& operator*() const { return *get(); } - const ValueType* operator->() const { return get(); } - - HashTableConstIteratorAdapter& operator++() { ++m_impl; return *this; } - // postfix ++ intentionally omitted - - Keys keys() { return Keys(*this); } - Values values() { return Values(*this); } - - typename HashTableType::const_iterator m_impl; - }; - - template struct HashTableIteratorAdapter > { - private: - typedef KeyValuePair ValueType; - public: - typedef HashTableKeysIterator Keys; - typedef HashTableValuesIterator Values; - - HashTableIteratorAdapter() {} - HashTableIteratorAdapter(const typename HashTableType::iterator& impl) : m_impl(impl) {} - - ValueType* get() const { return (ValueType*)m_impl.get(); } - ValueType& operator*() const { return *get(); } - ValueType* operator->() const { return get(); } - - HashTableIteratorAdapter& operator++() { ++m_impl; return *this; } - // postfix ++ intentionally omitted - - operator HashTableConstIteratorAdapter() { - typename HashTableType::const_iterator i = m_impl; - return i; - } - - Keys keys() { return Keys(*this); } - Values values() { return Values(*this); } - - typename HashTableType::iterator m_impl; - }; - - template struct HashTableConstKeysIterator { - private: - typedef HashTableConstIteratorAdapter > ConstIterator; - - public: - HashTableConstKeysIterator(const ConstIterator& impl) : m_impl(impl) {} - - const KeyType* get() const { return &(m_impl.get()->key); } - const KeyType& operator*() const { return *get(); } - const KeyType* operator->() const { return get(); } - - HashTableConstKeysIterator& operator++() { ++m_impl; return *this; } - // postfix ++ intentionally omitted - - ConstIterator m_impl; - }; - - template struct HashTableConstValuesIterator { - private: - typedef HashTableConstIteratorAdapter > ConstIterator; - - public: - HashTableConstValuesIterator(const ConstIterator& impl) : m_impl(impl) {} - - const MappedType* get() const { return &(m_impl.get()->value); } - const MappedType& operator*() const { return *get(); } - const MappedType* operator->() const { return get(); } - - HashTableConstValuesIterator& operator++() { ++m_impl; return *this; } - // postfix ++ intentionally omitted - - ConstIterator m_impl; - }; - - template struct HashTableKeysIterator { - private: - typedef HashTableIteratorAdapter > Iterator; - typedef HashTableConstIteratorAdapter > ConstIterator; - - public: - HashTableKeysIterator(const Iterator& impl) : m_impl(impl) {} - - KeyType* get() const { return &(m_impl.get()->key); } - KeyType& operator*() const { return *get(); } - KeyType* operator->() const { return get(); } - - HashTableKeysIterator& operator++() { ++m_impl; return *this; } - // postfix ++ intentionally omitted - - operator HashTableConstKeysIterator() { - ConstIterator i = m_impl; - return i; - } - - Iterator m_impl; - }; - - template struct HashTableValuesIterator { - private: - typedef HashTableIteratorAdapter > Iterator; - typedef HashTableConstIteratorAdapter > ConstIterator; - - public: - HashTableValuesIterator(const Iterator& impl) : m_impl(impl) {} - - MappedType* get() const { return &(m_impl.get()->value); } - MappedType& operator*() const { return *get(); } - MappedType* operator->() const { return get(); } - - HashTableValuesIterator& operator++() { ++m_impl; return *this; } - // postfix ++ intentionally omitted - - operator HashTableConstValuesIterator() { - ConstIterator i = m_impl; - return i; - } - - Iterator m_impl; - }; - - template - inline bool operator==(const HashTableConstKeysIterator& a, const HashTableConstKeysIterator& b) - { - return a.m_impl == b.m_impl; - } - - template - inline bool operator!=(const HashTableConstKeysIterator& a, const HashTableConstKeysIterator& b) - { - return a.m_impl != b.m_impl; - } - - template - inline bool operator==(const HashTableConstValuesIterator& a, const HashTableConstValuesIterator& b) - { - return a.m_impl == b.m_impl; - } - - template - inline bool operator!=(const HashTableConstValuesIterator& a, const HashTableConstValuesIterator& b) - { - return a.m_impl != b.m_impl; - } - - template - inline bool operator==(const HashTableKeysIterator& a, const HashTableKeysIterator& b) - { - return a.m_impl == b.m_impl; - } - - template - inline bool operator!=(const HashTableKeysIterator& a, const HashTableKeysIterator& b) - { - return a.m_impl != b.m_impl; - } - - template - inline bool operator==(const HashTableValuesIterator& a, const HashTableValuesIterator& b) - { - return a.m_impl == b.m_impl; - } - - template - inline bool operator!=(const HashTableValuesIterator& a, const HashTableValuesIterator& b) - { - return a.m_impl != b.m_impl; - } - - -} // namespace WTF +template +struct HashTableConstKeysIterator; +template +struct HashTableConstValuesIterator; +template +struct HashTableKeysIterator; +template +struct HashTableValuesIterator; + +template +struct HashTableConstIteratorAdapter> { + private: + typedef KeyValuePair ValueType; + + public: + typedef HashTableConstKeysIterator Keys; + typedef HashTableConstValuesIterator + Values; + + HashTableConstIteratorAdapter() {} + HashTableConstIteratorAdapter( + const typename HashTableType::const_iterator& impl) + : m_impl(impl) {} + + const ValueType* get() const { return (const ValueType*)m_impl.get(); } + const ValueType& operator*() const { return *get(); } + const ValueType* operator->() const { return get(); } + + HashTableConstIteratorAdapter& operator++() { + ++m_impl; + return *this; + } + // postfix ++ intentionally omitted + + Keys keys() { return Keys(*this); } + Values values() { return Values(*this); } + + typename HashTableType::const_iterator m_impl; +}; + +template +struct HashTableIteratorAdapter> { + private: + typedef KeyValuePair ValueType; + + public: + typedef HashTableKeysIterator Keys; + typedef HashTableValuesIterator Values; + + HashTableIteratorAdapter() {} + HashTableIteratorAdapter(const typename HashTableType::iterator& impl) + : m_impl(impl) {} + + ValueType* get() const { return (ValueType*)m_impl.get(); } + ValueType& operator*() const { return *get(); } + ValueType* operator->() const { return get(); } + + HashTableIteratorAdapter& operator++() { + ++m_impl; + return *this; + } + // postfix ++ intentionally omitted + + operator HashTableConstIteratorAdapter() { + typename HashTableType::const_iterator i = m_impl; + return i; + } + + Keys keys() { return Keys(*this); } + Values values() { return Values(*this); } + + typename HashTableType::iterator m_impl; +}; + +template +struct HashTableConstKeysIterator { + private: + typedef HashTableConstIteratorAdapter> + ConstIterator; + + public: + HashTableConstKeysIterator(const ConstIterator& impl) : m_impl(impl) {} + + const KeyType* get() const { return &(m_impl.get()->key); } + const KeyType& operator*() const { return *get(); } + const KeyType* operator->() const { return get(); } + + HashTableConstKeysIterator& operator++() { + ++m_impl; + return *this; + } + // postfix ++ intentionally omitted + + ConstIterator m_impl; +}; + +template +struct HashTableConstValuesIterator { + private: + typedef HashTableConstIteratorAdapter> + ConstIterator; + + public: + HashTableConstValuesIterator(const ConstIterator& impl) : m_impl(impl) {} + + const MappedType* get() const { return &(m_impl.get()->value); } + const MappedType& operator*() const { return *get(); } + const MappedType* operator->() const { return get(); } + + HashTableConstValuesIterator& operator++() { + ++m_impl; + return *this; + } + // postfix ++ intentionally omitted + + ConstIterator m_impl; +}; + +template +struct HashTableKeysIterator { + private: + typedef HashTableIteratorAdapter> + Iterator; + typedef HashTableConstIteratorAdapter> + ConstIterator; + + public: + HashTableKeysIterator(const Iterator& impl) : m_impl(impl) {} + + KeyType* get() const { return &(m_impl.get()->key); } + KeyType& operator*() const { return *get(); } + KeyType* operator->() const { return get(); } + + HashTableKeysIterator& operator++() { + ++m_impl; + return *this; + } + // postfix ++ intentionally omitted + + operator HashTableConstKeysIterator() { + ConstIterator i = m_impl; + return i; + } + + Iterator m_impl; +}; + +template +struct HashTableValuesIterator { + private: + typedef HashTableIteratorAdapter> + Iterator; + typedef HashTableConstIteratorAdapter> + ConstIterator; + + public: + HashTableValuesIterator(const Iterator& impl) : m_impl(impl) {} + + MappedType* get() const { return &(m_impl.get()->value); } + MappedType& operator*() const { return *get(); } + MappedType* operator->() const { return get(); } + + HashTableValuesIterator& operator++() { + ++m_impl; + return *this; + } + // postfix ++ intentionally omitted + + operator HashTableConstValuesIterator() { + ConstIterator i = m_impl; + return i; + } + + Iterator m_impl; +}; + +template +inline bool operator==(const HashTableConstKeysIterator& a, + const HashTableConstKeysIterator& b) { + return a.m_impl == b.m_impl; +} + +template +inline bool operator!=(const HashTableConstKeysIterator& a, + const HashTableConstKeysIterator& b) { + return a.m_impl != b.m_impl; +} + +template +inline bool operator==(const HashTableConstValuesIterator& a, + const HashTableConstValuesIterator& b) { + return a.m_impl == b.m_impl; +} + +template +inline bool operator!=(const HashTableConstValuesIterator& a, + const HashTableConstValuesIterator& b) { + return a.m_impl != b.m_impl; +} + +template +inline bool operator==(const HashTableKeysIterator& a, + const HashTableKeysIterator& b) { + return a.m_impl == b.m_impl; +} + +template +inline bool operator!=(const HashTableKeysIterator& a, + const HashTableKeysIterator& b) { + return a.m_impl != b.m_impl; +} + +template +inline bool operator==(const HashTableValuesIterator& a, + const HashTableValuesIterator& b) { + return a.m_impl == b.m_impl; +} + +template +inline bool operator!=(const HashTableValuesIterator& a, + const HashTableValuesIterator& b) { + return a.m_impl != b.m_impl; +} + +} // namespace WTF #endif // SKY_ENGINE_WTF_HASHITERATORS_H_ diff --git a/sky/engine/wtf/HashMap.h b/sky/engine/wtf/HashMap.h index f739513a8e1f9..f440406474084 100644 --- a/sky/engine/wtf/HashMap.h +++ b/sky/engine/wtf/HashMap.h @@ -26,472 +26,661 @@ namespace WTF { - template struct HashMapValueTraits; - - template struct ReferenceTypeMaker { - typedef T& ReferenceType; - }; - template struct ReferenceTypeMaker { - typedef T& ReferenceType; - }; - - struct KeyValuePairKeyExtractor { - template - static const typename T::KeyType& extract(const T& p) { return p.key; } - }; - - // Note: empty or deleted key values are not allowed, using them may lead to undefined behavior. - // For pointer keys this means that null pointers are not allowed unless you supply custom key traits. - template< - typename KeyArg, - typename MappedArg, - typename HashArg = typename DefaultHash::Hash, - typename KeyTraitsArg = HashTraits, - typename MappedTraitsArg = HashTraits, - typename Allocator = DefaultAllocator> - class HashMap { - WTF_USE_ALLOCATOR(HashMap, Allocator); - private: - typedef KeyTraitsArg KeyTraits; - typedef MappedTraitsArg MappedTraits; - typedef HashMapValueTraits ValueTraits; - - public: - typedef typename KeyTraits::TraitType KeyType; - typedef typename KeyTraits::PassInType KeyPassInType; - typedef const typename KeyTraits::PeekInType& KeyPeekInType; - typedef typename MappedTraits::TraitType MappedType; - typedef typename ValueTraits::TraitType ValueType; - - private: - typedef typename MappedTraits::PassInType MappedPassInType; - typedef typename MappedTraits::PassOutType MappedPassOutType; - typedef typename MappedTraits::PeekOutType MappedPeekType; - - typedef typename ReferenceTypeMaker::ReferenceType MappedPassInReferenceType; - - typedef HashArg HashFunctions; - - typedef HashTable HashTableType; - - class HashMapKeysProxy; - class HashMapValuesProxy; - - public: - typedef HashTableIteratorAdapter iterator; - typedef HashTableConstIteratorAdapter const_iterator; - typedef typename HashTableType::AddResult AddResult; - - public: - void swap(HashMap& ref) - { - m_impl.swap(ref.m_impl); - } - - void swap(typename Allocator::template OtherType::Type other) - { - HashMap& ref = Allocator::getOther(other); - m_impl.swap(ref.m_impl); - } - - unsigned size() const; - unsigned capacity() const; - bool isEmpty() const; - - // iterators iterate over pairs of keys and values - iterator begin(); - iterator end(); - const_iterator begin() const; - const_iterator end() const; - - HashMapKeysProxy& keys() { return static_cast(*this); } - const HashMapKeysProxy& keys() const { return static_cast(*this); } - - HashMapValuesProxy& values() { return static_cast(*this); } - const HashMapValuesProxy& values() const { return static_cast(*this); } - - iterator find(KeyPeekInType); - const_iterator find(KeyPeekInType) const; - bool contains(KeyPeekInType) const; - MappedPeekType get(KeyPeekInType) const; - - // replaces value but not key if key is already present - // return value is a pair of the iterator to the key location, - // and a boolean that's true if a new value was actually added - AddResult set(KeyPassInType, MappedPassInType); - - // does nothing if key is already present - // return value is a pair of the iterator to the key location, - // and a boolean that's true if a new value was actually added - AddResult add(KeyPassInType, MappedPassInType); - - void remove(KeyPeekInType); - void remove(iterator); - void clear(); - template - void removeAll(const Collection& toBeRemoved) { WTF::removeAll(*this, toBeRemoved); } - - MappedPassOutType take(KeyPeekInType); // efficient combination of get with remove - - // An alternate version of find() that finds the object by hashing and comparing - // with some other type, to avoid the cost of type conversion. HashTranslator - // must have the following function members: - // static unsigned hash(const T&); - // static bool equal(const ValueType&, const T&); - template iterator find(const T&); - template const_iterator find(const T&) const; - template bool contains(const T&) const; - - // An alternate version of add() that finds the object by hashing and comparing - // with some other type, to avoid the cost of type conversion if the object is already - // in the table. HashTranslator must have the following function members: - // static unsigned hash(const T&); - // static bool equal(const ValueType&, const T&); - // static translate(ValueType&, const T&, unsigned hashCode); - template AddResult add(const T&, MappedPassInType); - - static bool isValidKey(KeyPeekInType); - - private: - AddResult inlineAdd(KeyPassInType, MappedPassInReferenceType); - - HashTableType m_impl; - }; - - template - class HashMap::HashMapKeysProxy : - private HashMap { - public: - typedef HashMap HashMapType; - typedef typename HashMapType::iterator::Keys iterator; - typedef typename HashMapType::const_iterator::Keys const_iterator; - - iterator begin() - { - return HashMapType::begin().keys(); - } - - iterator end() - { - return HashMapType::end().keys(); - } - - const_iterator begin() const - { - return HashMapType::begin().keys(); - } - - const_iterator end() const - { - return HashMapType::end().keys(); - } - - private: - friend class HashMap; - - // These are intentionally not implemented. - HashMapKeysProxy(); - HashMapKeysProxy(const HashMapKeysProxy&); - HashMapKeysProxy& operator=(const HashMapKeysProxy&); - ~HashMapKeysProxy(); - }; - - template - class HashMap::HashMapValuesProxy : - private HashMap { - public: - typedef HashMap HashMapType; - typedef typename HashMapType::iterator::Values iterator; - typedef typename HashMapType::const_iterator::Values const_iterator; - - iterator begin() - { - return HashMapType::begin().values(); - } - - iterator end() - { - return HashMapType::end().values(); - } - - const_iterator begin() const - { - return HashMapType::begin().values(); - } - - const_iterator end() const - { - return HashMapType::end().values(); - } - - private: - friend class HashMap; - - // These are intentionally not implemented. - HashMapValuesProxy(); - HashMapValuesProxy(const HashMapValuesProxy&); - HashMapValuesProxy& operator=(const HashMapValuesProxy&); - ~HashMapValuesProxy(); - }; - - template struct HashMapValueTraits : KeyValuePairHashTraits { - static const bool hasIsEmptyValueFunction = true; - static bool isEmptyValue(const typename KeyValuePairHashTraits::TraitType& value) - { - return isHashTraitsEmptyValue(value.key); - } - }; - - template - struct HashMapTranslator { - template static unsigned hash(const T& key) { return HashFunctions::hash(key); } - template static bool equal(const T& a, const U& b) { return HashFunctions::equal(a, b); } - template static void translate(T& location, const U& key, const V& mapped) - { - location.key = key; - ValueTraits::ValueTraits::store(mapped, location.value); - } - }; - - template - struct HashMapTranslatorAdapter { - template static unsigned hash(const T& key) { return Translator::hash(key); } - template static bool equal(const T& a, const U& b) { return Translator::equal(a, b); } - template static void translate(T& location, const U& key, const V& mapped, unsigned hashCode) - { - Translator::translate(location.key, key, hashCode); - ValueTraits::ValueTraits::store(mapped, location.value); - } - }; - - template - inline unsigned HashMap::size() const - { - return m_impl.size(); - } - - template - inline unsigned HashMap::capacity() const - { - return m_impl.capacity(); - } - - template - inline bool HashMap::isEmpty() const - { - return m_impl.isEmpty(); - } - - template - inline typename HashMap::iterator HashMap::begin() - { - return m_impl.begin(); - } - - template - inline typename HashMap::iterator HashMap::end() - { - return m_impl.end(); - } - - template - inline typename HashMap::const_iterator HashMap::begin() const - { - return m_impl.begin(); - } - - template - inline typename HashMap::const_iterator HashMap::end() const - { - return m_impl.end(); - } - - template - inline typename HashMap::iterator HashMap::find(KeyPeekInType key) - { - return m_impl.find(key); - } - - template - inline typename HashMap::const_iterator HashMap::find(KeyPeekInType key) const - { - return m_impl.find(key); - } - - template - inline bool HashMap::contains(KeyPeekInType key) const - { - return m_impl.contains(key); - } - - template - template - inline typename HashMap::iterator - HashMap::find(const TYPE& value) - { - return m_impl.template find >(value); - } - - template - template - inline typename HashMap::const_iterator - HashMap::find(const TYPE& value) const - { - return m_impl.template find >(value); - } - - template - template - inline bool - HashMap::contains(const TYPE& value) const - { - return m_impl.template contains >(value); - } - - template - typename HashMap::AddResult - HashMap::inlineAdd(KeyPassInType key, MappedPassInReferenceType mapped) - { - return m_impl.template add >(key, mapped); - } - - template - typename HashMap::AddResult - HashMap::set(KeyPassInType key, MappedPassInType mapped) - { - AddResult result = inlineAdd(key, mapped); - if (!result.isNewEntry) { - // The inlineAdd call above found an existing hash table entry; we need to set the mapped value. - MappedTraits::store(mapped, result.storedValue->value); - } - return result; - } - - template - template - typename HashMap::AddResult - HashMap::add(const TYPE& key, MappedPassInType value) - { - return m_impl.template addPassingHashCode >(key, value); - } - - template - typename HashMap::AddResult - HashMap::add(KeyPassInType key, MappedPassInType mapped) - { - return inlineAdd(key, mapped); - } - - template - typename HashMap::MappedPeekType - HashMap::get(KeyPeekInType key) const - { - ValueType* entry = const_cast(m_impl).lookup(key); - if (!entry) - return MappedTraits::peek(MappedTraits::emptyValue()); - return MappedTraits::peek(entry->value); - } - - template - inline void HashMap::remove(iterator it) - { - m_impl.remove(it.m_impl); - } - - template - inline void HashMap::remove(KeyPeekInType key) - { - remove(find(key)); - } - - template - inline void HashMap::clear() - { - m_impl.clear(); - } - - template - typename HashMap::MappedPassOutType - HashMap::take(KeyPeekInType key) - { - iterator it = find(key); - if (it == end()) - return MappedTraits::passOut(MappedTraits::emptyValue()); - MappedPassOutType result = MappedTraits::passOut(it->value); - remove(it); - return result; - } - - template - inline bool HashMap::isValidKey(KeyPeekInType key) - { - if (KeyTraits::isDeletedValue(key)) - return false; - - if (HashFunctions::safeToCompareToEmptyOrDeleted) { - if (key == KeyTraits::emptyValue()) - return false; - } else { - if (isHashTraitsEmptyValue(key)) - return false; - } - - return true; - } - - template - bool operator==(const HashMap& a, const HashMap& b) - { - if (a.size() != b.size()) - return false; - - typedef typename HashMap::const_iterator const_iterator; - - const_iterator aEnd = a.end(); - const_iterator bEnd = b.end(); - for (const_iterator it = a.begin(); it != aEnd; ++it) { - const_iterator bPos = b.find(it->key); - if (bPos == bEnd || it->value != bPos->value) - return false; - } - - return true; - } - - template - inline bool operator!=(const HashMap& a, const HashMap& b) - { - return !(a == b); - } - - template - inline void copyKeysToVector(const HashMap& collection, Z& vector) - { - typedef typename HashMap::const_iterator::Keys iterator; - - vector.resize(collection.size()); - - iterator it = collection.begin().keys(); - iterator end = collection.end().keys(); - for (unsigned i = 0; it != end; ++it, ++i) - vector[i] = *it; - } - - template - inline void copyValuesToVector(const HashMap& collection, Z& vector) - { - typedef typename HashMap::const_iterator::Values iterator; - - vector.resize(collection.size()); - - iterator it = collection.begin().values(); - iterator end = collection.end().values(); - for (unsigned i = 0; it != end; ++it, ++i) - vector[i] = *it; - } - -} // namespace WTF +template +struct HashMapValueTraits; + +template +struct ReferenceTypeMaker { + typedef T& ReferenceType; +}; +template +struct ReferenceTypeMaker { + typedef T& ReferenceType; +}; + +struct KeyValuePairKeyExtractor { + template + static const typename T::KeyType& extract(const T& p) { + return p.key; + } +}; + +// Note: empty or deleted key values are not allowed, using them may lead to +// undefined behavior. For pointer keys this means that null pointers are not +// allowed unless you supply custom key traits. +template ::Hash, + typename KeyTraitsArg = HashTraits, + typename MappedTraitsArg = HashTraits, + typename Allocator = DefaultAllocator> +class HashMap { + WTF_USE_ALLOCATOR(HashMap, Allocator); + + private: + typedef KeyTraitsArg KeyTraits; + typedef MappedTraitsArg MappedTraits; + typedef HashMapValueTraits ValueTraits; + + public: + typedef typename KeyTraits::TraitType KeyType; + typedef typename KeyTraits::PassInType KeyPassInType; + typedef const typename KeyTraits::PeekInType& KeyPeekInType; + typedef typename MappedTraits::TraitType MappedType; + typedef typename ValueTraits::TraitType ValueType; + + private: + typedef typename MappedTraits::PassInType MappedPassInType; + typedef typename MappedTraits::PassOutType MappedPassOutType; + typedef typename MappedTraits::PeekOutType MappedPeekType; + + typedef typename ReferenceTypeMaker::ReferenceType + MappedPassInReferenceType; + + typedef HashArg HashFunctions; + + typedef HashTable + HashTableType; + + class HashMapKeysProxy; + class HashMapValuesProxy; + + public: + typedef HashTableIteratorAdapter iterator; + typedef HashTableConstIteratorAdapter + const_iterator; + typedef typename HashTableType::AddResult AddResult; + + public: + void swap(HashMap& ref) { m_impl.swap(ref.m_impl); } + + void swap(typename Allocator::template OtherType::Type other) { + HashMap& ref = Allocator::getOther(other); + m_impl.swap(ref.m_impl); + } + + unsigned size() const; + unsigned capacity() const; + bool isEmpty() const; + + // iterators iterate over pairs of keys and values + iterator begin(); + iterator end(); + const_iterator begin() const; + const_iterator end() const; + + HashMapKeysProxy& keys() { return static_cast(*this); } + const HashMapKeysProxy& keys() const { + return static_cast(*this); + } + + HashMapValuesProxy& values() { + return static_cast(*this); + } + const HashMapValuesProxy& values() const { + return static_cast(*this); + } + + iterator find(KeyPeekInType); + const_iterator find(KeyPeekInType) const; + bool contains(KeyPeekInType) const; + MappedPeekType get(KeyPeekInType) const; + + // replaces value but not key if key is already present + // return value is a pair of the iterator to the key location, + // and a boolean that's true if a new value was actually added + AddResult set(KeyPassInType, MappedPassInType); + + // does nothing if key is already present + // return value is a pair of the iterator to the key location, + // and a boolean that's true if a new value was actually added + AddResult add(KeyPassInType, MappedPassInType); + + void remove(KeyPeekInType); + void remove(iterator); + void clear(); + template + void removeAll(const Collection& toBeRemoved) { + WTF::removeAll(*this, toBeRemoved); + } + + MappedPassOutType take( + KeyPeekInType); // efficient combination of get with remove + + // An alternate version of find() that finds the object by hashing and + // comparing with some other type, to avoid the cost of type conversion. + // HashTranslator must have the following function members: + // static unsigned hash(const T&); + // static bool equal(const ValueType&, const T&); + template + iterator find(const T&); + template + const_iterator find(const T&) const; + template + bool contains(const T&) const; + + // An alternate version of add() that finds the object by hashing and + // comparing with some other type, to avoid the cost of type conversion if the + // object is already in the table. HashTranslator must have the following + // function members: + // static unsigned hash(const T&); + // static bool equal(const ValueType&, const T&); + // static translate(ValueType&, const T&, unsigned hashCode); + template + AddResult add(const T&, MappedPassInType); + + static bool isValidKey(KeyPeekInType); + + private: + AddResult inlineAdd(KeyPassInType, MappedPassInReferenceType); + + HashTableType m_impl; +}; + +template +class HashMap::HashMapKeysProxy : private HashMap { + public: + typedef HashMap + HashMapType; + typedef typename HashMapType::iterator::Keys iterator; + typedef typename HashMapType::const_iterator::Keys const_iterator; + + iterator begin() { return HashMapType::begin().keys(); } + + iterator end() { return HashMapType::end().keys(); } + + const_iterator begin() const { return HashMapType::begin().keys(); } + + const_iterator end() const { return HashMapType::end().keys(); } + + private: + friend class HashMap; + + // These are intentionally not implemented. + HashMapKeysProxy(); + HashMapKeysProxy(const HashMapKeysProxy&); + HashMapKeysProxy& operator=(const HashMapKeysProxy&); + ~HashMapKeysProxy(); +}; + +template +class HashMap::HashMapValuesProxy : private HashMap { + public: + typedef HashMap + HashMapType; + typedef typename HashMapType::iterator::Values iterator; + typedef typename HashMapType::const_iterator::Values const_iterator; + + iterator begin() { return HashMapType::begin().values(); } + + iterator end() { return HashMapType::end().values(); } + + const_iterator begin() const { return HashMapType::begin().values(); } + + const_iterator end() const { return HashMapType::end().values(); } + + private: + friend class HashMap; + + // These are intentionally not implemented. + HashMapValuesProxy(); + HashMapValuesProxy(const HashMapValuesProxy&); + HashMapValuesProxy& operator=(const HashMapValuesProxy&); + ~HashMapValuesProxy(); +}; + +template +struct HashMapValueTraits : KeyValuePairHashTraits { + static const bool hasIsEmptyValueFunction = true; + static bool isEmptyValue( + const typename KeyValuePairHashTraits::TraitType& + value) { + return isHashTraitsEmptyValue(value.key); + } +}; + +template +struct HashMapTranslator { + template + static unsigned hash(const T& key) { + return HashFunctions::hash(key); + } + template + static bool equal(const T& a, const U& b) { + return HashFunctions::equal(a, b); + } + template + static void translate(T& location, const U& key, const V& mapped) { + location.key = key; + ValueTraits::ValueTraits::store(mapped, location.value); + } +}; + +template +struct HashMapTranslatorAdapter { + template + static unsigned hash(const T& key) { + return Translator::hash(key); + } + template + static bool equal(const T& a, const U& b) { + return Translator::equal(a, b); + } + template + static void translate(T& location, + const U& key, + const V& mapped, + unsigned hashCode) { + Translator::translate(location.key, key, hashCode); + ValueTraits::ValueTraits::store(mapped, location.value); + } +}; + +template +inline unsigned HashMap::size() const { + return m_impl.size(); +} + +template +inline unsigned HashMap::capacity() const { + return m_impl.capacity(); +} + +template +inline bool HashMap::isEmpty() const { + return m_impl.isEmpty(); +} + +template +inline typename HashMap::iterator +HashMap::begin() { + return m_impl.begin(); +} + +template +inline typename HashMap::iterator +HashMap::end() { + return m_impl.end(); +} + +template +inline typename HashMap::const_iterator +HashMap::begin() const { + return m_impl.begin(); +} + +template +inline typename HashMap::const_iterator +HashMap::end() const { + return m_impl.end(); +} + +template +inline typename HashMap::iterator +HashMap::find(KeyPeekInType key) { + return m_impl.find(key); +} + +template +inline typename HashMap::const_iterator +HashMap::find(KeyPeekInType key) const { + return m_impl.find(key); +} + +template +inline bool HashMap::contains(KeyPeekInType key) const { + return m_impl.contains(key); +} + +template +template +inline typename HashMap::iterator +HashMap::find(const TYPE& value) { + return m_impl + .template find>( + value); +} + +template +template +inline typename HashMap::const_iterator +HashMap::find(const TYPE& value) const { + return m_impl + .template find>( + value); +} + +template +template +inline bool HashMap::contains(const TYPE& value) const { + return m_impl + .template contains>( + value); +} + +template +typename HashMap::AddResult +HashMap::inlineAdd(KeyPassInType key, + MappedPassInReferenceType mapped) { + return m_impl.template add>( + key, mapped); +} + +template +typename HashMap::AddResult HashMap::set( + KeyPassInType key, + MappedPassInType mapped) { + AddResult result = inlineAdd(key, mapped); + if (!result.isNewEntry) { + // The inlineAdd call above found an existing hash table entry; we need to + // set the mapped value. + MappedTraits::store(mapped, result.storedValue->value); + } + return result; +} + +template +template +typename HashMap::AddResult HashMap::add( + const TYPE& key, + MappedPassInType value) { + return m_impl.template addPassingHashCode< + HashMapTranslatorAdapter>(key, value); +} + +template +typename HashMap::AddResult HashMap::add( + KeyPassInType key, + MappedPassInType mapped) { + return inlineAdd(key, mapped); +} + +template +typename HashMap::MappedPeekType +HashMap::get(KeyPeekInType key) const { + ValueType* entry = const_cast(m_impl).lookup(key); + if (!entry) + return MappedTraits::peek(MappedTraits::emptyValue()); + return MappedTraits::peek(entry->value); +} + +template +inline void HashMap::remove(iterator it) { + m_impl.remove(it.m_impl); +} + +template +inline void HashMap::remove(KeyPeekInType key) { + remove(find(key)); +} + +template +inline void HashMap::clear() { + m_impl.clear(); +} + +template +typename HashMap::MappedPassOutType +HashMap::take(KeyPeekInType key) { + iterator it = find(key); + if (it == end()) + return MappedTraits::passOut(MappedTraits::emptyValue()); + MappedPassOutType result = MappedTraits::passOut(it->value); + remove(it); + return result; +} + +template +inline bool HashMap::isValidKey(KeyPeekInType key) { + if (KeyTraits::isDeletedValue(key)) + return false; + + if (HashFunctions::safeToCompareToEmptyOrDeleted) { + if (key == KeyTraits::emptyValue()) + return false; + } else { + if (isHashTraitsEmptyValue(key)) + return false; + } + + return true; +} + +template +bool operator==(const HashMap& a, + const HashMap& b) { + if (a.size() != b.size()) + return false; + + typedef typename HashMap::const_iterator const_iterator; + + const_iterator aEnd = a.end(); + const_iterator bEnd = b.end(); + for (const_iterator it = a.begin(); it != aEnd; ++it) { + const_iterator bPos = b.find(it->key); + if (bPos == bEnd || it->value != bPos->value) + return false; + } + + return true; +} + +template +inline bool operator!=(const HashMap& a, + const HashMap& b) { + return !(a == b); +} + +template +inline void copyKeysToVector(const HashMap& collection, + Z& vector) { + typedef typename HashMap::const_iterator::Keys iterator; + + vector.resize(collection.size()); + + iterator it = collection.begin().keys(); + iterator end = collection.end().keys(); + for (unsigned i = 0; it != end; ++it, ++i) + vector[i] = *it; +} + +template +inline void copyValuesToVector(const HashMap& collection, + Z& vector) { + typedef typename HashMap::const_iterator::Values iterator; + + vector.resize(collection.size()); + + iterator it = collection.begin().values(); + iterator end = collection.end().values(); + for (unsigned i = 0; it != end; ++it, ++i) + vector[i] = *it; +} + +} // namespace WTF using WTF::HashMap; diff --git a/sky/engine/wtf/HashMapTest.cpp b/sky/engine/wtf/HashMapTest.cpp index d2965d765d3cc..5dd817ccf41eb 100644 --- a/sky/engine/wtf/HashMapTest.cpp +++ b/sky/engine/wtf/HashMapTest.cpp @@ -23,7 +23,6 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ - #include #include "flutter/sky/engine/wtf/HashMap.h" #include "flutter/sky/engine/wtf/OwnPtr.h" @@ -35,228 +34,223 @@ namespace { typedef WTF::HashMap IntHashMap; -TEST(HashMapTest, IteratorComparison) -{ - IntHashMap map; - map.add(1, 2); - EXPECT_TRUE(map.begin() != map.end()); - EXPECT_FALSE(map.begin() == map.end()); - - IntHashMap::const_iterator begin = map.begin(); - EXPECT_TRUE(begin == map.begin()); - EXPECT_TRUE(map.begin() == begin); - EXPECT_TRUE(begin != map.end()); - EXPECT_TRUE(map.end() != begin); - EXPECT_FALSE(begin != map.begin()); - EXPECT_FALSE(map.begin() != begin); - EXPECT_FALSE(begin == map.end()); - EXPECT_FALSE(map.end() == begin); +TEST(HashMapTest, IteratorComparison) { + IntHashMap map; + map.add(1, 2); + EXPECT_TRUE(map.begin() != map.end()); + EXPECT_FALSE(map.begin() == map.end()); + + IntHashMap::const_iterator begin = map.begin(); + EXPECT_TRUE(begin == map.begin()); + EXPECT_TRUE(map.begin() == begin); + EXPECT_TRUE(begin != map.end()); + EXPECT_TRUE(map.end() != begin); + EXPECT_FALSE(begin != map.begin()); + EXPECT_FALSE(map.begin() != begin); + EXPECT_FALSE(begin == map.end()); + EXPECT_FALSE(map.end() == begin); } struct TestDoubleHashTraits : HashTraits { - static const unsigned minimumTableSize = 8; + static const unsigned minimumTableSize = 8; }; -typedef HashMap::Hash, TestDoubleHashTraits> DoubleHashMap; +typedef HashMap::Hash, + TestDoubleHashTraits> + DoubleHashMap; -static int bucketForKey(double key) -{ - return DefaultHash::Hash::hash(key) & (TestDoubleHashTraits::minimumTableSize - 1); +static int bucketForKey(double key) { + return DefaultHash::Hash::hash(key) & + (TestDoubleHashTraits::minimumTableSize - 1); } -TEST(HashMapTest, DoubleHashCollisions) -{ - // The "clobber" key here is one that ends up stealing the bucket that the -0 key - // originally wants to be in. This makes the 0 and -0 keys collide and the test then - // fails unless the FloatHash::equals() implementation can distinguish them. - const double clobberKey = 6; - const double zeroKey = 0; - const double negativeZeroKey = -zeroKey; - - DoubleHashMap map; - - map.add(clobberKey, 1); - map.add(zeroKey, 2); - map.add(negativeZeroKey, 3); - - EXPECT_EQ(bucketForKey(clobberKey), bucketForKey(negativeZeroKey)); - EXPECT_EQ(1, map.get(clobberKey)); - EXPECT_EQ(2, map.get(zeroKey)); - EXPECT_EQ(3, map.get(negativeZeroKey)); +TEST(HashMapTest, DoubleHashCollisions) { + // The "clobber" key here is one that ends up stealing the bucket that the -0 + // key originally wants to be in. This makes the 0 and -0 keys collide and the + // test then fails unless the FloatHash::equals() implementation can + // distinguish them. + const double clobberKey = 6; + const double zeroKey = 0; + const double negativeZeroKey = -zeroKey; + + DoubleHashMap map; + + map.add(clobberKey, 1); + map.add(zeroKey, 2); + map.add(negativeZeroKey, 3); + + EXPECT_EQ(bucketForKey(clobberKey), bucketForKey(negativeZeroKey)); + EXPECT_EQ(1, map.get(clobberKey)); + EXPECT_EQ(2, map.get(zeroKey)); + EXPECT_EQ(3, map.get(negativeZeroKey)); } class DestructCounter { -public: - explicit DestructCounter(int i, int* destructNumber) - : m_i(i) - , m_destructNumber(destructNumber) - { } - - ~DestructCounter() { ++(*m_destructNumber); } - int get() const { return m_i; } - -private: - int m_i; - int* m_destructNumber; + public: + explicit DestructCounter(int i, int* destructNumber) + : m_i(i), m_destructNumber(destructNumber) {} + + ~DestructCounter() { ++(*m_destructNumber); } + int get() const { return m_i; } + + private: + int m_i; + int* m_destructNumber; }; -typedef WTF::HashMap > OwnPtrHashMap; - -TEST(HashMapTest, OwnPtrAsValue) -{ - int destructNumber = 0; - OwnPtrHashMap map; - map.add(1, adoptPtr(new DestructCounter(1, &destructNumber))); - map.add(2, adoptPtr(new DestructCounter(2, &destructNumber))); - - DestructCounter* counter1 = map.get(1); - EXPECT_EQ(1, counter1->get()); - DestructCounter* counter2 = map.get(2); - EXPECT_EQ(2, counter2->get()); - EXPECT_EQ(0, destructNumber); - - for (OwnPtrHashMap::iterator iter = map.begin(); iter != map.end(); ++iter) { - OwnPtr& ownCounter = iter->value; - EXPECT_EQ(iter->key, ownCounter->get()); - } - ASSERT_EQ(0, destructNumber); - - OwnPtr ownCounter1 = map.take(1); - EXPECT_EQ(ownCounter1.get(), counter1); - EXPECT_FALSE(map.contains(1)); - EXPECT_EQ(0, destructNumber); - - map.remove(2); - EXPECT_FALSE(map.contains(2)); - EXPECT_EQ(0UL, map.size()); - EXPECT_EQ(1, destructNumber); - - ownCounter1.clear(); - EXPECT_EQ(2, destructNumber); +typedef WTF::HashMap> OwnPtrHashMap; + +TEST(HashMapTest, OwnPtrAsValue) { + int destructNumber = 0; + OwnPtrHashMap map; + map.add(1, adoptPtr(new DestructCounter(1, &destructNumber))); + map.add(2, adoptPtr(new DestructCounter(2, &destructNumber))); + + DestructCounter* counter1 = map.get(1); + EXPECT_EQ(1, counter1->get()); + DestructCounter* counter2 = map.get(2); + EXPECT_EQ(2, counter2->get()); + EXPECT_EQ(0, destructNumber); + + for (OwnPtrHashMap::iterator iter = map.begin(); iter != map.end(); ++iter) { + OwnPtr& ownCounter = iter->value; + EXPECT_EQ(iter->key, ownCounter->get()); + } + ASSERT_EQ(0, destructNumber); + + OwnPtr ownCounter1 = map.take(1); + EXPECT_EQ(ownCounter1.get(), counter1); + EXPECT_FALSE(map.contains(1)); + EXPECT_EQ(0, destructNumber); + + map.remove(2); + EXPECT_FALSE(map.contains(2)); + EXPECT_EQ(0UL, map.size()); + EXPECT_EQ(1, destructNumber); + + ownCounter1.clear(); + EXPECT_EQ(2, destructNumber); } - -class DummyRefCounted: public WTF::RefCounted { -public: - DummyRefCounted(bool& isDeleted) : m_isDeleted(isDeleted) { m_isDeleted = false; } - ~DummyRefCounted() - { - ASSERT(!m_isDeleted); - m_isDeleted = true; - } - - void ref() - { - ASSERT(!m_isDeleted); - WTF::RefCounted::ref(); - ++m_refInvokesCount; - } - - void deref() - { - ASSERT(!m_isDeleted); - WTF::RefCounted::deref(); - } - - static int m_refInvokesCount; - -private: - bool& m_isDeleted; +class DummyRefCounted : public WTF::RefCounted { + public: + DummyRefCounted(bool& isDeleted) : m_isDeleted(isDeleted) { + m_isDeleted = false; + } + ~DummyRefCounted() { + ASSERT(!m_isDeleted); + m_isDeleted = true; + } + + void ref() { + ASSERT(!m_isDeleted); + WTF::RefCounted::ref(); + ++m_refInvokesCount; + } + + void deref() { + ASSERT(!m_isDeleted); + WTF::RefCounted::deref(); + } + + static int m_refInvokesCount; + + private: + bool& m_isDeleted; }; int DummyRefCounted::m_refInvokesCount = 0; -TEST(HashMapTest, RefPtrAsKey) -{ - bool isDeleted = false; - DummyRefCounted::m_refInvokesCount = 0; - RefPtr ptr = adoptRef(new DummyRefCounted(isDeleted)); - EXPECT_EQ(0, DummyRefCounted::m_refInvokesCount); - HashMap, int> map; - map.add(ptr, 1); - // Referenced only once (to store a copy in the container). - EXPECT_EQ(1, DummyRefCounted::m_refInvokesCount); - EXPECT_EQ(1, map.get(ptr)); - - DummyRefCounted* rawPtr = ptr.get(); - - EXPECT_TRUE(map.contains(rawPtr)); - EXPECT_NE(map.end(), map.find(rawPtr)); - EXPECT_TRUE(map.contains(ptr)); - EXPECT_NE(map.end(), map.find(ptr)); - EXPECT_EQ(1, DummyRefCounted::m_refInvokesCount); - - ptr.clear(); - EXPECT_FALSE(isDeleted); - - map.remove(rawPtr); - EXPECT_EQ(1, DummyRefCounted::m_refInvokesCount); - EXPECT_TRUE(isDeleted); - EXPECT_TRUE(map.isEmpty()); +TEST(HashMapTest, RefPtrAsKey) { + bool isDeleted = false; + DummyRefCounted::m_refInvokesCount = 0; + RefPtr ptr = adoptRef(new DummyRefCounted(isDeleted)); + EXPECT_EQ(0, DummyRefCounted::m_refInvokesCount); + HashMap, int> map; + map.add(ptr, 1); + // Referenced only once (to store a copy in the container). + EXPECT_EQ(1, DummyRefCounted::m_refInvokesCount); + EXPECT_EQ(1, map.get(ptr)); + + DummyRefCounted* rawPtr = ptr.get(); + + EXPECT_TRUE(map.contains(rawPtr)); + EXPECT_NE(map.end(), map.find(rawPtr)); + EXPECT_TRUE(map.contains(ptr)); + EXPECT_NE(map.end(), map.find(ptr)); + EXPECT_EQ(1, DummyRefCounted::m_refInvokesCount); + + ptr.clear(); + EXPECT_FALSE(isDeleted); + + map.remove(rawPtr); + EXPECT_EQ(1, DummyRefCounted::m_refInvokesCount); + EXPECT_TRUE(isDeleted); + EXPECT_TRUE(map.isEmpty()); } -TEST(HashMaptest, RemoveAdd) -{ - DummyRefCounted::m_refInvokesCount = 0; - bool isDeleted = false; - - typedef HashMap> Map; - Map map; - - RefPtr ptr = adoptRef(new DummyRefCounted(isDeleted)); - EXPECT_EQ(0, DummyRefCounted::m_refInvokesCount); - - map.add(1, ptr); - // Referenced only once (to store a copy in the container). - EXPECT_EQ(1, DummyRefCounted::m_refInvokesCount); - EXPECT_EQ(ptr, map.get(1)); - - ptr.clear(); - EXPECT_FALSE(isDeleted); - - map.remove(1); - EXPECT_EQ(1, DummyRefCounted::m_refInvokesCount); - EXPECT_TRUE(isDeleted); - EXPECT_TRUE(map.isEmpty()); - - // Add and remove until the deleted slot is reused. - for (int i = 1; i < 100; i++) { - bool isDeleted2 = false; - RefPtr ptr2 = adoptRef(new DummyRefCounted(isDeleted2)); - map.add(i, ptr2); - EXPECT_FALSE(isDeleted2); - ptr2.clear(); - EXPECT_FALSE(isDeleted2); - map.remove(i); - EXPECT_TRUE(isDeleted2); - } +TEST(HashMaptest, RemoveAdd) { + DummyRefCounted::m_refInvokesCount = 0; + bool isDeleted = false; + + typedef HashMap> Map; + Map map; + + RefPtr ptr = adoptRef(new DummyRefCounted(isDeleted)); + EXPECT_EQ(0, DummyRefCounted::m_refInvokesCount); + + map.add(1, ptr); + // Referenced only once (to store a copy in the container). + EXPECT_EQ(1, DummyRefCounted::m_refInvokesCount); + EXPECT_EQ(ptr, map.get(1)); + + ptr.clear(); + EXPECT_FALSE(isDeleted); + + map.remove(1); + EXPECT_EQ(1, DummyRefCounted::m_refInvokesCount); + EXPECT_TRUE(isDeleted); + EXPECT_TRUE(map.isEmpty()); + + // Add and remove until the deleted slot is reused. + for (int i = 1; i < 100; i++) { + bool isDeleted2 = false; + RefPtr ptr2 = adoptRef(new DummyRefCounted(isDeleted2)); + map.add(i, ptr2); + EXPECT_FALSE(isDeleted2); + ptr2.clear(); + EXPECT_FALSE(isDeleted2); + map.remove(i); + EXPECT_TRUE(isDeleted2); + } } class SimpleClass { -public: - explicit SimpleClass(int v) : m_v(v) { } - int v() { return m_v; } + public: + explicit SimpleClass(int v) : m_v(v) {} + int v() { return m_v; } -private: - int m_v; + private: + int m_v; }; -typedef HashMap > IntSimpleMap; - -TEST(HashMapTest, AddResult) -{ - IntSimpleMap map; - IntSimpleMap::AddResult result = map.add(1, nullptr); - EXPECT_TRUE(result.isNewEntry); - EXPECT_EQ(1, result.storedValue->key); - EXPECT_EQ(0, result.storedValue->value.get()); - - SimpleClass* simple1 = new SimpleClass(1); - result.storedValue->value = adoptPtr(simple1); - EXPECT_EQ(simple1, map.get(1)); - - IntSimpleMap::AddResult result2 = map.add(1, adoptPtr(new SimpleClass(2))); - EXPECT_FALSE(result2.isNewEntry); - EXPECT_EQ(1, map.get(1)->v()); +typedef HashMap> IntSimpleMap; + +TEST(HashMapTest, AddResult) { + IntSimpleMap map; + IntSimpleMap::AddResult result = map.add(1, nullptr); + EXPECT_TRUE(result.isNewEntry); + EXPECT_EQ(1, result.storedValue->key); + EXPECT_EQ(0, result.storedValue->value.get()); + + SimpleClass* simple1 = new SimpleClass(1); + result.storedValue->value = adoptPtr(simple1); + EXPECT_EQ(simple1, map.get(1)); + + IntSimpleMap::AddResult result2 = map.add(1, adoptPtr(new SimpleClass(2))); + EXPECT_FALSE(result2.isNewEntry); + EXPECT_EQ(1, map.get(1)->v()); } -} // namespace +} // namespace diff --git a/sky/engine/wtf/HashSet.h b/sky/engine/wtf/HashSet.h index a2c01b2f66e59..6ec3f27ae1154 100644 --- a/sky/engine/wtf/HashSet.h +++ b/sky/engine/wtf/HashSet.h @@ -26,251 +26,278 @@ namespace WTF { - struct IdentityExtractor; - - // Note: empty or deleted values are not allowed, using them may lead to undefined behavior. - // For pointer valuess this means that null pointers are not allowed unless you supply custom traits. - template< - typename ValueArg, - typename HashArg = typename DefaultHash::Hash, - typename TraitsArg = HashTraits, - typename Allocator = DefaultAllocator> class HashSet { - WTF_USE_ALLOCATOR(HashSet, Allocator); - private: - typedef HashArg HashFunctions; - typedef TraitsArg ValueTraits; - typedef typename ValueTraits::PeekInType ValuePeekInType; - typedef typename ValueTraits::PassInType ValuePassInType; - typedef typename ValueTraits::PassOutType ValuePassOutType; - - public: - typedef typename ValueTraits::TraitType ValueType; - - private: - typedef HashTable HashTableType; - - public: - typedef HashTableConstIteratorAdapter iterator; - typedef HashTableConstIteratorAdapter const_iterator; - typedef typename HashTableType::AddResult AddResult; - - void swap(HashSet& ref) - { - m_impl.swap(ref.m_impl); - } - - void swap(typename Allocator::template OtherType::Type other) - { - HashSet& ref = Allocator::getOther(other); - m_impl.swap(ref.m_impl); - } - - unsigned size() const; - unsigned capacity() const; - bool isEmpty() const; - - iterator begin() const; - iterator end() const; - - iterator find(ValuePeekInType) const; - bool contains(ValuePeekInType) const; - - // An alternate version of find() that finds the object by hashing and comparing - // with some other type, to avoid the cost of type conversion. HashTranslator - // must have the following function members: - // static unsigned hash(const T&); - // static bool equal(const ValueType&, const T&); - template iterator find(const T&) const; - template bool contains(const T&) const; - - // The return value is a pair of an iterator to the new value's location, - // and a bool that is true if an new entry was added. - AddResult add(ValuePassInType); - - // An alternate version of add() that finds the object by hashing and comparing - // with some other type, to avoid the cost of type conversion if the object is already - // in the table. HashTranslator must have the following function members: - // static unsigned hash(const T&); - // static bool equal(const ValueType&, const T&); - // static translate(ValueType&, const T&, unsigned hashCode); - template AddResult add(const T&); - - void remove(ValuePeekInType); - void remove(iterator); - void clear(); - template - void removeAll(const Collection& toBeRemoved) { WTF::removeAll(*this, toBeRemoved); } - - static bool isValidValue(ValuePeekInType); - - ValuePassOutType take(iterator); - ValuePassOutType take(ValuePeekInType); - ValuePassOutType takeAny(); - - private: - HashTableType m_impl; - }; - - struct IdentityExtractor { - template - static const T& extract(const T& t) { return t; } - }; - - template - struct HashSetTranslatorAdapter { - template static unsigned hash(const T& key) { return Translator::hash(key); } - template static bool equal(const T& a, const U& b) { return Translator::equal(a, b); } - template static void translate(T& location, const U& key, const U&, unsigned hashCode) - { - Translator::translate(location, key, hashCode); - } - }; - - template - inline unsigned HashSet::size() const - { - return m_impl.size(); - } - - template - inline unsigned HashSet::capacity() const - { - return m_impl.capacity(); - } - - template - inline bool HashSet::isEmpty() const - { - return m_impl.isEmpty(); - } - - template - inline typename HashSet::iterator HashSet::begin() const - { - return m_impl.begin(); - } - - template - inline typename HashSet::iterator HashSet::end() const - { - return m_impl.end(); - } - - template - inline typename HashSet::iterator HashSet::find(ValuePeekInType value) const - { - return m_impl.find(value); - } - - template - inline bool HashSet::contains(ValuePeekInType value) const - { - return m_impl.contains(value); - } - - template - template - typename HashSet::iterator - inline HashSet::find(const T& value) const - { - return m_impl.template find >(value); - } - - template - template - inline bool HashSet::contains(const T& value) const - { - return m_impl.template contains >(value); - } - - template - inline typename HashSet::AddResult HashSet::add(ValuePassInType value) - { - return m_impl.add(value); - } - - template - template - inline typename HashSet::AddResult - HashSet::add(const T& value) - { - return m_impl.template addPassingHashCode >(value, value); - } - - template - inline void HashSet::remove(iterator it) - { - m_impl.remove(it.m_impl); - } - - template - inline void HashSet::remove(ValuePeekInType value) - { - remove(find(value)); - } - - template - inline void HashSet::clear() - { - m_impl.clear(); - } - - template - inline bool HashSet::isValidValue(ValuePeekInType value) - { - if (ValueTraits::isDeletedValue(value)) - return false; - - if (HashFunctions::safeToCompareToEmptyOrDeleted) { - if (value == ValueTraits::emptyValue()) - return false; - } else { - if (isHashTraitsEmptyValue(value)) - return false; - } - - return true; - } - - template - inline typename HashSet::ValuePassOutType HashSet::take(iterator it) - { - if (it == end()) - return ValueTraits::emptyValue(); - - ValuePassOutType result = ValueTraits::passOut(const_cast(*it)); - remove(it); - - return result; - } - - template - inline typename HashSet::ValuePassOutType HashSet::take(ValuePeekInType value) - { - return take(find(value)); - } - - template - inline typename HashSet::ValuePassOutType HashSet::takeAny() - { - return take(begin()); - } - - template - inline void copyToVector(const C& collection, W& vector) - { - typedef typename C::const_iterator iterator; - - vector.resize(collection.size()); - - iterator it = collection.begin(); - iterator end = collection.end(); - for (unsigned i = 0; it != end; ++it, ++i) - vector[i] = *it; - } - -} // namespace WTF +struct IdentityExtractor; + +// Note: empty or deleted values are not allowed, using them may lead to +// undefined behavior. For pointer valuess this means that null pointers are not +// allowed unless you supply custom traits. +template ::Hash, + typename TraitsArg = HashTraits, + typename Allocator = DefaultAllocator> +class HashSet { + WTF_USE_ALLOCATOR(HashSet, Allocator); + + private: + typedef HashArg HashFunctions; + typedef TraitsArg ValueTraits; + typedef typename ValueTraits::PeekInType ValuePeekInType; + typedef typename ValueTraits::PassInType ValuePassInType; + typedef typename ValueTraits::PassOutType ValuePassOutType; + + public: + typedef typename ValueTraits::TraitType ValueType; + + private: + typedef HashTable + HashTableType; + + public: + typedef HashTableConstIteratorAdapter iterator; + typedef HashTableConstIteratorAdapter + const_iterator; + typedef typename HashTableType::AddResult AddResult; + + void swap(HashSet& ref) { m_impl.swap(ref.m_impl); } + + void swap(typename Allocator::template OtherType::Type other) { + HashSet& ref = Allocator::getOther(other); + m_impl.swap(ref.m_impl); + } + + unsigned size() const; + unsigned capacity() const; + bool isEmpty() const; + + iterator begin() const; + iterator end() const; + + iterator find(ValuePeekInType) const; + bool contains(ValuePeekInType) const; + + // An alternate version of find() that finds the object by hashing and + // comparing with some other type, to avoid the cost of type conversion. + // HashTranslator must have the following function members: + // static unsigned hash(const T&); + // static bool equal(const ValueType&, const T&); + template + iterator find(const T&) const; + template + bool contains(const T&) const; + + // The return value is a pair of an iterator to the new value's location, + // and a bool that is true if an new entry was added. + AddResult add(ValuePassInType); + + // An alternate version of add() that finds the object by hashing and + // comparing with some other type, to avoid the cost of type conversion if the + // object is already in the table. HashTranslator must have the following + // function members: + // static unsigned hash(const T&); + // static bool equal(const ValueType&, const T&); + // static translate(ValueType&, const T&, unsigned hashCode); + template + AddResult add(const T&); + + void remove(ValuePeekInType); + void remove(iterator); + void clear(); + template + void removeAll(const Collection& toBeRemoved) { + WTF::removeAll(*this, toBeRemoved); + } + + static bool isValidValue(ValuePeekInType); + + ValuePassOutType take(iterator); + ValuePassOutType take(ValuePeekInType); + ValuePassOutType takeAny(); + + private: + HashTableType m_impl; +}; + +struct IdentityExtractor { + template + static const T& extract(const T& t) { + return t; + } +}; + +template +struct HashSetTranslatorAdapter { + template + static unsigned hash(const T& key) { + return Translator::hash(key); + } + template + static bool equal(const T& a, const U& b) { + return Translator::equal(a, b); + } + template + static void translate(T& location, + const U& key, + const U&, + unsigned hashCode) { + Translator::translate(location, key, hashCode); + } +}; + +template +inline unsigned HashSet::size() const { + return m_impl.size(); +} + +template +inline unsigned HashSet::capacity() const { + return m_impl.capacity(); +} + +template +inline bool HashSet::isEmpty() const { + return m_impl.isEmpty(); +} + +template +inline typename HashSet::iterator HashSet::begin() + const { + return m_impl.begin(); +} + +template +inline typename HashSet::iterator HashSet::end() const { + return m_impl.end(); +} + +template +inline typename HashSet::iterator HashSet::find( + ValuePeekInType value) const { + return m_impl.find(value); +} + +template +inline bool HashSet::contains( + ValuePeekInType value) const { + return m_impl.contains(value); +} + +template +template +typename HashSet:: + iterator inline HashSet::find( + const T& value) const { + return m_impl.template find>(value); +} + +template +template +inline bool HashSet::contains( + const T& value) const { + return m_impl.template contains>( + value); +} + +template +inline typename HashSet::AddResult HashSet::add( + ValuePassInType value) { + return m_impl.add(value); +} + +template +template +inline typename HashSet::AddResult +HashSet::add(const T& value) { + return m_impl + .template addPassingHashCode>( + value, value); +} + +template +inline void HashSet::remove(iterator it) { + m_impl.remove(it.m_impl); +} + +template +inline void HashSet::remove(ValuePeekInType value) { + remove(find(value)); +} + +template +inline void HashSet::clear() { + m_impl.clear(); +} + +template +inline bool HashSet::isValidValue(ValuePeekInType value) { + if (ValueTraits::isDeletedValue(value)) + return false; + + if (HashFunctions::safeToCompareToEmptyOrDeleted) { + if (value == ValueTraits::emptyValue()) + return false; + } else { + if (isHashTraitsEmptyValue(value)) + return false; + } + + return true; +} + +template +inline typename HashSet::ValuePassOutType HashSet::take( + iterator it) { + if (it == end()) + return ValueTraits::emptyValue(); + + ValuePassOutType result = ValueTraits::passOut(const_cast(*it)); + remove(it); + + return result; +} + +template +inline typename HashSet::ValuePassOutType HashSet::take( + ValuePeekInType value) { + return take(find(value)); +} + +template +inline typename HashSet::ValuePassOutType +HashSet::takeAny() { + return take(begin()); +} + +template +inline void copyToVector(const C& collection, W& vector) { + typedef typename C::const_iterator iterator; + + vector.resize(collection.size()); + + iterator it = collection.begin(); + iterator end = collection.end(); + for (unsigned i = 0; it != end; ++it, ++i) + vector[i] = *it; +} + +} // namespace WTF using WTF::HashSet; diff --git a/sky/engine/wtf/HashSetTest.cpp b/sky/engine/wtf/HashSetTest.cpp index b527cc553cd02..888e7df2a8e33 100644 --- a/sky/engine/wtf/HashSetTest.cpp +++ b/sky/engine/wtf/HashSetTest.cpp @@ -23,7 +23,6 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ - #include #include "flutter/sky/engine/wtf/HashSet.h" #include "flutter/sky/engine/wtf/OwnPtr.h" @@ -32,180 +31,177 @@ namespace { -template - struct InitialCapacityTestHashTraits : public WTF::UnsignedWithZeroKeyHashTraits { - static const int minimumTableSize = initialCapacity; +template +struct InitialCapacityTestHashTraits + : public WTF::UnsignedWithZeroKeyHashTraits { + static const int minimumTableSize = initialCapacity; }; -template -void testInitialCapacity() -{ - const unsigned initialCapacity = WTF::HashTableCapacityForSize::value; - HashSet::Hash, InitialCapacityTestHashTraits > testSet; - - // Initial capacity is null. - EXPECT_EQ(0UL, testSet.capacity()); - - // Adding items up to size should never change the capacity. - for (size_t i = 0; i < size; ++i) { - testSet.add(i); - EXPECT_EQ(initialCapacity, testSet.capacity()); - } - - // Adding items up to less than half the capacity should not change the capacity. - unsigned capacityLimit = initialCapacity / 2 - 1; - for (size_t i = size; i < capacityLimit; ++i) { - testSet.add(i); - EXPECT_EQ(initialCapacity, testSet.capacity()); - } - - // Adding one more item increase the capacity. - testSet.add(initialCapacity); - EXPECT_GT(testSet.capacity(), initialCapacity); +template +void testInitialCapacity() { + const unsigned initialCapacity = WTF::HashTableCapacityForSize::value; + HashSet::Hash, + InitialCapacityTestHashTraits> + testSet; + + // Initial capacity is null. + EXPECT_EQ(0UL, testSet.capacity()); + + // Adding items up to size should never change the capacity. + for (size_t i = 0; i < size; ++i) { + testSet.add(i); + EXPECT_EQ(initialCapacity, testSet.capacity()); + } + + // Adding items up to less than half the capacity should not change the + // capacity. + unsigned capacityLimit = initialCapacity / 2 - 1; + for (size_t i = size; i < capacityLimit; ++i) { + testSet.add(i); + EXPECT_EQ(initialCapacity, testSet.capacity()); + } + + // Adding one more item increase the capacity. + testSet.add(initialCapacity); + EXPECT_GT(testSet.capacity(), initialCapacity); } -template void generateTestCapacityUpToSize(); -template<> void generateTestCapacityUpToSize<0>() -{ -} -template void generateTestCapacityUpToSize() -{ - generateTestCapacityUpToSize(); - testInitialCapacity(); +template +void generateTestCapacityUpToSize(); +template <> +void generateTestCapacityUpToSize<0>() {} +template +void generateTestCapacityUpToSize() { + generateTestCapacityUpToSize(); + testInitialCapacity(); } -TEST(HashSetTest, InitialCapacity) -{ - generateTestCapacityUpToSize<128>(); +TEST(HashSetTest, InitialCapacity) { + generateTestCapacityUpToSize<128>(); } struct Dummy { - Dummy(bool& deleted) : deleted(deleted) { } + Dummy(bool& deleted) : deleted(deleted) {} - ~Dummy() - { - deleted = true; - } + ~Dummy() { deleted = true; } - bool& deleted; + bool& deleted; }; -TEST(HashSetTest, HashSetOwnPtr) -{ - bool deleted1 = false, deleted2 = false; - - typedef HashSet > OwnPtrSet; +TEST(HashSetTest, HashSetOwnPtr) { + bool deleted1 = false, deleted2 = false; + + typedef HashSet> OwnPtrSet; + OwnPtrSet set; + + Dummy* ptr1 = new Dummy(deleted1); + { + // AddResult in a separate scope to avoid assertion hit, + // since we modify the container further. + HashSet>::AddResult res1 = set.add(adoptPtr(ptr1)); + EXPECT_EQ(ptr1, res1.storedValue->get()); + } + + EXPECT_FALSE(deleted1); + EXPECT_EQ(1UL, set.size()); + OwnPtrSet::iterator it1 = set.find(ptr1); + EXPECT_NE(set.end(), it1); + EXPECT_EQ(ptr1, (*it1)); + + Dummy* ptr2 = new Dummy(deleted2); + { + HashSet>::AddResult res2 = set.add(adoptPtr(ptr2)); + EXPECT_EQ(res2.storedValue->get(), ptr2); + } + + EXPECT_FALSE(deleted2); + EXPECT_EQ(2UL, set.size()); + OwnPtrSet::iterator it2 = set.find(ptr2); + EXPECT_NE(set.end(), it2); + EXPECT_EQ(ptr2, (*it2)); + + set.remove(ptr1); + EXPECT_TRUE(deleted1); + + set.clear(); + EXPECT_TRUE(deleted2); + EXPECT_TRUE(set.isEmpty()); + + deleted1 = false; + deleted2 = false; + { OwnPtrSet set; - - Dummy* ptr1 = new Dummy(deleted1); - { - // AddResult in a separate scope to avoid assertion hit, - // since we modify the container further. - HashSet >::AddResult res1 = set.add(adoptPtr(ptr1)); - EXPECT_EQ(ptr1, res1.storedValue->get()); - } - - EXPECT_FALSE(deleted1); + set.add(adoptPtr(new Dummy(deleted1))); + set.add(adoptPtr(new Dummy(deleted2))); + } + EXPECT_TRUE(deleted1); + EXPECT_TRUE(deleted2); + + deleted1 = false; + deleted2 = false; + OwnPtr ownPtr1; + OwnPtr ownPtr2; + ptr1 = new Dummy(deleted1); + ptr2 = new Dummy(deleted2); + { + OwnPtrSet set; + set.add(adoptPtr(ptr1)); + set.add(adoptPtr(ptr2)); + ownPtr1 = set.take(ptr1); EXPECT_EQ(1UL, set.size()); - OwnPtrSet::iterator it1 = set.find(ptr1); - EXPECT_NE(set.end(), it1); - EXPECT_EQ(ptr1, (*it1)); - - Dummy* ptr2 = new Dummy(deleted2); - { - HashSet >::AddResult res2 = set.add(adoptPtr(ptr2)); - EXPECT_EQ(res2.storedValue->get(), ptr2); - } - - EXPECT_FALSE(deleted2); - EXPECT_EQ(2UL, set.size()); - OwnPtrSet::iterator it2 = set.find(ptr2); - EXPECT_NE(set.end(), it2); - EXPECT_EQ(ptr2, (*it2)); - - set.remove(ptr1); - EXPECT_TRUE(deleted1); - - set.clear(); - EXPECT_TRUE(deleted2); + ownPtr2 = set.takeAny(); EXPECT_TRUE(set.isEmpty()); + } + EXPECT_FALSE(deleted1); + EXPECT_FALSE(deleted2); - deleted1 = false; - deleted2 = false; - { - OwnPtrSet set; - set.add(adoptPtr(new Dummy(deleted1))); - set.add(adoptPtr(new Dummy(deleted2))); - } - EXPECT_TRUE(deleted1); - EXPECT_TRUE(deleted2); - - deleted1 = false; - deleted2 = false; - OwnPtr ownPtr1; - OwnPtr ownPtr2; - ptr1 = new Dummy(deleted1); - ptr2 = new Dummy(deleted2); - { - OwnPtrSet set; - set.add(adoptPtr(ptr1)); - set.add(adoptPtr(ptr2)); - ownPtr1 = set.take(ptr1); - EXPECT_EQ(1UL, set.size()); - ownPtr2 = set.takeAny(); - EXPECT_TRUE(set.isEmpty()); - } - EXPECT_FALSE(deleted1); - EXPECT_FALSE(deleted2); - - EXPECT_EQ(ptr1, ownPtr1); - EXPECT_EQ(ptr2, ownPtr2); + EXPECT_EQ(ptr1, ownPtr1); + EXPECT_EQ(ptr2, ownPtr2); } -class DummyRefCounted: public WTF::RefCounted { -public: - DummyRefCounted(bool& isDeleted) : m_isDeleted(isDeleted) { m_isDeleted = false; } - ~DummyRefCounted() { m_isDeleted = true; } +class DummyRefCounted : public WTF::RefCounted { + public: + DummyRefCounted(bool& isDeleted) : m_isDeleted(isDeleted) { + m_isDeleted = false; + } + ~DummyRefCounted() { m_isDeleted = true; } - void ref() - { - WTF::RefCounted::ref(); - ++s_refInvokesCount; - } + void ref() { + WTF::RefCounted::ref(); + ++s_refInvokesCount; + } - static int s_refInvokesCount; + static int s_refInvokesCount; -private: - bool& m_isDeleted; + private: + bool& m_isDeleted; }; int DummyRefCounted::s_refInvokesCount = 0; -TEST(HashSetTest, HashSetRefPtr) -{ - bool isDeleted = false; - RefPtr ptr = adoptRef(new DummyRefCounted(isDeleted)); - EXPECT_EQ(0, DummyRefCounted::s_refInvokesCount); - HashSet > set; - set.add(ptr); - // Referenced only once (to store a copy in the container). - EXPECT_EQ(1, DummyRefCounted::s_refInvokesCount); - - DummyRefCounted* rawPtr = ptr.get(); - - EXPECT_TRUE(set.contains(rawPtr)); - EXPECT_NE(set.end(), set.find(rawPtr)); - EXPECT_TRUE(set.contains(ptr)); - EXPECT_NE(set.end(), set.find(ptr)); - - ptr.clear(); - EXPECT_FALSE(isDeleted); - - set.remove(rawPtr); - EXPECT_TRUE(isDeleted); - EXPECT_TRUE(set.isEmpty()); - EXPECT_EQ(1, DummyRefCounted::s_refInvokesCount); +TEST(HashSetTest, HashSetRefPtr) { + bool isDeleted = false; + RefPtr ptr = adoptRef(new DummyRefCounted(isDeleted)); + EXPECT_EQ(0, DummyRefCounted::s_refInvokesCount); + HashSet> set; + set.add(ptr); + // Referenced only once (to store a copy in the container). + EXPECT_EQ(1, DummyRefCounted::s_refInvokesCount); + + DummyRefCounted* rawPtr = ptr.get(); + + EXPECT_TRUE(set.contains(rawPtr)); + EXPECT_NE(set.end(), set.find(rawPtr)); + EXPECT_TRUE(set.contains(ptr)); + EXPECT_NE(set.end(), set.find(ptr)); + + ptr.clear(); + EXPECT_FALSE(isDeleted); + + set.remove(rawPtr); + EXPECT_TRUE(isDeleted); + EXPECT_TRUE(set.isEmpty()); + EXPECT_EQ(1, DummyRefCounted::s_refInvokesCount); } - -} // namespace +} // namespace diff --git a/sky/engine/wtf/HashTable.cpp b/sky/engine/wtf/HashTable.cpp index d4bffc42cfc14..589d0320c8ea3 100644 --- a/sky/engine/wtf/HashTable.cpp +++ b/sky/engine/wtf/HashTable.cpp @@ -17,8 +17,8 @@ Boston, MA 02110-1301, USA. */ -#include "flutter/sky/engine/wtf/DataLog.h" #include "flutter/sky/engine/wtf/HashTable.h" +#include "flutter/sky/engine/wtf/DataLog.h" namespace WTF { @@ -32,36 +32,39 @@ int HashTableStats::numRehashes; int HashTableStats::numRemoves; int HashTableStats::numReinserts; -static Mutex& hashTableStatsMutex() -{ - AtomicallyInitializedStatic(Mutex&, mutex = *new Mutex); - return mutex; +static Mutex& hashTableStatsMutex() { + AtomicallyInitializedStatic(Mutex&, mutex = *new Mutex); + return mutex; } -void HashTableStats::recordCollisionAtCount(int count) -{ - MutexLocker lock(hashTableStatsMutex()); - if (count > maxCollisions) - maxCollisions = count; - numCollisions++; - collisionGraph[count]++; +void HashTableStats::recordCollisionAtCount(int count) { + MutexLocker lock(hashTableStatsMutex()); + if (count > maxCollisions) + maxCollisions = count; + numCollisions++; + collisionGraph[count]++; } -void HashTableStats::dumpStats() -{ - MutexLocker lock(hashTableStatsMutex()); +void HashTableStats::dumpStats() { + MutexLocker lock(hashTableStatsMutex()); - dataLogF("\nWTF::HashTable statistics\n\n"); - dataLogF("%d accesses\n", numAccesses); - dataLogF("%d total collisions, average %.2f probes per access\n", numCollisions, 1.0 * (numAccesses + numCollisions) / numAccesses); - dataLogF("longest collision chain: %d\n", maxCollisions); - for (int i = 1; i <= maxCollisions; i++) { - dataLogF(" %d lookups with exactly %d collisions (%.2f%% , %.2f%% with this many or more)\n", collisionGraph[i], i, 100.0 * (collisionGraph[i] - collisionGraph[i+1]) / numAccesses, 100.0 * collisionGraph[i] / numAccesses); - } - dataLogF("%d rehashes\n", numRehashes); - dataLogF("%d reinserts\n", numReinserts); + dataLogF("\nWTF::HashTable statistics\n\n"); + dataLogF("%d accesses\n", numAccesses); + dataLogF("%d total collisions, average %.2f probes per access\n", + numCollisions, 1.0 * (numAccesses + numCollisions) / numAccesses); + dataLogF("longest collision chain: %d\n", maxCollisions); + for (int i = 1; i <= maxCollisions; i++) { + dataLogF( + " %d lookups with exactly %d collisions (%.2f%% , %.2f%% with this " + "many or more)\n", + collisionGraph[i], i, + 100.0 * (collisionGraph[i] - collisionGraph[i + 1]) / numAccesses, + 100.0 * collisionGraph[i] / numAccesses); + } + dataLogF("%d rehashes\n", numRehashes); + dataLogF("%d reinserts\n", numReinserts); } #endif -} // namespace WTF +} // namespace WTF diff --git a/sky/engine/wtf/HashTable.h b/sky/engine/wtf/HashTable.h index 5e06584a68e93..5678e27997572 100644 --- a/sky/engine/wtf/HashTable.h +++ b/sky/engine/wtf/HashTable.h @@ -1,5 +1,6 @@ /* - * Copyright (C) 2005, 2006, 2007, 2008, 2011, 2012 Apple Inc. All rights reserved. + * Copyright (C) 2005, 2006, 2007, 2008, 2011, 2012 Apple Inc. + * All rights reserved. * Copyright (C) 2008 David Levin * * This library is free software; you can redistribute it and/or @@ -8,7 +9,9 @@ * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to @@ -35,35 +38,39 @@ #if DUMP_HASHTABLE_STATS #if DUMP_HASHTABLE_STATS_PER_TABLE -#define UPDATE_PROBE_COUNTS() \ - ++probeCount; \ - HashTableStats::recordCollisionAtCount(probeCount); \ - ++perTableProbeCount; \ - m_stats->recordCollisionAtCount(perTableProbeCount) -#define UPDATE_ACCESS_COUNTS() \ - atomicIncrement(&HashTableStats::numAccesses); \ - int probeCount = 0; \ - ++m_stats->numAccesses; \ - int perTableProbeCount = 0 +#define UPDATE_PROBE_COUNTS() \ + ++probeCount; \ + HashTableStats::recordCollisionAtCount(probeCount); \ + ++perTableProbeCount; \ + m_stats->recordCollisionAtCount(perTableProbeCount) +#define UPDATE_ACCESS_COUNTS() \ + atomicIncrement(&HashTableStats::numAccesses); \ + int probeCount = 0; \ + ++m_stats->numAccesses; \ + int perTableProbeCount = 0 #else -#define UPDATE_PROBE_COUNTS() \ - ++probeCount; \ - HashTableStats::recordCollisionAtCount(probeCount) -#define UPDATE_ACCESS_COUNTS() \ - atomicIncrement(&HashTableStats::numAccesses); \ - int probeCount = 0 +#define UPDATE_PROBE_COUNTS() \ + ++probeCount; \ + HashTableStats::recordCollisionAtCount(probeCount) +#define UPDATE_ACCESS_COUNTS() \ + atomicIncrement(&HashTableStats::numAccesses); \ + int probeCount = 0 #endif #else #if DUMP_HASHTABLE_STATS_PER_TABLE -#define UPDATE_PROBE_COUNTS() \ - ++perTableProbeCount; \ - m_stats->recordCollisionAtCount(perTableProbeCount) -#define UPDATE_ACCESS_COUNTS() \ - ++m_stats->numAccesses; \ - int perTableProbeCount = 0 +#define UPDATE_PROBE_COUNTS() \ + ++perTableProbeCount; \ + m_stats->recordCollisionAtCount(perTableProbeCount) +#define UPDATE_ACCESS_COUNTS() \ + ++m_stats->numAccesses; \ + int perTableProbeCount = 0 #else -#define UPDATE_PROBE_COUNTS() do { } while (0) -#define UPDATE_ACCESS_COUNTS() do { } while (0) +#define UPDATE_PROBE_COUNTS() \ + do { \ + } while (0) +#define UPDATE_ACCESS_COUNTS() \ + do { \ + } while (0) #endif #endif @@ -71,1183 +78,1673 @@ namespace WTF { #if DUMP_HASHTABLE_STATS - struct HashTableStats { - // The following variables are all atomically incremented when modified. - static int numAccesses; - static int numRehashes; - static int numRemoves; - static int numReinserts; +struct HashTableStats { + // The following variables are all atomically incremented when modified. + static int numAccesses; + static int numRehashes; + static int numRemoves; + static int numReinserts; - // The following variables are only modified in the recordCollisionAtCount method within a mutex. - static int maxCollisions; - static int numCollisions; - static int collisionGraph[4096]; + // The following variables are only modified in the recordCollisionAtCount + // method within a mutex. + static int maxCollisions; + static int numCollisions; + static int collisionGraph[4096]; - static void recordCollisionAtCount(int count); - static void dumpStats(); - }; + static void recordCollisionAtCount(int count); + static void dumpStats(); +}; #endif - template - class HashTable; - template - class HashTableIterator; - template - class HashTableConstIterator; - template - class LinkedHashSet; - - typedef enum { HashItemKnownGood } HashItemKnownGoodTag; - - template - class HashTableConstIterator { - private: - typedef HashTable HashTableType; - typedef HashTableIterator iterator; - typedef HashTableConstIterator const_iterator; - typedef Value ValueType; - typedef typename Traits::IteratorConstGetType GetType; - typedef const ValueType* PointerType; - - friend class HashTable; - friend class HashTableIterator; - - void skipEmptyBuckets() - { - while (m_position != m_endPosition && HashTableType::isEmptyOrDeletedBucket(*m_position)) - ++m_position; - } - - HashTableConstIterator(PointerType position, PointerType endPosition, const HashTableType* container) - : m_position(position) - , m_endPosition(endPosition) +template +class HashTable; +template +class HashTableIterator; +template +class HashTableConstIterator; +template +class LinkedHashSet; + +typedef enum { HashItemKnownGood } HashItemKnownGoodTag; + +template +class HashTableConstIterator { + private: + typedef HashTable + HashTableType; + typedef HashTableIterator + iterator; + typedef HashTableConstIterator + const_iterator; + typedef Value ValueType; + typedef typename Traits::IteratorConstGetType GetType; + typedef const ValueType* PointerType; + + friend class HashTable; + friend class HashTableIterator; + + void skipEmptyBuckets() { + while (m_position != m_endPosition && + HashTableType::isEmptyOrDeletedBucket(*m_position)) + ++m_position; + } + + HashTableConstIterator(PointerType position, + PointerType endPosition, + const HashTableType* container) + : m_position(position), + m_endPosition(endPosition) #if ENABLE(ASSERT) - , m_container(container) - , m_containerModifications(container->modifications()) + , + m_container(container), + m_containerModifications(container->modifications()) #endif - { - skipEmptyBuckets(); - } - - HashTableConstIterator(PointerType position, PointerType endPosition, const HashTableType* container, HashItemKnownGoodTag) - : m_position(position) - , m_endPosition(endPosition) + { + skipEmptyBuckets(); + } + + HashTableConstIterator(PointerType position, + PointerType endPosition, + const HashTableType* container, + HashItemKnownGoodTag) + : m_position(position), + m_endPosition(endPosition) #if ENABLE(ASSERT) - , m_container(container) - , m_containerModifications(container->modifications()) + , + m_container(container), + m_containerModifications(container->modifications()) #endif - { - ASSERT(m_containerModifications == m_container->modifications()); - } - - void checkModifications() const - { - // HashTable and collections that build on it do not support - // modifications while there is an iterator in use. The exception - // is ListHashSet, which has its own iterators that tolerate - // modification of the underlying set. - ASSERT(m_containerModifications == m_container->modifications()); - } - - public: - HashTableConstIterator() - { - } - - GetType get() const - { - checkModifications(); - return m_position; - } - typename Traits::IteratorConstReferenceType operator*() const { return Traits::getToReferenceConstConversion(get()); } - GetType operator->() const { return get(); } - - const_iterator& operator++() - { - ASSERT(m_position != m_endPosition); - checkModifications(); - ++m_position; - skipEmptyBuckets(); - return *this; - } - - // postfix ++ intentionally omitted - - // Comparison. - bool operator==(const const_iterator& other) const - { - return m_position == other.m_position; - } - bool operator!=(const const_iterator& other) const - { - return m_position != other.m_position; - } - bool operator==(const iterator& other) const - { - return *this == static_cast(other); - } - bool operator!=(const iterator& other) const - { - return *this != static_cast(other); - } - - private: - PointerType m_position; - PointerType m_endPosition; + { + ASSERT(m_containerModifications == m_container->modifications()); + } + + void checkModifications() const { + // HashTable and collections that build on it do not support + // modifications while there is an iterator in use. The exception + // is ListHashSet, which has its own iterators that tolerate + // modification of the underlying set. + ASSERT(m_containerModifications == m_container->modifications()); + } + + public: + HashTableConstIterator() {} + + GetType get() const { + checkModifications(); + return m_position; + } + typename Traits::IteratorConstReferenceType operator*() const { + return Traits::getToReferenceConstConversion(get()); + } + GetType operator->() const { return get(); } + + const_iterator& operator++() { + ASSERT(m_position != m_endPosition); + checkModifications(); + ++m_position; + skipEmptyBuckets(); + return *this; + } + + // postfix ++ intentionally omitted + + // Comparison. + bool operator==(const const_iterator& other) const { + return m_position == other.m_position; + } + bool operator!=(const const_iterator& other) const { + return m_position != other.m_position; + } + bool operator==(const iterator& other) const { + return *this == static_cast(other); + } + bool operator!=(const iterator& other) const { + return *this != static_cast(other); + } + + private: + PointerType m_position; + PointerType m_endPosition; #if ENABLE(ASSERT) - const HashTableType* m_container; - int64_t m_containerModifications; + const HashTableType* m_container; + int64_t m_containerModifications; #endif - }; - - template - class HashTableIterator { - private: - typedef HashTable HashTableType; - typedef HashTableIterator iterator; - typedef HashTableConstIterator const_iterator; - typedef Value ValueType; - typedef typename Traits::IteratorGetType GetType; - typedef ValueType* PointerType; - - friend class HashTable; - - HashTableIterator(PointerType pos, PointerType end, const HashTableType* container) : m_iterator(pos, end, container) { } - HashTableIterator(PointerType pos, PointerType end, const HashTableType* container, HashItemKnownGoodTag tag) : m_iterator(pos, end, container, tag) { } - - public: - HashTableIterator() { } - - // default copy, assignment and destructor are OK - - GetType get() const { return const_cast(m_iterator.get()); } - typename Traits::IteratorReferenceType operator*() const { return Traits::getToReferenceConversion(get()); } - GetType operator->() const { return get(); } - - iterator& operator++() { ++m_iterator; return *this; } - - // postfix ++ intentionally omitted - - // Comparison. - bool operator==(const iterator& other) const { return m_iterator == other.m_iterator; } - bool operator!=(const iterator& other) const { return m_iterator != other.m_iterator; } - bool operator==(const const_iterator& other) const { return m_iterator == other; } - bool operator!=(const const_iterator& other) const { return m_iterator != other; } - - operator const_iterator() const { return m_iterator; } - - private: - const_iterator m_iterator; - }; - - using std::swap; - - // Work around MSVC's standard library, whose swap for pairs does not swap by component. - template inline void hashTableSwap(T& a, T& b) - { - swap(a, b); - } - - template inline void hashTableSwap(KeyValuePair& a, KeyValuePair& b) - { - swap(a.key, b.key); - swap(a.value, b.value); - } - - template struct Mover; - template struct Mover { - static void move(T& from, T& to) - { - // A swap operation should not normally allocate, but it may do so - // if it is falling back on some sort of triple assignment in the - // style of t = a; a = b; b = t because there is no overloaded swap - // operation. We can't allow allocation both because it is slower - // than a true swap operation, but also because allocation implies - // allowing GC: We cannot allow a GC after swapping only the key. - // The value is only traced if the key is present and therefore the - // GC will not see the value in the old backing if the key has been - // moved to the new backing. Therefore, we cannot allow GC until - // after both key and value have been moved. - Allocator::enterNoAllocationScope(); - hashTableSwap(from, to); - Allocator::leaveNoAllocationScope(); - } - }; - template struct Mover { - static void move(T& from, T& to) { to = from; } - }; - - template class IdentityHashTranslator { - public: - template static unsigned hash(const T& key) { return HashFunctions::hash(key); } - template static bool equal(const T& a, const U& b) { return HashFunctions::equal(a, b); } - template static void translate(T& location, const U&, const V& value) { location = value; } - }; - - template struct HashTableAddResult { - HashTableAddResult(const HashTableType* container, ValueType* storedValue, bool isNewEntry) - : storedValue(storedValue) - , isNewEntry(isNewEntry) +}; + +template +class HashTableIterator { + private: + typedef HashTable + HashTableType; + typedef HashTableIterator + iterator; + typedef HashTableConstIterator + const_iterator; + typedef Value ValueType; + typedef typename Traits::IteratorGetType GetType; + typedef ValueType* PointerType; + + friend class HashTable; + + HashTableIterator(PointerType pos, + PointerType end, + const HashTableType* container) + : m_iterator(pos, end, container) {} + HashTableIterator(PointerType pos, + PointerType end, + const HashTableType* container, + HashItemKnownGoodTag tag) + : m_iterator(pos, end, container, tag) {} + + public: + HashTableIterator() {} + + // default copy, assignment and destructor are OK + + GetType get() const { return const_cast(m_iterator.get()); } + typename Traits::IteratorReferenceType operator*() const { + return Traits::getToReferenceConversion(get()); + } + GetType operator->() const { return get(); } + + iterator& operator++() { + ++m_iterator; + return *this; + } + + // postfix ++ intentionally omitted + + // Comparison. + bool operator==(const iterator& other) const { + return m_iterator == other.m_iterator; + } + bool operator!=(const iterator& other) const { + return m_iterator != other.m_iterator; + } + bool operator==(const const_iterator& other) const { + return m_iterator == other; + } + bool operator!=(const const_iterator& other) const { + return m_iterator != other; + } + + operator const_iterator() const { return m_iterator; } + + private: + const_iterator m_iterator; +}; + +using std::swap; + +// Work around MSVC's standard library, whose swap for pairs does not swap by +// component. +template +inline void hashTableSwap(T& a, T& b) { + swap(a, b); +} + +template +inline void hashTableSwap(KeyValuePair& a, KeyValuePair& b) { + swap(a.key, b.key); + swap(a.value, b.value); +} + +template +struct Mover; +template +struct Mover { + static void move(T& from, T& to) { + // A swap operation should not normally allocate, but it may do so + // if it is falling back on some sort of triple assignment in the + // style of t = a; a = b; b = t because there is no overloaded swap + // operation. We can't allow allocation both because it is slower + // than a true swap operation, but also because allocation implies + // allowing GC: We cannot allow a GC after swapping only the key. + // The value is only traced if the key is present and therefore the + // GC will not see the value in the old backing if the key has been + // moved to the new backing. Therefore, we cannot allow GC until + // after both key and value have been moved. + Allocator::enterNoAllocationScope(); + hashTableSwap(from, to); + Allocator::leaveNoAllocationScope(); + } +}; +template +struct Mover { + static void move(T& from, T& to) { to = from; } +}; + +template +class IdentityHashTranslator { + public: + template + static unsigned hash(const T& key) { + return HashFunctions::hash(key); + } + template + static bool equal(const T& a, const U& b) { + return HashFunctions::equal(a, b); + } + template + static void translate(T& location, const U&, const V& value) { + location = value; + } +}; + +template +struct HashTableAddResult { + HashTableAddResult(const HashTableType* container, + ValueType* storedValue, + bool isNewEntry) + : storedValue(storedValue), + isNewEntry(isNewEntry) #if ENABLE(SECURITY_ASSERT) - , m_container(container) - , m_containerModifications(container->modifications()) + , + m_container(container), + m_containerModifications(container->modifications()) #endif - { - ASSERT_UNUSED(container, container); - } - - ~HashTableAddResult() - { - // If rehash happened before accessing storedValue, it's - // use-after-free. Any modification may cause a rehash, so we check - // for modifications here. - // Rehash after accessing storedValue is harmless but will assert if - // the AddResult destructor takes place after a modification. You - // may need to limit the scope of the AddResult. - ASSERT_WITH_SECURITY_IMPLICATION(m_containerModifications == m_container->modifications()); - } - - ValueType* storedValue; - bool isNewEntry; + { + ASSERT_UNUSED(container, container); + } + + ~HashTableAddResult() { + // If rehash happened before accessing storedValue, it's + // use-after-free. Any modification may cause a rehash, so we check + // for modifications here. + // Rehash after accessing storedValue is harmless but will assert if + // the AddResult destructor takes place after a modification. You + // may need to limit the scope of the AddResult. + ASSERT_WITH_SECURITY_IMPLICATION(m_containerModifications == + m_container->modifications()); + } + + ValueType* storedValue; + bool isNewEntry; #if ENABLE(SECURITY_ASSERT) - private: - const HashTableType* m_container; - const int64_t m_containerModifications; + private: + const HashTableType* m_container; + const int64_t m_containerModifications; #endif - }; - - template - struct HashTableHelper { - static bool isEmptyBucket(const Value& value) { return isHashTraitsEmptyValue(Extractor::extract(value)); } - static bool isDeletedBucket(const Value& value) { return KeyTraits::isDeletedValue(Extractor::extract(value)); } - static bool isEmptyOrDeletedBucket(const Value& value) { return isEmptyBucket(value) || isDeletedBucket(value); } - }; - - template - struct HashTableKeyChecker { - // There's no simple generic way to make this check if safeToCompareToEmptyOrDeleted is false, - // so the check always passes. - template - static bool checkKey(const T&) { return true; } - }; - - template - struct HashTableKeyChecker { - template - static bool checkKey(const T& key) - { - // FIXME : Check also equality to the deleted value. - return !HashTranslator::equal(KeyTraits::emptyValue(), key); - } - }; - - // Don't declare a destructor for HeapAllocated hash tables. - template - class HashTableDestructorBase; - - template - class HashTableDestructorBase { }; - - template - class HashTableDestructorBase { - public: - ~HashTableDestructorBase() { static_cast(this)->finalize(); } - }; - - // Note: empty or deleted key values are not allowed, using them may lead to undefined behavior. - // For pointer keys this means that null pointers are not allowed unless you supply custom key traits. - template - class HashTable : public HashTableDestructorBase, Allocator::isGarbageCollected> { - public: - typedef HashTableIterator iterator; - typedef HashTableConstIterator const_iterator; - typedef Traits ValueTraits; - typedef Key KeyType; - typedef typename KeyTraits::PeekInType KeyPeekInType; - typedef typename KeyTraits::PassInType KeyPassInType; - typedef Value ValueType; - typedef Extractor ExtractorType; - typedef KeyTraits KeyTraitsType; - typedef typename Traits::PassInType ValuePassInType; - typedef IdentityHashTranslator IdentityTranslatorType; - typedef HashTableAddResult AddResult; +}; + +template +struct HashTableHelper { + static bool isEmptyBucket(const Value& value) { + return isHashTraitsEmptyValue(Extractor::extract(value)); + } + static bool isDeletedBucket(const Value& value) { + return KeyTraits::isDeletedValue(Extractor::extract(value)); + } + static bool isEmptyOrDeletedBucket(const Value& value) { + return isEmptyBucket(value) || isDeletedBucket(value); + } +}; + +template +struct HashTableKeyChecker { + // There's no simple generic way to make this check if + // safeToCompareToEmptyOrDeleted is false, so the check always passes. + template + static bool checkKey(const T&) { + return true; + } +}; + +template +struct HashTableKeyChecker { + template + static bool checkKey(const T& key) { + // FIXME : Check also equality to the deleted value. + return !HashTranslator::equal(KeyTraits::emptyValue(), key); + } +}; + +// Don't declare a destructor for HeapAllocated hash tables. +template +class HashTableDestructorBase; + +template +class HashTableDestructorBase {}; + +template +class HashTableDestructorBase { + public: + ~HashTableDestructorBase() { static_cast(this)->finalize(); } +}; + +// Note: empty or deleted key values are not allowed, using them may lead to +// undefined behavior. For pointer keys this means that null pointers are not +// allowed unless you supply custom key traits. +template +class HashTable + : public HashTableDestructorBase, + Allocator::isGarbageCollected> { + public: + typedef HashTableIterator + iterator; + typedef HashTableConstIterator + const_iterator; + typedef Traits ValueTraits; + typedef Key KeyType; + typedef typename KeyTraits::PeekInType KeyPeekInType; + typedef typename KeyTraits::PassInType KeyPassInType; + typedef Value ValueType; + typedef Extractor ExtractorType; + typedef KeyTraits KeyTraitsType; + typedef typename Traits::PassInType ValuePassInType; + typedef IdentityHashTranslator IdentityTranslatorType; + typedef HashTableAddResult AddResult; #if DUMP_HASHTABLE_STATS_PER_TABLE - struct Stats { - Stats() - : numAccesses(0) - , numRehashes(0) - , numRemoves(0) - , numReinserts(0) - , maxCollisions(0) - , numCollisions(0) - , collisionGraph() - { - } - - int numAccesses; - int numRehashes; - int numRemoves; - int numReinserts; - - int maxCollisions; - int numCollisions; - int collisionGraph[4096]; - - void recordCollisionAtCount(int count) - { - if (count > maxCollisions) - maxCollisions = count; - numCollisions++; - collisionGraph[count]++; - } - - void dumpStats() - { - dataLogF("\nWTF::HashTable::Stats dump\n\n"); - dataLogF("%d accesses\n", numAccesses); - dataLogF("%d total collisions, average %.2f probes per access\n", numCollisions, 1.0 * (numAccesses + numCollisions) / numAccesses); - dataLogF("longest collision chain: %d\n", maxCollisions); - for (int i = 1; i <= maxCollisions; i++) { - dataLogF(" %d lookups with exactly %d collisions (%.2f%% , %.2f%% with this many or more)\n", collisionGraph[i], i, 100.0 * (collisionGraph[i] - collisionGraph[i+1]) / numAccesses, 100.0 * collisionGraph[i] / numAccesses); - } - dataLogF("%d rehashes\n", numRehashes); - dataLogF("%d reinserts\n", numReinserts); - } - }; + struct Stats { + Stats() + : numAccesses(0), + numRehashes(0), + numRemoves(0), + numReinserts(0), + maxCollisions(0), + numCollisions(0), + collisionGraph() {} + + int numAccesses; + int numRehashes; + int numRemoves; + int numReinserts; + + int maxCollisions; + int numCollisions; + int collisionGraph[4096]; + + void recordCollisionAtCount(int count) { + if (count > maxCollisions) + maxCollisions = count; + numCollisions++; + collisionGraph[count]++; + } + + void dumpStats() { + dataLogF("\nWTF::HashTable::Stats dump\n\n"); + dataLogF("%d accesses\n", numAccesses); + dataLogF("%d total collisions, average %.2f probes per access\n", + numCollisions, + 1.0 * (numAccesses + numCollisions) / numAccesses); + dataLogF("longest collision chain: %d\n", maxCollisions); + for (int i = 1; i <= maxCollisions; i++) { + dataLogF( + " %d lookups with exactly %d collisions (%.2f%% , %.2f%% with " + "this many or more)\n", + collisionGraph[i], i, + 100.0 * (collisionGraph[i] - collisionGraph[i + 1]) / numAccesses, + 100.0 * collisionGraph[i] / numAccesses); + } + dataLogF("%d rehashes\n", numRehashes); + dataLogF("%d reinserts\n", numReinserts); + } + }; #endif - HashTable(); - void finalize() - { - ASSERT(!Allocator::isGarbageCollected); - if (LIKELY(!m_table)) - return; - deleteAllBucketsAndDeallocate(m_table, m_tableSize); - m_table = 0; - } - - HashTable(const HashTable&); - void swap(HashTable&); - HashTable& operator=(const HashTable&); - - // When the hash table is empty, just return the same iterator for end as for begin. - // This is more efficient because we don't have to skip all the empty and deleted - // buckets, and iterating an empty table is a common case that's worth optimizing. - iterator begin() { return isEmpty() ? end() : makeIterator(m_table); } - iterator end() { return makeKnownGoodIterator(m_table + m_tableSize); } - const_iterator begin() const { return isEmpty() ? end() : makeConstIterator(m_table); } - const_iterator end() const { return makeKnownGoodConstIterator(m_table + m_tableSize); } - - unsigned size() const { return m_keyCount; } - unsigned capacity() const { return m_tableSize; } - bool isEmpty() const { return !m_keyCount; } - - AddResult add(ValuePassInType value) - { - return add(Extractor::extract(value), value); - } - - // A special version of add() that finds the object by hashing and comparing - // with some other type, to avoid the cost of type conversion if the object is already - // in the table. - template AddResult add(const T& key, const Extra&); - template AddResult addPassingHashCode(const T& key, const Extra&); - - iterator find(KeyPeekInType key) { return find(key); } - const_iterator find(KeyPeekInType key) const { return find(key); } - bool contains(KeyPeekInType key) const { return contains(key); } - - template iterator find(const T&); - template const_iterator find(const T&) const; - template bool contains(const T&) const; - - void remove(KeyPeekInType); - void remove(iterator); - void remove(const_iterator); - void clear(); - - static bool isEmptyBucket(const ValueType& value) { return isHashTraitsEmptyValue(Extractor::extract(value)); } - static bool isDeletedBucket(const ValueType& value) { return KeyTraits::isDeletedValue(Extractor::extract(value)); } - static bool isEmptyOrDeletedBucket(const ValueType& value) { return HashTableHelper:: isEmptyOrDeletedBucket(value); } - - ValueType* lookup(KeyPeekInType key) { return lookup(key); } - template ValueType* lookup(T); - template const ValueType* lookup(T) const; + HashTable(); + void finalize() { + ASSERT(!Allocator::isGarbageCollected); + if (LIKELY(!m_table)) + return; + deleteAllBucketsAndDeallocate(m_table, m_tableSize); + m_table = 0; + } + + HashTable(const HashTable&); + void swap(HashTable&); + HashTable& operator=(const HashTable&); + + // When the hash table is empty, just return the same iterator for end as for + // begin. This is more efficient because we don't have to skip all the empty + // and deleted buckets, and iterating an empty table is a common case that's + // worth optimizing. + iterator begin() { return isEmpty() ? end() : makeIterator(m_table); } + iterator end() { return makeKnownGoodIterator(m_table + m_tableSize); } + const_iterator begin() const { + return isEmpty() ? end() : makeConstIterator(m_table); + } + const_iterator end() const { + return makeKnownGoodConstIterator(m_table + m_tableSize); + } + + unsigned size() const { return m_keyCount; } + unsigned capacity() const { return m_tableSize; } + bool isEmpty() const { return !m_keyCount; } + + AddResult add(ValuePassInType value) { + return add(Extractor::extract(value), value); + } + + // A special version of add() that finds the object by hashing and comparing + // with some other type, to avoid the cost of type conversion if the object is + // already in the table. + template + AddResult add(const T& key, const Extra&); + template + AddResult addPassingHashCode(const T& key, const Extra&); + + iterator find(KeyPeekInType key) { return find(key); } + const_iterator find(KeyPeekInType key) const { + return find(key); + } + bool contains(KeyPeekInType key) const { + return contains(key); + } + + template + iterator find(const T&); + template + const_iterator find(const T&) const; + template + bool contains(const T&) const; + + void remove(KeyPeekInType); + void remove(iterator); + void remove(const_iterator); + void clear(); + + static bool isEmptyBucket(const ValueType& value) { + return isHashTraitsEmptyValue(Extractor::extract(value)); + } + static bool isDeletedBucket(const ValueType& value) { + return KeyTraits::isDeletedValue(Extractor::extract(value)); + } + static bool isEmptyOrDeletedBucket(const ValueType& value) { + return HashTableHelper::isEmptyOrDeletedBucket(value); + } + + ValueType* lookup(KeyPeekInType key) { + return lookup(key); + } + template + ValueType* lookup(T); + template + const ValueType* lookup(T) const; #if ENABLE(ASSERT) - int64_t modifications() const { return m_modifications; } - void registerModification() { m_modifications++; } - // HashTable and collections that build on it do not support - // modifications while there is an iterator in use. The exception is - // ListHashSet, which has its own iterators that tolerate modification - // of the underlying set. - void checkModifications(int64_t mods) const { ASSERT(mods == m_modifications); } + int64_t modifications() const { return m_modifications; } + void registerModification() { m_modifications++; } + // HashTable and collections that build on it do not support + // modifications while there is an iterator in use. The exception is + // ListHashSet, which has its own iterators that tolerate modification + // of the underlying set. + void checkModifications(int64_t mods) const { + ASSERT(mods == m_modifications); + } #else - int64_t modifications() const { return 0; } - void registerModification() { } - void checkModifications(int64_t mods) const { } + int64_t modifications() const { return 0; } + void registerModification() {} + void checkModifications(int64_t mods) const {} #endif - private: - static ValueType* allocateTable(unsigned size); - static void deleteAllBucketsAndDeallocate(ValueType* table, unsigned size); - - typedef std::pair LookupType; - typedef std::pair FullLookupType; - - LookupType lookupForWriting(const Key& key) { return lookupForWriting(key); }; - template FullLookupType fullLookupForWriting(const T&); - template LookupType lookupForWriting(const T&); - - void remove(ValueType*); - - bool shouldExpand() const { return (m_keyCount + m_deletedCount) * m_maxLoad >= m_tableSize; } - bool mustRehashInPlace() const { return m_keyCount * m_minLoad < m_tableSize * 2; } - bool shouldShrink() const - { - // isAllocationAllowed check should be at the last because it's - // expensive. - return m_keyCount * m_minLoad < m_tableSize - && m_tableSize > KeyTraits::minimumTableSize - && Allocator::isAllocationAllowed(); - } - ValueType* expand(ValueType* entry = 0); - void shrink() { rehash(m_tableSize / 2, 0); } - - ValueType* rehash(unsigned newTableSize, ValueType* entry); - ValueType* reinsert(ValueType&); - - static void initializeBucket(ValueType& bucket); - static void deleteBucket(ValueType& bucket) { bucket.~ValueType(); Traits::constructDeletedValue(bucket, Allocator::isGarbageCollected); } - - FullLookupType makeLookupResult(ValueType* position, bool found, unsigned hash) - { return FullLookupType(LookupType(position, found), hash); } - - iterator makeIterator(ValueType* pos) { return iterator(pos, m_table + m_tableSize, this); } - const_iterator makeConstIterator(ValueType* pos) const { return const_iterator(pos, m_table + m_tableSize, this); } - iterator makeKnownGoodIterator(ValueType* pos) { return iterator(pos, m_table + m_tableSize, this, HashItemKnownGood); } - const_iterator makeKnownGoodConstIterator(ValueType* pos) const { return const_iterator(pos, m_table + m_tableSize, this, HashItemKnownGood); } - - static const unsigned m_maxLoad = 2; - static const unsigned m_minLoad = 6; - - unsigned tableSizeMask() const - { - size_t mask = m_tableSize - 1; - ASSERT((mask & m_tableSize) == 0); - return mask; - } - - void setEnqueued() { m_queueFlag = true; } - void clearEnqueued() { m_queueFlag = false; } - bool enqueued() { return m_queueFlag; } - - ValueType* m_table; - unsigned m_tableSize; - unsigned m_keyCount; - unsigned m_deletedCount:31; - bool m_queueFlag:1; + private: + static ValueType* allocateTable(unsigned size); + static void deleteAllBucketsAndDeallocate(ValueType* table, unsigned size); + + typedef std::pair LookupType; + typedef std::pair FullLookupType; + + LookupType lookupForWriting(const Key& key) { + return lookupForWriting(key); + }; + template + FullLookupType fullLookupForWriting(const T&); + template + LookupType lookupForWriting(const T&); + + void remove(ValueType*); + + bool shouldExpand() const { + return (m_keyCount + m_deletedCount) * m_maxLoad >= m_tableSize; + } + bool mustRehashInPlace() const { + return m_keyCount * m_minLoad < m_tableSize * 2; + } + bool shouldShrink() const { + // isAllocationAllowed check should be at the last because it's + // expensive. + return m_keyCount * m_minLoad < m_tableSize && + m_tableSize > KeyTraits::minimumTableSize && + Allocator::isAllocationAllowed(); + } + ValueType* expand(ValueType* entry = 0); + void shrink() { rehash(m_tableSize / 2, 0); } + + ValueType* rehash(unsigned newTableSize, ValueType* entry); + ValueType* reinsert(ValueType&); + + static void initializeBucket(ValueType& bucket); + static void deleteBucket(ValueType& bucket) { + bucket.~ValueType(); + Traits::constructDeletedValue(bucket, Allocator::isGarbageCollected); + } + + FullLookupType makeLookupResult(ValueType* position, + bool found, + unsigned hash) { + return FullLookupType(LookupType(position, found), hash); + } + + iterator makeIterator(ValueType* pos) { + return iterator(pos, m_table + m_tableSize, this); + } + const_iterator makeConstIterator(ValueType* pos) const { + return const_iterator(pos, m_table + m_tableSize, this); + } + iterator makeKnownGoodIterator(ValueType* pos) { + return iterator(pos, m_table + m_tableSize, this, HashItemKnownGood); + } + const_iterator makeKnownGoodConstIterator(ValueType* pos) const { + return const_iterator(pos, m_table + m_tableSize, this, HashItemKnownGood); + } + + static const unsigned m_maxLoad = 2; + static const unsigned m_minLoad = 6; + + unsigned tableSizeMask() const { + size_t mask = m_tableSize - 1; + ASSERT((mask & m_tableSize) == 0); + return mask; + } + + void setEnqueued() { m_queueFlag = true; } + void clearEnqueued() { m_queueFlag = false; } + bool enqueued() { return m_queueFlag; } + + ValueType* m_table; + unsigned m_tableSize; + unsigned m_keyCount; + unsigned m_deletedCount : 31; + bool m_queueFlag : 1; #if ENABLE(ASSERT) - unsigned m_modifications; + unsigned m_modifications; #endif #if DUMP_HASHTABLE_STATS_PER_TABLE - public: - mutable OwnPtr m_stats; + public: + mutable OwnPtr m_stats; #endif - template friend class LinkedHashSet; - }; - - // Set all the bits to one after the most significant bit: 00110101010 -> 00111111111. - template struct OneifyLowBits; - template<> - struct OneifyLowBits<0> { - static const unsigned value = 0; - }; - template - struct OneifyLowBits { - static const unsigned value = number | OneifyLowBits<(number >> 1)>::value; - }; - // Compute the first power of two integer that is an upper bound of the parameter 'number'. - template - struct UpperPowerOfTwoBound { - static const unsigned value = (OneifyLowBits::value + 1) * 2; - }; - - // Because power of two numbers are the limit of maxLoad, their capacity is twice the - // UpperPowerOfTwoBound, or 4 times their values. - template struct HashTableCapacityForSizeSplitter; - template - struct HashTableCapacityForSizeSplitter { - static const unsigned value = size * 4; - }; - template - struct HashTableCapacityForSizeSplitter { - static const unsigned value = UpperPowerOfTwoBound::value; - }; - - // HashTableCapacityForSize computes the upper power of two capacity to hold the size parameter. - // This is done at compile time to initialize the HashTraits. - template - struct HashTableCapacityForSize { - static const unsigned value = HashTableCapacityForSizeSplitter::value; - COMPILE_ASSERT(size > 0, HashTableNonZeroMinimumCapacity); - COMPILE_ASSERT(!static_cast(value >> 31), HashTableNoCapacityOverflow); - COMPILE_ASSERT(value > (2 * size), HashTableCapacityHoldsContentSize); - }; - - template - inline HashTable::HashTable() - : m_table(0) - , m_tableSize(0) - , m_keyCount(0) - , m_deletedCount(0) - , m_queueFlag(false) + template + friend class LinkedHashSet; +}; + +// Set all the bits to one after the most significant bit: 00110101010 -> +// 00111111111. +template +struct OneifyLowBits; +template <> +struct OneifyLowBits<0> { + static const unsigned value = 0; +}; +template +struct OneifyLowBits { + static const unsigned value = number | OneifyLowBits<(number >> 1)>::value; +}; +// Compute the first power of two integer that is an upper bound of the +// parameter 'number'. +template +struct UpperPowerOfTwoBound { + static const unsigned value = (OneifyLowBits::value + 1) * 2; +}; + +// Because power of two numbers are the limit of maxLoad, their capacity is +// twice the UpperPowerOfTwoBound, or 4 times their values. +template +struct HashTableCapacityForSizeSplitter; +template +struct HashTableCapacityForSizeSplitter { + static const unsigned value = size * 4; +}; +template +struct HashTableCapacityForSizeSplitter { + static const unsigned value = UpperPowerOfTwoBound::value; +}; + +// HashTableCapacityForSize computes the upper power of two capacity to hold the +// size parameter. This is done at compile time to initialize the HashTraits. +template +struct HashTableCapacityForSize { + static const unsigned value = + HashTableCapacityForSizeSplitter::value; + COMPILE_ASSERT(size > 0, HashTableNonZeroMinimumCapacity); + COMPILE_ASSERT(!static_cast(value >> 31), HashTableNoCapacityOverflow); + COMPILE_ASSERT(value > (2 * size), HashTableCapacityHoldsContentSize); +}; + +template +inline HashTable::HashTable() + : m_table(0), + m_tableSize(0), + m_keyCount(0), + m_deletedCount(0), + m_queueFlag(false) #if ENABLE(ASSERT) - , m_modifications(0) + , + m_modifications(0) #endif #if DUMP_HASHTABLE_STATS_PER_TABLE - , m_stats(adoptPtr(new Stats)) + , + m_stats(adoptPtr(new Stats)) #endif - { +{ +} + +inline unsigned doubleHash(unsigned key) { + key = ~key + (key >> 23); + key ^= (key << 12); + key ^= (key >> 7); + key ^= (key << 2); + key ^= (key >> 20); + return key; +} + +template +template +inline Value* +HashTable:: + lookup(T key) { + return const_cast( + const_cast(this)->lookup(key)); +} + +template +template +inline const Value* +HashTable:: + lookup(T key) const { + ASSERT((HashTableKeyChecker< + HashTranslator, KeyTraits, + HashFunctions::safeToCompareToEmptyOrDeleted>::checkKey(key))); + const ValueType* table = m_table; + if (!table) + return 0; + + size_t k = 0; + size_t sizeMask = tableSizeMask(); + unsigned h = HashTranslator::hash(key); + size_t i = h & sizeMask; + + UPDATE_ACCESS_COUNTS(); + + while (1) { + const ValueType* entry = table + i; + + if (HashFunctions::safeToCompareToEmptyOrDeleted) { + if (HashTranslator::equal(Extractor::extract(*entry), key)) + return entry; + + if (isEmptyBucket(*entry)) + return 0; + } else { + if (isEmptyBucket(*entry)) + return 0; + + if (!isDeletedBucket(*entry) && + HashTranslator::equal(Extractor::extract(*entry), key)) + return entry; } - - inline unsigned doubleHash(unsigned key) - { - key = ~key + (key >> 23); - key ^= (key << 12); - key ^= (key >> 7); - key ^= (key << 2); - key ^= (key >> 20); - return key; - } - - template - template - inline Value* HashTable::lookup(T key) - { - return const_cast(const_cast(this)->lookup(key)); + UPDATE_PROBE_COUNTS(); + if (!k) + k = 1 | doubleHash(h); + i = (i + k) & sizeMask; + } +} + +template +template +inline typename HashTable::LookupType +HashTable:: + lookupForWriting(const T& key) { + ASSERT(m_table); + registerModification(); + + ValueType* table = m_table; + size_t k = 0; + size_t sizeMask = tableSizeMask(); + unsigned h = HashTranslator::hash(key); + size_t i = h & sizeMask; + + UPDATE_ACCESS_COUNTS(); + + ValueType* deletedEntry = 0; + + while (1) { + ValueType* entry = table + i; + + if (isEmptyBucket(*entry)) + return LookupType(deletedEntry ? deletedEntry : entry, false); + + if (HashFunctions::safeToCompareToEmptyOrDeleted) { + if (HashTranslator::equal(Extractor::extract(*entry), key)) + return LookupType(entry, true); + + if (isDeletedBucket(*entry)) + deletedEntry = entry; + } else { + if (isDeletedBucket(*entry)) + deletedEntry = entry; + else if (HashTranslator::equal(Extractor::extract(*entry), key)) + return LookupType(entry, true); } - - template - template - inline const Value* HashTable::lookup(T key) const - { - ASSERT((HashTableKeyChecker::checkKey(key))); - const ValueType* table = m_table; - if (!table) - return 0; - - size_t k = 0; - size_t sizeMask = tableSizeMask(); - unsigned h = HashTranslator::hash(key); - size_t i = h & sizeMask; - - UPDATE_ACCESS_COUNTS(); - - while (1) { - const ValueType* entry = table + i; - - if (HashFunctions::safeToCompareToEmptyOrDeleted) { - if (HashTranslator::equal(Extractor::extract(*entry), key)) - return entry; - - if (isEmptyBucket(*entry)) - return 0; - } else { - if (isEmptyBucket(*entry)) - return 0; - - if (!isDeletedBucket(*entry) && HashTranslator::equal(Extractor::extract(*entry), key)) - return entry; - } - UPDATE_PROBE_COUNTS(); - if (!k) - k = 1 | doubleHash(h); - i = (i + k) & sizeMask; - } + UPDATE_PROBE_COUNTS(); + if (!k) + k = 1 | doubleHash(h); + i = (i + k) & sizeMask; + } +} + +template +template +inline typename HashTable::FullLookupType +HashTable:: + fullLookupForWriting(const T& key) { + ASSERT(m_table); + registerModification(); + + ValueType* table = m_table; + size_t k = 0; + size_t sizeMask = tableSizeMask(); + unsigned h = HashTranslator::hash(key); + size_t i = h & sizeMask; + + UPDATE_ACCESS_COUNTS(); + + ValueType* deletedEntry = 0; + + while (1) { + ValueType* entry = table + i; + + if (isEmptyBucket(*entry)) + return makeLookupResult(deletedEntry ? deletedEntry : entry, false, h); + + if (HashFunctions::safeToCompareToEmptyOrDeleted) { + if (HashTranslator::equal(Extractor::extract(*entry), key)) + return makeLookupResult(entry, true, h); + + if (isDeletedBucket(*entry)) + deletedEntry = entry; + } else { + if (isDeletedBucket(*entry)) + deletedEntry = entry; + else if (HashTranslator::equal(Extractor::extract(*entry), key)) + return makeLookupResult(entry, true, h); } - - template - template - inline typename HashTable::LookupType HashTable::lookupForWriting(const T& key) - { - ASSERT(m_table); - registerModification(); - - ValueType* table = m_table; - size_t k = 0; - size_t sizeMask = tableSizeMask(); - unsigned h = HashTranslator::hash(key); - size_t i = h & sizeMask; - - UPDATE_ACCESS_COUNTS(); - - ValueType* deletedEntry = 0; - - while (1) { - ValueType* entry = table + i; - - if (isEmptyBucket(*entry)) - return LookupType(deletedEntry ? deletedEntry : entry, false); - - if (HashFunctions::safeToCompareToEmptyOrDeleted) { - if (HashTranslator::equal(Extractor::extract(*entry), key)) - return LookupType(entry, true); - - if (isDeletedBucket(*entry)) - deletedEntry = entry; - } else { - if (isDeletedBucket(*entry)) - deletedEntry = entry; - else if (HashTranslator::equal(Extractor::extract(*entry), key)) - return LookupType(entry, true); - } - UPDATE_PROBE_COUNTS(); - if (!k) - k = 1 | doubleHash(h); - i = (i + k) & sizeMask; - } - } - - template - template - inline typename HashTable::FullLookupType HashTable::fullLookupForWriting(const T& key) - { - ASSERT(m_table); - registerModification(); - - ValueType* table = m_table; - size_t k = 0; - size_t sizeMask = tableSizeMask(); - unsigned h = HashTranslator::hash(key); - size_t i = h & sizeMask; - - UPDATE_ACCESS_COUNTS(); - - ValueType* deletedEntry = 0; - - while (1) { - ValueType* entry = table + i; - - if (isEmptyBucket(*entry)) - return makeLookupResult(deletedEntry ? deletedEntry : entry, false, h); - - if (HashFunctions::safeToCompareToEmptyOrDeleted) { - if (HashTranslator::equal(Extractor::extract(*entry), key)) - return makeLookupResult(entry, true, h); - - if (isDeletedBucket(*entry)) - deletedEntry = entry; - } else { - if (isDeletedBucket(*entry)) - deletedEntry = entry; - else if (HashTranslator::equal(Extractor::extract(*entry), key)) - return makeLookupResult(entry, true, h); - } - UPDATE_PROBE_COUNTS(); - if (!k) - k = 1 | doubleHash(h); - i = (i + k) & sizeMask; - } + UPDATE_PROBE_COUNTS(); + if (!k) + k = 1 | doubleHash(h); + i = (i + k) & sizeMask; + } +} + +template +struct HashTableBucketInitializer; + +template <> +struct HashTableBucketInitializer { + template + static void initialize(Value& bucket) { + new (NotNull, &bucket) Value(Traits::emptyValue()); + } +}; + +template <> +struct HashTableBucketInitializer { + template + static void initialize(Value& bucket) { + // This initializes the bucket without copying the empty value. + // That makes it possible to use this with types that don't support copying. + // The memset to 0 looks like a slow operation but is optimized by the + // compilers. + memset(&bucket, 0, sizeof(bucket)); + } +}; + +template +inline void +HashTable:: + initializeBucket(ValueType& bucket) { + // For hash maps the key and value cannot be initialied simultaneously, + // and it would be wrong to have a GC when only one was initialized and + // the other still contained garbage (eg. from a previous use of the + // same slot). Therefore we forbid allocation (and thus GC) while the + // slot is initalized to an empty value. + Allocator::enterNoAllocationScope(); + HashTableBucketInitializer::template initialize< + Traits>(bucket); + Allocator::leaveNoAllocationScope(); +} + +template +template +typename HashTable::AddResult +HashTable:: + add(const T& key, const Extra& extra) { + ASSERT(Allocator::isAllocationAllowed()); + if (!m_table) + expand(); + + ASSERT(m_table); + + ValueType* table = m_table; + size_t k = 0; + size_t sizeMask = tableSizeMask(); + unsigned h = HashTranslator::hash(key); + size_t i = h & sizeMask; + + UPDATE_ACCESS_COUNTS(); + + ValueType* deletedEntry = 0; + ValueType* entry; + while (1) { + entry = table + i; + + if (isEmptyBucket(*entry)) + break; + + if (HashFunctions::safeToCompareToEmptyOrDeleted) { + if (HashTranslator::equal(Extractor::extract(*entry), key)) + return AddResult(this, entry, false); + + if (isDeletedBucket(*entry)) + deletedEntry = entry; + } else { + if (isDeletedBucket(*entry)) + deletedEntry = entry; + else if (HashTranslator::equal(Extractor::extract(*entry), key)) + return AddResult(this, entry, false); } - - template struct HashTableBucketInitializer; - - template<> struct HashTableBucketInitializer { - template static void initialize(Value& bucket) - { - new (NotNull, &bucket) Value(Traits::emptyValue()); - } - }; - - template<> struct HashTableBucketInitializer { - template static void initialize(Value& bucket) - { - // This initializes the bucket without copying the empty value. - // That makes it possible to use this with types that don't support copying. - // The memset to 0 looks like a slow operation but is optimized by the compilers. - memset(&bucket, 0, sizeof(bucket)); - } - }; - - template - inline void HashTable::initializeBucket(ValueType& bucket) - { - // For hash maps the key and value cannot be initialied simultaneously, - // and it would be wrong to have a GC when only one was initialized and - // the other still contained garbage (eg. from a previous use of the - // same slot). Therefore we forbid allocation (and thus GC) while the - // slot is initalized to an empty value. - Allocator::enterNoAllocationScope(); - HashTableBucketInitializer::template initialize(bucket); - Allocator::leaveNoAllocationScope(); - } - - template - template - typename HashTable::AddResult HashTable::add(const T& key, const Extra& extra) - { - ASSERT(Allocator::isAllocationAllowed()); - if (!m_table) - expand(); - - ASSERT(m_table); - - ValueType* table = m_table; - size_t k = 0; - size_t sizeMask = tableSizeMask(); - unsigned h = HashTranslator::hash(key); - size_t i = h & sizeMask; - - UPDATE_ACCESS_COUNTS(); - - ValueType* deletedEntry = 0; - ValueType* entry; - while (1) { - entry = table + i; - - if (isEmptyBucket(*entry)) - break; - - if (HashFunctions::safeToCompareToEmptyOrDeleted) { - if (HashTranslator::equal(Extractor::extract(*entry), key)) - return AddResult(this, entry, false); - - if (isDeletedBucket(*entry)) - deletedEntry = entry; - } else { - if (isDeletedBucket(*entry)) - deletedEntry = entry; - else if (HashTranslator::equal(Extractor::extract(*entry), key)) - return AddResult(this, entry, false); - } - UPDATE_PROBE_COUNTS(); - if (!k) - k = 1 | doubleHash(h); - i = (i + k) & sizeMask; - } - - registerModification(); - - if (deletedEntry) { - // Overwrite any data left over from last use, using placement new - // or memset. - initializeBucket(*deletedEntry); - entry = deletedEntry; - --m_deletedCount; - } - - HashTranslator::translate(*entry, key, extra); - ASSERT(!isEmptyOrDeletedBucket(*entry)); - - ++m_keyCount; - - if (shouldExpand()) - entry = expand(entry); - - return AddResult(this, entry, true); - } - - template - template - typename HashTable::AddResult HashTable::addPassingHashCode(const T& key, const Extra& extra) - { - ASSERT(Allocator::isAllocationAllowed()); - if (!m_table) - expand(); - - FullLookupType lookupResult = fullLookupForWriting(key); - - ValueType* entry = lookupResult.first.first; - bool found = lookupResult.first.second; - unsigned h = lookupResult.second; - - if (found) - return AddResult(this, entry, false); - - registerModification(); - - if (isDeletedBucket(*entry)) { - initializeBucket(*entry); - --m_deletedCount; - } - - HashTranslator::translate(*entry, key, extra, h); - ASSERT(!isEmptyOrDeletedBucket(*entry)); - - ++m_keyCount; - if (shouldExpand()) - entry = expand(entry); - - return AddResult(this, entry, true); - } - - template - Value* HashTable::reinsert(ValueType& entry) - { - ASSERT(m_table); - registerModification(); - ASSERT(!lookupForWriting(Extractor::extract(entry)).second); - ASSERT(!isDeletedBucket(*(lookupForWriting(Extractor::extract(entry)).first))); + UPDATE_PROBE_COUNTS(); + if (!k) + k = 1 | doubleHash(h); + i = (i + k) & sizeMask; + } + + registerModification(); + + if (deletedEntry) { + // Overwrite any data left over from last use, using placement new + // or memset. + initializeBucket(*deletedEntry); + entry = deletedEntry; + --m_deletedCount; + } + + HashTranslator::translate(*entry, key, extra); + ASSERT(!isEmptyOrDeletedBucket(*entry)); + + ++m_keyCount; + + if (shouldExpand()) + entry = expand(entry); + + return AddResult(this, entry, true); +} + +template +template +typename HashTable::AddResult +HashTable:: + addPassingHashCode(const T& key, const Extra& extra) { + ASSERT(Allocator::isAllocationAllowed()); + if (!m_table) + expand(); + + FullLookupType lookupResult = fullLookupForWriting(key); + + ValueType* entry = lookupResult.first.first; + bool found = lookupResult.first.second; + unsigned h = lookupResult.second; + + if (found) + return AddResult(this, entry, false); + + registerModification(); + + if (isDeletedBucket(*entry)) { + initializeBucket(*entry); + --m_deletedCount; + } + + HashTranslator::translate(*entry, key, extra, h); + ASSERT(!isEmptyOrDeletedBucket(*entry)); + + ++m_keyCount; + if (shouldExpand()) + entry = expand(entry); + + return AddResult(this, entry, true); +} + +template +Value* +HashTable:: + reinsert(ValueType& entry) { + ASSERT(m_table); + registerModification(); + ASSERT(!lookupForWriting(Extractor::extract(entry)).second); + ASSERT( + !isDeletedBucket(*(lookupForWriting(Extractor::extract(entry)).first))); #if DUMP_HASHTABLE_STATS - atomicIncrement(&HashTableStats::numReinserts); + atomicIncrement(&HashTableStats::numReinserts); #endif #if DUMP_HASHTABLE_STATS_PER_TABLE - ++m_stats->numReinserts; + ++m_stats->numReinserts; #endif - Value* newEntry = lookupForWriting(Extractor::extract(entry)).first; - Mover::move(entry, *newEntry); - - return newEntry; - } - - template - template - inline typename HashTable::iterator HashTable::find(const T& key) - { - ValueType* entry = lookup(key); - if (!entry) - return end(); - - return makeKnownGoodIterator(entry); - } - - template - template - inline typename HashTable::const_iterator HashTable::find(const T& key) const - { - ValueType* entry = const_cast(this)->lookup(key); - if (!entry) - return end(); - - return makeKnownGoodConstIterator(entry); - } - - template - template - bool HashTable::contains(const T& key) const - { - return const_cast(this)->lookup(key); - } - - template - void HashTable::remove(ValueType* pos) - { - registerModification(); + Value* newEntry = lookupForWriting(Extractor::extract(entry)).first; + Mover::move(entry, *newEntry); + + return newEntry; +} + +template +template +inline typename HashTable::iterator +HashTable:: + find(const T& key) { + ValueType* entry = lookup(key); + if (!entry) + return end(); + + return makeKnownGoodIterator(entry); +} + +template +template +inline typename HashTable::const_iterator +HashTable:: + find(const T& key) const { + ValueType* entry = const_cast(this)->lookup(key); + if (!entry) + return end(); + + return makeKnownGoodConstIterator(entry); +} + +template +template +bool HashTable::contains(const T& key) const { + return const_cast(this)->lookup(key); +} + +template +void HashTable::remove(ValueType* pos) { + registerModification(); #if DUMP_HASHTABLE_STATS - atomicIncrement(&HashTableStats::numRemoves); + atomicIncrement(&HashTableStats::numRemoves); #endif #if DUMP_HASHTABLE_STATS_PER_TABLE - ++m_stats->numRemoves; + ++m_stats->numRemoves; #endif - deleteBucket(*pos); - ++m_deletedCount; - --m_keyCount; - - if (shouldShrink()) - shrink(); + deleteBucket(*pos); + ++m_deletedCount; + --m_keyCount; + + if (shouldShrink()) + shrink(); +} + +template +inline void +HashTable:: + remove(iterator it) { + if (it == end()) + return; + + remove(const_cast(it.m_iterator.m_position)); +} + +template +inline void +HashTable:: + remove(const_iterator it) { + if (it == end()) + return; + + remove(const_cast(it.m_position)); +} + +template +inline void +HashTable:: + remove(KeyPeekInType key) { + remove(find(key)); +} + +template +Value* +HashTable:: + allocateTable(unsigned size) { + typedef typename Allocator::template HashTableBackingHelper::Type + HashTableBacking; + + size_t allocSize = size * sizeof(ValueType); + ValueType* result; + // Assert that we will not use memset on things with a vtable entry. + // The compiler will also check this on some platforms. We would + // like to check this on the whole value (key-value pair), but + // IsPolymorphic will return false for a pair of two types, even if + // one of the components is polymorphic. + COMPILE_ASSERT(!Traits::emptyValueIsZero || !IsPolymorphic::value, + EmptyValueCannotBeZeroForThingsWithAVtable); + if (Traits::emptyValueIsZero) { + result = + Allocator::template zeroedBackingMalloc( + allocSize); + } else { + result = Allocator::template backingMalloc( + allocSize); + for (unsigned i = 0; i < size; i++) + initializeBucket(result[i]); + } + return result; +} + +template +void HashTable::deleteAllBucketsAndDeallocate(ValueType* table, + unsigned size) { + if (Traits::needsDestruction) { + for (unsigned i = 0; i < size; ++i) { + // This code is called when the hash table is cleared or + // resized. We have allocated a new backing store and we need + // to run the destructors on the old backing store, as it is + // being freed. If we are GCing we need to both call the + // destructor and mark the bucket as deleted, otherwise the + // destructor gets called again when the GC finds the backing + // store. With the default allocator it's enough to call the + // destructor, since we will free the memory explicitly and + // we won't see the memory with the bucket again. + if (!isEmptyOrDeletedBucket(table[i])) { + if (Allocator::isGarbageCollected) + deleteBucket(table[i]); + else + table[i].~ValueType(); + } } - - template - inline void HashTable::remove(iterator it) - { - if (it == end()) - return; - - remove(const_cast(it.m_iterator.m_position)); - } - - template - inline void HashTable::remove(const_iterator it) - { - if (it == end()) - return; - - remove(const_cast(it.m_position)); - } - - template - inline void HashTable::remove(KeyPeekInType key) - { - remove(find(key)); - } - - template - Value* HashTable::allocateTable(unsigned size) - { - typedef typename Allocator::template HashTableBackingHelper::Type HashTableBacking; - - size_t allocSize = size * sizeof(ValueType); - ValueType* result; - // Assert that we will not use memset on things with a vtable entry. - // The compiler will also check this on some platforms. We would - // like to check this on the whole value (key-value pair), but - // IsPolymorphic will return false for a pair of two types, even if - // one of the components is polymorphic. - COMPILE_ASSERT(!Traits::emptyValueIsZero || !IsPolymorphic::value, EmptyValueCannotBeZeroForThingsWithAVtable); - if (Traits::emptyValueIsZero) { - result = Allocator::template zeroedBackingMalloc(allocSize); - } else { - result = Allocator::template backingMalloc(allocSize); - for (unsigned i = 0; i < size; i++) - initializeBucket(result[i]); - } - return result; - } - - template - void HashTable::deleteAllBucketsAndDeallocate(ValueType* table, unsigned size) - { - if (Traits::needsDestruction) { - for (unsigned i = 0; i < size; ++i) { - // This code is called when the hash table is cleared or - // resized. We have allocated a new backing store and we need - // to run the destructors on the old backing store, as it is - // being freed. If we are GCing we need to both call the - // destructor and mark the bucket as deleted, otherwise the - // destructor gets called again when the GC finds the backing - // store. With the default allocator it's enough to call the - // destructor, since we will free the memory explicitly and - // we won't see the memory with the bucket again. - if (!isEmptyOrDeletedBucket(table[i])) { - if (Allocator::isGarbageCollected) - deleteBucket(table[i]); - else - table[i].~ValueType(); - } - } - } - Allocator::backingFree(table); - } - - template - Value* HashTable::expand(Value* entry) - { - unsigned newSize; - if (!m_tableSize) { - newSize = KeyTraits::minimumTableSize; - } else if (mustRehashInPlace()) { - newSize = m_tableSize; - } else { - newSize = m_tableSize * 2; - RELEASE_ASSERT(newSize > m_tableSize); - } - - return rehash(newSize, entry); - } - - template - Value* HashTable::rehash(unsigned newTableSize, Value* entry) - { - unsigned oldTableSize = m_tableSize; - ValueType* oldTable = m_table; + } + Allocator::backingFree(table); +} + +template +Value* +HashTable:: + expand(Value* entry) { + unsigned newSize; + if (!m_tableSize) { + newSize = KeyTraits::minimumTableSize; + } else if (mustRehashInPlace()) { + newSize = m_tableSize; + } else { + newSize = m_tableSize * 2; + RELEASE_ASSERT(newSize > m_tableSize); + } + + return rehash(newSize, entry); +} + +template +Value* +HashTable:: + rehash(unsigned newTableSize, Value* entry) { + unsigned oldTableSize = m_tableSize; + ValueType* oldTable = m_table; #if DUMP_HASHTABLE_STATS - if (oldTableSize != 0) - atomicIncrement(&HashTableStats::numRehashes); + if (oldTableSize != 0) + atomicIncrement(&HashTableStats::numRehashes); #endif #if DUMP_HASHTABLE_STATS_PER_TABLE - if (oldTableSize != 0) - ++m_stats->numRehashes; + if (oldTableSize != 0) + ++m_stats->numRehashes; #endif - m_table = allocateTable(newTableSize); - m_tableSize = newTableSize; - - Value* newEntry = 0; - for (unsigned i = 0; i != oldTableSize; ++i) { - if (isEmptyOrDeletedBucket(oldTable[i])) { - ASSERT(&oldTable[i] != entry); - continue; - } - - Value* reinsertedEntry = reinsert(oldTable[i]); - if (&oldTable[i] == entry) { - ASSERT(!newEntry); - newEntry = reinsertedEntry; - } - } - - m_deletedCount = 0; + m_table = allocateTable(newTableSize); + m_tableSize = newTableSize; - deleteAllBucketsAndDeallocate(oldTable, oldTableSize); - - return newEntry; + Value* newEntry = 0; + for (unsigned i = 0; i != oldTableSize; ++i) { + if (isEmptyOrDeletedBucket(oldTable[i])) { + ASSERT(&oldTable[i] != entry); + continue; } - template - void HashTable::clear() - { - registerModification(); - if (!m_table) - return; - - deleteAllBucketsAndDeallocate(m_table, m_tableSize); - m_table = 0; - m_tableSize = 0; - m_keyCount = 0; + Value* reinsertedEntry = reinsert(oldTable[i]); + if (&oldTable[i] == entry) { + ASSERT(!newEntry); + newEntry = reinsertedEntry; } - - template - HashTable::HashTable(const HashTable& other) - : m_table(0) - , m_tableSize(0) - , m_keyCount(0) - , m_deletedCount(0) - , m_queueFlag(false) + } + + m_deletedCount = 0; + + deleteAllBucketsAndDeallocate(oldTable, oldTableSize); + + return newEntry; +} + +template +void HashTable::clear() { + registerModification(); + if (!m_table) + return; + + deleteAllBucketsAndDeallocate(m_table, m_tableSize); + m_table = 0; + m_tableSize = 0; + m_keyCount = 0; +} + +template +HashTable:: + HashTable(const HashTable& other) + : m_table(0), + m_tableSize(0), + m_keyCount(0), + m_deletedCount(0), + m_queueFlag(false) #if ENABLE(ASSERT) - , m_modifications(0) + , + m_modifications(0) #endif #if DUMP_HASHTABLE_STATS_PER_TABLE - , m_stats(adoptPtr(new Stats(*other.m_stats))) + , + m_stats(adoptPtr(new Stats(*other.m_stats))) #endif - { - // Copy the hash table the dumb way, by adding each element to the new table. - // It might be more efficient to copy the table slots, but it's not clear that efficiency is needed. - const_iterator end = other.end(); - for (const_iterator it = other.begin(); it != end; ++it) - add(*it); - } - - template - void HashTable::swap(HashTable& other) - { - std::swap(m_table, other.m_table); - std::swap(m_tableSize, other.m_tableSize); - std::swap(m_keyCount, other.m_keyCount); - // std::swap does not work for bit fields. - unsigned deleted = m_deletedCount; - m_deletedCount = other.m_deletedCount; - other.m_deletedCount = deleted; - ASSERT(!m_queueFlag); - ASSERT(!other.m_queueFlag); +{ + // Copy the hash table the dumb way, by adding each element to the new table. + // It might be more efficient to copy the table slots, but it's not clear that + // efficiency is needed. + const_iterator end = other.end(); + for (const_iterator it = other.begin(); it != end; ++it) + add(*it); +} + +template +void HashTable::swap(HashTable& other) { + std::swap(m_table, other.m_table); + std::swap(m_tableSize, other.m_tableSize); + std::swap(m_keyCount, other.m_keyCount); + // std::swap does not work for bit fields. + unsigned deleted = m_deletedCount; + m_deletedCount = other.m_deletedCount; + other.m_deletedCount = deleted; + ASSERT(!m_queueFlag); + ASSERT(!other.m_queueFlag); #if ENABLE(ASSERT) - std::swap(m_modifications, other.m_modifications); + std::swap(m_modifications, other.m_modifications); #endif #if DUMP_HASHTABLE_STATS_PER_TABLE - m_stats.swap(other.m_stats); + m_stats.swap(other.m_stats); #endif - } - - template - HashTable& HashTable::operator=(const HashTable& other) - { - HashTable tmp(other); - swap(tmp); - return *this; - } - - // iterator adapters - - template struct HashTableConstIteratorAdapter { - HashTableConstIteratorAdapter() {} - HashTableConstIteratorAdapter(const typename HashTableType::const_iterator& impl) : m_impl(impl) {} - typedef typename Traits::IteratorConstGetType GetType; - typedef typename HashTableType::ValueTraits::IteratorConstGetType SourceGetType; - - GetType get() const { return const_cast(SourceGetType(m_impl.get())); } - typename Traits::IteratorConstReferenceType operator*() const { return Traits::getToReferenceConstConversion(get()); } - GetType operator->() const { return get(); } - - HashTableConstIteratorAdapter& operator++() { ++m_impl; return *this; } - // postfix ++ intentionally omitted - - typename HashTableType::const_iterator m_impl; - }; - - template struct HashTableIteratorAdapter { - typedef typename Traits::IteratorGetType GetType; - typedef typename HashTableType::ValueTraits::IteratorGetType SourceGetType; - - HashTableIteratorAdapter() {} - HashTableIteratorAdapter(const typename HashTableType::iterator& impl) : m_impl(impl) {} - - GetType get() const { return const_cast(SourceGetType(m_impl.get())); } - typename Traits::IteratorReferenceType operator*() const { return Traits::getToReferenceConversion(get()); } - GetType operator->() const { return get(); } - - HashTableIteratorAdapter& operator++() { ++m_impl; return *this; } - // postfix ++ intentionally omitted - - operator HashTableConstIteratorAdapter() - { - typename HashTableType::const_iterator i = m_impl; - return i; - } - - typename HashTableType::iterator m_impl; - }; - - template - inline bool operator==(const HashTableConstIteratorAdapter& a, const HashTableConstIteratorAdapter& b) - { - return a.m_impl == b.m_impl; - } - - template - inline bool operator!=(const HashTableConstIteratorAdapter& a, const HashTableConstIteratorAdapter& b) - { - return a.m_impl != b.m_impl; - } - - template - inline bool operator==(const HashTableIteratorAdapter& a, const HashTableIteratorAdapter& b) - { - return a.m_impl == b.m_impl; - } - - template - inline bool operator!=(const HashTableIteratorAdapter& a, const HashTableIteratorAdapter& b) - { - return a.m_impl != b.m_impl; - } - - // All 4 combinations of ==, != and Const,non const. - template - inline bool operator==(const HashTableConstIteratorAdapter& a, const HashTableIteratorAdapter& b) - { - return a.m_impl == b.m_impl; - } - - template - inline bool operator!=(const HashTableConstIteratorAdapter& a, const HashTableIteratorAdapter& b) - { - return a.m_impl != b.m_impl; - } - - template - inline bool operator==(const HashTableIteratorAdapter& a, const HashTableConstIteratorAdapter& b) - { - return a.m_impl == b.m_impl; - } - - template - inline bool operator!=(const HashTableIteratorAdapter& a, const HashTableConstIteratorAdapter& b) - { - return a.m_impl != b.m_impl; - } - - template - inline void removeAll(Collection1& collection, const Collection2& toBeRemoved) - { - if (collection.isEmpty() || toBeRemoved.isEmpty()) - return; - typedef typename Collection2::const_iterator CollectionIterator; - CollectionIterator end(toBeRemoved.end()); - for (CollectionIterator it(toBeRemoved.begin()); it != end; ++it) - collection.remove(*it); - } - -} // namespace WTF +} + +template +HashTable& +HashTable:: +operator=(const HashTable& other) { + HashTable tmp(other); + swap(tmp); + return *this; +} + +// iterator adapters + +template +struct HashTableConstIteratorAdapter { + HashTableConstIteratorAdapter() {} + HashTableConstIteratorAdapter( + const typename HashTableType::const_iterator& impl) + : m_impl(impl) {} + typedef typename Traits::IteratorConstGetType GetType; + typedef + typename HashTableType::ValueTraits::IteratorConstGetType SourceGetType; + + GetType get() const { + return const_cast(SourceGetType(m_impl.get())); + } + typename Traits::IteratorConstReferenceType operator*() const { + return Traits::getToReferenceConstConversion(get()); + } + GetType operator->() const { return get(); } + + HashTableConstIteratorAdapter& operator++() { + ++m_impl; + return *this; + } + // postfix ++ intentionally omitted + + typename HashTableType::const_iterator m_impl; +}; + +template +struct HashTableIteratorAdapter { + typedef typename Traits::IteratorGetType GetType; + typedef typename HashTableType::ValueTraits::IteratorGetType SourceGetType; + + HashTableIteratorAdapter() {} + HashTableIteratorAdapter(const typename HashTableType::iterator& impl) + : m_impl(impl) {} + + GetType get() const { + return const_cast(SourceGetType(m_impl.get())); + } + typename Traits::IteratorReferenceType operator*() const { + return Traits::getToReferenceConversion(get()); + } + GetType operator->() const { return get(); } + + HashTableIteratorAdapter& operator++() { + ++m_impl; + return *this; + } + // postfix ++ intentionally omitted + + operator HashTableConstIteratorAdapter() { + typename HashTableType::const_iterator i = m_impl; + return i; + } + + typename HashTableType::iterator m_impl; +}; + +template +inline bool operator==(const HashTableConstIteratorAdapter& a, + const HashTableConstIteratorAdapter& b) { + return a.m_impl == b.m_impl; +} + +template +inline bool operator!=(const HashTableConstIteratorAdapter& a, + const HashTableConstIteratorAdapter& b) { + return a.m_impl != b.m_impl; +} + +template +inline bool operator==(const HashTableIteratorAdapter& a, + const HashTableIteratorAdapter& b) { + return a.m_impl == b.m_impl; +} + +template +inline bool operator!=(const HashTableIteratorAdapter& a, + const HashTableIteratorAdapter& b) { + return a.m_impl != b.m_impl; +} + +// All 4 combinations of ==, != and Const,non const. +template +inline bool operator==(const HashTableConstIteratorAdapter& a, + const HashTableIteratorAdapter& b) { + return a.m_impl == b.m_impl; +} + +template +inline bool operator!=(const HashTableConstIteratorAdapter& a, + const HashTableIteratorAdapter& b) { + return a.m_impl != b.m_impl; +} + +template +inline bool operator==(const HashTableIteratorAdapter& a, + const HashTableConstIteratorAdapter& b) { + return a.m_impl == b.m_impl; +} + +template +inline bool operator!=(const HashTableIteratorAdapter& a, + const HashTableConstIteratorAdapter& b) { + return a.m_impl != b.m_impl; +} + +template +inline void removeAll(Collection1& collection, const Collection2& toBeRemoved) { + if (collection.isEmpty() || toBeRemoved.isEmpty()) + return; + typedef typename Collection2::const_iterator CollectionIterator; + CollectionIterator end(toBeRemoved.end()); + for (CollectionIterator it(toBeRemoved.begin()); it != end; ++it) + collection.remove(*it); +} + +} // namespace WTF #include "flutter/sky/engine/wtf/HashIterators.h" diff --git a/sky/engine/wtf/HashTableDeletedValueType.h b/sky/engine/wtf/HashTableDeletedValueType.h index 789218e2b0e1e..91a4881e80251 100644 --- a/sky/engine/wtf/HashTableDeletedValueType.h +++ b/sky/engine/wtf/HashTableDeletedValueType.h @@ -35,6 +35,6 @@ namespace WTF { enum HashTableDeletedValueType { HashTableDeletedValue }; -} // namespace WTF +} // namespace WTF #endif // SKY_ENGINE_WTF_HASHTABLEDELETEDVALUETYPE_H_ diff --git a/sky/engine/wtf/HashTraits.h b/sky/engine/wtf/HashTraits.h index 34f189468775d..4b668a55bbcb3 100644 --- a/sky/engine/wtf/HashTraits.h +++ b/sky/engine/wtf/HashTraits.h @@ -1,5 +1,6 @@ /* - * Copyright (C) 2005, 2006, 2007, 2008, 2011, 2012 Apple Inc. All rights reserved. + * Copyright (C) 2005, 2006, 2007, 2008, 2011, 2012 Apple Inc. + * All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -21,7 +22,7 @@ #ifndef SKY_ENGINE_WTF_HASHTRAITS_H_ #define SKY_ENGINE_WTF_HASHTRAITS_H_ -#include // For memset. +#include // For memset. #include #include #include "flutter/sky/engine/wtf/HashFunctions.h" @@ -31,293 +32,388 @@ namespace WTF { - class String; +class String; - template class OwnPtr; - template class PassOwnPtr; +template +class OwnPtr; +template +class PassOwnPtr; - template struct HashTraits; +template +struct HashTraits; - template struct GenericHashTraitsBase; +template +struct GenericHashTraitsBase; - enum ShouldWeakPointersBeMarkedStrongly { - WeakPointersActStrong, - WeakPointersActWeak - }; +enum ShouldWeakPointersBeMarkedStrongly { + WeakPointersActStrong, + WeakPointersActWeak +}; - template struct GenericHashTraitsBase { - // The emptyValueIsZero flag is used to optimize allocation of empty hash tables with zeroed memory. - static const bool emptyValueIsZero = false; +template +struct GenericHashTraitsBase { + // The emptyValueIsZero flag is used to optimize allocation of empty hash + // tables with zeroed memory. + static const bool emptyValueIsZero = false; - // The hasIsEmptyValueFunction flag allows the hash table to automatically generate code to check - // for the empty value when it can be done with the equality operator, but allows custom functions - // for cases like String that need them. - static const bool hasIsEmptyValueFunction = false; + // The hasIsEmptyValueFunction flag allows the hash table to automatically + // generate code to check for the empty value when it can be done with the + // equality operator, but allows custom functions for cases like String that + // need them. + static const bool hasIsEmptyValueFunction = false; - // The needsDestruction flag is used to optimize destruction and rehashing. - static const bool needsDestruction = true; + // The needsDestruction flag is used to optimize destruction and rehashing. + static const bool needsDestruction = true; - // The starting table size. Can be overridden when we know beforehand that - // a hash table will have at least N entries. + // The starting table size. Can be overridden when we know beforehand that + // a hash table will have at least N entries. #if defined(MEMORY_SANITIZER_INITIAL_SIZE) - static const unsigned minimumTableSize = 1; + static const unsigned minimumTableSize = 1; #else - static const unsigned minimumTableSize = 8; + static const unsigned minimumTableSize = 8; #endif - static const WeakHandlingFlag weakHandlingFlag = IsWeak::value ? WeakHandlingInCollections : NoWeakHandlingInCollections; - }; - - // Default integer traits disallow both 0 and -1 as keys (max value instead of -1 for unsigned). - template struct GenericHashTraitsBase : GenericHashTraitsBase { - static const bool emptyValueIsZero = true; - static const bool needsDestruction = false; - static void constructDeletedValue(T& slot, bool) { slot = static_cast(-1); } - static bool isDeletedValue(T value) { return value == static_cast(-1); } - }; - - template struct GenericHashTraits : GenericHashTraitsBase::value, T> { - typedef T TraitType; - typedef T EmptyValueType; - - static T emptyValue() { return T(); } - - // Type for functions that do not take ownership, such as contains. - typedef const T& PeekInType; - typedef T* IteratorGetType; - typedef const T* IteratorConstGetType; - typedef T& IteratorReferenceType; - typedef const T& IteratorConstReferenceType; - static IteratorReferenceType getToReferenceConversion(IteratorGetType x) { return *x; } - static IteratorConstReferenceType getToReferenceConstConversion(IteratorConstGetType x) { return *x; } - // Type for functions that take ownership, such as add. - // The store function either not be called or called once to store something passed in. - // The value passed to the store function will be PassInType. - typedef const T& PassInType; - static void store(const T& value, T& storage) { storage = value; } - - // Type for return value of functions that transfer ownership, such as take. - typedef T PassOutType; - static const T& passOut(const T& value) { return value; } - - // Type for return value of functions that do not transfer ownership, such as get. - // FIXME: We could change this type to const T& for better performance if we figured out - // a way to handle the return value from emptyValue, which is a temporary. - typedef T PeekOutType; - static const T& peek(const T& value) { return value; } - }; - - template struct HashTraits : GenericHashTraits { }; - - template struct FloatHashTraits : GenericHashTraits { - static const bool needsDestruction = false; - static T emptyValue() { return std::numeric_limits::infinity(); } - static void constructDeletedValue(T& slot, bool) { slot = -std::numeric_limits::infinity(); } - static bool isDeletedValue(T value) { return value == -std::numeric_limits::infinity(); } - }; - - template<> struct HashTraits : FloatHashTraits { }; - template<> struct HashTraits : FloatHashTraits { }; - - // Default unsigned traits disallow both 0 and max as keys -- use these traits to allow zero and disallow max - 1. - template struct UnsignedWithZeroKeyHashTraits : GenericHashTraits { - static const bool emptyValueIsZero = false; - static const bool needsDestruction = false; - static T emptyValue() { return std::numeric_limits::max(); } - static void constructDeletedValue(T& slot, bool) { slot = std::numeric_limits::max() - 1; } - static bool isDeletedValue(T value) { return value == std::numeric_limits::max() - 1; } - }; - - template struct HashTraits : GenericHashTraits { - static const bool emptyValueIsZero = true; - static const bool needsDestruction = false; - static void constructDeletedValue(P*& slot, bool) { slot = reinterpret_cast(-1); } - static bool isDeletedValue(P* value) { return value == reinterpret_cast(-1); } - }; - - template struct SimpleClassHashTraits : GenericHashTraits { - static const bool emptyValueIsZero = true; - static void constructDeletedValue(T& slot, bool) { new (NotNull, &slot) T(HashTableDeletedValue); } - static bool isDeletedValue(const T& value) { return value.isHashTableDeletedValue(); } - }; - - template struct HashTraits > : SimpleClassHashTraits > { - typedef std::nullptr_t EmptyValueType; - - static EmptyValueType emptyValue() { return nullptr; } - - static const bool hasIsEmptyValueFunction = true; - static bool isEmptyValue(const OwnPtr

& value) { return !value; } - - typedef typename OwnPtr

::PtrType PeekInType; - - typedef PassOwnPtr

PassInType; - static void store(PassOwnPtr

value, OwnPtr

& storage) { storage = value; } - - typedef PassOwnPtr

PassOutType; - static PassOwnPtr

passOut(OwnPtr

& value) { return value.release(); } - static PassOwnPtr

passOut(std::nullptr_t) { return nullptr; } - - typedef typename OwnPtr

::PtrType PeekOutType; - static PeekOutType peek(const OwnPtr

& value) { return value.get(); } - static PeekOutType peek(std::nullptr_t) { return 0; } - }; - - template struct HashTraits > : SimpleClassHashTraits > { - typedef std::nullptr_t EmptyValueType; - static EmptyValueType emptyValue() { return nullptr; } - - static const bool hasIsEmptyValueFunction = true; - static bool isEmptyValue(const RefPtr

& value) { return !value; } - - typedef RefPtrValuePeeker

PeekInType; - typedef RefPtr

* IteratorGetType; - typedef const RefPtr

* IteratorConstGetType; - typedef RefPtr

& IteratorReferenceType; - typedef const RefPtr

& IteratorConstReferenceType; - static IteratorReferenceType getToReferenceConversion(IteratorGetType x) { return *x; } - static IteratorConstReferenceType getToReferenceConstConversion(IteratorConstGetType x) { return *x; } - - typedef PassRefPtr

PassInType; - static void store(PassRefPtr

value, RefPtr

& storage) { storage = value; } - - typedef PassRefPtr

PassOutType; - static PassOutType passOut(RefPtr

& value) { return value.release(); } - static PassOutType passOut(std::nullptr_t) { return nullptr; } - - typedef P* PeekOutType; - static PeekOutType peek(const RefPtr

& value) { return value.get(); } - static PeekOutType peek(std::nullptr_t) { return 0; } - }; - - template struct HashTraits > : HashTraits { }; - - template<> struct HashTraits : SimpleClassHashTraits { - static const bool hasIsEmptyValueFunction = true; - static bool isEmptyValue(const String&); - }; - - // This struct template is an implementation detail of the isHashTraitsEmptyValue function, - // which selects either the emptyValue function or the isEmptyValue function to check for empty values. - template struct HashTraitsEmptyValueChecker; - template struct HashTraitsEmptyValueChecker { - template static bool isEmptyValue(const T& value) { return Traits::isEmptyValue(value); } - }; - template struct HashTraitsEmptyValueChecker { - template static bool isEmptyValue(const T& value) { return value == Traits::emptyValue(); } - }; - template inline bool isHashTraitsEmptyValue(const T& value) - { - return HashTraitsEmptyValueChecker::isEmptyValue(value); - } - - template - struct PairHashTraits : GenericHashTraits > { - typedef FirstTraitsArg FirstTraits; - typedef SecondTraitsArg SecondTraits; - typedef std::pair TraitType; - typedef std::pair EmptyValueType; - - static const bool emptyValueIsZero = FirstTraits::emptyValueIsZero && SecondTraits::emptyValueIsZero; - static EmptyValueType emptyValue() { return std::make_pair(FirstTraits::emptyValue(), SecondTraits::emptyValue()); } - - static const bool needsDestruction = FirstTraits::needsDestruction || SecondTraits::needsDestruction; - - static const unsigned minimumTableSize = FirstTraits::minimumTableSize; - - static void constructDeletedValue(TraitType& slot, bool zeroValue) - { - FirstTraits::constructDeletedValue(slot.first, zeroValue); - // For GC collections the memory for the backing is zeroed when it - // is allocated, and the constructors may take advantage of that, - // especially if a GC occurs during insertion of an entry into the - // table. This slot is being marked deleted, but If the slot is - // reused at a later point, the same assumptions around memory - // zeroing must hold as they did at the initial allocation. - // Therefore we zero the value part of the slot here for GC - // collections. - if (zeroValue) - memset(reinterpret_cast(&slot.second), 0, sizeof(slot.second)); - } - static bool isDeletedValue(const TraitType& value) { return FirstTraits::isDeletedValue(value.first); } - }; - - template - struct HashTraits > : public PairHashTraits, HashTraits > { }; - - template - struct KeyValuePair { - typedef KeyTypeArg KeyType; - - KeyValuePair() - { - } - - KeyValuePair(const KeyTypeArg& _key, const ValueTypeArg& _value) - : key(_key) - , value(_value) - { - } - - template - KeyValuePair(const KeyValuePair& other) - : key(other.key) - , value(other.value) - { - } - - KeyTypeArg key; - ValueTypeArg value; - }; - - template - struct KeyValuePairHashTraits : GenericHashTraits > { - typedef KeyTraitsArg KeyTraits; - typedef ValueTraitsArg ValueTraits; - typedef KeyValuePair TraitType; - typedef KeyValuePair EmptyValueType; - - static const bool emptyValueIsZero = KeyTraits::emptyValueIsZero && ValueTraits::emptyValueIsZero; - static EmptyValueType emptyValue() { return KeyValuePair(KeyTraits::emptyValue(), ValueTraits::emptyValue()); } - - static const bool needsDestruction = KeyTraits::needsDestruction || ValueTraits::needsDestruction; - static const WeakHandlingFlag weakHandlingFlag = (KeyTraits::weakHandlingFlag == WeakHandlingInCollections || ValueTraits::weakHandlingFlag == WeakHandlingInCollections) ? WeakHandlingInCollections : NoWeakHandlingInCollections; - - static const unsigned minimumTableSize = KeyTraits::minimumTableSize; - - static void constructDeletedValue(TraitType& slot, bool zeroValue) - { - KeyTraits::constructDeletedValue(slot.key, zeroValue); - // See similar code in this file for why we need to do this. - if (zeroValue) - memset(reinterpret_cast(&slot.value), 0, sizeof(slot.value)); - } - static bool isDeletedValue(const TraitType& value) { return KeyTraits::isDeletedValue(value.key); } - }; - - template - struct HashTraits > : public KeyValuePairHashTraits, HashTraits > { }; - - template - struct NullableHashTraits : public HashTraits { - static const bool emptyValueIsZero = false; - static T emptyValue() { return reinterpret_cast(1); } - }; - - // This is for tracing inside collections that have special support for weak - // pointers. The trait has a trace method which returns true if there are weak - // pointers to things that have not (yet) been marked live. Returning true - // indicates that the entry in the collection may yet be removed by weak - // handling. Default implementation for non-weak types is to use the regular - // non-weak TraceTrait. Default implementation for types with weakness is to - // call traceInCollection on the type's trait. - template - struct TraceInCollectionTrait; - -} // namespace WTF + static const WeakHandlingFlag weakHandlingFlag = + IsWeak::value ? WeakHandlingInCollections + : NoWeakHandlingInCollections; +}; + +// Default integer traits disallow both 0 and -1 as keys (max value instead of +// -1 for unsigned). +template +struct GenericHashTraitsBase : GenericHashTraitsBase { + static const bool emptyValueIsZero = true; + static const bool needsDestruction = false; + static void constructDeletedValue(T& slot, bool) { + slot = static_cast(-1); + } + static bool isDeletedValue(T value) { return value == static_cast(-1); } +}; + +template +struct GenericHashTraits : GenericHashTraitsBase::value, T> { + typedef T TraitType; + typedef T EmptyValueType; + + static T emptyValue() { return T(); } + + // Type for functions that do not take ownership, such as contains. + typedef const T& PeekInType; + typedef T* IteratorGetType; + typedef const T* IteratorConstGetType; + typedef T& IteratorReferenceType; + typedef const T& IteratorConstReferenceType; + static IteratorReferenceType getToReferenceConversion(IteratorGetType x) { + return *x; + } + static IteratorConstReferenceType getToReferenceConstConversion( + IteratorConstGetType x) { + return *x; + } + // Type for functions that take ownership, such as add. + // The store function either not be called or called once to store something + // passed in. The value passed to the store function will be PassInType. + typedef const T& PassInType; + static void store(const T& value, T& storage) { storage = value; } + + // Type for return value of functions that transfer ownership, such as take. + typedef T PassOutType; + static const T& passOut(const T& value) { return value; } + + // Type for return value of functions that do not transfer ownership, such as + // get. + // FIXME: We could change this type to const T& for better performance if we + // figured out a way to handle the return value from emptyValue, which is a + // temporary. + typedef T PeekOutType; + static const T& peek(const T& value) { return value; } +}; + +template +struct HashTraits : GenericHashTraits {}; + +template +struct FloatHashTraits : GenericHashTraits { + static const bool needsDestruction = false; + static T emptyValue() { return std::numeric_limits::infinity(); } + static void constructDeletedValue(T& slot, bool) { + slot = -std::numeric_limits::infinity(); + } + static bool isDeletedValue(T value) { + return value == -std::numeric_limits::infinity(); + } +}; + +template <> +struct HashTraits : FloatHashTraits {}; +template <> +struct HashTraits : FloatHashTraits {}; + +// Default unsigned traits disallow both 0 and max as keys -- use these traits +// to allow zero and disallow max - 1. +template +struct UnsignedWithZeroKeyHashTraits : GenericHashTraits { + static const bool emptyValueIsZero = false; + static const bool needsDestruction = false; + static T emptyValue() { return std::numeric_limits::max(); } + static void constructDeletedValue(T& slot, bool) { + slot = std::numeric_limits::max() - 1; + } + static bool isDeletedValue(T value) { + return value == std::numeric_limits::max() - 1; + } +}; + +template +struct HashTraits : GenericHashTraits { + static const bool emptyValueIsZero = true; + static const bool needsDestruction = false; + static void constructDeletedValue(P*& slot, bool) { + slot = reinterpret_cast(-1); + } + static bool isDeletedValue(P* value) { + return value == reinterpret_cast(-1); + } +}; + +template +struct SimpleClassHashTraits : GenericHashTraits { + static const bool emptyValueIsZero = true; + static void constructDeletedValue(T& slot, bool) { + new (NotNull, &slot) T(HashTableDeletedValue); + } + static bool isDeletedValue(const T& value) { + return value.isHashTableDeletedValue(); + } +}; + +template +struct HashTraits> : SimpleClassHashTraits> { + typedef std::nullptr_t EmptyValueType; + + static EmptyValueType emptyValue() { return nullptr; } + + static const bool hasIsEmptyValueFunction = true; + static bool isEmptyValue(const OwnPtr

& value) { return !value; } + + typedef typename OwnPtr

::PtrType PeekInType; + + typedef PassOwnPtr

PassInType; + static void store(PassOwnPtr

value, OwnPtr

& storage) { + storage = value; + } + + typedef PassOwnPtr

PassOutType; + static PassOwnPtr

passOut(OwnPtr

& value) { return value.release(); } + static PassOwnPtr

passOut(std::nullptr_t) { return nullptr; } + + typedef typename OwnPtr

::PtrType PeekOutType; + static PeekOutType peek(const OwnPtr

& value) { return value.get(); } + static PeekOutType peek(std::nullptr_t) { return 0; } +}; + +template +struct HashTraits> : SimpleClassHashTraits> { + typedef std::nullptr_t EmptyValueType; + static EmptyValueType emptyValue() { return nullptr; } + + static const bool hasIsEmptyValueFunction = true; + static bool isEmptyValue(const RefPtr

& value) { return !value; } + + typedef RefPtrValuePeeker

PeekInType; + typedef RefPtr

* IteratorGetType; + typedef const RefPtr

* IteratorConstGetType; + typedef RefPtr

& IteratorReferenceType; + typedef const RefPtr

& IteratorConstReferenceType; + static IteratorReferenceType getToReferenceConversion(IteratorGetType x) { + return *x; + } + static IteratorConstReferenceType getToReferenceConstConversion( + IteratorConstGetType x) { + return *x; + } + + typedef PassRefPtr

PassInType; + static void store(PassRefPtr

value, RefPtr

& storage) { + storage = value; + } + + typedef PassRefPtr

PassOutType; + static PassOutType passOut(RefPtr

& value) { return value.release(); } + static PassOutType passOut(std::nullptr_t) { return nullptr; } + + typedef P* PeekOutType; + static PeekOutType peek(const RefPtr

& value) { return value.get(); } + static PeekOutType peek(std::nullptr_t) { return 0; } +}; + +template +struct HashTraits> : HashTraits {}; + +template <> +struct HashTraits : SimpleClassHashTraits { + static const bool hasIsEmptyValueFunction = true; + static bool isEmptyValue(const String&); +}; + +// This struct template is an implementation detail of the +// isHashTraitsEmptyValue function, which selects either the emptyValue function +// or the isEmptyValue function to check for empty values. +template +struct HashTraitsEmptyValueChecker; +template +struct HashTraitsEmptyValueChecker { + template + static bool isEmptyValue(const T& value) { + return Traits::isEmptyValue(value); + } +}; +template +struct HashTraitsEmptyValueChecker { + template + static bool isEmptyValue(const T& value) { + return value == Traits::emptyValue(); + } +}; +template +inline bool isHashTraitsEmptyValue(const T& value) { + return HashTraitsEmptyValueChecker< + Traits, Traits::hasIsEmptyValueFunction>::isEmptyValue(value); +} + +template +struct PairHashTraits + : GenericHashTraits> { + typedef FirstTraitsArg FirstTraits; + typedef SecondTraitsArg SecondTraits; + typedef std::pair + TraitType; + typedef std::pair + EmptyValueType; + + static const bool emptyValueIsZero = + FirstTraits::emptyValueIsZero && SecondTraits::emptyValueIsZero; + static EmptyValueType emptyValue() { + return std::make_pair(FirstTraits::emptyValue(), + SecondTraits::emptyValue()); + } + + static const bool needsDestruction = + FirstTraits::needsDestruction || SecondTraits::needsDestruction; + + static const unsigned minimumTableSize = FirstTraits::minimumTableSize; + + static void constructDeletedValue(TraitType& slot, bool zeroValue) { + FirstTraits::constructDeletedValue(slot.first, zeroValue); + // For GC collections the memory for the backing is zeroed when it + // is allocated, and the constructors may take advantage of that, + // especially if a GC occurs during insertion of an entry into the + // table. This slot is being marked deleted, but If the slot is + // reused at a later point, the same assumptions around memory + // zeroing must hold as they did at the initial allocation. + // Therefore we zero the value part of the slot here for GC + // collections. + if (zeroValue) + memset(reinterpret_cast(&slot.second), 0, sizeof(slot.second)); + } + static bool isDeletedValue(const TraitType& value) { + return FirstTraits::isDeletedValue(value.first); + } +}; + +template +struct HashTraits> + : public PairHashTraits, HashTraits> {}; + +template +struct KeyValuePair { + typedef KeyTypeArg KeyType; + + KeyValuePair() {} + + KeyValuePair(const KeyTypeArg& _key, const ValueTypeArg& _value) + : key(_key), value(_value) {} + + template + KeyValuePair(const KeyValuePair& other) + : key(other.key), value(other.value) {} + + KeyTypeArg key; + ValueTypeArg value; +}; + +template +struct KeyValuePairHashTraits + : GenericHashTraits> { + typedef KeyTraitsArg KeyTraits; + typedef ValueTraitsArg ValueTraits; + typedef KeyValuePair + TraitType; + typedef KeyValuePair + EmptyValueType; + + static const bool emptyValueIsZero = + KeyTraits::emptyValueIsZero && ValueTraits::emptyValueIsZero; + static EmptyValueType emptyValue() { + return KeyValuePair( + KeyTraits::emptyValue(), ValueTraits::emptyValue()); + } + + static const bool needsDestruction = + KeyTraits::needsDestruction || ValueTraits::needsDestruction; + static const WeakHandlingFlag weakHandlingFlag = + (KeyTraits::weakHandlingFlag == WeakHandlingInCollections || + ValueTraits::weakHandlingFlag == WeakHandlingInCollections) + ? WeakHandlingInCollections + : NoWeakHandlingInCollections; + + static const unsigned minimumTableSize = KeyTraits::minimumTableSize; + + static void constructDeletedValue(TraitType& slot, bool zeroValue) { + KeyTraits::constructDeletedValue(slot.key, zeroValue); + // See similar code in this file for why we need to do this. + if (zeroValue) + memset(reinterpret_cast(&slot.value), 0, sizeof(slot.value)); + } + static bool isDeletedValue(const TraitType& value) { + return KeyTraits::isDeletedValue(value.key); + } +}; + +template +struct HashTraits> + : public KeyValuePairHashTraits, HashTraits> {}; + +template +struct NullableHashTraits : public HashTraits { + static const bool emptyValueIsZero = false; + static T emptyValue() { return reinterpret_cast(1); } +}; + +// This is for tracing inside collections that have special support for weak +// pointers. The trait has a trace method which returns true if there are weak +// pointers to things that have not (yet) been marked live. Returning true +// indicates that the entry in the collection may yet be removed by weak +// handling. Default implementation for non-weak types is to use the regular +// non-weak TraceTrait. Default implementation for types with weakness is to +// call traceInCollection on the type's trait. +template +struct TraceInCollectionTrait; + +} // namespace WTF using WTF::HashTraits; -using WTF::PairHashTraits; using WTF::NullableHashTraits; +using WTF::PairHashTraits; using WTF::SimpleClassHashTraits; #endif // SKY_ENGINE_WTF_HASHTRAITS_H_ diff --git a/sky/engine/wtf/HexNumber.h b/sky/engine/wtf/HexNumber.h index bc63cdc44ce8e..39fc0c3576017 100644 --- a/sky/engine/wtf/HexNumber.h +++ b/sky/engine/wtf/HexNumber.h @@ -24,84 +24,89 @@ namespace WTF { -enum HexConversionMode { - Lowercase, - Uppercase -}; +enum HexConversionMode { Lowercase, Uppercase }; namespace Internal { const LChar lowerHexDigits[17] = "0123456789abcdef"; const LChar upperHexDigits[17] = "0123456789ABCDEF"; -inline const LChar* hexDigitsForMode(HexConversionMode mode) -{ - return mode == Lowercase ? lowerHexDigits : upperHexDigits; +inline const LChar* hexDigitsForMode(HexConversionMode mode) { + return mode == Lowercase ? lowerHexDigits : upperHexDigits; } -}; // namespace Internal +}; // namespace Internal -template -inline void appendByteAsHex(unsigned char byte, T& destination, HexConversionMode mode = Uppercase) -{ - const LChar* hexDigits = Internal::hexDigitsForMode(mode); - destination.append(hexDigits[byte >> 4]); - destination.append(hexDigits[byte & 0xF]); +template +inline void appendByteAsHex(unsigned char byte, + T& destination, + HexConversionMode mode = Uppercase) { + const LChar* hexDigits = Internal::hexDigitsForMode(mode); + destination.append(hexDigits[byte >> 4]); + destination.append(hexDigits[byte & 0xF]); } -template -inline void placeByteAsHexCompressIfPossible(unsigned char byte, T& destination, unsigned& index, HexConversionMode mode = Uppercase) -{ - const LChar* hexDigits = Internal::hexDigitsForMode(mode); - if (byte >= 0x10) - destination[index++] = hexDigits[byte >> 4]; - destination[index++] = hexDigits[byte & 0xF]; +template +inline void placeByteAsHexCompressIfPossible( + unsigned char byte, + T& destination, + unsigned& index, + HexConversionMode mode = Uppercase) { + const LChar* hexDigits = Internal::hexDigitsForMode(mode); + if (byte >= 0x10) + destination[index++] = hexDigits[byte >> 4]; + destination[index++] = hexDigits[byte & 0xF]; } -template -inline void placeByteAsHex(unsigned char byte, T& destination, HexConversionMode mode = Uppercase) -{ - const LChar* hexDigits = Internal::hexDigitsForMode(mode); - *destination++ = hexDigits[byte >> 4]; - *destination++ = hexDigits[byte & 0xF]; +template +inline void placeByteAsHex(unsigned char byte, + T& destination, + HexConversionMode mode = Uppercase) { + const LChar* hexDigits = Internal::hexDigitsForMode(mode); + *destination++ = hexDigits[byte >> 4]; + *destination++ = hexDigits[byte & 0xF]; } -template -inline void appendUnsignedAsHex(unsigned number, T& destination, HexConversionMode mode = Uppercase) -{ - const LChar* hexDigits = Internal::hexDigitsForMode(mode); - Vector result; - do { - result.prepend(hexDigits[number % 16]); - number >>= 4; - } while (number > 0); - - destination.append(result.data(), result.size()); +template +inline void appendUnsignedAsHex(unsigned number, + T& destination, + HexConversionMode mode = Uppercase) { + const LChar* hexDigits = Internal::hexDigitsForMode(mode); + Vector result; + do { + result.prepend(hexDigits[number % 16]); + number >>= 4; + } while (number > 0); + + destination.append(result.data(), result.size()); } -// Same as appendUnsignedAsHex, but using exactly 'desiredDigits' for the conversion. -template -inline void appendUnsignedAsHexFixedSize(unsigned number, T& destination, unsigned desiredDigits, HexConversionMode mode = Uppercase) -{ - ASSERT(desiredDigits); - - const LChar* hexDigits = Internal::hexDigitsForMode(mode); - Vector result; - do { - result.prepend(hexDigits[number % 16]); - number >>= 4; - } while (result.size() < desiredDigits); - - ASSERT(result.size() == desiredDigits); - destination.append(result.data(), result.size()); +// Same as appendUnsignedAsHex, but using exactly 'desiredDigits' for the +// conversion. +template +inline void appendUnsignedAsHexFixedSize(unsigned number, + T& destination, + unsigned desiredDigits, + HexConversionMode mode = Uppercase) { + ASSERT(desiredDigits); + + const LChar* hexDigits = Internal::hexDigitsForMode(mode); + Vector result; + do { + result.prepend(hexDigits[number % 16]); + number >>= 4; + } while (result.size() < desiredDigits); + + ASSERT(result.size() == desiredDigits); + destination.append(result.data(), result.size()); } -} // namespace WTF +} // namespace WTF +using WTF::Lowercase; using WTF::appendByteAsHex; using WTF::appendUnsignedAsHex; using WTF::appendUnsignedAsHexFixedSize; using WTF::placeByteAsHex; using WTF::placeByteAsHexCompressIfPossible; -using WTF::Lowercase; #endif // SKY_ENGINE_WTF_HEXNUMBER_H_ diff --git a/sky/engine/wtf/InstanceCounter.cpp b/sky/engine/wtf/InstanceCounter.cpp index 29f3a92de9752..50ad7b3ae9faa 100644 --- a/sky/engine/wtf/InstanceCounter.cpp +++ b/sky/engine/wtf/InstanceCounter.cpp @@ -37,114 +37,118 @@ namespace WTF { #if ENABLE(INSTANCE_COUNTER) || ENABLE(GC_PROFILING) #if COMPILER(CLANG) -const size_t extractNameFunctionPrefixLength = sizeof("const char *WTF::extractNameFunction() [T = ") - 1; +const size_t extractNameFunctionPrefixLength = + sizeof("const char *WTF::extractNameFunction() [T = ") - 1; const size_t extractNameFunctionPostfixLength = sizeof("]") - 1; #elif COMPILER(GCC) -const size_t extractNameFunctionPrefixLength = sizeof("const char* WTF::extractNameFunction() [with T = ") - 1; +const size_t extractNameFunctionPrefixLength = + sizeof("const char* WTF::extractNameFunction() [with T = ") - 1; const size_t extractNameFunctionPostfixLength = sizeof("]") - 1; #else -#warning "Extracting typename is supported only in compiler GCC, CLANG and MSVC at this moment" +#warning \ + "Extracting typename is supported only in compiler GCC, CLANG and MSVC at this moment" #endif // This function is used to stringify a typename T without using RTTI. -// The result of extractNameFunction() is given as |funcName|. |extractTypeNameFromFunctionName| then extracts a typename string from |funcName|. -String extractTypeNameFromFunctionName(const char* funcName) -{ +// The result of extractNameFunction() is given as |funcName|. +// |extractTypeNameFromFunctionName| then extracts a typename string from +// |funcName|. +String extractTypeNameFromFunctionName(const char* funcName) { #if COMPILER(CLANG) || COMPILER(GCC) - size_t funcNameLength = strlen(funcName); - ASSERT(funcNameLength > extractNameFunctionPrefixLength + extractNameFunctionPostfixLength); - - const char* funcNameWithoutPrefix = funcName + extractNameFunctionPrefixLength; - return String(funcNameWithoutPrefix, funcNameLength - extractNameFunctionPrefixLength - extractNameFunctionPostfixLength); + size_t funcNameLength = strlen(funcName); + ASSERT(funcNameLength > + extractNameFunctionPrefixLength + extractNameFunctionPostfixLength); + + const char* funcNameWithoutPrefix = + funcName + extractNameFunctionPrefixLength; + return String(funcNameWithoutPrefix, funcNameLength - + extractNameFunctionPrefixLength - + extractNameFunctionPostfixLength); #else - return String("unknown"); + return String("unknown"); #endif } class InstanceCounter { -public: - void incrementInstanceCount(const String& instanceName, void* ptr); - void decrementInstanceCount(const String& instanceName, void* ptr); - String dump(); - - static InstanceCounter* instance() - { - DEFINE_STATIC_LOCAL(InstanceCounter, self, ()); - return &self; - } - -private: - InstanceCounter() { } - - Mutex m_mutex; - HashMap m_counterMap; + public: + void incrementInstanceCount(const String& instanceName, void* ptr); + void decrementInstanceCount(const String& instanceName, void* ptr); + String dump(); + + static InstanceCounter* instance() { + DEFINE_STATIC_LOCAL(InstanceCounter, self, ()); + return &self; + } + + private: + InstanceCounter() {} + + Mutex m_mutex; + HashMap m_counterMap; }; -void incrementInstanceCount(const char* extractNameFunctionName, void* ptr) -{ - String instanceName = extractTypeNameFromFunctionName(extractNameFunctionName); - InstanceCounter::instance()->incrementInstanceCount(instanceName, ptr); +void incrementInstanceCount(const char* extractNameFunctionName, void* ptr) { + String instanceName = + extractTypeNameFromFunctionName(extractNameFunctionName); + InstanceCounter::instance()->incrementInstanceCount(instanceName, ptr); } -void decrementInstanceCount(const char* extractNameFunctionName, void* ptr) -{ - String instanceName = extractTypeNameFromFunctionName(extractNameFunctionName); - InstanceCounter::instance()->decrementInstanceCount(instanceName, ptr); +void decrementInstanceCount(const char* extractNameFunctionName, void* ptr) { + String instanceName = + extractTypeNameFromFunctionName(extractNameFunctionName); + InstanceCounter::instance()->decrementInstanceCount(instanceName, ptr); } -String dumpRefCountedInstanceCounts() -{ - return InstanceCounter::instance()->dump(); +String dumpRefCountedInstanceCounts() { + return InstanceCounter::instance()->dump(); } -void InstanceCounter::incrementInstanceCount(const String& instanceName, void* ptr) -{ - MutexLocker locker(m_mutex); - HashMap::AddResult result = m_counterMap.add(instanceName, 1); - if (!result.isNewEntry) - ++(result.storedValue->value); +void InstanceCounter::incrementInstanceCount(const String& instanceName, + void* ptr) { + MutexLocker locker(m_mutex); + HashMap::AddResult result = m_counterMap.add(instanceName, 1); + if (!result.isNewEntry) + ++(result.storedValue->value); } -void InstanceCounter::decrementInstanceCount(const String& instanceName, void* ptr) -{ - MutexLocker locker(m_mutex); - HashMap::iterator it = m_counterMap.find(instanceName); - ASSERT(it != m_counterMap.end()); +void InstanceCounter::decrementInstanceCount(const String& instanceName, + void* ptr) { + MutexLocker locker(m_mutex); + HashMap::iterator it = m_counterMap.find(instanceName); + ASSERT(it != m_counterMap.end()); - --(it->value); - if (!it->value) - m_counterMap.remove(it); + --(it->value); + if (!it->value) + m_counterMap.remove(it); } -String InstanceCounter::dump() -{ - MutexLocker locker(m_mutex); - - StringBuilder builder; - - builder.append("{"); - HashMap::iterator it = m_counterMap.begin(); - HashMap::iterator itEnd = m_counterMap.end(); - for (; it != itEnd; ++it) { - if (it != m_counterMap.begin()) - builder.append(","); - builder.append("\""); - builder.append(it->key); - builder.append("\": "); - builder.append(String::number(it->value)); - } - builder.append("}"); - - return builder.toString(); +String InstanceCounter::dump() { + MutexLocker locker(m_mutex); + + StringBuilder builder; + + builder.append("{"); + HashMap::iterator it = m_counterMap.begin(); + HashMap::iterator itEnd = m_counterMap.end(); + for (; it != itEnd; ++it) { + if (it != m_counterMap.begin()) + builder.append(","); + builder.append("\""); + builder.append(it->key); + builder.append("\": "); + builder.append(String::number(it->value)); + } + builder.append("}"); + + return builder.toString(); } #else -String dumpRefCountedInstanceCounts() -{ - return String("{}"); +String dumpRefCountedInstanceCounts() { + return String("{}"); } -#endif // ENABLE(INSTANCE_COUNTER) || ENABLE(GC_PROFILING) +#endif // ENABLE(INSTANCE_COUNTER) || ENABLE(GC_PROFILING) -} // namespace WTF +} // namespace WTF diff --git a/sky/engine/wtf/InstanceCounter.h b/sky/engine/wtf/InstanceCounter.h index 1d4fe4ca97169..cc1fd530244a1 100644 --- a/sky/engine/wtf/InstanceCounter.h +++ b/sky/engine/wtf/InstanceCounter.h @@ -36,31 +36,30 @@ class String; WTF_EXPORT String dumpRefCountedInstanceCounts(); #if ENABLE(INSTANCE_COUNTER) || ENABLE(GC_PROFILING) -WTF_EXPORT void incrementInstanceCount(const char* extractNameFunctionName, void* ptr); -WTF_EXPORT void decrementInstanceCount(const char* extractNameFunctionName, void* ptr); +WTF_EXPORT void incrementInstanceCount(const char* extractNameFunctionName, + void* ptr); +WTF_EXPORT void decrementInstanceCount(const char* extractNameFunctionName, + void* ptr); WTF_EXPORT String extractTypeNameFromFunctionName(const char* funcName); -template -inline const char* extractNameFunction() -{ - return WTF_PRETTY_FUNCTION; +template +inline const char* extractNameFunction() { + return WTF_PRETTY_FUNCTION; } -template -inline void incrementInstanceCount(T* p) -{ - incrementInstanceCount(extractNameFunction(), p); +template +inline void incrementInstanceCount(T* p) { + incrementInstanceCount(extractNameFunction(), p); } -template -inline void decrementInstanceCount(T* p) -{ - decrementInstanceCount(extractNameFunction(), p); +template +inline void decrementInstanceCount(T* p) { + decrementInstanceCount(extractNameFunction(), p); } -#endif // ENABLE(INSTANCE_COUNTER) || ENABLE(GC_PROFILING) +#endif // ENABLE(INSTANCE_COUNTER) || ENABLE(GC_PROFILING) -} // namespace WTF +} // namespace WTF #endif // SKY_ENGINE_WTF_INSTANCECOUNTER_H_ diff --git a/sky/engine/wtf/LeakAnnotations.h b/sky/engine/wtf/LeakAnnotations.h index 52ba0b626c25f..269f2e1bbb224 100644 --- a/sky/engine/wtf/LeakAnnotations.h +++ b/sky/engine/wtf/LeakAnnotations.h @@ -55,37 +55,32 @@ namespace WTF { extern "C" { void __lsan_disable(); void __lsan_enable(); -void __lsan_ignore_object(const void *p); -} // extern "C" +void __lsan_ignore_object(const void* p); +} // extern "C" class LeakSanitizerDisabler { - WTF_MAKE_NONCOPYABLE(LeakSanitizerDisabler); -public: - LeakSanitizerDisabler() - { - __lsan_disable(); - } + WTF_MAKE_NONCOPYABLE(LeakSanitizerDisabler); - ~LeakSanitizerDisabler() - { - __lsan_enable(); - } + public: + LeakSanitizerDisabler() { __lsan_disable(); } + + ~LeakSanitizerDisabler() { __lsan_enable(); } }; -#define WTF_ANNOTATE_SCOPED_MEMORY_LEAK \ - WTF::LeakSanitizerDisabler leakSanitizerDisabler; static_cast(0) +#define WTF_ANNOTATE_SCOPED_MEMORY_LEAK \ + WTF::LeakSanitizerDisabler leakSanitizerDisabler; \ + static_cast(0) -#define WTF_ANNOTATE_LEAKING_OBJECT_PTR(X) \ - WTF::__lsan_ignore_object(X) +#define WTF_ANNOTATE_LEAKING_OBJECT_PTR(X) WTF::__lsan_ignore_object(X) -#else // USE(LEAK_SANITIZER) +#else // USE(LEAK_SANITIZER) // If Leak Sanitizer is not being used, the annotations should be no-ops. #define WTF_ANNOTATE_SCOPED_MEMORY_LEAK #define WTF_ANNOTATE_LEAKING_OBJECT_PTR(X) -#endif // USE(LEAK_SANITIZER) +#endif // USE(LEAK_SANITIZER) -} // namespace WTF +} // namespace WTF #endif // SKY_ENGINE_WTF_LEAKANNOTATIONS_H_ diff --git a/sky/engine/wtf/LinkedHashSet.h b/sky/engine/wtf/LinkedHashSet.h index dae2fc07885e0..a6c0d792ad886 100644 --- a/sky/engine/wtf/LinkedHashSet.h +++ b/sky/engine/wtf/LinkedHashSet.h @@ -1,5 +1,6 @@ /* - * Copyright (C) 2005, 2006, 2007, 2008, 2011, 2012 Apple Inc. All rights reserved. + * Copyright (C) 2005, 2006, 2007, 2008, 2011, 2012 Apple Inc. + * All rights reserved. * Copyright (C) 2011, Benjamin Poulain * * This library is free software; you can redistribute it and/or @@ -38,694 +39,781 @@ namespace WTF { // Unlike ListHashSet, but like most WTF collections, iteration is NOT safe // against mutation of the LinkedHashSet. -template class LinkedHashSet; - -template class LinkedHashSetIterator; -template class LinkedHashSetConstIterator; -template class LinkedHashSetReverseIterator; -template class LinkedHashSetConstReverseIterator; - -template struct LinkedHashSetTranslator; -template struct LinkedHashSetExtractor; -template struct LinkedHashSetTraits; +template +class LinkedHashSet; + +template +class LinkedHashSetIterator; +template +class LinkedHashSetConstIterator; +template +class LinkedHashSetReverseIterator; +template +class LinkedHashSetConstReverseIterator; + +template +struct LinkedHashSetTranslator; +template +struct LinkedHashSetExtractor; +template +struct LinkedHashSetTraits; class LinkedHashSetNodeBase { -public: - LinkedHashSetNodeBase() : m_prev(this), m_next(this) { } - - void unlink() - { - if (!m_next) - return; - ASSERT(m_prev); - ASSERT(m_next->m_prev == this); - ASSERT(m_prev->m_next == this); - m_next->m_prev = m_prev; - m_prev->m_next = m_next; - } - - ~LinkedHashSetNodeBase() - { - unlink(); - } - - void insertBefore(LinkedHashSetNodeBase& other) - { - other.m_next = this; - other.m_prev = m_prev; - m_prev->m_next = &other; - m_prev = &other; - ASSERT(other.m_next); - ASSERT(other.m_prev); - ASSERT(m_next); - ASSERT(m_prev); - } - - void insertAfter(LinkedHashSetNodeBase& other) - { - other.m_prev = this; - other.m_next = m_next; - m_next->m_prev = &other; - m_next = &other; - ASSERT(other.m_next); - ASSERT(other.m_prev); - ASSERT(m_next); - ASSERT(m_prev); - } - - LinkedHashSetNodeBase(LinkedHashSetNodeBase* prev, LinkedHashSetNodeBase* next) - : m_prev(prev) - , m_next(next) - { - ASSERT((prev && next) || (!prev && !next)); - } - - LinkedHashSetNodeBase* m_prev; - LinkedHashSetNodeBase* m_next; - -protected: - // If we take a copy of a node we can't copy the next and prev pointers, - // since they point to something that does not point at us. This is used - // inside the shouldExpand() "if" in HashTable::add. - LinkedHashSetNodeBase(const LinkedHashSetNodeBase& other) - : m_prev(0) - , m_next(0) { } - -private: - // Should not be used. - LinkedHashSetNodeBase& operator=(const LinkedHashSetNodeBase& other); + public: + LinkedHashSetNodeBase() : m_prev(this), m_next(this) {} + + void unlink() { + if (!m_next) + return; + ASSERT(m_prev); + ASSERT(m_next->m_prev == this); + ASSERT(m_prev->m_next == this); + m_next->m_prev = m_prev; + m_prev->m_next = m_next; + } + + ~LinkedHashSetNodeBase() { unlink(); } + + void insertBefore(LinkedHashSetNodeBase& other) { + other.m_next = this; + other.m_prev = m_prev; + m_prev->m_next = &other; + m_prev = &other; + ASSERT(other.m_next); + ASSERT(other.m_prev); + ASSERT(m_next); + ASSERT(m_prev); + } + + void insertAfter(LinkedHashSetNodeBase& other) { + other.m_prev = this; + other.m_next = m_next; + m_next->m_prev = &other; + m_next = &other; + ASSERT(other.m_next); + ASSERT(other.m_prev); + ASSERT(m_next); + ASSERT(m_prev); + } + + LinkedHashSetNodeBase(LinkedHashSetNodeBase* prev, + LinkedHashSetNodeBase* next) + : m_prev(prev), m_next(next) { + ASSERT((prev && next) || (!prev && !next)); + } + + LinkedHashSetNodeBase* m_prev; + LinkedHashSetNodeBase* m_next; + + protected: + // If we take a copy of a node we can't copy the next and prev pointers, + // since they point to something that does not point at us. This is used + // inside the shouldExpand() "if" in HashTable::add. + LinkedHashSetNodeBase(const LinkedHashSetNodeBase& other) + : m_prev(0), m_next(0) {} + + private: + // Should not be used. + LinkedHashSetNodeBase& operator=(const LinkedHashSetNodeBase& other); }; -template +template class LinkedHashSetNode : public LinkedHashSetNodeBase { -public: - LinkedHashSetNode(const ValueArg& value, LinkedHashSetNodeBase* prev, LinkedHashSetNodeBase* next) - : LinkedHashSetNodeBase(prev, next) - , m_value(value) - { - } - - ValueArg m_value; - -private: - // Not used. - LinkedHashSetNode(const LinkedHashSetNode&); + public: + LinkedHashSetNode(const ValueArg& value, + LinkedHashSetNodeBase* prev, + LinkedHashSetNodeBase* next) + : LinkedHashSetNodeBase(prev, next), m_value(value) {} + + ValueArg m_value; + + private: + // Not used. + LinkedHashSetNode(const LinkedHashSetNode&); }; -template< - typename ValueArg, - typename HashFunctions = typename DefaultHash::Hash, - typename TraitsArg = HashTraits, - typename Allocator = DefaultAllocator> +template ::Hash, + typename TraitsArg = HashTraits, + typename Allocator = DefaultAllocator> class LinkedHashSet { - WTF_USE_ALLOCATOR(LinkedHashSet, Allocator); -private: - typedef ValueArg Value; - typedef TraitsArg Traits; - typedef LinkedHashSetNode Node; - typedef LinkedHashSetNodeBase NodeBase; - typedef LinkedHashSetTranslator NodeHashFunctions; - typedef LinkedHashSetTraits NodeHashTraits; - - typedef HashTable ImplType; - -public: - typedef LinkedHashSetIterator iterator; - friend class LinkedHashSetIterator; - typedef LinkedHashSetConstIterator const_iterator; - friend class LinkedHashSetConstIterator; - - typedef LinkedHashSetReverseIterator reverse_iterator; - friend class LinkedHashSetReverseIterator; - typedef LinkedHashSetConstReverseIterator const_reverse_iterator; - friend class LinkedHashSetConstReverseIterator; - - struct AddResult { - AddResult(const typename ImplType::AddResult& hashTableAddResult) - : storedValue(&hashTableAddResult.storedValue->m_value) - , isNewEntry(hashTableAddResult.isNewEntry) - { - } - - Value* storedValue; - bool isNewEntry; - }; - - typedef typename HashTraits::PeekInType ValuePeekInType; - - LinkedHashSet(); - LinkedHashSet(const LinkedHashSet&); - LinkedHashSet& operator=(const LinkedHashSet&); - - // Needs finalization. The anchor needs to unlink itself from the chain. - ~LinkedHashSet(); - - static void finalize(void* pointer) { reinterpret_cast(pointer)->~LinkedHashSet(); } - - void swap(LinkedHashSet&); - - unsigned size() const { return m_impl.size(); } - unsigned capacity() const { return m_impl.capacity(); } - bool isEmpty() const { return m_impl.isEmpty(); } - - iterator begin() { return makeIterator(firstNode()); } - iterator end() { return makeIterator(anchor()); } - const_iterator begin() const { return makeConstIterator(firstNode()); } - const_iterator end() const { return makeConstIterator(anchor()); } - - reverse_iterator rbegin() { return makeReverseIterator(lastNode()); } - reverse_iterator rend() { return makeReverseIterator(anchor()); } - const_reverse_iterator rbegin() const { return makeConstReverseIterator(lastNode()); } - const_reverse_iterator rend() const { return makeConstReverseIterator(anchor()); } - - Value& first(); - const Value& first() const; - void removeFirst(); - - Value& last(); - const Value& last() const; - void removeLast(); - - iterator find(ValuePeekInType); - const_iterator find(ValuePeekInType) const; - bool contains(ValuePeekInType) const; - - // An alternate version of find() that finds the object by hashing and comparing - // with some other type, to avoid the cost of type conversion. - // The HashTranslator interface is defined in HashSet. - template iterator find(const T&); - template const_iterator find(const T&) const; - template bool contains(const T&) const; - - // The return value of add is a pair of a pointer to the stored value, - // and a bool that is true if an new entry was added. - AddResult add(ValuePeekInType); - - // Same as add() except that the return value is an - // iterator. Useful in cases where it's needed to have the - // same return value as find() and where it's not possible to - // use a pointer to the storedValue. - iterator addReturnIterator(ValuePeekInType); - - // Add the value to the end of the collection. If the value was already in - // the list, it is moved to the end. - AddResult appendOrMoveToLast(ValuePeekInType); - - // Add the value to the beginning of the collection. If the value was already in - // the list, it is moved to the beginning. - AddResult prependOrMoveToFirst(ValuePeekInType); - - AddResult insertBefore(ValuePeekInType beforeValue, ValuePeekInType newValue); - AddResult insertBefore(iterator it, ValuePeekInType newValue) { return m_impl.template add(newValue, it.node()); } - - void remove(ValuePeekInType); - void remove(iterator); - void clear() { m_impl.clear(); } - template - void removeAll(const Collection& other) { WTF::removeAll(*this, other); } - - int64_t modifications() const { return m_impl.modifications(); } - void checkModifications(int64_t mods) const { m_impl.checkModifications(mods); } - -private: - Node* anchor() { return reinterpret_cast(&m_anchor); } - const Node* anchor() const { return reinterpret_cast(&m_anchor); } - Node* firstNode() { return reinterpret_cast(m_anchor.m_next); } - const Node* firstNode() const { return reinterpret_cast(m_anchor.m_next); } - Node* lastNode() { return reinterpret_cast(m_anchor.m_prev); } - const Node* lastNode() const { return reinterpret_cast(m_anchor.m_prev); } - - iterator makeIterator(const Node* position) { return iterator(position, this); } - const_iterator makeConstIterator(const Node* position) const { return const_iterator(position, this); } - reverse_iterator makeReverseIterator(const Node* position) { return reverse_iterator(position, this); } - const_reverse_iterator makeConstReverseIterator(const Node* position) const { return const_reverse_iterator(position, this); } - - ImplType m_impl; - NodeBase m_anchor; + WTF_USE_ALLOCATOR(LinkedHashSet, Allocator); + + private: + typedef ValueArg Value; + typedef TraitsArg Traits; + typedef LinkedHashSetNode Node; + typedef LinkedHashSetNodeBase NodeBase; + typedef LinkedHashSetTranslator + NodeHashFunctions; + typedef LinkedHashSetTraits NodeHashTraits; + + typedef HashTable + ImplType; + + public: + typedef LinkedHashSetIterator iterator; + friend class LinkedHashSetIterator; + typedef LinkedHashSetConstIterator const_iterator; + friend class LinkedHashSetConstIterator; + + typedef LinkedHashSetReverseIterator reverse_iterator; + friend class LinkedHashSetReverseIterator; + typedef LinkedHashSetConstReverseIterator + const_reverse_iterator; + friend class LinkedHashSetConstReverseIterator; + + struct AddResult { + AddResult(const typename ImplType::AddResult& hashTableAddResult) + : storedValue(&hashTableAddResult.storedValue->m_value), + isNewEntry(hashTableAddResult.isNewEntry) {} + + Value* storedValue; + bool isNewEntry; + }; + + typedef typename HashTraits::PeekInType ValuePeekInType; + + LinkedHashSet(); + LinkedHashSet(const LinkedHashSet&); + LinkedHashSet& operator=(const LinkedHashSet&); + + // Needs finalization. The anchor needs to unlink itself from the chain. + ~LinkedHashSet(); + + static void finalize(void* pointer) { + reinterpret_cast(pointer)->~LinkedHashSet(); + } + + void swap(LinkedHashSet&); + + unsigned size() const { return m_impl.size(); } + unsigned capacity() const { return m_impl.capacity(); } + bool isEmpty() const { return m_impl.isEmpty(); } + + iterator begin() { return makeIterator(firstNode()); } + iterator end() { return makeIterator(anchor()); } + const_iterator begin() const { return makeConstIterator(firstNode()); } + const_iterator end() const { return makeConstIterator(anchor()); } + + reverse_iterator rbegin() { return makeReverseIterator(lastNode()); } + reverse_iterator rend() { return makeReverseIterator(anchor()); } + const_reverse_iterator rbegin() const { + return makeConstReverseIterator(lastNode()); + } + const_reverse_iterator rend() const { + return makeConstReverseIterator(anchor()); + } + + Value& first(); + const Value& first() const; + void removeFirst(); + + Value& last(); + const Value& last() const; + void removeLast(); + + iterator find(ValuePeekInType); + const_iterator find(ValuePeekInType) const; + bool contains(ValuePeekInType) const; + + // An alternate version of find() that finds the object by hashing and + // comparing with some other type, to avoid the cost of type conversion. The + // HashTranslator interface is defined in HashSet. + template + iterator find(const T&); + template + const_iterator find(const T&) const; + template + bool contains(const T&) const; + + // The return value of add is a pair of a pointer to the stored value, + // and a bool that is true if an new entry was added. + AddResult add(ValuePeekInType); + + // Same as add() except that the return value is an + // iterator. Useful in cases where it's needed to have the + // same return value as find() and where it's not possible to + // use a pointer to the storedValue. + iterator addReturnIterator(ValuePeekInType); + + // Add the value to the end of the collection. If the value was already in + // the list, it is moved to the end. + AddResult appendOrMoveToLast(ValuePeekInType); + + // Add the value to the beginning of the collection. If the value was already + // in the list, it is moved to the beginning. + AddResult prependOrMoveToFirst(ValuePeekInType); + + AddResult insertBefore(ValuePeekInType beforeValue, ValuePeekInType newValue); + AddResult insertBefore(iterator it, ValuePeekInType newValue) { + return m_impl.template add(newValue, it.node()); + } + + void remove(ValuePeekInType); + void remove(iterator); + void clear() { m_impl.clear(); } + template + void removeAll(const Collection& other) { + WTF::removeAll(*this, other); + } + + int64_t modifications() const { return m_impl.modifications(); } + void checkModifications(int64_t mods) const { + m_impl.checkModifications(mods); + } + + private: + Node* anchor() { return reinterpret_cast(&m_anchor); } + const Node* anchor() const { + return reinterpret_cast(&m_anchor); + } + Node* firstNode() { return reinterpret_cast(m_anchor.m_next); } + const Node* firstNode() const { + return reinterpret_cast(m_anchor.m_next); + } + Node* lastNode() { return reinterpret_cast(m_anchor.m_prev); } + const Node* lastNode() const { + return reinterpret_cast(m_anchor.m_prev); + } + + iterator makeIterator(const Node* position) { + return iterator(position, this); + } + const_iterator makeConstIterator(const Node* position) const { + return const_iterator(position, this); + } + reverse_iterator makeReverseIterator(const Node* position) { + return reverse_iterator(position, this); + } + const_reverse_iterator makeConstReverseIterator(const Node* position) const { + return const_reverse_iterator(position, this); + } + + ImplType m_impl; + NodeBase m_anchor; }; -template +template struct LinkedHashSetTranslator { - typedef LinkedHashSetNode Node; - typedef LinkedHashSetNodeBase NodeBase; - typedef typename HashTraits::PeekInType ValuePeekInType; - static unsigned hash(const Node& node) { return HashFunctions::hash(node.m_value); } - static unsigned hash(const ValuePeekInType& key) { return HashFunctions::hash(key); } - static bool equal(const Node& a, const ValuePeekInType& b) { return HashFunctions::equal(a.m_value, b); } - static bool equal(const Node& a, const Node& b) { return HashFunctions::equal(a.m_value, b.m_value); } - static void translate(Node& location, ValuePeekInType key, NodeBase* anchor) - { - anchor->insertBefore(location); - location.m_value = key; - } - - // Empty (or deleted) slots have the m_next pointer set to null, but we - // don't do anything to the other fields, which may contain junk. - // Therefore you can't compare a newly constructed empty value with a - // slot and get the right answer. - static const bool safeToCompareToEmptyOrDeleted = false; + typedef LinkedHashSetNode Node; + typedef LinkedHashSetNodeBase NodeBase; + typedef typename HashTraits::PeekInType ValuePeekInType; + static unsigned hash(const Node& node) { + return HashFunctions::hash(node.m_value); + } + static unsigned hash(const ValuePeekInType& key) { + return HashFunctions::hash(key); + } + static bool equal(const Node& a, const ValuePeekInType& b) { + return HashFunctions::equal(a.m_value, b); + } + static bool equal(const Node& a, const Node& b) { + return HashFunctions::equal(a.m_value, b.m_value); + } + static void translate(Node& location, ValuePeekInType key, NodeBase* anchor) { + anchor->insertBefore(location); + location.m_value = key; + } + + // Empty (or deleted) slots have the m_next pointer set to null, but we + // don't do anything to the other fields, which may contain junk. + // Therefore you can't compare a newly constructed empty value with a + // slot and get the right answer. + static const bool safeToCompareToEmptyOrDeleted = false; }; -template +template struct LinkedHashSetExtractor { - static const Value& extract(const LinkedHashSetNode& node) { return node.m_value; } + static const Value& extract(const LinkedHashSetNode& node) { + return node.m_value; + } }; -template -struct LinkedHashSetTraits : public SimpleClassHashTraits > { - typedef LinkedHashSetNode Node; - typedef ValueTraitsArg ValueTraits; - - // The slot is empty when the m_next field is zero so it's safe to zero - // the backing. - static const bool emptyValueIsZero = true; - - static const bool hasIsEmptyValueFunction = true; - static bool isEmptyValue(const Node& node) { return !node.m_next; } - - static const int deletedValue = -1; - - static void constructDeletedValue(Node& slot, bool) { slot.m_next = reinterpret_cast(deletedValue); } - static bool isDeletedValue(const Node& slot) { return slot.m_next == reinterpret_cast(deletedValue); } - - // We always need to call destructors, that's how we get linked and - // unlinked from the chain. - static const bool needsDestruction = true; - - // Whether we need to trace and do weak processing depends on the traits of - // the type inside the node. - template - struct NeedsTracingLazily { - static const bool value = ValueTraits::template NeedsTracingLazily<>::value; - }; - static const WeakHandlingFlag weakHandlingFlag = ValueTraits::weakHandlingFlag; +template +struct LinkedHashSetTraits + : public SimpleClassHashTraits> { + typedef LinkedHashSetNode Node; + typedef ValueTraitsArg ValueTraits; + + // The slot is empty when the m_next field is zero so it's safe to zero + // the backing. + static const bool emptyValueIsZero = true; + + static const bool hasIsEmptyValueFunction = true; + static bool isEmptyValue(const Node& node) { return !node.m_next; } + + static const int deletedValue = -1; + + static void constructDeletedValue(Node& slot, bool) { + slot.m_next = reinterpret_cast(deletedValue); + } + static bool isDeletedValue(const Node& slot) { + return slot.m_next == reinterpret_cast(deletedValue); + } + + // We always need to call destructors, that's how we get linked and + // unlinked from the chain. + static const bool needsDestruction = true; + + // Whether we need to trace and do weak processing depends on the traits of + // the type inside the node. + template + struct NeedsTracingLazily { + static const bool value = ValueTraits::template NeedsTracingLazily<>::value; + }; + static const WeakHandlingFlag weakHandlingFlag = + ValueTraits::weakHandlingFlag; }; -template +template class LinkedHashSetIterator { -private: - typedef typename LinkedHashSetType::Node Node; - typedef typename LinkedHashSetType::Traits Traits; + private: + typedef typename LinkedHashSetType::Node Node; + typedef typename LinkedHashSetType::Traits Traits; - typedef typename LinkedHashSetType::Value& ReferenceType; - typedef typename LinkedHashSetType::Value* PointerType; + typedef typename LinkedHashSetType::Value& ReferenceType; + typedef typename LinkedHashSetType::Value* PointerType; - typedef LinkedHashSetConstIterator const_iterator; + typedef LinkedHashSetConstIterator const_iterator; - Node* node() { return const_cast(m_iterator.node()); } + Node* node() { return const_cast(m_iterator.node()); } -protected: - LinkedHashSetIterator(const Node* position, LinkedHashSetType* m_container) - : m_iterator(position , m_container) - { - } + protected: + LinkedHashSetIterator(const Node* position, LinkedHashSetType* m_container) + : m_iterator(position, m_container) {} -public: - // Default copy, assignment and destructor are OK. + public: + // Default copy, assignment and destructor are OK. - PointerType get() const { return const_cast(m_iterator.get()); } - ReferenceType operator*() const { return *get(); } - PointerType operator->() const { return get(); } + PointerType get() const { return const_cast(m_iterator.get()); } + ReferenceType operator*() const { return *get(); } + PointerType operator->() const { return get(); } - LinkedHashSetIterator& operator++() { ++m_iterator; return *this; } - LinkedHashSetIterator& operator--() { --m_iterator; return *this; } + LinkedHashSetIterator& operator++() { + ++m_iterator; + return *this; + } + LinkedHashSetIterator& operator--() { + --m_iterator; + return *this; + } - // Postfix ++ and -- intentionally omitted. + // Postfix ++ and -- intentionally omitted. - // Comparison. - bool operator==(const LinkedHashSetIterator& other) const { return m_iterator == other.m_iterator; } - bool operator!=(const LinkedHashSetIterator& other) const { return m_iterator != other.m_iterator; } + // Comparison. + bool operator==(const LinkedHashSetIterator& other) const { + return m_iterator == other.m_iterator; + } + bool operator!=(const LinkedHashSetIterator& other) const { + return m_iterator != other.m_iterator; + } - operator const_iterator() const { return m_iterator; } + operator const_iterator() const { return m_iterator; } -protected: - const_iterator m_iterator; - template friend class LinkedHashSet; + protected: + const_iterator m_iterator; + template + friend class LinkedHashSet; }; -template +template class LinkedHashSetConstIterator { -private: - typedef typename LinkedHashSetType::Node Node; - typedef typename LinkedHashSetType::Traits Traits; + private: + typedef typename LinkedHashSetType::Node Node; + typedef typename LinkedHashSetType::Traits Traits; - typedef const typename LinkedHashSetType::Value& ReferenceType; - typedef const typename LinkedHashSetType::Value* PointerType; + typedef const typename LinkedHashSetType::Value& ReferenceType; + typedef const typename LinkedHashSetType::Value* PointerType; - const Node* node() const { return static_cast(m_position); } + const Node* node() const { return static_cast(m_position); } -protected: - LinkedHashSetConstIterator(const LinkedHashSetNodeBase* position, const LinkedHashSetType* container) - : m_position(position) + protected: + LinkedHashSetConstIterator(const LinkedHashSetNodeBase* position, + const LinkedHashSetType* container) + : m_position(position) #if ENABLE(ASSERT) - , m_container(container) - , m_containerModifications(container->modifications()) + , + m_container(container), + m_containerModifications(container->modifications()) #endif - { - } - -public: - PointerType get() const - { - checkModifications(); - return &static_cast(m_position)->m_value; - } - ReferenceType operator*() const { return *get(); } - PointerType operator->() const { return get(); } - - LinkedHashSetConstIterator& operator++() - { - ASSERT(m_position); - checkModifications(); - m_position = m_position->m_next; - return *this; - } - - LinkedHashSetConstIterator& operator--() - { - ASSERT(m_position); - checkModifications(); - m_position = m_position->m_prev; - return *this; - } - - // Postfix ++ and -- intentionally omitted. - - // Comparison. - bool operator==(const LinkedHashSetConstIterator& other) const - { - return m_position == other.m_position; - } - bool operator!=(const LinkedHashSetConstIterator& other) const - { - return m_position != other.m_position; - } - -private: - const LinkedHashSetNodeBase* m_position; + { + } + + public: + PointerType get() const { + checkModifications(); + return &static_cast(m_position)->m_value; + } + ReferenceType operator*() const { return *get(); } + PointerType operator->() const { return get(); } + + LinkedHashSetConstIterator& operator++() { + ASSERT(m_position); + checkModifications(); + m_position = m_position->m_next; + return *this; + } + + LinkedHashSetConstIterator& operator--() { + ASSERT(m_position); + checkModifications(); + m_position = m_position->m_prev; + return *this; + } + + // Postfix ++ and -- intentionally omitted. + + // Comparison. + bool operator==(const LinkedHashSetConstIterator& other) const { + return m_position == other.m_position; + } + bool operator!=(const LinkedHashSetConstIterator& other) const { + return m_position != other.m_position; + } + + private: + const LinkedHashSetNodeBase* m_position; #if ENABLE(ASSERT) - void checkModifications() const { m_container->checkModifications(m_containerModifications); } - const LinkedHashSetType* m_container; - int64_t m_containerModifications; + void checkModifications() const { + m_container->checkModifications(m_containerModifications); + } + const LinkedHashSetType* m_container; + int64_t m_containerModifications; #else - void checkModifications() const { } + void checkModifications() const {} #endif - template friend class LinkedHashSet; - friend class LinkedHashSetIterator; + template + friend class LinkedHashSet; + friend class LinkedHashSetIterator; }; -template -class LinkedHashSetReverseIterator : public LinkedHashSetIterator { - typedef LinkedHashSetIterator Superclass; - typedef LinkedHashSetConstReverseIterator const_reverse_iterator; - typedef typename LinkedHashSetType::Node Node; - -protected: - LinkedHashSetReverseIterator(const Node* position, LinkedHashSetType* container) - : Superclass(position, container) { } - -public: - LinkedHashSetReverseIterator& operator++() { Superclass::operator--(); return *this; } - LinkedHashSetReverseIterator& operator--() { Superclass::operator++(); return *this; } +template +class LinkedHashSetReverseIterator + : public LinkedHashSetIterator { + typedef LinkedHashSetIterator Superclass; + typedef LinkedHashSetConstReverseIterator + const_reverse_iterator; + typedef typename LinkedHashSetType::Node Node; + + protected: + LinkedHashSetReverseIterator(const Node* position, + LinkedHashSetType* container) + : Superclass(position, container) {} + + public: + LinkedHashSetReverseIterator& operator++() { + Superclass::operator--(); + return *this; + } + LinkedHashSetReverseIterator& operator--() { + Superclass::operator++(); + return *this; + } - // Postfix ++ and -- intentionally omitted. + // Postfix ++ and -- intentionally omitted. - operator const_reverse_iterator() const { return *reinterpret_cast(this); } + operator const_reverse_iterator() const { + return *reinterpret_cast(this); + } - template friend class LinkedHashSet; + template + friend class LinkedHashSet; }; -template -class LinkedHashSetConstReverseIterator : public LinkedHashSetConstIterator { - typedef LinkedHashSetConstIterator Superclass; - typedef typename LinkedHashSetType::Node Node; +template +class LinkedHashSetConstReverseIterator + : public LinkedHashSetConstIterator { + typedef LinkedHashSetConstIterator Superclass; + typedef typename LinkedHashSetType::Node Node; -public: - LinkedHashSetConstReverseIterator(const Node* position, const LinkedHashSetType* container) - : Superclass(position, container) { } + public: + LinkedHashSetConstReverseIterator(const Node* position, + const LinkedHashSetType* container) + : Superclass(position, container) {} - LinkedHashSetConstReverseIterator& operator++() { Superclass::operator--(); return *this; } - LinkedHashSetConstReverseIterator& operator--() { Superclass::operator++(); return *this; } + LinkedHashSetConstReverseIterator& operator++() { + Superclass::operator--(); + return *this; + } + LinkedHashSetConstReverseIterator& operator--() { + Superclass::operator++(); + return *this; + } - // Postfix ++ and -- intentionally omitted. + // Postfix ++ and -- intentionally omitted. - template friend class LinkedHashSet; + template + friend class LinkedHashSet; }; -template -inline LinkedHashSet::LinkedHashSet() { } +template +inline LinkedHashSet::LinkedHashSet() {} -template +template inline LinkedHashSet::LinkedHashSet(const LinkedHashSet& other) - : m_anchor() -{ - const_iterator end = other.end(); - for (const_iterator it = other.begin(); it != end; ++it) - add(*it); + : m_anchor() { + const_iterator end = other.end(); + for (const_iterator it = other.begin(); it != end; ++it) + add(*it); } -template -inline LinkedHashSet& LinkedHashSet::operator=(const LinkedHashSet& other) -{ - LinkedHashSet tmp(other); - swap(tmp); - return *this; +template +inline LinkedHashSet& LinkedHashSet::operator=( + const LinkedHashSet& other) { + LinkedHashSet tmp(other); + swap(tmp); + return *this; } -template -inline void LinkedHashSet::swap(LinkedHashSet& other) -{ - m_impl.swap(other.m_impl); - swapAnchor(m_anchor, other.m_anchor); +template +inline void LinkedHashSet::swap(LinkedHashSet& other) { + m_impl.swap(other.m_impl); + swapAnchor(m_anchor, other.m_anchor); } -template -inline LinkedHashSet::~LinkedHashSet() -{ - // The destructor of m_anchor will implicitly be called here, which will - // unlink the anchor from the collection. +template +inline LinkedHashSet::~LinkedHashSet() { + // The destructor of m_anchor will implicitly be called here, which will + // unlink the anchor from the collection. } -template -inline T& LinkedHashSet::first() -{ - ASSERT(!isEmpty()); - return firstNode()->m_value; +template +inline T& LinkedHashSet::first() { + ASSERT(!isEmpty()); + return firstNode()->m_value; } -template -inline const T& LinkedHashSet::first() const -{ - ASSERT(!isEmpty()); - return firstNode()->m_value; +template +inline const T& LinkedHashSet::first() const { + ASSERT(!isEmpty()); + return firstNode()->m_value; } -template -inline void LinkedHashSet::removeFirst() -{ - ASSERT(!isEmpty()); - m_impl.remove(static_cast(m_anchor.m_next)); +template +inline void LinkedHashSet::removeFirst() { + ASSERT(!isEmpty()); + m_impl.remove(static_cast(m_anchor.m_next)); } -template -inline T& LinkedHashSet::last() -{ - ASSERT(!isEmpty()); - return lastNode()->m_value; +template +inline T& LinkedHashSet::last() { + ASSERT(!isEmpty()); + return lastNode()->m_value; } -template -inline const T& LinkedHashSet::last() const -{ - ASSERT(!isEmpty()); - return lastNode()->m_value; +template +inline const T& LinkedHashSet::last() const { + ASSERT(!isEmpty()); + return lastNode()->m_value; } -template -inline void LinkedHashSet::removeLast() -{ - ASSERT(!isEmpty()); - m_impl.remove(static_cast(m_anchor.m_prev)); +template +inline void LinkedHashSet::removeLast() { + ASSERT(!isEmpty()); + m_impl.remove(static_cast(m_anchor.m_prev)); } -template -inline typename LinkedHashSet::iterator LinkedHashSet::find(ValuePeekInType value) -{ - LinkedHashSet::Node* node = m_impl.template lookup(value); - if (!node) - return end(); - return makeIterator(node); +template +inline typename LinkedHashSet::iterator +LinkedHashSet::find(ValuePeekInType value) { + LinkedHashSet::Node* node = + m_impl.template lookup( + value); + if (!node) + return end(); + return makeIterator(node); } -template -inline typename LinkedHashSet::const_iterator LinkedHashSet::find(ValuePeekInType value) const -{ - const LinkedHashSet::Node* node = m_impl.template lookup(value); - if (!node) - return end(); - return makeConstIterator(node); +template +inline typename LinkedHashSet::const_iterator +LinkedHashSet::find(ValuePeekInType value) const { + const LinkedHashSet::Node* node = + m_impl.template lookup( + value); + if (!node) + return end(); + return makeConstIterator(node); } -template +template struct LinkedHashSetTranslatorAdapter { - template static unsigned hash(const T& key) { return Translator::hash(key); } - template static bool equal(const T& a, const U& b) { return Translator::equal(a.m_value, b); } + template + static unsigned hash(const T& key) { + return Translator::hash(key); + } + template + static bool equal(const T& a, const U& b) { + return Translator::equal(a.m_value, b); + } }; -template -template -inline typename LinkedHashSet::iterator LinkedHashSet::find(const T& value) -{ - typedef LinkedHashSetTranslatorAdapter TranslatedFunctions; - const LinkedHashSet::Node* node = m_impl.template lookup(value); - if (!node) - return end(); - return makeIterator(node); +template +template +inline typename LinkedHashSet::iterator +LinkedHashSet::find(const T& value) { + typedef LinkedHashSetTranslatorAdapter TranslatedFunctions; + const LinkedHashSet::Node* node = + m_impl.template lookup(value); + if (!node) + return end(); + return makeIterator(node); } -template -template -inline typename LinkedHashSet::const_iterator LinkedHashSet::find(const T& value) const -{ - typedef LinkedHashSetTranslatorAdapter TranslatedFunctions; - const LinkedHashSet::Node* node = m_impl.template lookup(value); - if (!node) - return end(); - return makeConstIterator(node); +template +template +inline typename LinkedHashSet::const_iterator +LinkedHashSet::find(const T& value) const { + typedef LinkedHashSetTranslatorAdapter TranslatedFunctions; + const LinkedHashSet::Node* node = + m_impl.template lookup(value); + if (!node) + return end(); + return makeConstIterator(node); } -template -template -inline bool LinkedHashSet::contains(const T& value) const -{ - return m_impl.template contains >(value); +template +template +inline bool LinkedHashSet::contains(const T& value) const { + return m_impl + .template contains>(value); } -template -inline bool LinkedHashSet::contains(ValuePeekInType value) const -{ - return m_impl.template contains(value); +template +inline bool LinkedHashSet::contains(ValuePeekInType value) const { + return m_impl.template contains(value); } -template -typename LinkedHashSet::AddResult LinkedHashSet::add(ValuePeekInType value) -{ - return m_impl.template add(value, &m_anchor); +template +typename LinkedHashSet::AddResult +LinkedHashSet::add( + ValuePeekInType value) { + return m_impl.template add(value, &m_anchor); } -template -typename LinkedHashSet::iterator LinkedHashSet::addReturnIterator(ValuePeekInType value) -{ - typename ImplType::AddResult result = m_impl.template add(value, &m_anchor); - return makeIterator(result.storedValue); +template +typename LinkedHashSet::iterator +LinkedHashSet::addReturnIterator(ValuePeekInType value) { + typename ImplType::AddResult result = + m_impl.template add(value, &m_anchor); + return makeIterator(result.storedValue); } -template -typename LinkedHashSet::AddResult LinkedHashSet::appendOrMoveToLast(ValuePeekInType value) -{ - typename ImplType::AddResult result = m_impl.template add(value, &m_anchor); - Node* node = result.storedValue; - if (!result.isNewEntry) { - node->unlink(); - m_anchor.insertBefore(*node); - } - return result; +template +typename LinkedHashSet::AddResult +LinkedHashSet::appendOrMoveToLast(ValuePeekInType value) { + typename ImplType::AddResult result = + m_impl.template add(value, &m_anchor); + Node* node = result.storedValue; + if (!result.isNewEntry) { + node->unlink(); + m_anchor.insertBefore(*node); + } + return result; } -template -typename LinkedHashSet::AddResult LinkedHashSet::prependOrMoveToFirst(ValuePeekInType value) -{ - typename ImplType::AddResult result = m_impl.template add(value, m_anchor.m_next); - Node* node = result.storedValue; - if (!result.isNewEntry) { - node->unlink(); - m_anchor.insertAfter(*node); - } - return result; +template +typename LinkedHashSet::AddResult +LinkedHashSet::prependOrMoveToFirst(ValuePeekInType value) { + typename ImplType::AddResult result = + m_impl.template add(value, m_anchor.m_next); + Node* node = result.storedValue; + if (!result.isNewEntry) { + node->unlink(); + m_anchor.insertAfter(*node); + } + return result; } -template -typename LinkedHashSet::AddResult LinkedHashSet::insertBefore(ValuePeekInType beforeValue, ValuePeekInType newValue) -{ - return insertBefore(find(beforeValue), newValue); +template +typename LinkedHashSet::AddResult +LinkedHashSet::insertBefore(ValuePeekInType beforeValue, + ValuePeekInType newValue) { + return insertBefore(find(beforeValue), newValue); } -template -inline void LinkedHashSet::remove(iterator it) -{ - if (it == end()) - return; - m_impl.remove(it.node()); +template +inline void LinkedHashSet::remove(iterator it) { + if (it == end()) + return; + m_impl.remove(it.node()); } -template -inline void LinkedHashSet::remove(ValuePeekInType value) -{ - remove(find(value)); +template +inline void LinkedHashSet::remove(ValuePeekInType value) { + remove(find(value)); } -inline void swapAnchor(LinkedHashSetNodeBase& a, LinkedHashSetNodeBase& b) -{ - ASSERT(a.m_prev && a.m_next && b.m_prev && b.m_next); - swap(a.m_prev, b.m_prev); - swap(a.m_next, b.m_next); - if (b.m_next == &a) { - ASSERT(b.m_prev == &a); - b.m_next = &b; - b.m_prev = &b; - } else { - b.m_next->m_prev = &b; - b.m_prev->m_next = &b; - } - if (a.m_next == &b) { - ASSERT(a.m_prev == &b); - a.m_next = &a; - a.m_prev = &a; - } else { - a.m_next->m_prev = &a; - a.m_prev->m_next = &a; - } +inline void swapAnchor(LinkedHashSetNodeBase& a, LinkedHashSetNodeBase& b) { + ASSERT(a.m_prev && a.m_next && b.m_prev && b.m_next); + swap(a.m_prev, b.m_prev); + swap(a.m_next, b.m_next); + if (b.m_next == &a) { + ASSERT(b.m_prev == &a); + b.m_next = &b; + b.m_prev = &b; + } else { + b.m_next->m_prev = &b; + b.m_prev->m_next = &b; + } + if (a.m_next == &b) { + ASSERT(a.m_prev == &b); + a.m_next = &a; + a.m_prev = &a; + } else { + a.m_next->m_prev = &a; + a.m_prev->m_next = &a; + } } -inline void swap(LinkedHashSetNodeBase& a, LinkedHashSetNodeBase& b) -{ - ASSERT(a.m_next != &a && b.m_next != &b); - swap(a.m_prev, b.m_prev); - swap(a.m_next, b.m_next); - if (b.m_next) { - b.m_next->m_prev = &b; - b.m_prev->m_next = &b; - } - if (a.m_next) { - a.m_next->m_prev = &a; - a.m_prev->m_next = &a; - } +inline void swap(LinkedHashSetNodeBase& a, LinkedHashSetNodeBase& b) { + ASSERT(a.m_next != &a && b.m_next != &b); + swap(a.m_prev, b.m_prev); + swap(a.m_next, b.m_next); + if (b.m_next) { + b.m_next->m_prev = &b; + b.m_prev->m_next = &b; + } + if (a.m_next) { + a.m_next->m_prev = &a; + a.m_prev->m_next = &a; + } } -template -inline void swap(LinkedHashSetNode& a, LinkedHashSetNode& b) -{ - typedef LinkedHashSetNodeBase Base; - Allocator::enterNoAllocationScope(); - swap(static_cast(a), static_cast(b)); - swap(a.m_value, b.m_value); - Allocator::leaveNoAllocationScope(); +template +inline void swap(LinkedHashSetNode& a, + LinkedHashSetNode& b) { + typedef LinkedHashSetNodeBase Base; + Allocator::enterNoAllocationScope(); + swap(static_cast(a), static_cast(b)); + swap(a.m_value, b.m_value); + Allocator::leaveNoAllocationScope(); } // Warning: After and while calling this you have a collection with deleted // pointers. Consider using a smart pointer like OwnPtr and calling clear() // instead. -template -void deleteAllValues(const LinkedHashSet& set) -{ - typedef typename LinkedHashSet::const_iterator iterator; - iterator end = set.end(); - for (iterator it = set.begin(); it != end; ++it) - delete *it; +template +void deleteAllValues(const LinkedHashSet& set) { + typedef typename LinkedHashSet::const_iterator iterator; + iterator end = set.end(); + for (iterator it = set.begin(); it != end; ++it) + delete *it; } -} +} // namespace WTF using WTF::LinkedHashSet; diff --git a/sky/engine/wtf/LinkedStack.h b/sky/engine/wtf/LinkedStack.h index a631f75b1296a..7930a6ecb6e1b 100644 --- a/sky/engine/wtf/LinkedStack.h +++ b/sky/engine/wtf/LinkedStack.h @@ -38,76 +38,70 @@ namespace WTF { template class LinkedStack { - WTF_MAKE_FAST_ALLOCATED; -public: - LinkedStack() : m_size(0) { } + WTF_MAKE_FAST_ALLOCATED; + + public: + LinkedStack() : m_size(0) {} + + bool isEmpty(); - bool isEmpty(); + void push(const T&); + const T& peek(); + void pop(); - void push(const T&); - const T& peek(); - void pop(); + size_t size(); - size_t size(); + // This inner class used to be private but is now public on account of a + // possible MSVC bug. It can be made private again if we get rid of + // WTF_MAKE_FAST_ALLOCATED ever. + class Node { + WTF_MAKE_FAST_ALLOCATED; - // This inner class used to be private but is now public on account of a - // possible MSVC bug. It can be made private again if we get rid of - // WTF_MAKE_FAST_ALLOCATED ever. - class Node { - WTF_MAKE_FAST_ALLOCATED; - public: - Node(const T&, PassOwnPtr next); + public: + Node(const T&, PassOwnPtr next); - T m_data; - OwnPtr m_next; - }; + T m_data; + OwnPtr m_next; + }; -private: - OwnPtr m_head; - size_t m_size; + private: + OwnPtr m_head; + size_t m_size; }; template LinkedStack::Node::Node(const T& data, PassOwnPtr next) - : m_data(data) - , m_next(next) -{ -} + : m_data(data), m_next(next) {} template -inline bool LinkedStack::isEmpty() -{ - return !m_head; +inline bool LinkedStack::isEmpty() { + return !m_head; } template -inline void LinkedStack::push(const T& data) -{ - m_head = adoptPtr(new Node(data, m_head.release())); - ++m_size; +inline void LinkedStack::push(const T& data) { + m_head = adoptPtr(new Node(data, m_head.release())); + ++m_size; } template -inline const T& LinkedStack::peek() -{ - return m_head->m_data; +inline const T& LinkedStack::peek() { + return m_head->m_data; } template -inline void LinkedStack::pop() -{ - ASSERT(m_head && m_size); - m_head = m_head->m_next.release(); - --m_size; +inline void LinkedStack::pop() { + ASSERT(m_head && m_size); + m_head = m_head->m_next.release(); + --m_size; } template -inline size_t LinkedStack::size() -{ - return m_size; +inline size_t LinkedStack::size() { + return m_size; } -} +} // namespace WTF using WTF::LinkedStack; diff --git a/sky/engine/wtf/ListHashSet.h b/sky/engine/wtf/ListHashSet.h index 40eaf5cda0c5c..c4bd804d34c26 100644 --- a/sky/engine/wtf/ListHashSet.h +++ b/sky/engine/wtf/ListHashSet.h @@ -1,5 +1,6 @@ /* - * Copyright (C) 2005, 2006, 2007, 2008, 2011, 2012 Apple Inc. All rights reserved. + * Copyright (C) 2005, 2006, 2007, 2008, 2011, 2012 Apple Inc. + * All rights reserved. * Copyright (C) 2011, Benjamin Poulain * * This library is free software; you can redistribute it and/or @@ -29,947 +30,1018 @@ namespace WTF { - // ListHashSet: Just like HashSet, this class provides a Set - // interface - a collection of unique objects with O(1) insertion, - // removal and test for containership. However, it also has an - // order - iterating it will always give back values in the order - // in which they are added. - - // Unlike iteration of most WTF Hash data structures, iteration is - // guaranteed safe against mutation of the ListHashSet, except for - // removal of the item currently pointed to by a given iterator. - - template class ListHashSet; - - template class ListHashSetIterator; - template class ListHashSetConstIterator; - template class ListHashSetReverseIterator; - template class ListHashSetConstReverseIterator; - - template class ListHashSetNodeBase; - template class ListHashSetNode; - template struct ListHashSetAllocator; - - template struct ListHashSetNodeHashFunctions; - template struct ListHashSetTranslator; - - // Don't declare a destructor for HeapAllocated ListHashSet. - template - class ListHashSetDestructorBase; - - template - class ListHashSetDestructorBase { - protected: - typename Allocator::AllocatorProvider m_allocatorProvider; - }; - - template - class ListHashSetDestructorBase { - public: - ~ListHashSetDestructorBase() { static_cast(this)->finalize(); } - protected: - typename Allocator::AllocatorProvider m_allocatorProvider; - }; - - // Note that for a ListHashSet you cannot specify the HashTraits as a - // template argument. It uses the default hash traits for the ValueArg - // type. - template::Hash, typename AllocatorArg = ListHashSetAllocator > class ListHashSet - : public ListHashSetDestructorBase, AllocatorArg, AllocatorArg::isGarbageCollected> { - typedef AllocatorArg Allocator; - WTF_USE_ALLOCATOR(ListHashSet, Allocator); - - typedef ListHashSetNode Node; - typedef HashTraits NodeTraits; - typedef ListHashSetNodeHashFunctions NodeHash; - typedef ListHashSetTranslator BaseTranslator; - - typedef HashTable ImplType; - typedef HashTableIterator ImplTypeIterator; - typedef HashTableConstIterator ImplTypeConstIterator; - - typedef HashArg HashFunctions; - - public: - typedef ValueArg ValueType; - typedef HashTraits ValueTraits; - typedef typename ValueTraits::PeekInType ValuePeekInType; - typedef typename ValueTraits::PassInType ValuePassInType; - typedef typename ValueTraits::PassOutType ValuePassOutType; - - typedef ListHashSetIterator iterator; - typedef ListHashSetConstIterator const_iterator; - friend class ListHashSetIterator; - friend class ListHashSetConstIterator; - - typedef ListHashSetReverseIterator reverse_iterator; - typedef ListHashSetConstReverseIterator const_reverse_iterator; - friend class ListHashSetReverseIterator; - friend class ListHashSetConstReverseIterator; - - template struct HashTableAddResult { - HashTableAddResult(Node* storedValue, bool isNewEntry) : storedValue(storedValue), isNewEntry(isNewEntry) { } - Node* storedValue; - bool isNewEntry; - }; - typedef HashTableAddResult AddResult; - - ListHashSet(); - ListHashSet(const ListHashSet&); - ListHashSet& operator=(const ListHashSet&); - void finalize(); - - void swap(ListHashSet&); - - unsigned size() const { return m_impl.size(); } - unsigned capacity() const { return m_impl.capacity(); } - bool isEmpty() const { return m_impl.isEmpty(); } - - iterator begin() { return makeIterator(m_head); } - iterator end() { return makeIterator(0); } - const_iterator begin() const { return makeConstIterator(m_head); } - const_iterator end() const { return makeConstIterator(0); } - - reverse_iterator rbegin() { return makeReverseIterator(m_tail); } - reverse_iterator rend() { return makeReverseIterator(0); } - const_reverse_iterator rbegin() const { return makeConstReverseIterator(m_tail); } - const_reverse_iterator rend() const { return makeConstReverseIterator(0); } - - ValueType& first(); - const ValueType& first() const; - void removeFirst(); - - ValueType& last(); - const ValueType& last() const; - void removeLast(); - - iterator find(ValuePeekInType); - const_iterator find(ValuePeekInType) const; - bool contains(ValuePeekInType) const; - - // An alternate version of find() that finds the object by hashing and comparing - // with some other type, to avoid the cost of type conversion. - // The HashTranslator interface is defined in HashSet. - template iterator find(const T&); - template const_iterator find(const T&) const; - template bool contains(const T&) const; - - // The return value of add is a pair of a pointer to the stored value, - // and a bool that is true if an new entry was added. - AddResult add(ValuePassInType); - - // Same as add() except that the return value is an - // iterator. Useful in cases where it's needed to have the - // same return value as find() and where it's not possible to - // use a pointer to the storedValue. - iterator addReturnIterator(ValuePassInType); - - // Add the value to the end of the collection. If the value was already in - // the list, it is moved to the end. - AddResult appendOrMoveToLast(ValuePassInType); - - // Add the value to the beginning of the collection. If the value was already in - // the list, it is moved to the beginning. - AddResult prependOrMoveToFirst(ValuePassInType); - - AddResult insertBefore(ValuePeekInType beforeValue, ValuePassInType newValue); - AddResult insertBefore(iterator, ValuePassInType); - - void remove(ValuePeekInType value) { return remove(find(value)); } - void remove(iterator); - void clear(); - template - void removeAll(const Collection& other) { WTF::removeAll(*this, other); } - - ValuePassOutType take(iterator); - ValuePassOutType take(ValuePeekInType); - ValuePassOutType takeFirst(); - - private: - void unlink(Node*); - void unlinkAndDelete(Node*); - void appendNode(Node*); - void prependNode(Node*); - void insertNodeBefore(Node* beforeNode, Node* newNode); - void deleteAllNodes(); - Allocator* allocator() const { return this->m_allocatorProvider.get(); } - void createAllocatorIfNeeded() { this->m_allocatorProvider.createAllocatorIfNeeded(); } - void deallocate(Node* node) const { this->m_allocatorProvider.deallocate(node); } - - iterator makeIterator(Node* position) { return iterator(this, position); } - const_iterator makeConstIterator(Node* position) const { return const_iterator(this, position); } - reverse_iterator makeReverseIterator(Node* position) { return reverse_iterator(this, position); } - const_reverse_iterator makeConstReverseIterator(Node* position) const { return const_reverse_iterator(this, position); } - - ImplType m_impl; - Node* m_head; - Node* m_tail; - }; - - // ListHashSetNode has this base class to hold the members because the MSVC - // compiler otherwise gets into circular template dependencies when trying - // to do sizeof on a node. - template class ListHashSetNodeBase { - protected: - ListHashSetNodeBase(const ValueArg& value) - : m_value(value) - , m_prev(0) - , m_next(0) +// ListHashSet: Just like HashSet, this class provides a Set +// interface - a collection of unique objects with O(1) insertion, +// removal and test for containership. However, it also has an +// order - iterating it will always give back values in the order +// in which they are added. + +// Unlike iteration of most WTF Hash data structures, iteration is +// guaranteed safe against mutation of the ListHashSet, except for +// removal of the item currently pointed to by a given iterator. + +template +class ListHashSet; + +template +class ListHashSetIterator; +template +class ListHashSetConstIterator; +template +class ListHashSetReverseIterator; +template +class ListHashSetConstReverseIterator; + +template +class ListHashSetNodeBase; +template +class ListHashSetNode; +template +struct ListHashSetAllocator; + +template +struct ListHashSetNodeHashFunctions; +template +struct ListHashSetTranslator; + +// Don't declare a destructor for HeapAllocated ListHashSet. +template +class ListHashSetDestructorBase; + +template +class ListHashSetDestructorBase { + protected: + typename Allocator::AllocatorProvider m_allocatorProvider; +}; + +template +class ListHashSetDestructorBase { + public: + ~ListHashSetDestructorBase() { static_cast(this)->finalize(); } + + protected: + typename Allocator::AllocatorProvider m_allocatorProvider; +}; + +// Note that for a ListHashSet you cannot specify the HashTraits as a +// template argument. It uses the default hash traits for the ValueArg +// type. +template ::Hash, + typename AllocatorArg = + ListHashSetAllocator> +class ListHashSet + : public ListHashSetDestructorBase< + ListHashSet, + AllocatorArg, + AllocatorArg::isGarbageCollected> { + typedef AllocatorArg Allocator; + WTF_USE_ALLOCATOR(ListHashSet, Allocator); + + typedef ListHashSetNode Node; + typedef HashTraits NodeTraits; + typedef ListHashSetNodeHashFunctions NodeHash; + typedef ListHashSetTranslator BaseTranslator; + + typedef HashTable + ImplType; + typedef HashTableIterator + ImplTypeIterator; + typedef HashTableConstIterator + ImplTypeConstIterator; + + typedef HashArg HashFunctions; + + public: + typedef ValueArg ValueType; + typedef HashTraits ValueTraits; + typedef typename ValueTraits::PeekInType ValuePeekInType; + typedef typename ValueTraits::PassInType ValuePassInType; + typedef typename ValueTraits::PassOutType ValuePassOutType; + + typedef ListHashSetIterator iterator; + typedef ListHashSetConstIterator const_iterator; + friend class ListHashSetIterator; + friend class ListHashSetConstIterator; + + typedef ListHashSetReverseIterator reverse_iterator; + typedef ListHashSetConstReverseIterator const_reverse_iterator; + friend class ListHashSetReverseIterator; + friend class ListHashSetConstReverseIterator; + + template + struct HashTableAddResult { + HashTableAddResult(Node* storedValue, bool isNewEntry) + : storedValue(storedValue), isNewEntry(isNewEntry) {} + Node* storedValue; + bool isNewEntry; + }; + typedef HashTableAddResult AddResult; + + ListHashSet(); + ListHashSet(const ListHashSet&); + ListHashSet& operator=(const ListHashSet&); + void finalize(); + + void swap(ListHashSet&); + + unsigned size() const { return m_impl.size(); } + unsigned capacity() const { return m_impl.capacity(); } + bool isEmpty() const { return m_impl.isEmpty(); } + + iterator begin() { return makeIterator(m_head); } + iterator end() { return makeIterator(0); } + const_iterator begin() const { return makeConstIterator(m_head); } + const_iterator end() const { return makeConstIterator(0); } + + reverse_iterator rbegin() { return makeReverseIterator(m_tail); } + reverse_iterator rend() { return makeReverseIterator(0); } + const_reverse_iterator rbegin() const { + return makeConstReverseIterator(m_tail); + } + const_reverse_iterator rend() const { return makeConstReverseIterator(0); } + + ValueType& first(); + const ValueType& first() const; + void removeFirst(); + + ValueType& last(); + const ValueType& last() const; + void removeLast(); + + iterator find(ValuePeekInType); + const_iterator find(ValuePeekInType) const; + bool contains(ValuePeekInType) const; + + // An alternate version of find() that finds the object by hashing and + // comparing with some other type, to avoid the cost of type conversion. The + // HashTranslator interface is defined in HashSet. + template + iterator find(const T&); + template + const_iterator find(const T&) const; + template + bool contains(const T&) const; + + // The return value of add is a pair of a pointer to the stored value, + // and a bool that is true if an new entry was added. + AddResult add(ValuePassInType); + + // Same as add() except that the return value is an + // iterator. Useful in cases where it's needed to have the + // same return value as find() and where it's not possible to + // use a pointer to the storedValue. + iterator addReturnIterator(ValuePassInType); + + // Add the value to the end of the collection. If the value was already in + // the list, it is moved to the end. + AddResult appendOrMoveToLast(ValuePassInType); + + // Add the value to the beginning of the collection. If the value was already + // in the list, it is moved to the beginning. + AddResult prependOrMoveToFirst(ValuePassInType); + + AddResult insertBefore(ValuePeekInType beforeValue, ValuePassInType newValue); + AddResult insertBefore(iterator, ValuePassInType); + + void remove(ValuePeekInType value) { return remove(find(value)); } + void remove(iterator); + void clear(); + template + void removeAll(const Collection& other) { + WTF::removeAll(*this, other); + } + + ValuePassOutType take(iterator); + ValuePassOutType take(ValuePeekInType); + ValuePassOutType takeFirst(); + + private: + void unlink(Node*); + void unlinkAndDelete(Node*); + void appendNode(Node*); + void prependNode(Node*); + void insertNodeBefore(Node* beforeNode, Node* newNode); + void deleteAllNodes(); + Allocator* allocator() const { return this->m_allocatorProvider.get(); } + void createAllocatorIfNeeded() { + this->m_allocatorProvider.createAllocatorIfNeeded(); + } + void deallocate(Node* node) const { + this->m_allocatorProvider.deallocate(node); + } + + iterator makeIterator(Node* position) { return iterator(this, position); } + const_iterator makeConstIterator(Node* position) const { + return const_iterator(this, position); + } + reverse_iterator makeReverseIterator(Node* position) { + return reverse_iterator(this, position); + } + const_reverse_iterator makeConstReverseIterator(Node* position) const { + return const_reverse_iterator(this, position); + } + + ImplType m_impl; + Node* m_head; + Node* m_tail; +}; + +// ListHashSetNode has this base class to hold the members because the MSVC +// compiler otherwise gets into circular template dependencies when trying +// to do sizeof on a node. +template +class ListHashSetNodeBase { + protected: + ListHashSetNodeBase(const ValueArg& value) + : m_value(value), + m_prev(0), + m_next(0) #if ENABLE(ASSERT) - , m_isAllocated(true) + , + m_isAllocated(true) #endif - { - } - - template - ListHashSetNodeBase(const U& value) - : m_value(value) - , m_prev(0) - , m_next(0) + { + } + + template + ListHashSetNodeBase(const U& value) + : m_value(value), + m_prev(0), + m_next(0) #if ENABLE(ASSERT) - , m_isAllocated(true) + , + m_isAllocated(true) #endif - { - } + { + } - public: - ValueArg m_value; - ListHashSetNodeBase* m_prev; - ListHashSetNodeBase* m_next; + public: + ValueArg m_value; + ListHashSetNodeBase* m_prev; + ListHashSetNodeBase* m_next; #if ENABLE(ASSERT) - bool m_isAllocated; + bool m_isAllocated; #endif - }; - - // This allocator is only used for non-Heap ListHashSets. - template - struct ListHashSetAllocator : public DefaultAllocator { - typedef DefaultAllocator TableAllocator; - typedef ListHashSetNode Node; - typedef ListHashSetNodeBase NodeBase; - class AllocatorProvider { - public: - void createAllocatorIfNeeded() - { - if (!m_allocator) - m_allocator = adoptPtr(new ListHashSetAllocator); - } - - void swap(AllocatorProvider& other) - { - m_allocator.swap(other.m_allocator); - } - - void deallocate(Node* node) const - { - ASSERT(m_allocator); - m_allocator->deallocate(node); - } - - ListHashSetAllocator* get() const - { - ASSERT(m_allocator); - return m_allocator.get(); - } - - private: - OwnPtr m_allocator; - }; - - ListHashSetAllocator() - : m_freeList(pool()) - , m_isDoneWithInitialFreeList(false) - { - memset(m_pool.buffer, 0, sizeof(m_pool.buffer)); - } - - Node* allocateNode() - { - Node* result = m_freeList; - - if (!result) - return static_cast(fastMalloc(sizeof(NodeBase))); - - ASSERT(!result->m_isAllocated); - - Node* next = result->next(); - ASSERT(!next || !next->m_isAllocated); - if (!next && !m_isDoneWithInitialFreeList) { - next = result + 1; - if (next == pastPool()) { - m_isDoneWithInitialFreeList = true; - next = 0; - } else { - ASSERT(inPool(next)); - ASSERT(!next->m_isAllocated); - } - } - m_freeList = next; - - return result; - } - - void deallocate(Node* node) - { - if (inPool(node)) { -#if ENABLE(ASSERT) - node->m_isAllocated = false; -#endif - node->m_next = m_freeList; - m_freeList = node; - return; - } - - fastFree(node); - } - - bool inPool(Node* node) - { - return node >= pool() && node < pastPool(); - } - - private: - Node* pool() { return reinterpret_cast_ptr(m_pool.buffer); } - Node* pastPool() { return pool() + m_poolSize; } - - Node* m_freeList; - bool m_isDoneWithInitialFreeList; -#if defined(MEMORY_SANITIZER_INITIAL_SIZE) - // The allocation pool for nodes is one big chunk that ASAN has no - // insight into, so it can cloak errors. Make it as small as possible - // to force nodes to be allocated individually where ASAN can see them. - static const size_t m_poolSize = 1; -#else - static const size_t m_poolSize = inlineCapacity; -#endif - AlignedBuffer m_pool; - }; - - template class ListHashSetNode : public ListHashSetNodeBase { - public: - typedef AllocatorArg NodeAllocator; - typedef ValueArg Value; - - template - ListHashSetNode(U value) - : ListHashSetNodeBase(value) { } - - void* operator new(size_t, NodeAllocator* allocator) - { - COMPILE_ASSERT(sizeof(ListHashSetNode) == sizeof(ListHashSetNodeBase), PleaseAddAnyFieldsToTheBase); - return allocator->allocateNode(); - } - - void setWasAlreadyDestructed() - { - if (NodeAllocator::isGarbageCollected && HashTraits::needsDestruction) - this->m_prev = unlinkedNodePointer(); - } - - bool wasAlreadyDestructed() const - { - ASSERT(NodeAllocator::isGarbageCollected); - return this->m_prev == unlinkedNodePointer(); - } - - static void finalize(void* pointer) - { - ASSERT(HashTraits::needsDestruction); // No need to waste time calling finalize if it's not needed. - ListHashSetNode* self = reinterpret_cast_ptr(pointer); - - // Check whether this node was already destructed before being - // unlinked from the collection. - if (self->wasAlreadyDestructed()) - return; - - self->m_value.~ValueArg(); - } - - void destroy(NodeAllocator* allocator) - { - this->~ListHashSetNode(); - setWasAlreadyDestructed(); - allocator->deallocate(this); - } - - ListHashSetNode* next() const { return reinterpret_cast(this->m_next); } - ListHashSetNode* prev() const { return reinterpret_cast(this->m_prev); } - - // Don't add fields here, the ListHashSetNodeBase and this should have - // the same size. - - static ListHashSetNode* unlinkedNodePointer() { return reinterpret_cast(-1); } - - template - friend struct ListHashSetNodeHashFunctions; - }; - - template struct ListHashSetNodeHashFunctions { - template static unsigned hash(const T& key) { return HashArg::hash(key->m_value); } - template static bool equal(const T& a, const T& b) { return HashArg::equal(a->m_value, b->m_value); } - static const bool safeToCompareToEmptyOrDeleted = false; - }; - - template class ListHashSetIterator { - private: - typedef typename Set::const_iterator const_iterator; - typedef typename Set::Node Node; - typedef typename Set::ValueType ValueType; - typedef ValueType& ReferenceType; - typedef ValueType* PointerType; - - ListHashSetIterator(const Set* set, Node* position) : m_iterator(set, position) { } - - public: - ListHashSetIterator() { } - - // default copy, assignment and destructor are OK - - PointerType get() const { return const_cast(m_iterator.get()); } - ReferenceType operator*() const { return *get(); } - PointerType operator->() const { return get(); } - - ListHashSetIterator& operator++() { ++m_iterator; return *this; } - ListHashSetIterator& operator--() { --m_iterator; return *this; } - - // Postfix ++ and -- intentionally omitted. - - // Comparison. - bool operator==(const ListHashSetIterator& other) const { return m_iterator == other.m_iterator; } - bool operator!=(const ListHashSetIterator& other) const { return m_iterator != other.m_iterator; } - - operator const_iterator() const { return m_iterator; } - - private: - Node* node() { return m_iterator.node(); } - - const_iterator m_iterator; - - template - friend class ListHashSet; - }; - - template - class ListHashSetConstIterator { - private: - typedef typename Set::const_iterator const_iterator; - typedef typename Set::Node Node; - typedef typename Set::ValueType ValueType; - typedef const ValueType& ReferenceType; - typedef const ValueType* PointerType; - - friend class ListHashSetIterator; - - ListHashSetConstIterator(const Set* set, Node* position) - : m_set(set) - , m_position(position) - { - } - - public: - ListHashSetConstIterator() - { - } - - PointerType get() const - { - return &m_position->m_value; - } - ReferenceType operator*() const { return *get(); } - PointerType operator->() const { return get(); } - - ListHashSetConstIterator& operator++() - { - ASSERT(m_position != 0); - m_position = m_position->next(); - return *this; - } - - ListHashSetConstIterator& operator--() - { - ASSERT(m_position != m_set->m_head); - if (!m_position) - m_position = m_set->m_tail; - else - m_position = m_position->prev(); - return *this; - } - - // Postfix ++ and -- intentionally omitted. - - // Comparison. - bool operator==(const ListHashSetConstIterator& other) const - { - return m_position == other.m_position; - } - bool operator!=(const ListHashSetConstIterator& other) const - { - return m_position != other.m_position; - } - - private: - Node* node() { return m_position; } - - const Set* m_set; - Node* m_position; - - template - friend class ListHashSet; - }; - - template - class ListHashSetReverseIterator { - private: - typedef typename Set::const_reverse_iterator const_reverse_iterator; - typedef typename Set::Node Node; - typedef typename Set::ValueType ValueType; - typedef ValueType& ReferenceType; - typedef ValueType* PointerType; - - ListHashSetReverseIterator(const Set* set, Node* position) : m_iterator(set, position) { } - - public: - ListHashSetReverseIterator() { } - - // default copy, assignment and destructor are OK - - PointerType get() const { return const_cast(m_iterator.get()); } - ReferenceType operator*() const { return *get(); } - PointerType operator->() const { return get(); } - - ListHashSetReverseIterator& operator++() { ++m_iterator; return *this; } - ListHashSetReverseIterator& operator--() { --m_iterator; return *this; } - - // Postfix ++ and -- intentionally omitted. - - // Comparison. - bool operator==(const ListHashSetReverseIterator& other) const { return m_iterator == other.m_iterator; } - bool operator!=(const ListHashSetReverseIterator& other) const { return m_iterator != other.m_iterator; } - - operator const_reverse_iterator() const { return m_iterator; } - - private: - Node* node() { return m_iterator.node(); } - - const_reverse_iterator m_iterator; - - template - friend class ListHashSet; - }; - - template class ListHashSetConstReverseIterator { - private: - typedef typename Set::reverse_iterator reverse_iterator; - typedef typename Set::Node Node; - typedef typename Set::ValueType ValueType; - typedef const ValueType& ReferenceType; - typedef const ValueType* PointerType; - - friend class ListHashSetReverseIterator; - - ListHashSetConstReverseIterator(const Set* set, Node* position) - : m_set(set) - , m_position(position) - { - } - - public: - ListHashSetConstReverseIterator() - { - } - - PointerType get() const - { - return &m_position->m_value; - } - ReferenceType operator*() const { return *get(); } - PointerType operator->() const { return get(); } - - ListHashSetConstReverseIterator& operator++() - { - ASSERT(m_position != 0); - m_position = m_position->prev(); - return *this; - } - - ListHashSetConstReverseIterator& operator--() - { - ASSERT(m_position != m_set->m_tail); - if (!m_position) - m_position = m_set->m_head; - else - m_position = m_position->next(); - return *this; - } - - // Postfix ++ and -- intentionally omitted. - - // Comparison. - bool operator==(const ListHashSetConstReverseIterator& other) const - { - return m_position == other.m_position; - } - bool operator!=(const ListHashSetConstReverseIterator& other) const - { - return m_position != other.m_position; - } - - private: - Node* node() { return m_position; } - - const Set* m_set; - Node* m_position; - - template - friend class ListHashSet; - }; - - template - struct ListHashSetTranslator { - template static unsigned hash(const T& key) { return HashFunctions::hash(key); } - template static bool equal(const T& a, const U& b) { return HashFunctions::equal(a->m_value, b); } - template static void translate(T*& location, const U& key, const V& allocator) - { - location = new (const_cast(&allocator)) T(key); - } - }; - - template - inline ListHashSet::ListHashSet() - : m_head(0) - , m_tail(0) - { - } - - template - inline ListHashSet::ListHashSet(const ListHashSet& other) - : m_head(0) - , m_tail(0) - { - const_iterator end = other.end(); - for (const_iterator it = other.begin(); it != end; ++it) - add(*it); - } - - template - inline ListHashSet& ListHashSet::operator=(const ListHashSet& other) - { - ListHashSet tmp(other); - swap(tmp); - return *this; - } - - template - inline void ListHashSet::swap(ListHashSet& other) - { - m_impl.swap(other.m_impl); - std::swap(m_head, other.m_head); - std::swap(m_tail, other.m_tail); - this->m_allocatorProvider.swap(other.m_allocatorProvider); - } - - template - inline void ListHashSet::finalize() - { - COMPILE_ASSERT(!Allocator::isGarbageCollected, FinalizeOnHeapAllocatedListHashSetShouldNeverBeCalled); - deleteAllNodes(); - } - - template - inline T& ListHashSet::first() - { - ASSERT(!isEmpty()); - return m_head->m_value; - } - - template - inline void ListHashSet::removeFirst() - { - ASSERT(!isEmpty()); - m_impl.remove(m_head); - unlinkAndDelete(m_head); - } - - template - inline const T& ListHashSet::first() const - { - ASSERT(!isEmpty()); - return m_head->m_value; - } - - template - inline T& ListHashSet::last() - { - ASSERT(!isEmpty()); - return m_tail->m_value; - } - - template - inline const T& ListHashSet::last() const - { - ASSERT(!isEmpty()); - return m_tail->m_value; - } - - template - inline void ListHashSet::removeLast() - { - ASSERT(!isEmpty()); - m_impl.remove(m_tail); - unlinkAndDelete(m_tail); - } - - template - inline typename ListHashSet::iterator ListHashSet::find(ValuePeekInType value) - { - ImplTypeIterator it = m_impl.template find(value); - if (it == m_impl.end()) - return end(); - return makeIterator(*it); - } - - template - inline typename ListHashSet::const_iterator ListHashSet::find(ValuePeekInType value) const - { - ImplTypeConstIterator it = m_impl.template find(value); - if (it == m_impl.end()) - return end(); - return makeConstIterator(*it); - } - - template - struct ListHashSetTranslatorAdapter { - template static unsigned hash(const T& key) { return Translator::hash(key); } - template static bool equal(const T& a, const U& b) { return Translator::equal(a->m_value, b); } - }; - - template - template - inline typename ListHashSet::iterator ListHashSet::find(const T& value) - { - ImplTypeConstIterator it = m_impl.template find >(value); - if (it == m_impl.end()) - return end(); - return makeIterator(*it); - } - - template - template - inline typename ListHashSet::const_iterator ListHashSet::find(const T& value) const - { - ImplTypeConstIterator it = m_impl.template find >(value); - if (it == m_impl.end()) - return end(); - return makeConstIterator(*it); - } - - template - template - inline bool ListHashSet::contains(const T& value) const - { - return m_impl.template contains >(value); - } - - template - inline bool ListHashSet::contains(ValuePeekInType value) const - { - return m_impl.template contains(value); - } - - template - typename ListHashSet::AddResult ListHashSet::add(ValuePassInType value) - { - createAllocatorIfNeeded(); - // The second argument is a const ref. This is useful for the HashTable - // because it lets it take lvalues by reference, but for our purposes - // it's inconvenient, since it constrains us to be const, whereas the - // allocator actually changes when it does allocations. - typename ImplType::AddResult result = m_impl.template add(value, *this->allocator()); - if (result.isNewEntry) - appendNode(*result.storedValue); - return AddResult(*result.storedValue, result.isNewEntry); - } - - template - typename ListHashSet::iterator ListHashSet::addReturnIterator(ValuePassInType value) - { - return makeIterator(add(value).storedValue); - } - - template - typename ListHashSet::AddResult ListHashSet::appendOrMoveToLast(ValuePassInType value) - { - createAllocatorIfNeeded(); - typename ImplType::AddResult result = m_impl.template add(value, *this->allocator()); - Node* node = *result.storedValue; - if (!result.isNewEntry) - unlink(node); - appendNode(node); - return AddResult(*result.storedValue, result.isNewEntry); - } - - template - typename ListHashSet::AddResult ListHashSet::prependOrMoveToFirst(ValuePassInType value) - { - createAllocatorIfNeeded(); - typename ImplType::AddResult result = m_impl.template add(value, *this->allocator()); - Node* node = *result.storedValue; - if (!result.isNewEntry) - unlink(node); - prependNode(node); - return AddResult(*result.storedValue, result.isNewEntry); - } - - template - typename ListHashSet::AddResult ListHashSet::insertBefore(iterator it, ValuePassInType newValue) - { - createAllocatorIfNeeded(); - typename ImplType::AddResult result = m_impl.template add(newValue, *this->allocator()); - if (result.isNewEntry) - insertNodeBefore(it.node(), *result.storedValue); - return AddResult(*result.storedValue, result.isNewEntry); +}; + +// This allocator is only used for non-Heap ListHashSets. +template +struct ListHashSetAllocator : public DefaultAllocator { + typedef DefaultAllocator TableAllocator; + typedef ListHashSetNode Node; + typedef ListHashSetNodeBase NodeBase; + class AllocatorProvider { + public: + void createAllocatorIfNeeded() { + if (!m_allocator) + m_allocator = adoptPtr(new ListHashSetAllocator); } - template - typename ListHashSet::AddResult ListHashSet::insertBefore(ValuePeekInType beforeValue, ValuePassInType newValue) - { - createAllocatorIfNeeded(); - return insertBefore(find(beforeValue), newValue); - } - - template - inline void ListHashSet::remove(iterator it) - { - if (it == end()) - return; - m_impl.remove(it.node()); - unlinkAndDelete(it.node()); - } + void swap(AllocatorProvider& other) { m_allocator.swap(other.m_allocator); } - template - inline void ListHashSet::clear() - { - deleteAllNodes(); - m_impl.clear(); - m_head = 0; - m_tail = 0; + void deallocate(Node* node) const { + ASSERT(m_allocator); + m_allocator->deallocate(node); } - template - typename ListHashSet::ValuePassOutType ListHashSet::take(iterator it) - { - if (it == end()) - return ValueTraits::emptyValue(); - - m_impl.remove(it.node()); - ValuePassOutType result = ValueTraits::passOut(it.node()->m_value); - unlinkAndDelete(it.node()); - - return result; + ListHashSetAllocator* get() const { + ASSERT(m_allocator); + return m_allocator.get(); } - template - typename ListHashSet::ValuePassOutType ListHashSet::take(ValuePeekInType value) - { - return take(find(value)); + private: + OwnPtr m_allocator; + }; + + ListHashSetAllocator() + : m_freeList(pool()), m_isDoneWithInitialFreeList(false) { + memset(m_pool.buffer, 0, sizeof(m_pool.buffer)); + } + + Node* allocateNode() { + Node* result = m_freeList; + + if (!result) + return static_cast(fastMalloc(sizeof(NodeBase))); + + ASSERT(!result->m_isAllocated); + + Node* next = result->next(); + ASSERT(!next || !next->m_isAllocated); + if (!next && !m_isDoneWithInitialFreeList) { + next = result + 1; + if (next == pastPool()) { + m_isDoneWithInitialFreeList = true; + next = 0; + } else { + ASSERT(inPool(next)); + ASSERT(!next->m_isAllocated); + } } + m_freeList = next; - template - typename ListHashSet::ValuePassOutType ListHashSet::takeFirst() - { - ASSERT(!isEmpty()); - m_impl.remove(m_head); - ValuePassOutType result = ValueTraits::passOut(m_head->m_value); - unlinkAndDelete(m_head); + return result; + } - return result; - } - - template - void ListHashSet::unlink(Node* node) - { - if (!node->m_prev) { - ASSERT(node == m_head); - m_head = node->next(); - } else { - ASSERT(node != m_head); - node->m_prev->m_next = node->m_next; - } - - if (!node->m_next) { - ASSERT(node == m_tail); - m_tail = node->prev(); - } else { - ASSERT(node != m_tail); - node->m_next->m_prev = node->m_prev; - } - } - - template - void ListHashSet::unlinkAndDelete(Node* node) - { - unlink(node); - node->destroy(this->allocator()); - } - - template - void ListHashSet::appendNode(Node* node) - { - node->m_prev = m_tail; - node->m_next = 0; - - if (m_tail) { - ASSERT(m_head); - m_tail->m_next = node; - } else { - ASSERT(!m_head); - m_head = node; - } - - m_tail = node; - } - - template - void ListHashSet::prependNode(Node* node) - { - node->m_prev = 0; - node->m_next = m_head; - - if (m_head) - m_head->m_prev = node; - else - m_tail = node; - - m_head = node; + void deallocate(Node* node) { + if (inPool(node)) { +#if ENABLE(ASSERT) + node->m_isAllocated = false; +#endif + node->m_next = m_freeList; + m_freeList = node; + return; } - template - void ListHashSet::insertNodeBefore(Node* beforeNode, Node* newNode) - { - if (!beforeNode) - return appendNode(newNode); + fastFree(node); + } - newNode->m_next = beforeNode; - newNode->m_prev = beforeNode->m_prev; - if (beforeNode->m_prev) - beforeNode->m_prev->m_next = newNode; - beforeNode->m_prev = newNode; + bool inPool(Node* node) { return node >= pool() && node < pastPool(); } - if (!newNode->m_prev) - m_head = newNode; - } + private: + Node* pool() { return reinterpret_cast_ptr(m_pool.buffer); } + Node* pastPool() { return pool() + m_poolSize; } - template - void ListHashSet::deleteAllNodes() - { - if (!m_head) - return; - - for (Node* node = m_head, *next = m_head->next(); node; node = next, next = node ? node->next() : 0) - node->destroy(this->allocator()); - } - -} // namespace WTF + Node* m_freeList; + bool m_isDoneWithInitialFreeList; +#if defined(MEMORY_SANITIZER_INITIAL_SIZE) + // The allocation pool for nodes is one big chunk that ASAN has no + // insight into, so it can cloak errors. Make it as small as possible + // to force nodes to be allocated individually where ASAN can see them. + static const size_t m_poolSize = 1; +#else + static const size_t m_poolSize = inlineCapacity; +#endif + AlignedBuffer m_pool; +}; + +template +class ListHashSetNode : public ListHashSetNodeBase { + public: + typedef AllocatorArg NodeAllocator; + typedef ValueArg Value; + + template + ListHashSetNode(U value) : ListHashSetNodeBase(value) {} + + void* operator new(size_t, NodeAllocator* allocator) { + COMPILE_ASSERT( + sizeof(ListHashSetNode) == sizeof(ListHashSetNodeBase), + PleaseAddAnyFieldsToTheBase); + return allocator->allocateNode(); + } + + void setWasAlreadyDestructed() { + if (NodeAllocator::isGarbageCollected && + HashTraits::needsDestruction) + this->m_prev = unlinkedNodePointer(); + } + + bool wasAlreadyDestructed() const { + ASSERT(NodeAllocator::isGarbageCollected); + return this->m_prev == unlinkedNodePointer(); + } + + static void finalize(void* pointer) { + ASSERT(HashTraits::needsDestruction); // No need to waste time + // calling finalize if it's + // not needed. + ListHashSetNode* self = reinterpret_cast_ptr(pointer); + + // Check whether this node was already destructed before being + // unlinked from the collection. + if (self->wasAlreadyDestructed()) + return; + + self->m_value.~ValueArg(); + } + + void destroy(NodeAllocator* allocator) { + this->~ListHashSetNode(); + setWasAlreadyDestructed(); + allocator->deallocate(this); + } + + ListHashSetNode* next() const { + return reinterpret_cast(this->m_next); + } + ListHashSetNode* prev() const { + return reinterpret_cast(this->m_prev); + } + + // Don't add fields here, the ListHashSetNodeBase and this should have + // the same size. + + static ListHashSetNode* unlinkedNodePointer() { + return reinterpret_cast(-1); + } + + template + friend struct ListHashSetNodeHashFunctions; +}; + +template +struct ListHashSetNodeHashFunctions { + template + static unsigned hash(const T& key) { + return HashArg::hash(key->m_value); + } + template + static bool equal(const T& a, const T& b) { + return HashArg::equal(a->m_value, b->m_value); + } + static const bool safeToCompareToEmptyOrDeleted = false; +}; + +template +class ListHashSetIterator { + private: + typedef typename Set::const_iterator const_iterator; + typedef typename Set::Node Node; + typedef typename Set::ValueType ValueType; + typedef ValueType& ReferenceType; + typedef ValueType* PointerType; + + ListHashSetIterator(const Set* set, Node* position) + : m_iterator(set, position) {} + + public: + ListHashSetIterator() {} + + // default copy, assignment and destructor are OK + + PointerType get() const { return const_cast(m_iterator.get()); } + ReferenceType operator*() const { return *get(); } + PointerType operator->() const { return get(); } + + ListHashSetIterator& operator++() { + ++m_iterator; + return *this; + } + ListHashSetIterator& operator--() { + --m_iterator; + return *this; + } + + // Postfix ++ and -- intentionally omitted. + + // Comparison. + bool operator==(const ListHashSetIterator& other) const { + return m_iterator == other.m_iterator; + } + bool operator!=(const ListHashSetIterator& other) const { + return m_iterator != other.m_iterator; + } + + operator const_iterator() const { return m_iterator; } + + private: + Node* node() { return m_iterator.node(); } + + const_iterator m_iterator; + + template + friend class ListHashSet; +}; + +template +class ListHashSetConstIterator { + private: + typedef typename Set::const_iterator const_iterator; + typedef typename Set::Node Node; + typedef typename Set::ValueType ValueType; + typedef const ValueType& ReferenceType; + typedef const ValueType* PointerType; + + friend class ListHashSetIterator; + + ListHashSetConstIterator(const Set* set, Node* position) + : m_set(set), m_position(position) {} + + public: + ListHashSetConstIterator() {} + + PointerType get() const { return &m_position->m_value; } + ReferenceType operator*() const { return *get(); } + PointerType operator->() const { return get(); } + + ListHashSetConstIterator& operator++() { + ASSERT(m_position != 0); + m_position = m_position->next(); + return *this; + } + + ListHashSetConstIterator& operator--() { + ASSERT(m_position != m_set->m_head); + if (!m_position) + m_position = m_set->m_tail; + else + m_position = m_position->prev(); + return *this; + } + + // Postfix ++ and -- intentionally omitted. + + // Comparison. + bool operator==(const ListHashSetConstIterator& other) const { + return m_position == other.m_position; + } + bool operator!=(const ListHashSetConstIterator& other) const { + return m_position != other.m_position; + } + + private: + Node* node() { return m_position; } + + const Set* m_set; + Node* m_position; + + template + friend class ListHashSet; +}; + +template +class ListHashSetReverseIterator { + private: + typedef typename Set::const_reverse_iterator const_reverse_iterator; + typedef typename Set::Node Node; + typedef typename Set::ValueType ValueType; + typedef ValueType& ReferenceType; + typedef ValueType* PointerType; + + ListHashSetReverseIterator(const Set* set, Node* position) + : m_iterator(set, position) {} + + public: + ListHashSetReverseIterator() {} + + // default copy, assignment and destructor are OK + + PointerType get() const { return const_cast(m_iterator.get()); } + ReferenceType operator*() const { return *get(); } + PointerType operator->() const { return get(); } + + ListHashSetReverseIterator& operator++() { + ++m_iterator; + return *this; + } + ListHashSetReverseIterator& operator--() { + --m_iterator; + return *this; + } + + // Postfix ++ and -- intentionally omitted. + + // Comparison. + bool operator==(const ListHashSetReverseIterator& other) const { + return m_iterator == other.m_iterator; + } + bool operator!=(const ListHashSetReverseIterator& other) const { + return m_iterator != other.m_iterator; + } + + operator const_reverse_iterator() const { return m_iterator; } + + private: + Node* node() { return m_iterator.node(); } + + const_reverse_iterator m_iterator; + + template + friend class ListHashSet; +}; + +template +class ListHashSetConstReverseIterator { + private: + typedef typename Set::reverse_iterator reverse_iterator; + typedef typename Set::Node Node; + typedef typename Set::ValueType ValueType; + typedef const ValueType& ReferenceType; + typedef const ValueType* PointerType; + + friend class ListHashSetReverseIterator; + + ListHashSetConstReverseIterator(const Set* set, Node* position) + : m_set(set), m_position(position) {} + + public: + ListHashSetConstReverseIterator() {} + + PointerType get() const { return &m_position->m_value; } + ReferenceType operator*() const { return *get(); } + PointerType operator->() const { return get(); } + + ListHashSetConstReverseIterator& operator++() { + ASSERT(m_position != 0); + m_position = m_position->prev(); + return *this; + } + + ListHashSetConstReverseIterator& operator--() { + ASSERT(m_position != m_set->m_tail); + if (!m_position) + m_position = m_set->m_head; + else + m_position = m_position->next(); + return *this; + } + + // Postfix ++ and -- intentionally omitted. + + // Comparison. + bool operator==(const ListHashSetConstReverseIterator& other) const { + return m_position == other.m_position; + } + bool operator!=(const ListHashSetConstReverseIterator& other) const { + return m_position != other.m_position; + } + + private: + Node* node() { return m_position; } + + const Set* m_set; + Node* m_position; + + template + friend class ListHashSet; +}; + +template +struct ListHashSetTranslator { + template + static unsigned hash(const T& key) { + return HashFunctions::hash(key); + } + template + static bool equal(const T& a, const U& b) { + return HashFunctions::equal(a->m_value, b); + } + template + static void translate(T*& location, const U& key, const V& allocator) { + location = new (const_cast(&allocator)) T(key); + } +}; + +template +inline ListHashSet::ListHashSet() + : m_head(0), m_tail(0) {} + +template +inline ListHashSet::ListHashSet( + const ListHashSet& other) + : m_head(0), m_tail(0) { + const_iterator end = other.end(); + for (const_iterator it = other.begin(); it != end; ++it) + add(*it); +} + +template +inline ListHashSet& +ListHashSet::operator=(const ListHashSet& other) { + ListHashSet tmp(other); + swap(tmp); + return *this; +} + +template +inline void ListHashSet::swap(ListHashSet& other) { + m_impl.swap(other.m_impl); + std::swap(m_head, other.m_head); + std::swap(m_tail, other.m_tail); + this->m_allocatorProvider.swap(other.m_allocatorProvider); +} + +template +inline void ListHashSet::finalize() { + COMPILE_ASSERT(!Allocator::isGarbageCollected, + FinalizeOnHeapAllocatedListHashSetShouldNeverBeCalled); + deleteAllNodes(); +} + +template +inline T& ListHashSet::first() { + ASSERT(!isEmpty()); + return m_head->m_value; +} + +template +inline void ListHashSet::removeFirst() { + ASSERT(!isEmpty()); + m_impl.remove(m_head); + unlinkAndDelete(m_head); +} + +template +inline const T& ListHashSet::first() const { + ASSERT(!isEmpty()); + return m_head->m_value; +} + +template +inline T& ListHashSet::last() { + ASSERT(!isEmpty()); + return m_tail->m_value; +} + +template +inline const T& ListHashSet::last() const { + ASSERT(!isEmpty()); + return m_tail->m_value; +} + +template +inline void ListHashSet::removeLast() { + ASSERT(!isEmpty()); + m_impl.remove(m_tail); + unlinkAndDelete(m_tail); +} + +template +inline typename ListHashSet::iterator +ListHashSet::find(ValuePeekInType value) { + ImplTypeIterator it = m_impl.template find(value); + if (it == m_impl.end()) + return end(); + return makeIterator(*it); +} + +template +inline typename ListHashSet::const_iterator +ListHashSet::find(ValuePeekInType value) const { + ImplTypeConstIterator it = m_impl.template find(value); + if (it == m_impl.end()) + return end(); + return makeConstIterator(*it); +} + +template +struct ListHashSetTranslatorAdapter { + template + static unsigned hash(const T& key) { + return Translator::hash(key); + } + template + static bool equal(const T& a, const U& b) { + return Translator::equal(a->m_value, b); + } +}; + +template +template +inline typename ListHashSet::iterator +ListHashSet::find(const T& value) { + ImplTypeConstIterator it = + m_impl.template find>(value); + if (it == m_impl.end()) + return end(); + return makeIterator(*it); +} + +template +template +inline typename ListHashSet::const_iterator +ListHashSet::find(const T& value) const { + ImplTypeConstIterator it = + m_impl.template find>(value); + if (it == m_impl.end()) + return end(); + return makeConstIterator(*it); +} + +template +template +inline bool ListHashSet::contains( + const T& value) const { + return m_impl.template contains>( + value); +} + +template +inline bool ListHashSet::contains( + ValuePeekInType value) const { + return m_impl.template contains(value); +} + +template +typename ListHashSet::AddResult +ListHashSet::add(ValuePassInType value) { + createAllocatorIfNeeded(); + // The second argument is a const ref. This is useful for the HashTable + // because it lets it take lvalues by reference, but for our purposes + // it's inconvenient, since it constrains us to be const, whereas the + // allocator actually changes when it does allocations. + typename ImplType::AddResult result = + m_impl.template add(value, *this->allocator()); + if (result.isNewEntry) + appendNode(*result.storedValue); + return AddResult(*result.storedValue, result.isNewEntry); +} + +template +typename ListHashSet::iterator +ListHashSet::addReturnIterator(ValuePassInType value) { + return makeIterator(add(value).storedValue); +} + +template +typename ListHashSet::AddResult +ListHashSet::appendOrMoveToLast( + ValuePassInType value) { + createAllocatorIfNeeded(); + typename ImplType::AddResult result = + m_impl.template add(value, *this->allocator()); + Node* node = *result.storedValue; + if (!result.isNewEntry) + unlink(node); + appendNode(node); + return AddResult(*result.storedValue, result.isNewEntry); +} + +template +typename ListHashSet::AddResult +ListHashSet::prependOrMoveToFirst( + ValuePassInType value) { + createAllocatorIfNeeded(); + typename ImplType::AddResult result = + m_impl.template add(value, *this->allocator()); + Node* node = *result.storedValue; + if (!result.isNewEntry) + unlink(node); + prependNode(node); + return AddResult(*result.storedValue, result.isNewEntry); +} + +template +typename ListHashSet::AddResult +ListHashSet::insertBefore(iterator it, + ValuePassInType newValue) { + createAllocatorIfNeeded(); + typename ImplType::AddResult result = + m_impl.template add(newValue, *this->allocator()); + if (result.isNewEntry) + insertNodeBefore(it.node(), *result.storedValue); + return AddResult(*result.storedValue, result.isNewEntry); +} + +template +typename ListHashSet::AddResult +ListHashSet::insertBefore(ValuePeekInType beforeValue, + ValuePassInType newValue) { + createAllocatorIfNeeded(); + return insertBefore(find(beforeValue), newValue); +} + +template +inline void ListHashSet::remove(iterator it) { + if (it == end()) + return; + m_impl.remove(it.node()); + unlinkAndDelete(it.node()); +} + +template +inline void ListHashSet::clear() { + deleteAllNodes(); + m_impl.clear(); + m_head = 0; + m_tail = 0; +} + +template +typename ListHashSet::ValuePassOutType +ListHashSet::take(iterator it) { + if (it == end()) + return ValueTraits::emptyValue(); + + m_impl.remove(it.node()); + ValuePassOutType result = ValueTraits::passOut(it.node()->m_value); + unlinkAndDelete(it.node()); + + return result; +} + +template +typename ListHashSet::ValuePassOutType +ListHashSet::take(ValuePeekInType value) { + return take(find(value)); +} + +template +typename ListHashSet::ValuePassOutType +ListHashSet::takeFirst() { + ASSERT(!isEmpty()); + m_impl.remove(m_head); + ValuePassOutType result = ValueTraits::passOut(m_head->m_value); + unlinkAndDelete(m_head); + + return result; +} + +template +void ListHashSet::unlink(Node* node) { + if (!node->m_prev) { + ASSERT(node == m_head); + m_head = node->next(); + } else { + ASSERT(node != m_head); + node->m_prev->m_next = node->m_next; + } + + if (!node->m_next) { + ASSERT(node == m_tail); + m_tail = node->prev(); + } else { + ASSERT(node != m_tail); + node->m_next->m_prev = node->m_prev; + } +} + +template +void ListHashSet::unlinkAndDelete(Node* node) { + unlink(node); + node->destroy(this->allocator()); +} + +template +void ListHashSet::appendNode(Node* node) { + node->m_prev = m_tail; + node->m_next = 0; + + if (m_tail) { + ASSERT(m_head); + m_tail->m_next = node; + } else { + ASSERT(!m_head); + m_head = node; + } + + m_tail = node; +} + +template +void ListHashSet::prependNode(Node* node) { + node->m_prev = 0; + node->m_next = m_head; + + if (m_head) + m_head->m_prev = node; + else + m_tail = node; + + m_head = node; +} + +template +void ListHashSet::insertNodeBefore(Node* beforeNode, + Node* newNode) { + if (!beforeNode) + return appendNode(newNode); + + newNode->m_next = beforeNode; + newNode->m_prev = beforeNode->m_prev; + if (beforeNode->m_prev) + beforeNode->m_prev->m_next = newNode; + beforeNode->m_prev = newNode; + + if (!newNode->m_prev) + m_head = newNode; +} + +template +void ListHashSet::deleteAllNodes() { + if (!m_head) + return; + + for (Node *node = m_head, *next = m_head->next(); node; + node = next, next = node ? node->next() : 0) + node->destroy(this->allocator()); +} + +} // namespace WTF using WTF::ListHashSet; diff --git a/sky/engine/wtf/ListHashSetTest.cpp b/sky/engine/wtf/ListHashSetTest.cpp index 351b72907bfe9..32809172845f4 100644 --- a/sky/engine/wtf/ListHashSetTest.cpp +++ b/sky/engine/wtf/ListHashSetTest.cpp @@ -23,7 +23,6 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ - #include #include "flutter/sky/engine/wtf/LinkedHashSet.h" #include "flutter/sky/engine/wtf/ListHashSet.h" @@ -33,688 +32,655 @@ namespace { -template -void removeFirstHelper() -{ - Set list; - list.add(-1); - list.add(0); - list.add(1); - list.add(2); - list.add(3); - - EXPECT_EQ(-1, list.first()); - EXPECT_EQ(3, list.last()); - - list.removeFirst(); - EXPECT_EQ(0, list.first()); - - list.removeLast(); - EXPECT_EQ(2, list.last()); - - list.removeFirst(); - EXPECT_EQ(1, list.first()); +template +void removeFirstHelper() { + Set list; + list.add(-1); + list.add(0); + list.add(1); + list.add(2); + list.add(3); - list.removeFirst(); - EXPECT_EQ(2, list.first()); + EXPECT_EQ(-1, list.first()); + EXPECT_EQ(3, list.last()); - list.removeFirst(); - EXPECT_TRUE(list.isEmpty()); -} + list.removeFirst(); + EXPECT_EQ(0, list.first()); -TEST(ListHashSetTest, RemoveFirst) -{ - removeFirstHelper >(); - removeFirstHelper >(); -} - -TEST(LinkedHashSetTest, RemoveFirst) -{ - removeFirstHelper >(); -} - -template -void appendOrMoveToLastNewItems() -{ - Set list; - typename Set::AddResult result = list.appendOrMoveToLast(1); - EXPECT_TRUE(result.isNewEntry); - result = list.add(2); - EXPECT_TRUE(result.isNewEntry); - result = list.appendOrMoveToLast(3); - EXPECT_TRUE(result.isNewEntry); - - EXPECT_EQ(list.size(), 3UL); - - // The list should be in order 1, 2, 3. - typename Set::iterator iterator = list.begin(); - EXPECT_EQ(1, *iterator); - ++iterator; - EXPECT_EQ(2, *iterator); - ++iterator; - EXPECT_EQ(3, *iterator); - ++iterator; -} - -TEST(ListHashSetTest, AppendOrMoveToLastNewItems) -{ - appendOrMoveToLastNewItems >(); - appendOrMoveToLastNewItems >(); -} - -TEST(LinkedHashSetTest, AppendOrMoveToLastNewItems) -{ - appendOrMoveToLastNewItems >(); -} - -template -void appendOrMoveToLastWithDuplicates() -{ - Set list; - - // Add a single element twice. - typename Set::AddResult result = list.add(1); - EXPECT_TRUE(result.isNewEntry); - result = list.appendOrMoveToLast(1); - EXPECT_FALSE(result.isNewEntry); - EXPECT_EQ(1UL, list.size()); - - list.add(2); - list.add(3); - EXPECT_EQ(3UL, list.size()); - - // Appending 2 move it to the end. - EXPECT_EQ(3, list.last()); - result = list.appendOrMoveToLast(2); - EXPECT_FALSE(result.isNewEntry); - EXPECT_EQ(2, list.last()); - - // Inverse the list by moving each element to end end. - result = list.appendOrMoveToLast(3); - EXPECT_FALSE(result.isNewEntry); - result = list.appendOrMoveToLast(2); - EXPECT_FALSE(result.isNewEntry); - result = list.appendOrMoveToLast(1); - EXPECT_FALSE(result.isNewEntry); - EXPECT_EQ(3UL, list.size()); - - typename Set::iterator iterator = list.begin(); - EXPECT_EQ(3, *iterator); - ++iterator; - EXPECT_EQ(2, *iterator); - ++iterator; - EXPECT_EQ(1, *iterator); - ++iterator; -} - -TEST(ListHashSetTest, AppendOrMoveToLastWithDuplicates) -{ - appendOrMoveToLastWithDuplicates >(); - appendOrMoveToLastWithDuplicates >(); -} - -TEST(LinkedHashSetTest, AppendOrMoveToLastWithDuplicates) -{ - appendOrMoveToLastWithDuplicates >(); -} - -template -void prependOrMoveToFirstNewItems() -{ - Set list; - typename Set::AddResult result = list.prependOrMoveToFirst(1); - EXPECT_TRUE(result.isNewEntry); - result = list.add(2); - EXPECT_TRUE(result.isNewEntry); - result = list.prependOrMoveToFirst(3); - EXPECT_TRUE(result.isNewEntry); - - EXPECT_EQ(list.size(), 3UL); - - // The list should be in order 3, 1, 2. - typename Set::iterator iterator = list.begin(); - EXPECT_EQ(3, *iterator); - ++iterator; - EXPECT_EQ(1, *iterator); - ++iterator; - EXPECT_EQ(2, *iterator); - ++iterator; -} - -TEST(ListHashSetTest, PrependOrMoveToFirstNewItems) -{ - prependOrMoveToFirstNewItems >(); - prependOrMoveToFirstNewItems >(); -} - -TEST(LinkedHashSetTest, PrependOrMoveToFirstNewItems) -{ - prependOrMoveToFirstNewItems >(); -} - -template -void prependOrMoveToLastWithDuplicates() -{ - Set list; - - // Add a single element twice. - typename Set::AddResult result = list.add(1); - EXPECT_TRUE(result.isNewEntry); - result = list.prependOrMoveToFirst(1); - EXPECT_FALSE(result.isNewEntry); - EXPECT_EQ(1UL, list.size()); - - list.add(2); - list.add(3); - EXPECT_EQ(3UL, list.size()); - - // Prepending 2 move it to the beginning. - EXPECT_EQ(1, list.first()); - result = list.prependOrMoveToFirst(2); - EXPECT_FALSE(result.isNewEntry); - EXPECT_EQ(2, list.first()); - - // Inverse the list by moving each element to the first position. - result = list.prependOrMoveToFirst(1); - EXPECT_FALSE(result.isNewEntry); - result = list.prependOrMoveToFirst(2); - EXPECT_FALSE(result.isNewEntry); - result = list.prependOrMoveToFirst(3); - EXPECT_FALSE(result.isNewEntry); - EXPECT_EQ(3UL, list.size()); - - typename Set::iterator iterator = list.begin(); - EXPECT_EQ(3, *iterator); - ++iterator; - EXPECT_EQ(2, *iterator); - ++iterator; - EXPECT_EQ(1, *iterator); - ++iterator; -} - -TEST(ListHashSetTest, PrependOrMoveToLastWithDuplicates) -{ - prependOrMoveToLastWithDuplicates >(); - prependOrMoveToLastWithDuplicates >(); -} - -TEST(LinkedHashSetTest, PrependOrMoveToLastWithDuplicates) -{ - prependOrMoveToLastWithDuplicates >(); -} - -class DummyRefCounted: public WTF::RefCounted { -public: - DummyRefCounted(bool& isDeleted) : m_isDeleted(isDeleted) { m_isDeleted = false; } - ~DummyRefCounted() { m_isDeleted = true; } - void ref() - { - WTF::RefCounted::ref(); - ++m_refInvokesCount; - } - - static int m_refInvokesCount; - -private: - bool& m_isDeleted; -}; + list.removeLast(); + EXPECT_EQ(2, list.last()); -int DummyRefCounted::m_refInvokesCount = 0; + list.removeFirst(); + EXPECT_EQ(1, list.first()); -template -void withRefPtr() -{ - bool isDeleted = false; - DummyRefCounted::m_refInvokesCount = 0; - RefPtr ptr = adoptRef(new DummyRefCounted(isDeleted)); - EXPECT_EQ(0, DummyRefCounted::m_refInvokesCount); + list.removeFirst(); + EXPECT_EQ(2, list.first()); - Set set; - set.add(ptr); - // Referenced only once (to store a copy in the container). - EXPECT_EQ(1, DummyRefCounted::m_refInvokesCount); - EXPECT_EQ(ptr, set.first()); - EXPECT_EQ(1, DummyRefCounted::m_refInvokesCount); + list.removeFirst(); + EXPECT_TRUE(list.isEmpty()); +} - DummyRefCounted* rawPtr = ptr.get(); +TEST(ListHashSetTest, RemoveFirst) { + removeFirstHelper>(); + removeFirstHelper>(); +} - EXPECT_TRUE(set.contains(ptr)); - EXPECT_TRUE(set.contains(rawPtr)); - EXPECT_EQ(1, DummyRefCounted::m_refInvokesCount); +TEST(LinkedHashSetTest, RemoveFirst) { + removeFirstHelper>(); +} - ptr.clear(); - EXPECT_FALSE(isDeleted); - EXPECT_EQ(1, DummyRefCounted::m_refInvokesCount); +template +void appendOrMoveToLastNewItems() { + Set list; + typename Set::AddResult result = list.appendOrMoveToLast(1); + EXPECT_TRUE(result.isNewEntry); + result = list.add(2); + EXPECT_TRUE(result.isNewEntry); + result = list.appendOrMoveToLast(3); + EXPECT_TRUE(result.isNewEntry); - set.remove(rawPtr); - EXPECT_TRUE(isDeleted); + EXPECT_EQ(list.size(), 3UL); - EXPECT_EQ(1, DummyRefCounted::m_refInvokesCount); + // The list should be in order 1, 2, 3. + typename Set::iterator iterator = list.begin(); + EXPECT_EQ(1, *iterator); + ++iterator; + EXPECT_EQ(2, *iterator); + ++iterator; + EXPECT_EQ(3, *iterator); + ++iterator; } -TEST(ListHashSetTest, WithRefPtr) -{ - withRefPtr > >(); - withRefPtr, 1> >(); +TEST(ListHashSetTest, AppendOrMoveToLastNewItems) { + appendOrMoveToLastNewItems>(); + appendOrMoveToLastNewItems>(); } -TEST(LinkedHashSetTest, WithRefPtr) -{ - withRefPtr > >(); +TEST(LinkedHashSetTest, AppendOrMoveToLastNewItems) { + appendOrMoveToLastNewItems>(); } -template -void findHelper() -{ - Set set; - set.add(-1); - set.add(0); - set.add(1); - set.add(2); - set.add(3); +template +void appendOrMoveToLastWithDuplicates() { + Set list; - SetRef ref = set; - Iterator it = ref.find(2); - EXPECT_EQ(2, *it); - ++it; - EXPECT_EQ(3, *it); - --it; - --it; - EXPECT_EQ(1, *it); + // Add a single element twice. + typename Set::AddResult result = list.add(1); + EXPECT_TRUE(result.isNewEntry); + result = list.appendOrMoveToLast(1); + EXPECT_FALSE(result.isNewEntry); + EXPECT_EQ(1UL, list.size()); + + list.add(2); + list.add(3); + EXPECT_EQ(3UL, list.size()); + + // Appending 2 move it to the end. + EXPECT_EQ(3, list.last()); + result = list.appendOrMoveToLast(2); + EXPECT_FALSE(result.isNewEntry); + EXPECT_EQ(2, list.last()); + + // Inverse the list by moving each element to end end. + result = list.appendOrMoveToLast(3); + EXPECT_FALSE(result.isNewEntry); + result = list.appendOrMoveToLast(2); + EXPECT_FALSE(result.isNewEntry); + result = list.appendOrMoveToLast(1); + EXPECT_FALSE(result.isNewEntry); + EXPECT_EQ(3UL, list.size()); + + typename Set::iterator iterator = list.begin(); + EXPECT_EQ(3, *iterator); + ++iterator; + EXPECT_EQ(2, *iterator); + ++iterator; + EXPECT_EQ(1, *iterator); + ++iterator; } -TEST(ListHashSetTest, Find) -{ - findHelper, const ListHashSet&, ListHashSet::const_iterator>(); - findHelper, ListHashSet&, ListHashSet::iterator>(); - findHelper, const ListHashSet&, ListHashSet::const_iterator>(); - findHelper, ListHashSet&, ListHashSet::iterator>(); +TEST(ListHashSetTest, AppendOrMoveToLastWithDuplicates) { + appendOrMoveToLastWithDuplicates>(); + appendOrMoveToLastWithDuplicates>(); } -TEST(LinkedHashSetTest, Find) -{ - findHelper, const LinkedHashSet&, LinkedHashSet::const_iterator>(); - findHelper, LinkedHashSet&, LinkedHashSet::iterator>(); +TEST(LinkedHashSetTest, AppendOrMoveToLastWithDuplicates) { + appendOrMoveToLastWithDuplicates>(); } -template -void insertBeforeHelper(bool canModifyWhileIterating) -{ - Set set; - set.add(-1); - set.add(0); - set.add(2); - set.add(3); +template +void prependOrMoveToFirstNewItems() { + Set list; + typename Set::AddResult result = list.prependOrMoveToFirst(1); + EXPECT_TRUE(result.isNewEntry); + result = list.add(2); + EXPECT_TRUE(result.isNewEntry); + result = list.prependOrMoveToFirst(3); + EXPECT_TRUE(result.isNewEntry); - typename Set::iterator it = set.find(2); - EXPECT_EQ(2, *it); - set.insertBefore(it, 1); - if (!canModifyWhileIterating) - it = set.find(2); + EXPECT_EQ(list.size(), 3UL); + + // The list should be in order 3, 1, 2. + typename Set::iterator iterator = list.begin(); + EXPECT_EQ(3, *iterator); + ++iterator; + EXPECT_EQ(1, *iterator); + ++iterator; + EXPECT_EQ(2, *iterator); + ++iterator; +} + +TEST(ListHashSetTest, PrependOrMoveToFirstNewItems) { + prependOrMoveToFirstNewItems>(); + prependOrMoveToFirstNewItems>(); +} + +TEST(LinkedHashSetTest, PrependOrMoveToFirstNewItems) { + prependOrMoveToFirstNewItems>(); +} + +template +void prependOrMoveToLastWithDuplicates() { + Set list; + + // Add a single element twice. + typename Set::AddResult result = list.add(1); + EXPECT_TRUE(result.isNewEntry); + result = list.prependOrMoveToFirst(1); + EXPECT_FALSE(result.isNewEntry); + EXPECT_EQ(1UL, list.size()); + + list.add(2); + list.add(3); + EXPECT_EQ(3UL, list.size()); + + // Prepending 2 move it to the beginning. + EXPECT_EQ(1, list.first()); + result = list.prependOrMoveToFirst(2); + EXPECT_FALSE(result.isNewEntry); + EXPECT_EQ(2, list.first()); + + // Inverse the list by moving each element to the first position. + result = list.prependOrMoveToFirst(1); + EXPECT_FALSE(result.isNewEntry); + result = list.prependOrMoveToFirst(2); + EXPECT_FALSE(result.isNewEntry); + result = list.prependOrMoveToFirst(3); + EXPECT_FALSE(result.isNewEntry); + EXPECT_EQ(3UL, list.size()); + + typename Set::iterator iterator = list.begin(); + EXPECT_EQ(3, *iterator); + ++iterator; + EXPECT_EQ(2, *iterator); + ++iterator; + EXPECT_EQ(1, *iterator); + ++iterator; +} + +TEST(ListHashSetTest, PrependOrMoveToLastWithDuplicates) { + prependOrMoveToLastWithDuplicates>(); + prependOrMoveToLastWithDuplicates>(); +} + +TEST(LinkedHashSetTest, PrependOrMoveToLastWithDuplicates) { + prependOrMoveToLastWithDuplicates>(); +} + +class DummyRefCounted : public WTF::RefCounted { + public: + DummyRefCounted(bool& isDeleted) : m_isDeleted(isDeleted) { + m_isDeleted = false; + } + ~DummyRefCounted() { m_isDeleted = true; } + void ref() { + WTF::RefCounted::ref(); + ++m_refInvokesCount; + } + + static int m_refInvokesCount; + + private: + bool& m_isDeleted; +}; + +int DummyRefCounted::m_refInvokesCount = 0; + +template +void withRefPtr() { + bool isDeleted = false; + DummyRefCounted::m_refInvokesCount = 0; + RefPtr ptr = adoptRef(new DummyRefCounted(isDeleted)); + EXPECT_EQ(0, DummyRefCounted::m_refInvokesCount); + + Set set; + set.add(ptr); + // Referenced only once (to store a copy in the container). + EXPECT_EQ(1, DummyRefCounted::m_refInvokesCount); + EXPECT_EQ(ptr, set.first()); + EXPECT_EQ(1, DummyRefCounted::m_refInvokesCount); + + DummyRefCounted* rawPtr = ptr.get(); + + EXPECT_TRUE(set.contains(ptr)); + EXPECT_TRUE(set.contains(rawPtr)); + EXPECT_EQ(1, DummyRefCounted::m_refInvokesCount); + + ptr.clear(); + EXPECT_FALSE(isDeleted); + EXPECT_EQ(1, DummyRefCounted::m_refInvokesCount); + + set.remove(rawPtr); + EXPECT_TRUE(isDeleted); + + EXPECT_EQ(1, DummyRefCounted::m_refInvokesCount); +} + +TEST(ListHashSetTest, WithRefPtr) { + withRefPtr>>(); + withRefPtr, 1>>(); +} + +TEST(LinkedHashSetTest, WithRefPtr) { + withRefPtr>>(); +} + +template +void findHelper() { + Set set; + set.add(-1); + set.add(0); + set.add(1); + set.add(2); + set.add(3); + + SetRef ref = set; + Iterator it = ref.find(2); + EXPECT_EQ(2, *it); + ++it; + EXPECT_EQ(3, *it); + --it; + --it; + EXPECT_EQ(1, *it); +} + +TEST(ListHashSetTest, Find) { + findHelper, const ListHashSet&, + ListHashSet::const_iterator>(); + findHelper, ListHashSet&, ListHashSet::iterator>(); + findHelper, const ListHashSet&, + ListHashSet::const_iterator>(); + findHelper, ListHashSet&, + ListHashSet::iterator>(); +} + +TEST(LinkedHashSetTest, Find) { + findHelper, const LinkedHashSet&, + LinkedHashSet::const_iterator>(); + findHelper, LinkedHashSet&, + LinkedHashSet::iterator>(); +} + +template +void insertBeforeHelper(bool canModifyWhileIterating) { + Set set; + set.add(-1); + set.add(0); + set.add(2); + set.add(3); + + typename Set::iterator it = set.find(2); + EXPECT_EQ(2, *it); + set.insertBefore(it, 1); + if (!canModifyWhileIterating) + it = set.find(2); + ++it; + EXPECT_EQ(3, *it); + EXPECT_EQ(5u, set.size()); + --it; + --it; + EXPECT_EQ(1, *it); + if (canModifyWhileIterating) { + set.remove(-1); + set.remove(0); + set.remove(2); + set.remove(3); + EXPECT_EQ(1u, set.size()); + EXPECT_EQ(1, *it); ++it; - EXPECT_EQ(3, *it); - EXPECT_EQ(5u, set.size()); - --it; + EXPECT_EQ(it, set.end()); --it; EXPECT_EQ(1, *it); - if (canModifyWhileIterating) { - set.remove(-1); - set.remove(0); - set.remove(2); - set.remove(3); - EXPECT_EQ(1u, set.size()); - EXPECT_EQ(1, *it); - ++it; - EXPECT_EQ(it, set.end()); - --it; - EXPECT_EQ(1, *it); - set.insertBefore(it, -1); - set.insertBefore(it, 0); - set.add(2); - set.add(3); - } - set.insertBefore(2, 42); - set.insertBefore(-1, 103); - EXPECT_EQ(103, set.first()); - if (!canModifyWhileIterating) - it = set.find(1); - ++it; - EXPECT_EQ(42, *it); - EXPECT_EQ(7u, set.size()); + set.insertBefore(it, -1); + set.insertBefore(it, 0); + set.add(2); + set.add(3); + } + set.insertBefore(2, 42); + set.insertBefore(-1, 103); + EXPECT_EQ(103, set.first()); + if (!canModifyWhileIterating) + it = set.find(1); + ++it; + EXPECT_EQ(42, *it); + EXPECT_EQ(7u, set.size()); +} + +TEST(ListHashSetTest, InsertBefore) { + insertBeforeHelper>(true); + insertBeforeHelper>(true); +} + +TEST(LinkedHashSetTest, InsertBefore) { + insertBeforeHelper>(false); +} + +template +void addReturnIterator(bool canModifyWhileIterating) { + Set set; + set.add(-1); + set.add(0); + set.add(1); + set.add(2); + + typename Set::iterator it = set.addReturnIterator(3); + EXPECT_EQ(3, *it); + --it; + EXPECT_EQ(2, *it); + EXPECT_EQ(5u, set.size()); + --it; + EXPECT_EQ(1, *it); + --it; + EXPECT_EQ(0, *it); + it = set.addReturnIterator(4); + if (canModifyWhileIterating) { + set.remove(3); + set.remove(2); + set.remove(1); + set.remove(0); + set.remove(-1); + EXPECT_EQ(1u, set.size()); + } + EXPECT_EQ(4, *it); + ++it; + EXPECT_EQ(it, set.end()); + --it; + EXPECT_EQ(4, *it); + if (canModifyWhileIterating) { + set.insertBefore(it, -1); + set.insertBefore(it, 0); + set.insertBefore(it, 1); + set.insertBefore(it, 2); + set.insertBefore(it, 3); + } + EXPECT_EQ(6u, set.size()); + it = set.addReturnIterator(5); + EXPECT_EQ(7u, set.size()); + set.remove(it); + EXPECT_EQ(6u, set.size()); + EXPECT_EQ(4, set.last()); } -TEST(ListHashSetTest, InsertBefore) -{ - insertBeforeHelper >(true); - insertBeforeHelper >(true); +TEST(ListHashSetTest, AddReturnIterator) { + addReturnIterator>(true); + addReturnIterator>(true); } -TEST(LinkedHashSetTest, InsertBefore) -{ - insertBeforeHelper >(false); +TEST(LinkedHashSetTest, AddReturnIterator) { + addReturnIterator>(false); } -template -void addReturnIterator(bool canModifyWhileIterating) -{ - Set set; - set.add(-1); - set.add(0); - set.add(1); - set.add(2); +template +void excerciseValuePeekInType() { + Set set; + bool isDeleted = false; + bool isDeleted2 = false; - typename Set::iterator it = set.addReturnIterator(3); - EXPECT_EQ(3, *it); - --it; - EXPECT_EQ(2, *it); - EXPECT_EQ(5u, set.size()); - --it; - EXPECT_EQ(1, *it); - --it; - EXPECT_EQ(0, *it); - it = set.addReturnIterator(4); - if (canModifyWhileIterating) { - set.remove(3); - set.remove(2); - set.remove(1); - set.remove(0); - set.remove(-1); - EXPECT_EQ(1u, set.size()); - } - EXPECT_EQ(4, *it); - ++it; - EXPECT_EQ(it, set.end()); - --it; - EXPECT_EQ(4, *it); - if (canModifyWhileIterating) { - set.insertBefore(it, -1); - set.insertBefore(it, 0); - set.insertBefore(it, 1); - set.insertBefore(it, 2); - set.insertBefore(it, 3); - } - EXPECT_EQ(6u, set.size()); - it = set.addReturnIterator(5); - EXPECT_EQ(7u, set.size()); - set.remove(it); - EXPECT_EQ(6u, set.size()); - EXPECT_EQ(4, set.last()); -} - -TEST(ListHashSetTest, AddReturnIterator) -{ - addReturnIterator >(true); - addReturnIterator >(true); -} - -TEST(LinkedHashSetTest, AddReturnIterator) -{ - addReturnIterator >(false); -} - -template -void excerciseValuePeekInType() -{ - Set set; - bool isDeleted = false; - bool isDeleted2 = false; - - RefPtr ptr = adoptRef(new DummyRefCounted(isDeleted)); - RefPtr ptr2 = adoptRef(new DummyRefCounted(isDeleted2)); - - typename Set::AddResult addResult = set.add(ptr); - EXPECT_TRUE(addResult.isNewEntry); - set.find(ptr); - const Set& constSet(set); - constSet.find(ptr); - EXPECT_TRUE(set.contains(ptr)); - typename Set::iterator it = set.addReturnIterator(ptr); - set.appendOrMoveToLast(ptr); - set.prependOrMoveToFirst(ptr); - set.insertBefore(ptr, ptr); - set.insertBefore(it, ptr); - EXPECT_EQ(1u, set.size()); - set.add(ptr2); - ptr2.clear(); - set.remove(ptr); + RefPtr ptr = adoptRef(new DummyRefCounted(isDeleted)); + RefPtr ptr2 = adoptRef(new DummyRefCounted(isDeleted2)); + + typename Set::AddResult addResult = set.add(ptr); + EXPECT_TRUE(addResult.isNewEntry); + set.find(ptr); + const Set& constSet(set); + constSet.find(ptr); + EXPECT_TRUE(set.contains(ptr)); + typename Set::iterator it = set.addReturnIterator(ptr); + set.appendOrMoveToLast(ptr); + set.prependOrMoveToFirst(ptr); + set.insertBefore(ptr, ptr); + set.insertBefore(it, ptr); + EXPECT_EQ(1u, set.size()); + set.add(ptr2); + ptr2.clear(); + set.remove(ptr); - EXPECT_FALSE(isDeleted); - ptr.clear(); - EXPECT_TRUE(isDeleted); + EXPECT_FALSE(isDeleted); + ptr.clear(); + EXPECT_TRUE(isDeleted); - EXPECT_FALSE(isDeleted2); - set.removeFirst(); - EXPECT_TRUE(isDeleted2); + EXPECT_FALSE(isDeleted2); + set.removeFirst(); + EXPECT_TRUE(isDeleted2); - EXPECT_EQ(0u, set.size()); + EXPECT_EQ(0u, set.size()); } -TEST(ListHashSetTest, ExcerciseValuePeekInType) -{ - excerciseValuePeekInType > >(); - excerciseValuePeekInType, 1> >(); +TEST(ListHashSetTest, ExcerciseValuePeekInType) { + excerciseValuePeekInType>>(); + excerciseValuePeekInType, 1>>(); } -TEST(LinkedHashSetTest, ExcerciseValuePeekInType) -{ - excerciseValuePeekInType > >(); +TEST(LinkedHashSetTest, ExcerciseValuePeekInType) { + excerciseValuePeekInType>>(); } struct Simple { - Simple(int value) : m_value(value) { }; - int m_value; + Simple(int value) : m_value(value){}; + int m_value; }; struct Complicated { - Complicated(int value) : m_simple(value) - { - s_objectsConstructed++; - } + Complicated(int value) : m_simple(value) { s_objectsConstructed++; } - Complicated(const Complicated& other) : m_simple(other.m_simple) - { - s_objectsConstructed++; - } + Complicated(const Complicated& other) : m_simple(other.m_simple) { + s_objectsConstructed++; + } - Simple m_simple; - static int s_objectsConstructed; + Simple m_simple; + static int s_objectsConstructed; -private: - Complicated(); + private: + Complicated(); }; int Complicated::s_objectsConstructed = 0; struct ComplicatedHashFunctions { - static unsigned hash(const Complicated& key) { return key.m_simple.m_value; } - static bool equal(const Complicated& a, const Complicated& b) { return a.m_simple.m_value == b.m_simple.m_value; } + static unsigned hash(const Complicated& key) { return key.m_simple.m_value; } + static bool equal(const Complicated& a, const Complicated& b) { + return a.m_simple.m_value == b.m_simple.m_value; + } }; struct ComplexityTranslator { - static unsigned hash(const Simple& key) { return key.m_value; } - static bool equal(const Complicated& a, const Simple& b) { return a.m_simple.m_value == b.m_value; } + static unsigned hash(const Simple& key) { return key.m_value; } + static bool equal(const Complicated& a, const Simple& b) { + return a.m_simple.m_value == b.m_value; + } }; -template -void translatorTest() -{ - Set set; - set.add(Complicated(42)); - int baseLine = Complicated::s_objectsConstructed; +template +void translatorTest() { + Set set; + set.add(Complicated(42)); + int baseLine = Complicated::s_objectsConstructed; - typename Set::iterator it = set.template find(Simple(42)); - EXPECT_NE(it, set.end()); - EXPECT_EQ(baseLine, Complicated::s_objectsConstructed); + typename Set::iterator it = + set.template find(Simple(42)); + EXPECT_NE(it, set.end()); + EXPECT_EQ(baseLine, Complicated::s_objectsConstructed); - it = set.template find(Simple(103)); - EXPECT_EQ(it, set.end()); - EXPECT_EQ(baseLine, Complicated::s_objectsConstructed); + it = set.template find(Simple(103)); + EXPECT_EQ(it, set.end()); + EXPECT_EQ(baseLine, Complicated::s_objectsConstructed); - const Set& constSet(set); + const Set& constSet(set); - typename Set::const_iterator constIterator = constSet.template find(Simple(42)); - EXPECT_NE(constIterator, constSet.end()); - EXPECT_EQ(baseLine, Complicated::s_objectsConstructed); + typename Set::const_iterator constIterator = + constSet.template find(Simple(42)); + EXPECT_NE(constIterator, constSet.end()); + EXPECT_EQ(baseLine, Complicated::s_objectsConstructed); - constIterator = constSet.template find(Simple(103)); - EXPECT_EQ(constIterator, constSet.end()); - EXPECT_EQ(baseLine, Complicated::s_objectsConstructed); + constIterator = constSet.template find(Simple(103)); + EXPECT_EQ(constIterator, constSet.end()); + EXPECT_EQ(baseLine, Complicated::s_objectsConstructed); } -TEST(ListHashSetTest, ComplexityTranslator) -{ - translatorTest >(); - translatorTest >(); +TEST(ListHashSetTest, ComplexityTranslator) { + translatorTest>(); + translatorTest>(); } -TEST(LinkedHashSetTest, ComplexityTranslator) -{ - translatorTest >(); +TEST(LinkedHashSetTest, ComplexityTranslator) { + translatorTest>(); } struct Dummy { - Dummy(bool& deleted) : deleted(deleted) { } + Dummy(bool& deleted) : deleted(deleted) {} - ~Dummy() - { - deleted = true; - } + ~Dummy() { deleted = true; } - bool& deleted; + bool& deleted; }; -TEST(ListHashSetTest, WithOwnPtr) -{ - bool deleted1 = false, deleted2 = false; - - typedef ListHashSet > OwnPtrSet; +TEST(ListHashSetTest, WithOwnPtr) { + bool deleted1 = false, deleted2 = false; + + typedef ListHashSet> OwnPtrSet; + OwnPtrSet set; + + Dummy* ptr1 = new Dummy(deleted1); + { + // AddResult in a separate scope to avoid assertion hit, + // since we modify the container further. + OwnPtrSet::AddResult res1 = set.add(adoptPtr(ptr1)); + EXPECT_EQ(res1.storedValue->m_value.get(), ptr1); + } + + EXPECT_FALSE(deleted1); + EXPECT_EQ(1UL, set.size()); + OwnPtrSet::iterator it1 = set.find(ptr1); + EXPECT_NE(set.end(), it1); + EXPECT_EQ(ptr1, (*it1)); + + Dummy* ptr2 = new Dummy(deleted2); + { + OwnPtrSet::AddResult res2 = set.add(adoptPtr(ptr2)); + EXPECT_EQ(res2.storedValue->m_value.get(), ptr2); + } + + EXPECT_FALSE(deleted2); + EXPECT_EQ(2UL, set.size()); + OwnPtrSet::iterator it2 = set.find(ptr2); + EXPECT_NE(set.end(), it2); + EXPECT_EQ(ptr2, (*it2)); + + set.remove(ptr1); + EXPECT_TRUE(deleted1); + + set.clear(); + EXPECT_TRUE(deleted2); + EXPECT_TRUE(set.isEmpty()); + + deleted1 = false; + deleted2 = false; + { OwnPtrSet set; - - Dummy* ptr1 = new Dummy(deleted1); - { - // AddResult in a separate scope to avoid assertion hit, - // since we modify the container further. - OwnPtrSet::AddResult res1 = set.add(adoptPtr(ptr1)); - EXPECT_EQ(res1.storedValue->m_value.get(), ptr1); - } - - EXPECT_FALSE(deleted1); + set.add(adoptPtr(new Dummy(deleted1))); + set.add(adoptPtr(new Dummy(deleted2))); + } + EXPECT_TRUE(deleted1); + EXPECT_TRUE(deleted2); + + deleted1 = false; + deleted2 = false; + OwnPtr ownPtr1; + OwnPtr ownPtr2; + ptr1 = new Dummy(deleted1); + ptr2 = new Dummy(deleted2); + { + OwnPtrSet set; + set.add(adoptPtr(ptr1)); + set.add(adoptPtr(ptr2)); + ownPtr1 = set.takeFirst(); EXPECT_EQ(1UL, set.size()); - OwnPtrSet::iterator it1 = set.find(ptr1); - EXPECT_NE(set.end(), it1); - EXPECT_EQ(ptr1, (*it1)); - - Dummy* ptr2 = new Dummy(deleted2); - { - OwnPtrSet::AddResult res2 = set.add(adoptPtr(ptr2)); - EXPECT_EQ(res2.storedValue->m_value.get(), ptr2); - } - - EXPECT_FALSE(deleted2); - EXPECT_EQ(2UL, set.size()); - OwnPtrSet::iterator it2 = set.find(ptr2); - EXPECT_NE(set.end(), it2); - EXPECT_EQ(ptr2, (*it2)); - - set.remove(ptr1); - EXPECT_TRUE(deleted1); - - set.clear(); - EXPECT_TRUE(deleted2); + ownPtr2 = set.take(ptr2); EXPECT_TRUE(set.isEmpty()); - - deleted1 = false; - deleted2 = false; - { - OwnPtrSet set; - set.add(adoptPtr(new Dummy(deleted1))); - set.add(adoptPtr(new Dummy(deleted2))); - } - EXPECT_TRUE(deleted1); - EXPECT_TRUE(deleted2); - - - deleted1 = false; - deleted2 = false; - OwnPtr ownPtr1; - OwnPtr ownPtr2; - ptr1 = new Dummy(deleted1); - ptr2 = new Dummy(deleted2); - { - OwnPtrSet set; - set.add(adoptPtr(ptr1)); - set.add(adoptPtr(ptr2)); - ownPtr1 = set.takeFirst(); - EXPECT_EQ(1UL, set.size()); - ownPtr2 = set.take(ptr2); - EXPECT_TRUE(set.isEmpty()); - } - EXPECT_FALSE(deleted1); - EXPECT_FALSE(deleted2); - - EXPECT_EQ(ptr1, ownPtr1); - EXPECT_EQ(ptr2, ownPtr2); -} - -template -void swapTestHelper() -{ - int num = 10; - Set set0; - Set set1; - Set set2; - for (int i = 0; i < num; ++i) { - set1.add(i + 1); - set2.add(num - i); - } - - typename Set::iterator it1 = set1.begin(); - typename Set::iterator it2 = set2.begin(); - for (int i = 0; i < num; ++i, ++it1, ++it2) { - EXPECT_EQ(*it1, i + 1); - EXPECT_EQ(*it2, num - i); - } - EXPECT_EQ(set0.begin(), set0.end()); - EXPECT_EQ(it1, set1.end()); - EXPECT_EQ(it2, set2.end()); - - // Shift sets: 2->1, 1->0, 0->2 - set1.swap(set2); // Swap with non-empty sets. - set0.swap(set2); // Swap with an empty set. - - it1 = set0.begin(); - it2 = set1.begin(); - for (int i = 0; i < num; ++i, ++it1, ++it2) { - EXPECT_EQ(*it1, i + 1); - EXPECT_EQ(*it2, num - i); - } - EXPECT_EQ(it1, set0.end()); - EXPECT_EQ(it2, set1.end()); - EXPECT_EQ(set2.begin(), set2.end()); - - int removedIndex = num >> 1; - set0.remove(removedIndex + 1); - set1.remove(num - removedIndex); - - it1 = set0.begin(); - it2 = set1.begin(); - for (int i = 0; i < num; ++i, ++it1, ++it2) { - if (i == removedIndex) - ++i; - EXPECT_EQ(*it1, i + 1); - EXPECT_EQ(*it2, num - i); - } - EXPECT_EQ(it1, set0.end()); - EXPECT_EQ(it2, set1.end()); -} - -TEST(ListHashSetTest, Swap) -{ - swapTestHelper >(); -} - -TEST(LinkedHashSetTest, Swap) -{ - swapTestHelper >(); -} - -} // namespace + } + EXPECT_FALSE(deleted1); + EXPECT_FALSE(deleted2); + + EXPECT_EQ(ptr1, ownPtr1); + EXPECT_EQ(ptr2, ownPtr2); +} + +template +void swapTestHelper() { + int num = 10; + Set set0; + Set set1; + Set set2; + for (int i = 0; i < num; ++i) { + set1.add(i + 1); + set2.add(num - i); + } + + typename Set::iterator it1 = set1.begin(); + typename Set::iterator it2 = set2.begin(); + for (int i = 0; i < num; ++i, ++it1, ++it2) { + EXPECT_EQ(*it1, i + 1); + EXPECT_EQ(*it2, num - i); + } + EXPECT_EQ(set0.begin(), set0.end()); + EXPECT_EQ(it1, set1.end()); + EXPECT_EQ(it2, set2.end()); + + // Shift sets: 2->1, 1->0, 0->2 + set1.swap(set2); // Swap with non-empty sets. + set0.swap(set2); // Swap with an empty set. + + it1 = set0.begin(); + it2 = set1.begin(); + for (int i = 0; i < num; ++i, ++it1, ++it2) { + EXPECT_EQ(*it1, i + 1); + EXPECT_EQ(*it2, num - i); + } + EXPECT_EQ(it1, set0.end()); + EXPECT_EQ(it2, set1.end()); + EXPECT_EQ(set2.begin(), set2.end()); + + int removedIndex = num >> 1; + set0.remove(removedIndex + 1); + set1.remove(num - removedIndex); + + it1 = set0.begin(); + it2 = set1.begin(); + for (int i = 0; i < num; ++i, ++it1, ++it2) { + if (i == removedIndex) + ++i; + EXPECT_EQ(*it1, i + 1); + EXPECT_EQ(*it2, num - i); + } + EXPECT_EQ(it1, set0.end()); + EXPECT_EQ(it2, set1.end()); +} + +TEST(ListHashSetTest, Swap) { + swapTestHelper>(); +} + +TEST(LinkedHashSetTest, Swap) { + swapTestHelper>(); +} + +} // namespace diff --git a/sky/engine/wtf/Locker.h b/sky/engine/wtf/Locker.h index be48b6882e04e..d601da3f565f1 100644 --- a/sky/engine/wtf/Locker.h +++ b/sky/engine/wtf/Locker.h @@ -32,16 +32,19 @@ namespace WTF { -template class Locker { - WTF_MAKE_NONCOPYABLE(Locker); -public: - Locker(T& lockable) : m_lockable(lockable) { m_lockable.lock(); } - ~Locker() { m_lockable.unlock(); } -private: - T& m_lockable; +template +class Locker { + WTF_MAKE_NONCOPYABLE(Locker); + + public: + Locker(T& lockable) : m_lockable(lockable) { m_lockable.lock(); } + ~Locker() { m_lockable.unlock(); } + + private: + T& m_lockable; }; -} +} // namespace WTF using WTF::Locker; diff --git a/sky/engine/wtf/MainThread.cpp b/sky/engine/wtf/MainThread.cpp index 5f00faf9d083e..c35e15ab1e4ae 100644 --- a/sky/engine/wtf/MainThread.cpp +++ b/sky/engine/wtf/MainThread.cpp @@ -1,32 +1,32 @@ /* -* Copyright (C) 2009 Google Inc. All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are -* met: -* -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above -* copyright notice, this list of conditions and the following disclaimer -* in the documentation and/or other materials provided with the -* distribution. -* * Neither the name of Google Inc. nor the names of its -* contributors may be used to endorse or promote products derived from -* this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ #include "flutter/sky/engine/wtf/MainThread.h" @@ -38,21 +38,18 @@ namespace WTF { static ThreadIdentifier mainThreadIdentifier; -void initializeMainThread() -{ - static bool initializedMainThread; - if (initializedMainThread) - return; - initializedMainThread = true; +void initializeMainThread() { + static bool initializedMainThread; + if (initializedMainThread) + return; + initializedMainThread = true; - mainThreadIdentifier = currentThread(); - AtomicString::init(); + mainThreadIdentifier = currentThread(); + AtomicString::init(); } -bool isMainThread() -{ - return currentThread() == mainThreadIdentifier; +bool isMainThread() { + return currentThread() == mainThreadIdentifier; } -} // namespace WTF - +} // namespace WTF diff --git a/sky/engine/wtf/MainThread.h b/sky/engine/wtf/MainThread.h index 4a38c5b26be88..f87734278e411 100644 --- a/sky/engine/wtf/MainThread.h +++ b/sky/engine/wtf/MainThread.h @@ -43,7 +43,7 @@ WTF_EXPORT void initializeMainThread(); WTF_EXPORT bool isMainThread(); -} // namespace WTF +} // namespace WTF using WTF::isMainThread; #endif // SKY_ENGINE_WTF_MAINTHREAD_H_ diff --git a/sky/engine/wtf/MallocZoneSupport.h b/sky/engine/wtf/MallocZoneSupport.h index 897fcfc3e0b20..2a3150b72d362 100644 --- a/sky/engine/wtf/MallocZoneSupport.h +++ b/sky/engine/wtf/MallocZoneSupport.h @@ -34,34 +34,32 @@ namespace WTF { class RemoteMemoryReader { - task_t m_task; - memory_reader_t* m_reader; + task_t m_task; + memory_reader_t* m_reader; -public: - RemoteMemoryReader(task_t task, memory_reader_t* reader) - : m_task(task) - , m_reader(reader) - { } + public: + RemoteMemoryReader(task_t task, memory_reader_t* reader) + : m_task(task), m_reader(reader) {} - void* operator()(vm_address_t address, size_t size) const - { - void* output; - kern_return_t err = (*m_reader)(m_task, address, size, static_cast(&output)); - if (err) - output = 0; - return output; - } + void* operator()(vm_address_t address, size_t size) const { + void* output; + kern_return_t err = + (*m_reader)(m_task, address, size, static_cast(&output)); + if (err) + output = 0; + return output; + } - template - T* operator()(T* address, size_t size=sizeof(T)) const - { - return static_cast((*this)(reinterpret_cast(address), size)); - } + template + T* operator()(T* address, size_t size = sizeof(T)) const { + return static_cast( + (*this)(reinterpret_cast(address), size)); + } - template - T* nextEntryInHardenedLinkedList(T** address, uintptr_t entropy) const; + template + T* nextEntryInHardenedLinkedList(T** address, uintptr_t entropy) const; }; -} // namespace WTF +} // namespace WTF #endif // SKY_ENGINE_WTF_MALLOCZONESUPPORT_H_ diff --git a/sky/engine/wtf/MathExtras.h b/sky/engine/wtf/MathExtras.h index d0eb0118a9660..849850756d3ca 100644 --- a/sky/engine/wtf/MathExtras.h +++ b/sky/engine/wtf/MathExtras.h @@ -47,153 +47,206 @@ const float twoPiFloat = piFloat * 2.0f; #if OS(ANDROID) // ANDROID and MSVC's math.h does not currently supply log2 or log2f. -inline double log2(double num) -{ - // This constant is roughly M_LN2, which is not provided by default on Windows and Android. - return log(num) / 0.693147180559945309417232121458176568; +inline double log2(double num) { + // This constant is roughly M_LN2, which is not provided by default on Windows + // and Android. + return log(num) / 0.693147180559945309417232121458176568; } -inline float log2f(float num) -{ - // This constant is roughly M_LN2, which is not provided by default on Windows and Android. - return logf(num) / 0.693147180559945309417232121458176568f; +inline float log2f(float num) { + // This constant is roughly M_LN2, which is not provided by default on Windows + // and Android. + return logf(num) / 0.693147180559945309417232121458176568f; } #endif -inline double deg2rad(double d) { return d * piDouble / 180.0; } -inline double rad2deg(double r) { return r * 180.0 / piDouble; } -inline double deg2grad(double d) { return d * 400.0 / 360.0; } -inline double grad2deg(double g) { return g * 360.0 / 400.0; } -inline double turn2deg(double t) { return t * 360.0; } -inline double deg2turn(double d) { return d / 360.0; } -inline double rad2grad(double r) { return r * 200.0 / piDouble; } -inline double grad2rad(double g) { return g * piDouble / 200.0; } -inline double turn2grad(double t) { return t * 400; } -inline double grad2turn(double g) { return g / 400; } - -inline float deg2rad(float d) { return d * piFloat / 180.0f; } -inline float rad2deg(float r) { return r * 180.0f / piFloat; } -inline float deg2grad(float d) { return d * 400.0f / 360.0f; } -inline float grad2deg(float g) { return g * 360.0f / 400.0f; } -inline float turn2deg(float t) { return t * 360.0f; } -inline float deg2turn(float d) { return d / 360.0f; } -inline float rad2grad(float r) { return r * 200.0f / piFloat; } -inline float grad2rad(float g) { return g * piFloat / 200.0f; } -inline float turn2grad(float t) { return t * 400; } -inline float grad2turn(float g) { return g / 400; } +inline double deg2rad(double d) { + return d * piDouble / 180.0; +} +inline double rad2deg(double r) { + return r * 180.0 / piDouble; +} +inline double deg2grad(double d) { + return d * 400.0 / 360.0; +} +inline double grad2deg(double g) { + return g * 360.0 / 400.0; +} +inline double turn2deg(double t) { + return t * 360.0; +} +inline double deg2turn(double d) { + return d / 360.0; +} +inline double rad2grad(double r) { + return r * 200.0 / piDouble; +} +inline double grad2rad(double g) { + return g * piDouble / 200.0; +} +inline double turn2grad(double t) { + return t * 400; +} +inline double grad2turn(double g) { + return g / 400; +} -// std::numeric_limits::min() returns the smallest positive value for floating point types -template inline T defaultMinimumForClamp() { return std::numeric_limits::min(); } -template<> inline float defaultMinimumForClamp() { return -std::numeric_limits::max(); } -template<> inline double defaultMinimumForClamp() { return -std::numeric_limits::max(); } -template inline T defaultMaximumForClamp() { return std::numeric_limits::max(); } +inline float deg2rad(float d) { + return d * piFloat / 180.0f; +} +inline float rad2deg(float r) { + return r * 180.0f / piFloat; +} +inline float deg2grad(float d) { + return d * 400.0f / 360.0f; +} +inline float grad2deg(float g) { + return g * 360.0f / 400.0f; +} +inline float turn2deg(float t) { + return t * 360.0f; +} +inline float deg2turn(float d) { + return d / 360.0f; +} +inline float rad2grad(float r) { + return r * 200.0f / piFloat; +} +inline float grad2rad(float g) { + return g * piFloat / 200.0f; +} +inline float turn2grad(float t) { + return t * 400; +} +inline float grad2turn(float g) { + return g / 400; +} -template inline T clampTo(double value, T min = defaultMinimumForClamp(), T max = defaultMaximumForClamp()) -{ - if (value >= static_cast(max)) - return max; - if (value <= static_cast(min)) - return min; - return static_cast(value); +// std::numeric_limits::min() returns the smallest positive value for +// floating point types +template +inline T defaultMinimumForClamp() { + return std::numeric_limits::min(); +} +template <> +inline float defaultMinimumForClamp() { + return -std::numeric_limits::max(); +} +template <> +inline double defaultMinimumForClamp() { + return -std::numeric_limits::max(); +} +template +inline T defaultMaximumForClamp() { + return std::numeric_limits::max(); } -template<> inline long long int clampTo(double, long long int, long long int); // clampTo does not support long long ints. -inline int clampToInteger(double value) -{ - return clampTo(value); +template +inline T clampTo(double value, + T min = defaultMinimumForClamp(), + T max = defaultMaximumForClamp()) { + if (value >= static_cast(max)) + return max; + if (value <= static_cast(min)) + return min; + return static_cast(value); +} +template <> +inline long long int clampTo( + double, + long long int, + long long int); // clampTo does not support long long ints. + +inline int clampToInteger(double value) { + return clampTo(value); } -inline unsigned clampToUnsigned(double value) -{ - return clampTo(value); +inline unsigned clampToUnsigned(double value) { + return clampTo(value); } -inline float clampToFloat(double value) -{ - return clampTo(value); +inline float clampToFloat(double value) { + return clampTo(value); } -inline int clampToPositiveInteger(double value) -{ - return clampTo(value, 0); +inline int clampToPositiveInteger(double value) { + return clampTo(value, 0); } -inline int clampToInteger(float value) -{ - return clampTo(value); +inline int clampToInteger(float value) { + return clampTo(value); } -inline int clampToInteger(unsigned x) -{ - const unsigned intMax = static_cast(std::numeric_limits::max()); +inline int clampToInteger(unsigned x) { + const unsigned intMax = + static_cast(std::numeric_limits::max()); - if (x >= intMax) - return std::numeric_limits::max(); - return static_cast(x); + if (x >= intMax) + return std::numeric_limits::max(); + return static_cast(x); } -inline bool isWithinIntRange(float x) -{ - return x > static_cast(std::numeric_limits::min()) && x < static_cast(std::numeric_limits::max()); +inline bool isWithinIntRange(float x) { + return x > static_cast(std::numeric_limits::min()) && + x < static_cast(std::numeric_limits::max()); } -static size_t greatestCommonDivisor(size_t a, size_t b) -{ - return b ? greatestCommonDivisor(b, a % b) : a; +static size_t greatestCommonDivisor(size_t a, size_t b) { + return b ? greatestCommonDivisor(b, a % b) : a; } -inline size_t lowestCommonMultiple(size_t a, size_t b) -{ - return a && b ? a / greatestCommonDivisor(a, b) * b : 0; +inline size_t lowestCommonMultiple(size_t a, size_t b) { + return a && b ? a / greatestCommonDivisor(a, b) * b : 0; } #ifndef UINT64_C -#define UINT64_C(c) c ## ull +#define UINT64_C(c) c##ull #endif // Calculate d % 2^{64}. -inline void doubleToInteger(double d, unsigned long long& value) -{ - if (std::isnan(d) || std::isinf(d)) - value = 0; - else { - // -2^{64} < fmodValue < 2^{64}. - double fmodValue = fmod(trunc(d), std::numeric_limits::max() + 1.0); - if (fmodValue >= 0) { - // 0 <= fmodValue < 2^{64}. - // 0 <= value < 2^{64}. This cast causes no loss. - value = static_cast(fmodValue); - } else { - // -2^{64} < fmodValue < 0. - // 0 < fmodValueInUnsignedLongLong < 2^{64}. This cast causes no loss. - unsigned long long fmodValueInUnsignedLongLong = static_cast(-fmodValue); - // -1 < (std::numeric_limits::max() - fmodValueInUnsignedLongLong) < 2^{64} - 1. - // 0 < value < 2^{64}. - value = std::numeric_limits::max() - fmodValueInUnsignedLongLong + 1; - } +inline void doubleToInteger(double d, unsigned long long& value) { + if (std::isnan(d) || std::isinf(d)) + value = 0; + else { + // -2^{64} < fmodValue < 2^{64}. + double fmodValue = + fmod(trunc(d), std::numeric_limits::max() + 1.0); + if (fmodValue >= 0) { + // 0 <= fmodValue < 2^{64}. + // 0 <= value < 2^{64}. This cast causes no loss. + value = static_cast(fmodValue); + } else { + // -2^{64} < fmodValue < 0. + // 0 < fmodValueInUnsignedLongLong < 2^{64}. This cast causes no loss. + unsigned long long fmodValueInUnsignedLongLong = + static_cast(-fmodValue); + // -1 < (std::numeric_limits::max() - + // fmodValueInUnsignedLongLong) < 2^{64} - 1. 0 < value < 2^{64}. + value = std::numeric_limits::max() - + fmodValueInUnsignedLongLong + 1; } + } } namespace WTF { -inline unsigned fastLog2(unsigned i) -{ - unsigned log2 = 0; - if (i & (i - 1)) - log2 += 1; - if (i >> 16) - log2 += 16, i >>= 16; - if (i >> 8) - log2 += 8, i >>= 8; - if (i >> 4) - log2 += 4, i >>= 4; - if (i >> 2) - log2 += 2, i >>= 2; - if (i >> 1) - log2 += 1; - return log2; -} - -} // namespace WTF +inline unsigned fastLog2(unsigned i) { + unsigned log2 = 0; + if (i & (i - 1)) + log2 += 1; + if (i >> 16) + log2 += 16, i >>= 16; + if (i >> 8) + log2 += 8, i >>= 8; + if (i >> 4) + log2 += 4, i >>= 4; + if (i >> 2) + log2 += 2, i >>= 2; + if (i >> 1) + log2 += 1; + return log2; +} + +} // namespace WTF #endif // SKY_ENGINE_WTF_MATHEXTRAS_H_ diff --git a/sky/engine/wtf/MathExtrasTest.cpp b/sky/engine/wtf/MathExtrasTest.cpp index bdcd621e3ba41..7ec2405f864d0 100644 --- a/sky/engine/wtf/MathExtrasTest.cpp +++ b/sky/engine/wtf/MathExtrasTest.cpp @@ -23,180 +23,173 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ - #include #include "flutter/sky/engine/wtf/MathExtras.h" namespace { -TEST(MathExtrasTest, Lrint) -{ - EXPECT_EQ(-8, lrint(-7.5)); - EXPECT_EQ(-8, lrint(-8.5)); - EXPECT_EQ(0, lrint(-0.5)); - EXPECT_EQ(0, lrint(0.5)); - EXPECT_EQ(0, lrint(-0.5)); - EXPECT_EQ(1, lrint(1.3)); - EXPECT_EQ(2, lrint(1.7)); - EXPECT_EQ(0, lrint(0)); - EXPECT_EQ(0, lrint(-0)); - if (sizeof(long int) == 8) { - // Largest double number with 0.5 precision and one halfway rounding case below. - EXPECT_EQ(pow(2.0, 52), lrint(pow(2.0, 52) - 0.5)); - EXPECT_EQ(pow(2.0, 52) - 2, lrint(pow(2.0, 52) - 1.5)); - // Smallest double number with 0.5 precision and one halfway rounding case above. - EXPECT_EQ(-pow(2.0, 52), lrint(-pow(2.0, 52) + 0.5)); - EXPECT_EQ(-pow(2.0, 52) + 2, lrint(-pow(2.0, 52) + 1.5)); - } +TEST(MathExtrasTest, Lrint) { + EXPECT_EQ(-8, lrint(-7.5)); + EXPECT_EQ(-8, lrint(-8.5)); + EXPECT_EQ(0, lrint(-0.5)); + EXPECT_EQ(0, lrint(0.5)); + EXPECT_EQ(0, lrint(-0.5)); + EXPECT_EQ(1, lrint(1.3)); + EXPECT_EQ(2, lrint(1.7)); + EXPECT_EQ(0, lrint(0)); + EXPECT_EQ(0, lrint(-0)); + if (sizeof(long int) == 8) { + // Largest double number with 0.5 precision and one halfway rounding case + // below. + EXPECT_EQ(pow(2.0, 52), lrint(pow(2.0, 52) - 0.5)); + EXPECT_EQ(pow(2.0, 52) - 2, lrint(pow(2.0, 52) - 1.5)); + // Smallest double number with 0.5 precision and one halfway rounding case + // above. + EXPECT_EQ(-pow(2.0, 52), lrint(-pow(2.0, 52) + 0.5)); + EXPECT_EQ(-pow(2.0, 52) + 2, lrint(-pow(2.0, 52) + 1.5)); + } } -TEST(MathExtrasTest, clampToIntLong) -{ - if (sizeof(long) == sizeof(int)) - return; +TEST(MathExtrasTest, clampToIntLong) { + if (sizeof(long) == sizeof(int)) + return; - long maxInt = std::numeric_limits::max(); - long minInt = std::numeric_limits::min(); - long overflowInt = maxInt + 1; - long underflowInt = minInt - 1; + long maxInt = std::numeric_limits::max(); + long minInt = std::numeric_limits::min(); + long overflowInt = maxInt + 1; + long underflowInt = minInt - 1; - EXPECT_GT(overflowInt, maxInt); - EXPECT_LT(underflowInt, minInt); + EXPECT_GT(overflowInt, maxInt); + EXPECT_LT(underflowInt, minInt); - EXPECT_EQ(maxInt, clampTo(maxInt)); - EXPECT_EQ(minInt, clampTo(minInt)); + EXPECT_EQ(maxInt, clampTo(maxInt)); + EXPECT_EQ(minInt, clampTo(minInt)); - EXPECT_EQ(maxInt, clampTo(overflowInt)); - EXPECT_EQ(minInt, clampTo(underflowInt)); + EXPECT_EQ(maxInt, clampTo(overflowInt)); + EXPECT_EQ(minInt, clampTo(underflowInt)); } -TEST(MathExtrasTest, clampToIntLongLong) -{ - long long maxInt = std::numeric_limits::max(); - long long minInt = std::numeric_limits::min(); - long long overflowInt = maxInt + 1; - long long underflowInt = minInt - 1; +TEST(MathExtrasTest, clampToIntLongLong) { + long long maxInt = std::numeric_limits::max(); + long long minInt = std::numeric_limits::min(); + long long overflowInt = maxInt + 1; + long long underflowInt = minInt - 1; - EXPECT_GT(overflowInt, maxInt); - EXPECT_LT(underflowInt, minInt); + EXPECT_GT(overflowInt, maxInt); + EXPECT_LT(underflowInt, minInt); - EXPECT_EQ(maxInt, clampTo(maxInt)); - EXPECT_EQ(minInt, clampTo(minInt)); + EXPECT_EQ(maxInt, clampTo(maxInt)); + EXPECT_EQ(minInt, clampTo(minInt)); - EXPECT_EQ(maxInt, clampTo(overflowInt)); - EXPECT_EQ(minInt, clampTo(underflowInt)); + EXPECT_EQ(maxInt, clampTo(overflowInt)); + EXPECT_EQ(minInt, clampTo(underflowInt)); } -TEST(MathExtrasTest, clampToIntegerFloat) -{ - // This test is inaccurate as floats will round the min / max integer - // due to the narrow mantissa. However it will properly checks within - // (close to the extreme) and outside the integer range. - float maxInt = std::numeric_limits::max(); - float minInt = std::numeric_limits::min(); - float overflowInt = maxInt * 1.1; - float underflowInt = minInt * 1.1; - - EXPECT_GT(overflowInt, maxInt); - EXPECT_LT(underflowInt, minInt); - - // If maxInt == 2^31 - 1 (ie on I32 architecture), the closest float used to represent it is 2^31. - EXPECT_NEAR(clampToInteger(maxInt), maxInt, 1); - EXPECT_EQ(minInt, clampToInteger(minInt)); - - EXPECT_NEAR(clampToInteger(overflowInt), maxInt, 1); - EXPECT_EQ(minInt, clampToInteger(underflowInt)); +TEST(MathExtrasTest, clampToIntegerFloat) { + // This test is inaccurate as floats will round the min / max integer + // due to the narrow mantissa. However it will properly checks within + // (close to the extreme) and outside the integer range. + float maxInt = std::numeric_limits::max(); + float minInt = std::numeric_limits::min(); + float overflowInt = maxInt * 1.1; + float underflowInt = minInt * 1.1; + + EXPECT_GT(overflowInt, maxInt); + EXPECT_LT(underflowInt, minInt); + + // If maxInt == 2^31 - 1 (ie on I32 architecture), the closest float used to + // represent it is 2^31. + EXPECT_NEAR(clampToInteger(maxInt), maxInt, 1); + EXPECT_EQ(minInt, clampToInteger(minInt)); + + EXPECT_NEAR(clampToInteger(overflowInt), maxInt, 1); + EXPECT_EQ(minInt, clampToInteger(underflowInt)); } -TEST(MathExtrasTest, clampToIntegerDouble) -{ - double maxInt = std::numeric_limits::max(); - double minInt = std::numeric_limits::min(); - double overflowInt = maxInt + 1; - double underflowInt = minInt - 1; +TEST(MathExtrasTest, clampToIntegerDouble) { + double maxInt = std::numeric_limits::max(); + double minInt = std::numeric_limits::min(); + double overflowInt = maxInt + 1; + double underflowInt = minInt - 1; - EXPECT_GT(overflowInt, maxInt); - EXPECT_LT(underflowInt, minInt); + EXPECT_GT(overflowInt, maxInt); + EXPECT_LT(underflowInt, minInt); - EXPECT_EQ(maxInt, clampToInteger(maxInt)); - EXPECT_EQ(minInt, clampToInteger(minInt)); + EXPECT_EQ(maxInt, clampToInteger(maxInt)); + EXPECT_EQ(minInt, clampToInteger(minInt)); - EXPECT_EQ(clampToInteger(overflowInt), maxInt); - EXPECT_EQ(clampToInteger(underflowInt), minInt); + EXPECT_EQ(clampToInteger(overflowInt), maxInt); + EXPECT_EQ(clampToInteger(underflowInt), minInt); } -TEST(MathExtrasTest, clampToFloat) -{ - double maxFloat = std::numeric_limits::max(); - double minFloat = -maxFloat; - double overflowFloat = maxFloat * 1.1; - double underflowFloat = minFloat * 1.1; +TEST(MathExtrasTest, clampToFloat) { + double maxFloat = std::numeric_limits::max(); + double minFloat = -maxFloat; + double overflowFloat = maxFloat * 1.1; + double underflowFloat = minFloat * 1.1; - EXPECT_GT(overflowFloat, maxFloat); - EXPECT_LT(underflowFloat, minFloat); + EXPECT_GT(overflowFloat, maxFloat); + EXPECT_LT(underflowFloat, minFloat); - EXPECT_EQ(maxFloat, clampToFloat(maxFloat)); - EXPECT_EQ(minFloat, clampToFloat(minFloat)); + EXPECT_EQ(maxFloat, clampToFloat(maxFloat)); + EXPECT_EQ(minFloat, clampToFloat(minFloat)); - EXPECT_EQ(maxFloat, clampToFloat(overflowFloat)); - EXPECT_EQ(minFloat, clampToFloat(underflowFloat)); + EXPECT_EQ(maxFloat, clampToFloat(overflowFloat)); + EXPECT_EQ(minFloat, clampToFloat(underflowFloat)); - EXPECT_EQ(maxFloat, clampToFloat(std::numeric_limits::infinity())); - EXPECT_EQ(minFloat, clampToFloat(-std::numeric_limits::infinity())); + EXPECT_EQ(maxFloat, clampToFloat(std::numeric_limits::infinity())); + EXPECT_EQ(minFloat, clampToFloat(-std::numeric_limits::infinity())); } -TEST(MathExtrasTest, clampToUnsignedLong) -{ - if (sizeof(unsigned long) == sizeof(unsigned)) - return; +TEST(MathExtrasTest, clampToUnsignedLong) { + if (sizeof(unsigned long) == sizeof(unsigned)) + return; - unsigned long maxUnsigned = std::numeric_limits::max(); - unsigned long overflowUnsigned = maxUnsigned + 1; + unsigned long maxUnsigned = std::numeric_limits::max(); + unsigned long overflowUnsigned = maxUnsigned + 1; - EXPECT_GT(overflowUnsigned, maxUnsigned); + EXPECT_GT(overflowUnsigned, maxUnsigned); - EXPECT_EQ(maxUnsigned, clampTo(maxUnsigned)); + EXPECT_EQ(maxUnsigned, clampTo(maxUnsigned)); - EXPECT_EQ(maxUnsigned, clampTo(overflowUnsigned)); - EXPECT_EQ(0u, clampTo(-1)); + EXPECT_EQ(maxUnsigned, clampTo(overflowUnsigned)); + EXPECT_EQ(0u, clampTo(-1)); } -TEST(MathExtrasTest, clampToUnsignedLongLong) -{ - unsigned long long maxUnsigned = std::numeric_limits::max(); - unsigned long long overflowUnsigned = maxUnsigned + 1; +TEST(MathExtrasTest, clampToUnsignedLongLong) { + unsigned long long maxUnsigned = std::numeric_limits::max(); + unsigned long long overflowUnsigned = maxUnsigned + 1; - EXPECT_GT(overflowUnsigned, maxUnsigned); + EXPECT_GT(overflowUnsigned, maxUnsigned); - EXPECT_EQ(maxUnsigned, clampTo(maxUnsigned)); + EXPECT_EQ(maxUnsigned, clampTo(maxUnsigned)); - EXPECT_EQ(maxUnsigned, clampTo(overflowUnsigned)); - EXPECT_EQ(0u, clampTo(-1)); + EXPECT_EQ(maxUnsigned, clampTo(overflowUnsigned)); + EXPECT_EQ(0u, clampTo(-1)); } // Make sure that various +-inf cases are handled properly (they aren't // by default on VS). -TEST(MathExtrasTest, infinityMath) -{ - double posInf = std::numeric_limits::infinity(); - double negInf = -std::numeric_limits::infinity(); - double nan = std::numeric_limits::quiet_NaN(); - - EXPECT_EQ(M_PI_4, atan2(posInf, posInf)); - EXPECT_EQ(3.0 * M_PI_4, atan2(posInf, negInf)); - EXPECT_EQ(-M_PI_4, atan2(negInf, posInf)); - EXPECT_EQ(-3.0 * M_PI_4, atan2(negInf, negInf)); - - EXPECT_EQ(0.0, fmod(0.0, posInf)); - EXPECT_EQ(7.0, fmod(7.0, posInf)); - EXPECT_EQ(-7.0, fmod(-7.0, posInf)); - EXPECT_EQ(0.0, fmod(0.0, negInf)); - EXPECT_EQ(7.0, fmod(7.0, negInf)); - EXPECT_EQ(-7.0, fmod(-7.0, negInf)); - - EXPECT_EQ(1.0, pow(5.0, 0.0)); - EXPECT_EQ(1.0, pow(-5.0, 0.0)); - EXPECT_EQ(1.0, pow(nan, 0.0)); +TEST(MathExtrasTest, infinityMath) { + double posInf = std::numeric_limits::infinity(); + double negInf = -std::numeric_limits::infinity(); + double nan = std::numeric_limits::quiet_NaN(); + + EXPECT_EQ(M_PI_4, atan2(posInf, posInf)); + EXPECT_EQ(3.0 * M_PI_4, atan2(posInf, negInf)); + EXPECT_EQ(-M_PI_4, atan2(negInf, posInf)); + EXPECT_EQ(-3.0 * M_PI_4, atan2(negInf, negInf)); + + EXPECT_EQ(0.0, fmod(0.0, posInf)); + EXPECT_EQ(7.0, fmod(7.0, posInf)); + EXPECT_EQ(-7.0, fmod(-7.0, posInf)); + EXPECT_EQ(0.0, fmod(0.0, negInf)); + EXPECT_EQ(7.0, fmod(7.0, negInf)); + EXPECT_EQ(-7.0, fmod(-7.0, negInf)); + + EXPECT_EQ(1.0, pow(5.0, 0.0)); + EXPECT_EQ(1.0, pow(-5.0, 0.0)); + EXPECT_EQ(1.0, pow(nan, 0.0)); } -} // namespace +} // namespace diff --git a/sky/engine/wtf/NonCopyingSort.h b/sky/engine/wtf/NonCopyingSort.h index 4c6994bd76bc7..e7e65a0d8757f 100644 --- a/sky/engine/wtf/NonCopyingSort.h +++ b/sky/engine/wtf/NonCopyingSort.h @@ -31,58 +31,64 @@ namespace WTF { using std::swap; -template -inline void siftDown(RandomAccessIterator array, ptrdiff_t start, ptrdiff_t end, Predicate compareLess) -{ - ptrdiff_t root = start; +template +inline void siftDown(RandomAccessIterator array, + ptrdiff_t start, + ptrdiff_t end, + Predicate compareLess) { + ptrdiff_t root = start; - while (root * 2 + 1 <= end) { - ptrdiff_t child = root * 2 + 1; - if (child < end && compareLess(array[child], array[child + 1])) - child++; + while (root * 2 + 1 <= end) { + ptrdiff_t child = root * 2 + 1; + if (child < end && compareLess(array[child], array[child + 1])) + child++; - if (compareLess(array[root], array[child])) { - swap(array[root], array[child]); - root = child; - } else - return; - } + if (compareLess(array[root], array[child])) { + swap(array[root], array[child]); + root = child; + } else + return; + } } -template -inline void heapify(RandomAccessIterator array, ptrdiff_t count, Predicate compareLess) -{ - ptrdiff_t start = (count - 2) / 2; +template +inline void heapify(RandomAccessIterator array, + ptrdiff_t count, + Predicate compareLess) { + ptrdiff_t start = (count - 2) / 2; - while (start >= 0) { - siftDown(array, start, count - 1, compareLess); - start--; - } + while (start >= 0) { + siftDown(array, start, count - 1, compareLess); + start--; + } } -template -void heapSort(RandomAccessIterator start, RandomAccessIterator end, Predicate compareLess) -{ - ptrdiff_t count = end - start; - heapify(start, count, compareLess); +template +void heapSort(RandomAccessIterator start, + RandomAccessIterator end, + Predicate compareLess) { + ptrdiff_t count = end - start; + heapify(start, count, compareLess); - ptrdiff_t endIndex = count - 1; - while (endIndex > 0) { - swap(start[endIndex], start[0]); - siftDown(start, 0, endIndex - 1, compareLess); - endIndex--; - } + ptrdiff_t endIndex = count - 1; + while (endIndex > 0) { + swap(start[endIndex], start[0]); + siftDown(start, 0, endIndex - 1, compareLess); + endIndex--; + } } -template -inline void nonCopyingSort(RandomAccessIterator start, RandomAccessIterator end, Predicate compareLess) -{ - // heapsort happens to use only swaps, not copies, but the essential thing about - // this function is the fact that it does not copy, not the specific algorithm - heapSort(start, end, compareLess); +template +inline void nonCopyingSort(RandomAccessIterator start, + RandomAccessIterator end, + Predicate compareLess) { + // heapsort happens to use only swaps, not copies, but the essential thing + // about this function is the fact that it does not copy, not the specific + // algorithm + heapSort(start, end, compareLess); } -} // namespace WTF +} // namespace WTF using WTF::nonCopyingSort; diff --git a/sky/engine/wtf/Noncopyable.h b/sky/engine/wtf/Noncopyable.h index 0158f3d78a3c5..7254ed80e9c84 100644 --- a/sky/engine/wtf/Noncopyable.h +++ b/sky/engine/wtf/Noncopyable.h @@ -24,8 +24,8 @@ #include "flutter/sky/engine/wtf/Compiler.h" #define WTF_MAKE_NONCOPYABLE(ClassName) \ - private: \ - ClassName(const ClassName&) = delete; \ - ClassName& operator=(const ClassName&) = delete; + private: \ + ClassName(const ClassName&) = delete; \ + ClassName& operator=(const ClassName&) = delete; #endif // SKY_ENGINE_WTF_NONCOPYABLE_H_ diff --git a/sky/engine/wtf/NullPtr.h b/sky/engine/wtf/NullPtr.h index a8d342c6b317b..de5a13c471f39 100644 --- a/sky/engine/wtf/NullPtr.h +++ b/sky/engine/wtf/NullPtr.h @@ -27,18 +27,20 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef SKY_ENGINE_WTF_NULLPTR_H_ #define SKY_ENGINE_WTF_NULLPTR_H_ -// libstdc++ supports nullptr_t starting with gcc 4.6. STLport doesn't define it. -#if (defined(__GLIBCXX__) && __GLIBCXX__ < 20110325) || defined(_STLPORT_VERSION) +// libstdc++ supports nullptr_t starting with gcc 4.6. STLport doesn't define +// it. +#if (defined(__GLIBCXX__) && __GLIBCXX__ < 20110325) || \ + defined(_STLPORT_VERSION) namespace std { typedef decltype(nullptr) nullptr_t; } #endif #define WTF_DISALLOW_CONSTRUCTION_FROM_ZERO(ClassName) \ - private: \ - ClassName(int) = delete + private: \ + ClassName(int) = delete #define WTF_DISALLOW_ZERO_ASSIGNMENT(ClassName) \ - private: \ - ClassName& operator=(int) = delete + private: \ + ClassName& operator=(int) = delete #endif // SKY_ENGINE_WTF_NULLPTR_H_ diff --git a/sky/engine/wtf/OperatingSystem.h b/sky/engine/wtf/OperatingSystem.h index def9360b431f2..9989ed8104066 100644 --- a/sky/engine/wtf/OperatingSystem.h +++ b/sky/engine/wtf/OperatingSystem.h @@ -24,23 +24,28 @@ #ifndef SKY_ENGINE_WTF_OPERATING_SYSTEM_H_ #define SKY_ENGINE_WTF_OPERATING_SYSTEM_H_ -/* ==== Platform adaptation macros: these describe properties of the target environment. ==== */ +/* ==== Platform adaptation macros: these describe properties of the target + * environment. ==== */ -/* HAVE() - specific system features (headers, functions or similar) that are present or not */ -#define HAVE(WTF_FEATURE) (defined HAVE_##WTF_FEATURE && HAVE_##WTF_FEATURE) -/* OS() - underlying operating system; only to be used for mandated low-level services like - virtual memory, not to choose a GUI toolkit */ -#define OS(WTF_FEATURE) (defined WTF_OS_##WTF_FEATURE && WTF_OS_##WTF_FEATURE) +/* HAVE() - specific system features (headers, functions or similar) that are + * present or not */ +#define HAVE(WTF_FEATURE) (defined HAVE_##WTF_FEATURE && HAVE_##WTF_FEATURE) +/* OS() - underlying operating system; only to be used for mandated low-level + services like virtual memory, not to choose a GUI toolkit */ +#define OS(WTF_FEATURE) (defined WTF_OS_##WTF_FEATURE && WTF_OS_##WTF_FEATURE) -/* ==== Policy decision macros: these define policy choices for a particular port. ==== */ +/* ==== Policy decision macros: these define policy choices for a particular + * port. ==== */ /* USE() - use a particular third-party library or optional OS service */ -#define USE(WTF_FEATURE) (defined WTF_USE_##WTF_FEATURE && WTF_USE_##WTF_FEATURE) +#define USE(WTF_FEATURE) \ + (defined WTF_USE_##WTF_FEATURE && WTF_USE_##WTF_FEATURE) /* ENABLE() - turn on a specific feature of WebKit */ -#define ENABLE(WTF_FEATURE) (defined ENABLE_##WTF_FEATURE && ENABLE_##WTF_FEATURE) +#define ENABLE(WTF_FEATURE) \ + (defined ENABLE_##WTF_FEATURE && ENABLE_##WTF_FEATURE) -/* ==== OS() - underlying operating system; only to be used for mandated low-level services like - virtual memory, not to choose a GUI toolkit ==== */ +/* ==== OS() - underlying operating system; only to be used for mandated + low-level services like virtual memory, not to choose a GUI toolkit ==== */ /* OS(ANDROID) - Android */ #ifdef ANDROID diff --git a/sky/engine/wtf/OwnPtr.h b/sky/engine/wtf/OwnPtr.h index 9aaa40fdbaf42..a94211e9d9ac1 100644 --- a/sky/engine/wtf/OwnPtr.h +++ b/sky/engine/wtf/OwnPtr.h @@ -30,198 +30,236 @@ namespace WTF { - template class PassOwnPtr; - - template class OwnPtr { - // If rvalue references are not supported, the copy constructor is - // public so OwnPtr cannot be marked noncopyable. See note below. - WTF_MAKE_NONCOPYABLE(OwnPtr); - WTF_DISALLOW_CONSTRUCTION_FROM_ZERO(OwnPtr); - public: - typedef typename RemoveExtent::Type ValueType; - typedef ValueType* PtrType; - - OwnPtr() : m_ptr(0) { } - OwnPtr(std::nullptr_t) : m_ptr(0) { } - - // See comment in PassOwnPtr.h for why this takes a const reference. - OwnPtr(const PassOwnPtr&); - template OwnPtr(const PassOwnPtr&, EnsurePtrConvertibleArgDecl(U, T)); - - // Hash table deleted values, which are only constructed and never copied or destroyed. - OwnPtr(HashTableDeletedValueType) : m_ptr(hashTableDeletedValue()) { } - bool isHashTableDeletedValue() const { return m_ptr == hashTableDeletedValue(); } - - ~OwnPtr() - { - OwnedPtrDeleter::deletePtr(m_ptr); - m_ptr = 0; - } - - PtrType get() const { return m_ptr; } - - void clear(); - PassOwnPtr release(); - PtrType leakPtr() WARN_UNUSED_RETURN; - - ValueType& operator*() const { ASSERT(m_ptr); return *m_ptr; } - PtrType operator->() const { ASSERT(m_ptr); return m_ptr; } - - ValueType& operator[](std::ptrdiff_t i) const; - - bool operator!() const { return !m_ptr; } - - // This conversion operator allows implicit conversion to bool but not to other integer types. - typedef PtrType OwnPtr::*UnspecifiedBoolType; - operator UnspecifiedBoolType() const { return m_ptr ? &OwnPtr::m_ptr : 0; } - - OwnPtr& operator=(const PassOwnPtr&); - OwnPtr& operator=(std::nullptr_t) { clear(); return *this; } - template OwnPtr& operator=(const PassOwnPtr&); - - OwnPtr(OwnPtr&&); - template OwnPtr(OwnPtr&&); - - OwnPtr& operator=(OwnPtr&&); - template OwnPtr& operator=(OwnPtr&&); - - void swap(OwnPtr& o) { std::swap(m_ptr, o.m_ptr); } - - static T* hashTableDeletedValue() { return reinterpret_cast(-1); } - - private: - // We should never have two OwnPtrs for the same underlying object (otherwise we'll get - // double-destruction), so these equality operators should never be needed. - template bool operator==(const OwnPtr&) const { COMPILE_ASSERT(!sizeof(U*), OwnPtrs_should_never_be_equal); return false; } - template bool operator!=(const OwnPtr&) const { COMPILE_ASSERT(!sizeof(U*), OwnPtrs_should_never_be_equal); return false; } - template bool operator==(const PassOwnPtr&) const { COMPILE_ASSERT(!sizeof(U*), OwnPtrs_should_never_be_equal); return false; } - template bool operator!=(const PassOwnPtr&) const { COMPILE_ASSERT(!sizeof(U*), OwnPtrs_should_never_be_equal); return false; } - - PtrType m_ptr; - }; - - template inline OwnPtr::OwnPtr(const PassOwnPtr& o) - : m_ptr(o.leakPtr()) - { - } - - template template inline OwnPtr::OwnPtr(const PassOwnPtr& o, EnsurePtrConvertibleArgDefn(U, T)) - : m_ptr(o.leakPtr()) - { - COMPILE_ASSERT(!IsArray::value, Pointers_to_array_must_never_be_converted); - } - - template inline void OwnPtr::clear() - { - PtrType ptr = m_ptr; - m_ptr = 0; - OwnedPtrDeleter::deletePtr(ptr); - } - - template inline PassOwnPtr OwnPtr::release() - { - PtrType ptr = m_ptr; - m_ptr = 0; - return PassOwnPtr(ptr); - } - - template inline typename OwnPtr::PtrType OwnPtr::leakPtr() - { - PtrType ptr = m_ptr; - m_ptr = 0; - return ptr; - } - - template inline typename OwnPtr::ValueType& OwnPtr::operator[](std::ptrdiff_t i) const - { - COMPILE_ASSERT(IsArray::value, Elements_access_is_possible_for_arrays_only); - ASSERT(m_ptr); - ASSERT(i >= 0); - return m_ptr[i]; - } - - template inline OwnPtr& OwnPtr::operator=(const PassOwnPtr& o) - { - PtrType ptr = m_ptr; - m_ptr = o.leakPtr(); - ASSERT(!ptr || m_ptr != ptr); - OwnedPtrDeleter::deletePtr(ptr); - return *this; - } - - template template inline OwnPtr& OwnPtr::operator=(const PassOwnPtr& o) - { - COMPILE_ASSERT(!IsArray::value, Pointers_to_array_must_never_be_converted); - PtrType ptr = m_ptr; - m_ptr = o.leakPtr(); - ASSERT(!ptr || m_ptr != ptr); - OwnedPtrDeleter::deletePtr(ptr); - return *this; - } - - template inline OwnPtr::OwnPtr(OwnPtr&& o) - : m_ptr(o.leakPtr()) - { - } - - template template inline OwnPtr::OwnPtr(OwnPtr&& o) - : m_ptr(o.leakPtr()) - { - COMPILE_ASSERT(!IsArray::value, Pointers_to_array_must_never_be_converted); - } - - template inline OwnPtr& OwnPtr::operator=(OwnPtr&& o) - { - PtrType ptr = m_ptr; - m_ptr = o.leakPtr(); - ASSERT(!ptr || m_ptr != ptr); - OwnedPtrDeleter::deletePtr(ptr); - - return *this; - } - - template template inline OwnPtr& OwnPtr::operator=(OwnPtr&& o) - { - COMPILE_ASSERT(!IsArray::value, Pointers_to_array_must_never_be_converted); - PtrType ptr = m_ptr; - m_ptr = o.leakPtr(); - ASSERT(!ptr || m_ptr != ptr); - OwnedPtrDeleter::deletePtr(ptr); - - return *this; - } - - template inline void swap(OwnPtr& a, OwnPtr& b) - { - a.swap(b); - } - - template inline bool operator==(const OwnPtr& a, U* b) - { - return a.get() == b; - } - - template inline bool operator==(T* a, const OwnPtr& b) - { - return a == b.get(); - } - - template inline bool operator!=(const OwnPtr& a, U* b) - { - return a.get() != b; - } - - template inline bool operator!=(T* a, const OwnPtr& b) - { - return a != b.get(); - } - - template inline typename OwnPtr::PtrType getPtr(const OwnPtr& p) - { - return p.get(); - } - -} // namespace WTF +template +class PassOwnPtr; + +template +class OwnPtr { + // If rvalue references are not supported, the copy constructor is + // public so OwnPtr cannot be marked noncopyable. See note below. + WTF_MAKE_NONCOPYABLE(OwnPtr); + WTF_DISALLOW_CONSTRUCTION_FROM_ZERO(OwnPtr); + + public: + typedef typename RemoveExtent::Type ValueType; + typedef ValueType* PtrType; + + OwnPtr() : m_ptr(0) {} + OwnPtr(std::nullptr_t) : m_ptr(0) {} + + // See comment in PassOwnPtr.h for why this takes a const reference. + OwnPtr(const PassOwnPtr&); + template + OwnPtr(const PassOwnPtr&, EnsurePtrConvertibleArgDecl(U, T)); + + // Hash table deleted values, which are only constructed and never copied or + // destroyed. + OwnPtr(HashTableDeletedValueType) : m_ptr(hashTableDeletedValue()) {} + bool isHashTableDeletedValue() const { + return m_ptr == hashTableDeletedValue(); + } + + ~OwnPtr() { + OwnedPtrDeleter::deletePtr(m_ptr); + m_ptr = 0; + } + + PtrType get() const { return m_ptr; } + + void clear(); + PassOwnPtr release(); + PtrType leakPtr() WARN_UNUSED_RETURN; + + ValueType& operator*() const { + ASSERT(m_ptr); + return *m_ptr; + } + PtrType operator->() const { + ASSERT(m_ptr); + return m_ptr; + } + + ValueType& operator[](std::ptrdiff_t i) const; + + bool operator!() const { return !m_ptr; } + + // This conversion operator allows implicit conversion to bool but not to + // other integer types. + typedef PtrType OwnPtr::*UnspecifiedBoolType; + operator UnspecifiedBoolType() const { return m_ptr ? &OwnPtr::m_ptr : 0; } + + OwnPtr& operator=(const PassOwnPtr&); + OwnPtr& operator=(std::nullptr_t) { + clear(); + return *this; + } + template + OwnPtr& operator=(const PassOwnPtr&); + + OwnPtr(OwnPtr&&); + template + OwnPtr(OwnPtr&&); + + OwnPtr& operator=(OwnPtr&&); + template + OwnPtr& operator=(OwnPtr&&); + + void swap(OwnPtr& o) { std::swap(m_ptr, o.m_ptr); } + + static T* hashTableDeletedValue() { return reinterpret_cast(-1); } + + private: + // We should never have two OwnPtrs for the same underlying object (otherwise + // we'll get double-destruction), so these equality operators should never be + // needed. + template + bool operator==(const OwnPtr&) const { + COMPILE_ASSERT(!sizeof(U*), OwnPtrs_should_never_be_equal); + return false; + } + template + bool operator!=(const OwnPtr&) const { + COMPILE_ASSERT(!sizeof(U*), OwnPtrs_should_never_be_equal); + return false; + } + template + bool operator==(const PassOwnPtr&) const { + COMPILE_ASSERT(!sizeof(U*), OwnPtrs_should_never_be_equal); + return false; + } + template + bool operator!=(const PassOwnPtr&) const { + COMPILE_ASSERT(!sizeof(U*), OwnPtrs_should_never_be_equal); + return false; + } + + PtrType m_ptr; +}; + +template +inline OwnPtr::OwnPtr(const PassOwnPtr& o) : m_ptr(o.leakPtr()) {} + +template +template +inline OwnPtr::OwnPtr(const PassOwnPtr& o, + EnsurePtrConvertibleArgDefn(U, T)) + : m_ptr(o.leakPtr()) { + COMPILE_ASSERT(!IsArray::value, Pointers_to_array_must_never_be_converted); +} + +template +inline void OwnPtr::clear() { + PtrType ptr = m_ptr; + m_ptr = 0; + OwnedPtrDeleter::deletePtr(ptr); +} + +template +inline PassOwnPtr OwnPtr::release() { + PtrType ptr = m_ptr; + m_ptr = 0; + return PassOwnPtr(ptr); +} + +template +inline typename OwnPtr::PtrType OwnPtr::leakPtr() { + PtrType ptr = m_ptr; + m_ptr = 0; + return ptr; +} + +template +inline typename OwnPtr::ValueType& OwnPtr::operator[]( + std::ptrdiff_t i) const { + COMPILE_ASSERT(IsArray::value, + Elements_access_is_possible_for_arrays_only); + ASSERT(m_ptr); + ASSERT(i >= 0); + return m_ptr[i]; +} + +template +inline OwnPtr& OwnPtr::operator=(const PassOwnPtr& o) { + PtrType ptr = m_ptr; + m_ptr = o.leakPtr(); + ASSERT(!ptr || m_ptr != ptr); + OwnedPtrDeleter::deletePtr(ptr); + return *this; +} + +template +template +inline OwnPtr& OwnPtr::operator=(const PassOwnPtr& o) { + COMPILE_ASSERT(!IsArray::value, Pointers_to_array_must_never_be_converted); + PtrType ptr = m_ptr; + m_ptr = o.leakPtr(); + ASSERT(!ptr || m_ptr != ptr); + OwnedPtrDeleter::deletePtr(ptr); + return *this; +} + +template +inline OwnPtr::OwnPtr(OwnPtr&& o) : m_ptr(o.leakPtr()) {} + +template +template +inline OwnPtr::OwnPtr(OwnPtr&& o) : m_ptr(o.leakPtr()) { + COMPILE_ASSERT(!IsArray::value, Pointers_to_array_must_never_be_converted); +} + +template +inline OwnPtr& OwnPtr::operator=(OwnPtr&& o) { + PtrType ptr = m_ptr; + m_ptr = o.leakPtr(); + ASSERT(!ptr || m_ptr != ptr); + OwnedPtrDeleter::deletePtr(ptr); + + return *this; +} + +template +template +inline OwnPtr& OwnPtr::operator=(OwnPtr&& o) { + COMPILE_ASSERT(!IsArray::value, Pointers_to_array_must_never_be_converted); + PtrType ptr = m_ptr; + m_ptr = o.leakPtr(); + ASSERT(!ptr || m_ptr != ptr); + OwnedPtrDeleter::deletePtr(ptr); + + return *this; +} + +template +inline void swap(OwnPtr& a, OwnPtr& b) { + a.swap(b); +} + +template +inline bool operator==(const OwnPtr& a, U* b) { + return a.get() == b; +} + +template +inline bool operator==(T* a, const OwnPtr& b) { + return a == b.get(); +} + +template +inline bool operator!=(const OwnPtr& a, U* b) { + return a.get() != b; +} + +template +inline bool operator!=(T* a, const OwnPtr& b) { + return a != b.get(); +} + +template +inline typename OwnPtr::PtrType getPtr(const OwnPtr& p) { + return p.get(); +} + +} // namespace WTF using WTF::OwnPtr; diff --git a/sky/engine/wtf/OwnPtrCommon.h b/sky/engine/wtf/OwnPtrCommon.h index c075bab6f8dac..b14c7f55400ed 100644 --- a/sky/engine/wtf/OwnPtrCommon.h +++ b/sky/engine/wtf/OwnPtrCommon.h @@ -37,37 +37,35 @@ namespace WTF { class RefCountedBase; class ThreadSafeRefCountedBase; -template +template struct IsRefCounted { - static const bool value = IsSubclass::value - || IsSubclass::value; + static const bool value = IsSubclass::value || + IsSubclass::value; }; template struct OwnedPtrDeleter { - static void deletePtr(T* ptr) - { - COMPILE_ASSERT(!IsRefCounted::value, UseRefPtrForRefCountedObjects); - COMPILE_ASSERT(sizeof(T) > 0, TypeMustBeComplete); - delete ptr; - } + static void deletePtr(T* ptr) { + COMPILE_ASSERT(!IsRefCounted::value, UseRefPtrForRefCountedObjects); + COMPILE_ASSERT(sizeof(T) > 0, TypeMustBeComplete); + delete ptr; + } }; template struct OwnedPtrDeleter { - static void deletePtr(T* ptr) - { - COMPILE_ASSERT(!IsRefCounted::value, UseRefPtrForRefCountedObjects); - COMPILE_ASSERT(sizeof(T) > 0, TypeMustBeComplete); - delete[] ptr; - } + static void deletePtr(T* ptr) { + COMPILE_ASSERT(!IsRefCounted::value, UseRefPtrForRefCountedObjects); + COMPILE_ASSERT(sizeof(T) > 0, TypeMustBeComplete); + delete[] ptr; + } }; template struct OwnedPtrDeleter { - COMPILE_ASSERT(sizeof(T) < 0, DoNotUseArrayWithSizeAsType); + COMPILE_ASSERT(sizeof(T) < 0, DoNotUseArrayWithSizeAsType); }; -} // namespace WTF +} // namespace WTF #endif // SKY_ENGINE_WTF_OWNPTRCOMMON_H_ diff --git a/sky/engine/wtf/PageAllocator.cpp b/sky/engine/wtf/PageAllocator.cpp index 41d7f4bd807d2..25921a2345afd 100644 --- a/sky/engine/wtf/PageAllocator.cpp +++ b/sky/engine/wtf/PageAllocator.cpp @@ -49,163 +49,163 @@ #else #error Unknown OS -#endif // OS(POSIX) +#endif // OS(POSIX) namespace WTF { - // This simple internal function wraps the OS-specific page allocation call so // that it behaves consistently: the address is a hint and if it cannot be used, // the allocation will be placed elsewhere. -static void* systemAllocPages(void* addr, size_t len) -{ - ASSERT(!(len & kPageAllocationGranularityOffsetMask)); - ASSERT(!(reinterpret_cast(addr) & kPageAllocationGranularityOffsetMask)); - void* ret = 0; - ret = mmap(addr, len, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); - if (ret == MAP_FAILED) - ret = 0; - return ret; +static void* systemAllocPages(void* addr, size_t len) { + ASSERT(!(len & kPageAllocationGranularityOffsetMask)); + ASSERT(!(reinterpret_cast(addr) & + kPageAllocationGranularityOffsetMask)); + void* ret = 0; + ret = mmap(addr, len, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, + 0); + if (ret == MAP_FAILED) + ret = 0; + return ret; } -static bool trimMapping(void* baseAddr, size_t baseLen, void* trimAddr, size_t trimLen) -{ - char* basePtr = static_cast(baseAddr); - char* trimPtr = static_cast(trimAddr); - ASSERT(trimPtr >= basePtr); - ASSERT(trimPtr + trimLen <= basePtr + baseLen); - size_t preLen = trimPtr - basePtr; - if (preLen) { - int ret = munmap(basePtr, preLen); - RELEASE_ASSERT(!ret); - } - size_t postLen = (basePtr + baseLen) - (trimPtr + trimLen); - if (postLen) { - int ret = munmap(trimPtr + trimLen, postLen); - RELEASE_ASSERT(!ret); - } - return true; +static bool trimMapping(void* baseAddr, + size_t baseLen, + void* trimAddr, + size_t trimLen) { + char* basePtr = static_cast(baseAddr); + char* trimPtr = static_cast(trimAddr); + ASSERT(trimPtr >= basePtr); + ASSERT(trimPtr + trimLen <= basePtr + baseLen); + size_t preLen = trimPtr - basePtr; + if (preLen) { + int ret = munmap(basePtr, preLen); + RELEASE_ASSERT(!ret); + } + size_t postLen = (basePtr + baseLen) - (trimPtr + trimLen); + if (postLen) { + int ret = munmap(trimPtr + trimLen, postLen); + RELEASE_ASSERT(!ret); + } + return true; } -void* allocPages(void* addr, size_t len, size_t align) -{ - ASSERT(len >= kPageAllocationGranularity); - ASSERT(!(len & kPageAllocationGranularityOffsetMask)); - ASSERT(align >= kPageAllocationGranularity); - ASSERT(!(align & kPageAllocationGranularityOffsetMask)); - ASSERT(!(reinterpret_cast(addr) & kPageAllocationGranularityOffsetMask)); - size_t alignOffsetMask = align - 1; - size_t alignBaseMask = ~alignOffsetMask; - ASSERT(!(reinterpret_cast(addr) & alignOffsetMask)); - // If the client passed null as the address, choose a good one. - if (!addr) { - addr = getRandomPageBase(); - addr = reinterpret_cast(reinterpret_cast(addr) & alignBaseMask); - } - - // The common case, which is also the least work we can do, is that the - // address and length are suitable. Just try it. - void* ret = systemAllocPages(addr, len); - // If the alignment is to our liking, we're done. - if (!ret || !(reinterpret_cast(ret) & alignOffsetMask)) - return ret; - - // Annoying. Unmap and map a larger range to be sure to succeed on the - // second, slower attempt. - freePages(ret, len); +void* allocPages(void* addr, size_t len, size_t align) { + ASSERT(len >= kPageAllocationGranularity); + ASSERT(!(len & kPageAllocationGranularityOffsetMask)); + ASSERT(align >= kPageAllocationGranularity); + ASSERT(!(align & kPageAllocationGranularityOffsetMask)); + ASSERT(!(reinterpret_cast(addr) & + kPageAllocationGranularityOffsetMask)); + size_t alignOffsetMask = align - 1; + size_t alignBaseMask = ~alignOffsetMask; + ASSERT(!(reinterpret_cast(addr) & alignOffsetMask)); + // If the client passed null as the address, choose a good one. + if (!addr) { + addr = getRandomPageBase(); + addr = reinterpret_cast(reinterpret_cast(addr) & + alignBaseMask); + } + + // The common case, which is also the least work we can do, is that the + // address and length are suitable. Just try it. + void* ret = systemAllocPages(addr, len); + // If the alignment is to our liking, we're done. + if (!ret || !(reinterpret_cast(ret) & alignOffsetMask)) + return ret; - size_t tryLen = len + (align - kPageAllocationGranularity); - RELEASE_ASSERT(tryLen > len); - - // We loop to cater for the unlikely case where another thread maps on top - // of the aligned location we choose. - int count = 0; - while (count++ < 100) { - ret = systemAllocPages(addr, tryLen); - if (!ret) - return 0; - // We can now try and trim out a subset of the mapping. - addr = reinterpret_cast((reinterpret_cast(ret) + alignOffsetMask) & alignBaseMask); - - // On POSIX systems, we can trim the oversized mapping to fit exactly. - // This will always work on POSIX systems. - if (trimMapping(ret, tryLen, addr, len)) - return addr; - - // On Windows, you can't trim an existing mapping so we unmap and remap - // a subset. We used to do for all platforms, but OSX 10.8 has a - // broken mmap() that ignores address hints for valid, unused addresses. - freePages(ret, tryLen); - ret = systemAllocPages(addr, len); - if (ret == addr || !ret) - return ret; - - // Unlikely race / collision. Do the simple thing and just start again. - freePages(ret, len); - addr = getRandomPageBase(); - addr = reinterpret_cast(reinterpret_cast(addr) & alignBaseMask); - } - IMMEDIATE_CRASH(); - return 0; + // Annoying. Unmap and map a larger range to be sure to succeed on the + // second, slower attempt. + freePages(ret, len); + + size_t tryLen = len + (align - kPageAllocationGranularity); + RELEASE_ASSERT(tryLen > len); + + // We loop to cater for the unlikely case where another thread maps on top + // of the aligned location we choose. + int count = 0; + while (count++ < 100) { + ret = systemAllocPages(addr, tryLen); + if (!ret) + return 0; + // We can now try and trim out a subset of the mapping. + addr = reinterpret_cast( + (reinterpret_cast(ret) + alignOffsetMask) & alignBaseMask); + + // On POSIX systems, we can trim the oversized mapping to fit exactly. + // This will always work on POSIX systems. + if (trimMapping(ret, tryLen, addr, len)) + return addr; + + // On Windows, you can't trim an existing mapping so we unmap and remap + // a subset. We used to do for all platforms, but OSX 10.8 has a + // broken mmap() that ignores address hints for valid, unused addresses. + freePages(ret, tryLen); + ret = systemAllocPages(addr, len); + if (ret == addr || !ret) + return ret; + + // Unlikely race / collision. Do the simple thing and just start again. + freePages(ret, len); + addr = getRandomPageBase(); + addr = reinterpret_cast(reinterpret_cast(addr) & + alignBaseMask); + } + IMMEDIATE_CRASH(); + return 0; } -void freePages(void* addr, size_t len) -{ - ASSERT(!(reinterpret_cast(addr) & kPageAllocationGranularityOffsetMask)); - ASSERT(!(len & kPageAllocationGranularityOffsetMask)); +void freePages(void* addr, size_t len) { + ASSERT(!(reinterpret_cast(addr) & + kPageAllocationGranularityOffsetMask)); + ASSERT(!(len & kPageAllocationGranularityOffsetMask)); #if OS(POSIX) - int ret = munmap(addr, len); - RELEASE_ASSERT(!ret); + int ret = munmap(addr, len); + RELEASE_ASSERT(!ret); #else - BOOL ret = VirtualFree(addr, 0, MEM_RELEASE); - RELEASE_ASSERT(ret); + BOOL ret = VirtualFree(addr, 0, MEM_RELEASE); + RELEASE_ASSERT(ret); #endif } -void setSystemPagesInaccessible(void* addr, size_t len) -{ - ASSERT(!(len & kSystemPageOffsetMask)); +void setSystemPagesInaccessible(void* addr, size_t len) { + ASSERT(!(len & kSystemPageOffsetMask)); #if OS(POSIX) - int ret = mprotect(addr, len, PROT_NONE); - RELEASE_ASSERT(!ret); + int ret = mprotect(addr, len, PROT_NONE); + RELEASE_ASSERT(!ret); #else - BOOL ret = VirtualFree(addr, len, MEM_DECOMMIT); - RELEASE_ASSERT(ret); + BOOL ret = VirtualFree(addr, len, MEM_DECOMMIT); + RELEASE_ASSERT(ret); #endif } -void setSystemPagesAccessible(void* addr, size_t len) -{ - ASSERT(!(len & kSystemPageOffsetMask)); +void setSystemPagesAccessible(void* addr, size_t len) { + ASSERT(!(len & kSystemPageOffsetMask)); #if OS(POSIX) - int ret = mprotect(addr, len, PROT_READ | PROT_WRITE); - RELEASE_ASSERT(!ret); + int ret = mprotect(addr, len, PROT_READ | PROT_WRITE); + RELEASE_ASSERT(!ret); #else - void* ret = VirtualAlloc(addr, len, MEM_COMMIT, PAGE_READWRITE); - RELEASE_ASSERT(ret); + void* ret = VirtualAlloc(addr, len, MEM_COMMIT, PAGE_READWRITE); + RELEASE_ASSERT(ret); #endif } -void decommitSystemPages(void* addr, size_t len) -{ - ASSERT(!(len & kSystemPageOffsetMask)); +void decommitSystemPages(void* addr, size_t len) { + ASSERT(!(len & kSystemPageOffsetMask)); #if OS(POSIX) - int ret = madvise(addr, len, MADV_FREE); - RELEASE_ASSERT(!ret); + int ret = madvise(addr, len, MADV_FREE); + RELEASE_ASSERT(!ret); #else - setSystemPagesInaccessible(addr, len); + setSystemPagesInaccessible(addr, len); #endif } -void recommitSystemPages(void* addr, size_t len) -{ - ASSERT(!(len & kSystemPageOffsetMask)); +void recommitSystemPages(void* addr, size_t len) { + ASSERT(!(len & kSystemPageOffsetMask)); #if OS(POSIX) - (void) addr; + (void)addr; #else - setSystemPagesAccessible(addr, len); + setSystemPagesAccessible(addr, len); #endif } -} // namespace WTF - +} // namespace WTF diff --git a/sky/engine/wtf/PageAllocator.h b/sky/engine/wtf/PageAllocator.h index 69396bce8689a..63af754f708bc 100644 --- a/sky/engine/wtf/PageAllocator.h +++ b/sky/engine/wtf/PageAllocator.h @@ -39,13 +39,16 @@ namespace WTF { #if OS(WIN) -static const size_t kPageAllocationGranularityShift = 16; // 64KB +static const size_t kPageAllocationGranularityShift = 16; // 64KB #else -static const size_t kPageAllocationGranularityShift = 12; // 4KB +static const size_t kPageAllocationGranularityShift = 12; // 4KB #endif -static const size_t kPageAllocationGranularity = 1 << kPageAllocationGranularityShift; -static const size_t kPageAllocationGranularityOffsetMask = kPageAllocationGranularity - 1; -static const size_t kPageAllocationGranularityBaseMask = ~kPageAllocationGranularityOffsetMask; +static const size_t kPageAllocationGranularity = + 1 << kPageAllocationGranularityShift; +static const size_t kPageAllocationGranularityOffsetMask = + kPageAllocationGranularity - 1; +static const size_t kPageAllocationGranularityBaseMask = + ~kPageAllocationGranularityOffsetMask; // All Blink-supported systems have 4096 sized system pages and can handle // permissions and commit / decommit at this granularity. @@ -87,8 +90,9 @@ WTF_EXPORT void setSystemPagesAccessible(void* addr, size_t len); // Clients should not make any assumptions about the contents of decommitted // system pages, before or after they write to the page. The only guarantee // provided is that the contents of the system page will be deterministic again -// after recommitting and writing to it. In particlar note that system pages are// not guaranteed to be zero-filled upon re-commit. -// len must be a multiple of kSystemPageSize bytes. +// after recommitting and writing to it. In particlar note that system pages +// are// not guaranteed to be zero-filled upon re-commit. len must be a multiple +// of kSystemPageSize bytes. WTF_EXPORT void decommitSystemPages(void* addr, size_t len); // Recommit one or more system pages. Decommitted system pages must be @@ -97,6 +101,6 @@ WTF_EXPORT void decommitSystemPages(void* addr, size_t len); // len must be a multiple of kSystemPageSize bytes. WTF_EXPORT void recommitSystemPages(void* addr, size_t len); -} // namespace WTF +} // namespace WTF #endif // SKY_ENGINE_WTF_PAGEALLOCATOR_H_ diff --git a/sky/engine/wtf/PartitionAlloc.cpp b/sky/engine/wtf/PartitionAlloc.cpp index d531a0fffc6cc..aaf56a830af22 100644 --- a/sky/engine/wtf/PartitionAlloc.cpp +++ b/sky/engine/wtf/PartitionAlloc.cpp @@ -38,16 +38,26 @@ // Two partition pages are used as guard / metadata page so make sure the super // page size is bigger. -COMPILE_ASSERT(WTF::kPartitionPageSize * 4 <= WTF::kSuperPageSize, ok_super_page_size); -COMPILE_ASSERT(!(WTF::kSuperPageSize % WTF::kPartitionPageSize), ok_super_page_multiple); +COMPILE_ASSERT(WTF::kPartitionPageSize * 4 <= WTF::kSuperPageSize, + ok_super_page_size); +COMPILE_ASSERT(!(WTF::kSuperPageSize % WTF::kPartitionPageSize), + ok_super_page_multiple); // Four system pages gives us room to hack out a still-guard-paged piece // of metadata in the middle of a guard partition page. -COMPILE_ASSERT(WTF::kSystemPageSize * 4 <= WTF::kPartitionPageSize, ok_partition_page_size); -COMPILE_ASSERT(!(WTF::kPartitionPageSize % WTF::kSystemPageSize), ok_partition_page_multiple); -COMPILE_ASSERT(sizeof(WTF::PartitionPage) <= WTF::kPageMetadataSize, PartitionPage_not_too_big); -COMPILE_ASSERT(sizeof(WTF::PartitionBucket) <= WTF::kPageMetadataSize, PartitionBucket_not_too_big); -COMPILE_ASSERT(sizeof(WTF::PartitionSuperPageExtentEntry) <= WTF::kPageMetadataSize, PartitionSuperPageExtentEntry_not_too_big); -COMPILE_ASSERT(WTF::kPageMetadataSize * WTF::kNumPartitionPagesPerSuperPage <= WTF::kSystemPageSize, page_metadata_fits_in_hole); +COMPILE_ASSERT(WTF::kSystemPageSize * 4 <= WTF::kPartitionPageSize, + ok_partition_page_size); +COMPILE_ASSERT(!(WTF::kPartitionPageSize % WTF::kSystemPageSize), + ok_partition_page_multiple); +COMPILE_ASSERT(sizeof(WTF::PartitionPage) <= WTF::kPageMetadataSize, + PartitionPage_not_too_big); +COMPILE_ASSERT(sizeof(WTF::PartitionBucket) <= WTF::kPageMetadataSize, + PartitionBucket_not_too_big); +COMPILE_ASSERT(sizeof(WTF::PartitionSuperPageExtentEntry) <= + WTF::kPageMetadataSize, + PartitionSuperPageExtentEntry_not_too_big); +COMPILE_ASSERT(WTF::kPageMetadataSize* WTF::kNumPartitionPagesPerSuperPage <= + WTF::kSystemPageSize, + page_metadata_fits_in_hole); // Check that some of our zanier calculations worked out as expected. COMPILE_ASSERT(WTF::kGenericSmallestBucket == 8, generic_smallest_bucket); COMPILE_ASSERT(WTF::kGenericMaxBucketed == 983040, generic_max_bucketed); @@ -59,442 +69,477 @@ bool PartitionRootBase::gInitialized = false; PartitionPage PartitionRootBase::gSeedPage; PartitionBucket PartitionRootBase::gPagedBucket; -static size_t partitionBucketNumSystemPages(size_t size) -{ - // This works out reasonably for the current bucket sizes of the generic - // allocator, and the current values of partition page size and constants. - // Specifically, we have enough room to always pack the slots perfectly into - // some number of system pages. The only waste is the waste associated with - // unfaulted pages (i.e. wasted address space). - // TODO: we end up using a lot of system pages for very small sizes. For - // example, we'll use 12 system pages for slot size 24. The slot size is - // so small that the waste would be tiny with just 4, or 1, system pages. - // Later, we can investigate whether there are anti-fragmentation benefits - // to using fewer system pages. - double bestWasteRatio = 1.0f; - size_t bestPages = 0; - if (size > kMaxSystemPagesPerSlotSpan * kSystemPageSize) { - ASSERT(!(size % kSystemPageSize)); - return size / kSystemPageSize; +static size_t partitionBucketNumSystemPages(size_t size) { + // This works out reasonably for the current bucket sizes of the generic + // allocator, and the current values of partition page size and constants. + // Specifically, we have enough room to always pack the slots perfectly into + // some number of system pages. The only waste is the waste associated with + // unfaulted pages (i.e. wasted address space). + // TODO: we end up using a lot of system pages for very small sizes. For + // example, we'll use 12 system pages for slot size 24. The slot size is + // so small that the waste would be tiny with just 4, or 1, system pages. + // Later, we can investigate whether there are anti-fragmentation benefits + // to using fewer system pages. + double bestWasteRatio = 1.0f; + size_t bestPages = 0; + if (size > kMaxSystemPagesPerSlotSpan * kSystemPageSize) { + ASSERT(!(size % kSystemPageSize)); + return size / kSystemPageSize; + } + ASSERT(size <= kMaxSystemPagesPerSlotSpan * kSystemPageSize); + for (size_t i = kNumSystemPagesPerPartitionPage - 1; + i <= kMaxSystemPagesPerSlotSpan; ++i) { + size_t pageSize = kSystemPageSize * i; + size_t numSlots = pageSize / size; + size_t waste = pageSize - (numSlots * size); + // Leaving a page unfaulted is not free; the page will occupy an empty page + // table entry. Make a simple attempt to account for that. + size_t numRemainderPages = i & (kNumSystemPagesPerPartitionPage - 1); + size_t numUnfaultedPages = + numRemainderPages + ? (kNumSystemPagesPerPartitionPage - numRemainderPages) + : 0; + waste += sizeof(void*) * numUnfaultedPages; + double wasteRatio = (double)waste / (double)pageSize; + if (wasteRatio < bestWasteRatio) { + bestWasteRatio = wasteRatio; + bestPages = i; } - ASSERT(size <= kMaxSystemPagesPerSlotSpan * kSystemPageSize); - for (size_t i = kNumSystemPagesPerPartitionPage - 1; i <= kMaxSystemPagesPerSlotSpan; ++i) { - size_t pageSize = kSystemPageSize * i; - size_t numSlots = pageSize / size; - size_t waste = pageSize - (numSlots * size); - // Leaving a page unfaulted is not free; the page will occupy an empty page table entry. - // Make a simple attempt to account for that. - size_t numRemainderPages = i & (kNumSystemPagesPerPartitionPage - 1); - size_t numUnfaultedPages = numRemainderPages ? (kNumSystemPagesPerPartitionPage - numRemainderPages) : 0; - waste += sizeof(void*) * numUnfaultedPages; - double wasteRatio = (double) waste / (double) pageSize; - if (wasteRatio < bestWasteRatio) { - bestWasteRatio = wasteRatio; - bestPages = i; - } - } - ASSERT(bestPages > 0); - return bestPages; + } + ASSERT(bestPages > 0); + return bestPages; } -static void parititonAllocBaseInit(PartitionRootBase* root) -{ - ASSERT(!root->initialized); - - spinLockLock(&PartitionRootBase::gInitializedLock); - if (!PartitionRootBase::gInitialized) { - PartitionRootBase::gInitialized = true; - // We mark the seed page as free to make sure it is skipped by our - // logic to find a new active page. - PartitionRootBase::gPagedBucket.activePagesHead = &PartitionRootGeneric::gSeedPage; - } - spinLockUnlock(&PartitionRootBase::gInitializedLock); - - root->initialized = true; - root->totalSizeOfCommittedPages = 0; - root->totalSizeOfSuperPages = 0; - root->nextSuperPage = 0; - root->nextPartitionPage = 0; - root->nextPartitionPageEnd = 0; - root->firstExtent = 0; - root->currentExtent = 0; - - memset(&root->globalEmptyPageRing, '\0', sizeof(root->globalEmptyPageRing)); - root->globalEmptyPageRingIndex = 0; - - // This is a "magic" value so we can test if a root pointer is valid. - root->invertedSelf = ~reinterpret_cast(root); +static void parititonAllocBaseInit(PartitionRootBase* root) { + ASSERT(!root->initialized); + + spinLockLock(&PartitionRootBase::gInitializedLock); + if (!PartitionRootBase::gInitialized) { + PartitionRootBase::gInitialized = true; + // We mark the seed page as free to make sure it is skipped by our + // logic to find a new active page. + PartitionRootBase::gPagedBucket.activePagesHead = + &PartitionRootGeneric::gSeedPage; + } + spinLockUnlock(&PartitionRootBase::gInitializedLock); + + root->initialized = true; + root->totalSizeOfCommittedPages = 0; + root->totalSizeOfSuperPages = 0; + root->nextSuperPage = 0; + root->nextPartitionPage = 0; + root->nextPartitionPageEnd = 0; + root->firstExtent = 0; + root->currentExtent = 0; + + memset(&root->globalEmptyPageRing, '\0', sizeof(root->globalEmptyPageRing)); + root->globalEmptyPageRingIndex = 0; + + // This is a "magic" value so we can test if a root pointer is valid. + root->invertedSelf = ~reinterpret_cast(root); } -static void partitionBucketInitBase(PartitionBucket* bucket, PartitionRootBase* root) -{ - bucket->activePagesHead = &PartitionRootGeneric::gSeedPage; - bucket->freePagesHead = 0; - bucket->numFullPages = 0; - bucket->numSystemPagesPerSlotSpan = partitionBucketNumSystemPages(bucket->slotSize); +static void partitionBucketInitBase(PartitionBucket* bucket, + PartitionRootBase* root) { + bucket->activePagesHead = &PartitionRootGeneric::gSeedPage; + bucket->freePagesHead = 0; + bucket->numFullPages = 0; + bucket->numSystemPagesPerSlotSpan = + partitionBucketNumSystemPages(bucket->slotSize); } -void partitionAllocInit(PartitionRoot* root, size_t numBuckets, size_t maxAllocation) -{ - parititonAllocBaseInit(root); - - root->numBuckets = numBuckets; - root->maxAllocation = maxAllocation; - size_t i; - for (i = 0; i < root->numBuckets; ++i) { - PartitionBucket* bucket = &root->buckets()[i]; - if (!i) - bucket->slotSize = kAllocationGranularity; - else - bucket->slotSize = i << kBucketShift; - partitionBucketInitBase(bucket, root); - } +void partitionAllocInit(PartitionRoot* root, + size_t numBuckets, + size_t maxAllocation) { + parititonAllocBaseInit(root); + + root->numBuckets = numBuckets; + root->maxAllocation = maxAllocation; + size_t i; + for (i = 0; i < root->numBuckets; ++i) { + PartitionBucket* bucket = &root->buckets()[i]; + if (!i) + bucket->slotSize = kAllocationGranularity; + else + bucket->slotSize = i << kBucketShift; + partitionBucketInitBase(bucket, root); + } } -void partitionAllocGenericInit(PartitionRootGeneric* root) -{ - parititonAllocBaseInit(root); - - root->lock = 0; - - // Precalculate some shift and mask constants used in the hot path. - // Example: malloc(41) == 101001 binary. - // Order is 6 (1 << 6-1)==32 is highest bit set. - // orderIndex is the next three MSB == 010 == 2. - // subOrderIndexMask is a mask for the remaining bits == 11 (masking to 01 for the subOrderIndex). - size_t order; - for (order = 0; order <= kBitsPerSizet; ++order) { - size_t orderIndexShift; - if (order < kGenericNumBucketsPerOrderBits + 1) - orderIndexShift = 0; - else - orderIndexShift = order - (kGenericNumBucketsPerOrderBits + 1); - root->orderIndexShifts[order] = orderIndexShift; - size_t subOrderIndexMask; - if (order == kBitsPerSizet) { - // This avoids invoking undefined behavior for an excessive shift. - subOrderIndexMask = static_cast(-1) >> (kGenericNumBucketsPerOrderBits + 1); - } else { - subOrderIndexMask = ((1 << order) - 1) >> (kGenericNumBucketsPerOrderBits + 1); - } - root->orderSubIndexMasks[order] = subOrderIndexMask; +void partitionAllocGenericInit(PartitionRootGeneric* root) { + parititonAllocBaseInit(root); + + root->lock = 0; + + // Precalculate some shift and mask constants used in the hot path. + // Example: malloc(41) == 101001 binary. + // Order is 6 (1 << 6-1)==32 is highest bit set. + // orderIndex is the next three MSB == 010 == 2. + // subOrderIndexMask is a mask for the remaining bits == 11 (masking to 01 for + // the subOrderIndex). + size_t order; + for (order = 0; order <= kBitsPerSizet; ++order) { + size_t orderIndexShift; + if (order < kGenericNumBucketsPerOrderBits + 1) + orderIndexShift = 0; + else + orderIndexShift = order - (kGenericNumBucketsPerOrderBits + 1); + root->orderIndexShifts[order] = orderIndexShift; + size_t subOrderIndexMask; + if (order == kBitsPerSizet) { + // This avoids invoking undefined behavior for an excessive shift. + subOrderIndexMask = + static_cast(-1) >> (kGenericNumBucketsPerOrderBits + 1); + } else { + subOrderIndexMask = + ((1 << order) - 1) >> (kGenericNumBucketsPerOrderBits + 1); } - - // Set up the actual usable buckets first. - // Note that typical values (i.e. min allocation size of 8) will result in - // invalid buckets (size==9 etc. or more generally, size is not a multiple - // of the smallest allocation granularity). - // We avoid them in the bucket lookup map, but we tolerate them to keep the - // code simpler and the structures more generic. - size_t i, j; - size_t currentSize = kGenericSmallestBucket; - size_t currentIncrement = kGenericSmallestBucket >> kGenericNumBucketsPerOrderBits; - PartitionBucket* bucket = &root->buckets[0]; - for (i = 0; i < kGenericNumBucketedOrders; ++i) { - for (j = 0; j < kGenericNumBucketsPerOrder; ++j) { - bucket->slotSize = currentSize; - partitionBucketInitBase(bucket, root); - // Disable invalid buckets so that touching them faults. - if (currentSize % kGenericSmallestBucket) - bucket->activePagesHead = 0; - currentSize += currentIncrement; - ++bucket; - } - currentIncrement <<= 1; + root->orderSubIndexMasks[order] = subOrderIndexMask; + } + + // Set up the actual usable buckets first. + // Note that typical values (i.e. min allocation size of 8) will result in + // invalid buckets (size==9 etc. or more generally, size is not a multiple + // of the smallest allocation granularity). + // We avoid them in the bucket lookup map, but we tolerate them to keep the + // code simpler and the structures more generic. + size_t i, j; + size_t currentSize = kGenericSmallestBucket; + size_t currentIncrement = + kGenericSmallestBucket >> kGenericNumBucketsPerOrderBits; + PartitionBucket* bucket = &root->buckets[0]; + for (i = 0; i < kGenericNumBucketedOrders; ++i) { + for (j = 0; j < kGenericNumBucketsPerOrder; ++j) { + bucket->slotSize = currentSize; + partitionBucketInitBase(bucket, root); + // Disable invalid buckets so that touching them faults. + if (currentSize % kGenericSmallestBucket) + bucket->activePagesHead = 0; + currentSize += currentIncrement; + ++bucket; } - ASSERT(currentSize == 1 << kGenericMaxBucketedOrder); - ASSERT(bucket == &root->buckets[0] + (kGenericNumBucketedOrders * kGenericNumBucketsPerOrder)); - - // Then set up the fast size -> bucket lookup table. - bucket = &root->buckets[0]; - PartitionBucket** bucketPtr = &root->bucketLookups[0]; - for (order = 0; order <= kBitsPerSizet; ++order) { - for (j = 0; j < kGenericNumBucketsPerOrder; ++j) { - if (order < kGenericMinBucketedOrder) { - // Use the bucket of finest granularity for malloc(0) etc. - *bucketPtr++ = &root->buckets[0]; - } else if (order > kGenericMaxBucketedOrder) { - *bucketPtr++ = &PartitionRootGeneric::gPagedBucket; - } else { - PartitionBucket* validBucket = bucket; - // Skip over invalid buckets. - while (validBucket->slotSize % kGenericSmallestBucket) - validBucket++; - *bucketPtr++ = validBucket; - bucket++; - } - } + currentIncrement <<= 1; + } + ASSERT(currentSize == 1 << kGenericMaxBucketedOrder); + ASSERT(bucket == &root->buckets[0] + (kGenericNumBucketedOrders * + kGenericNumBucketsPerOrder)); + + // Then set up the fast size -> bucket lookup table. + bucket = &root->buckets[0]; + PartitionBucket** bucketPtr = &root->bucketLookups[0]; + for (order = 0; order <= kBitsPerSizet; ++order) { + for (j = 0; j < kGenericNumBucketsPerOrder; ++j) { + if (order < kGenericMinBucketedOrder) { + // Use the bucket of finest granularity for malloc(0) etc. + *bucketPtr++ = &root->buckets[0]; + } else if (order > kGenericMaxBucketedOrder) { + *bucketPtr++ = &PartitionRootGeneric::gPagedBucket; + } else { + PartitionBucket* validBucket = bucket; + // Skip over invalid buckets. + while (validBucket->slotSize % kGenericSmallestBucket) + validBucket++; + *bucketPtr++ = validBucket; + bucket++; + } } - ASSERT(bucket == &root->buckets[0] + (kGenericNumBucketedOrders * kGenericNumBucketsPerOrder)); - ASSERT(bucketPtr == &root->bucketLookups[0] + ((kBitsPerSizet + 1) * kGenericNumBucketsPerOrder)); - // And there's one last bucket lookup that will be hit for e.g. malloc(-1), - // which tries to overflow to a non-existant order. - *bucketPtr = &PartitionRootGeneric::gPagedBucket; + } + ASSERT(bucket == &root->buckets[0] + (kGenericNumBucketedOrders * + kGenericNumBucketsPerOrder)); + ASSERT(bucketPtr == &root->bucketLookups[0] + + ((kBitsPerSizet + 1) * kGenericNumBucketsPerOrder)); + // And there's one last bucket lookup that will be hit for e.g. malloc(-1), + // which tries to overflow to a non-existant order. + *bucketPtr = &PartitionRootGeneric::gPagedBucket; } -static bool partitionAllocShutdownBucket(PartitionBucket* bucket) -{ - // Failure here indicates a memory leak. - bool noLeaks = !bucket->numFullPages; - PartitionPage* page = bucket->activePagesHead; - while (page) { - if (page->numAllocatedSlots) - noLeaks = false; - page = page->nextPage; - } - - return noLeaks; +static bool partitionAllocShutdownBucket(PartitionBucket* bucket) { + // Failure here indicates a memory leak. + bool noLeaks = !bucket->numFullPages; + PartitionPage* page = bucket->activePagesHead; + while (page) { + if (page->numAllocatedSlots) + noLeaks = false; + page = page->nextPage; + } + + return noLeaks; } -static void partitionAllocBaseShutdown(PartitionRootBase* root) -{ - ASSERT(root->initialized); - root->initialized = false; - - // Now that we've examined all partition pages in all buckets, it's safe - // to free all our super pages. We first collect the super page pointers - // on the stack because some of them are themselves store in super pages. - char* superPages[kMaxPartitionSize / kSuperPageSize]; - size_t numSuperPages = 0; - PartitionSuperPageExtentEntry* entry = root->firstExtent; - while (entry) { - char* superPage = entry->superPageBase; - while (superPage != entry->superPagesEnd) { - superPages[numSuperPages] = superPage; - numSuperPages++; - superPage += kSuperPageSize; - } - entry = entry->next; +static void partitionAllocBaseShutdown(PartitionRootBase* root) { + ASSERT(root->initialized); + root->initialized = false; + + // Now that we've examined all partition pages in all buckets, it's safe + // to free all our super pages. We first collect the super page pointers + // on the stack because some of them are themselves store in super pages. + char* superPages[kMaxPartitionSize / kSuperPageSize]; + size_t numSuperPages = 0; + PartitionSuperPageExtentEntry* entry = root->firstExtent; + while (entry) { + char* superPage = entry->superPageBase; + while (superPage != entry->superPagesEnd) { + superPages[numSuperPages] = superPage; + numSuperPages++; + superPage += kSuperPageSize; } - ASSERT(numSuperPages == root->totalSizeOfSuperPages / kSuperPageSize); - for (size_t i = 0; i < numSuperPages; ++i) - freePages(superPages[i], kSuperPageSize); + entry = entry->next; + } + ASSERT(numSuperPages == root->totalSizeOfSuperPages / kSuperPageSize); + for (size_t i = 0; i < numSuperPages; ++i) + freePages(superPages[i], kSuperPageSize); } -bool partitionAllocShutdown(PartitionRoot* root) -{ - bool noLeaks = true; - size_t i; - for (i = 0; i < root->numBuckets; ++i) { - PartitionBucket* bucket = &root->buckets()[i]; - if (!partitionAllocShutdownBucket(bucket)) - noLeaks = false; - } - - partitionAllocBaseShutdown(root); - return noLeaks; +bool partitionAllocShutdown(PartitionRoot* root) { + bool noLeaks = true; + size_t i; + for (i = 0; i < root->numBuckets; ++i) { + PartitionBucket* bucket = &root->buckets()[i]; + if (!partitionAllocShutdownBucket(bucket)) + noLeaks = false; + } + + partitionAllocBaseShutdown(root); + return noLeaks; } -bool partitionAllocGenericShutdown(PartitionRootGeneric* root) -{ - bool noLeaks = true; - size_t i; - for (i = 0; i < kGenericNumBucketedOrders * kGenericNumBucketsPerOrder; ++i) { - PartitionBucket* bucket = &root->buckets[i]; - if (!partitionAllocShutdownBucket(bucket)) - noLeaks = false; - } - partitionAllocBaseShutdown(root); - return noLeaks; +bool partitionAllocGenericShutdown(PartitionRootGeneric* root) { + bool noLeaks = true; + size_t i; + for (i = 0; i < kGenericNumBucketedOrders * kGenericNumBucketsPerOrder; ++i) { + PartitionBucket* bucket = &root->buckets[i]; + if (!partitionAllocShutdownBucket(bucket)) + noLeaks = false; + } + partitionAllocBaseShutdown(root); + return noLeaks; } -static NEVER_INLINE void partitionOutOfMemory() -{ - IMMEDIATE_CRASH(); +static NEVER_INLINE void partitionOutOfMemory() { + IMMEDIATE_CRASH(); } -static NEVER_INLINE void partitionFull() -{ - IMMEDIATE_CRASH(); +static NEVER_INLINE void partitionFull() { + IMMEDIATE_CRASH(); } -static ALWAYS_INLINE void partitionDecommitSystemPages(PartitionRootBase* root, void* addr, size_t len) -{ - decommitSystemPages(addr, len); - ASSERT(root->totalSizeOfCommittedPages > len); - root->totalSizeOfCommittedPages -= len; +static ALWAYS_INLINE void partitionDecommitSystemPages(PartitionRootBase* root, + void* addr, + size_t len) { + decommitSystemPages(addr, len); + ASSERT(root->totalSizeOfCommittedPages > len); + root->totalSizeOfCommittedPages -= len; } -static ALWAYS_INLINE void partitionRecommitSystemPages(PartitionRootBase* root, void* addr, size_t len) -{ - recommitSystemPages(addr, len); - root->totalSizeOfCommittedPages += len; +static ALWAYS_INLINE void partitionRecommitSystemPages(PartitionRootBase* root, + void* addr, + size_t len) { + recommitSystemPages(addr, len); + root->totalSizeOfCommittedPages += len; } -static ALWAYS_INLINE void* partitionAllocPartitionPages(PartitionRootBase* root, int flags, size_t numPartitionPages) -{ - ASSERT(!(reinterpret_cast(root->nextPartitionPage) % kPartitionPageSize)); - ASSERT(!(reinterpret_cast(root->nextPartitionPageEnd) % kPartitionPageSize)); - RELEASE_ASSERT(numPartitionPages <= kNumPartitionPagesPerSuperPage); - size_t totalSize = kPartitionPageSize * numPartitionPages; - root->totalSizeOfCommittedPages += totalSize; - size_t numPartitionPagesLeft = (root->nextPartitionPageEnd - root->nextPartitionPage) >> kPartitionPageShift; - if (LIKELY(numPartitionPagesLeft >= numPartitionPages)) { - // In this case, we can still hand out pages from the current super page - // allocation. - char* ret = root->nextPartitionPage; - root->nextPartitionPage += totalSize; - return ret; - } - - // Need a new super page. - root->totalSizeOfSuperPages += kSuperPageSize; - if (root->totalSizeOfSuperPages > kMaxPartitionSize) - partitionFull(); - char* requestedAddress = root->nextSuperPage; - char* superPage = reinterpret_cast(allocPages(requestedAddress, kSuperPageSize, kSuperPageSize)); - if (UNLIKELY(!superPage)) { - if (flags & PartitionAllocReturnNull) - return 0; - partitionOutOfMemory(); - } - root->nextSuperPage = superPage + kSuperPageSize; - char* ret = superPage + kPartitionPageSize; - root->nextPartitionPage = ret + totalSize; - root->nextPartitionPageEnd = root->nextSuperPage - kPartitionPageSize; - // Make the first partition page in the super page a guard page, but leave a - // hole in the middle. - // This is where we put page metadata and also a tiny amount of extent - // metadata. - setSystemPagesInaccessible(superPage, kSystemPageSize); - setSystemPagesInaccessible(superPage + (kSystemPageSize * 2), kPartitionPageSize - (kSystemPageSize * 2)); - // Also make the last partition page a guard page. - setSystemPagesInaccessible(superPage + (kSuperPageSize - kPartitionPageSize), kPartitionPageSize); - - // If we were after a specific address, but didn't get it, assume that - // the system chose a lousy address and re-randomize the next +static ALWAYS_INLINE void* partitionAllocPartitionPages( + PartitionRootBase* root, + int flags, + size_t numPartitionPages) { + ASSERT(!(reinterpret_cast(root->nextPartitionPage) % + kPartitionPageSize)); + ASSERT(!(reinterpret_cast(root->nextPartitionPageEnd) % + kPartitionPageSize)); + RELEASE_ASSERT(numPartitionPages <= kNumPartitionPagesPerSuperPage); + size_t totalSize = kPartitionPageSize * numPartitionPages; + root->totalSizeOfCommittedPages += totalSize; + size_t numPartitionPagesLeft = + (root->nextPartitionPageEnd - root->nextPartitionPage) >> + kPartitionPageShift; + if (LIKELY(numPartitionPagesLeft >= numPartitionPages)) { + // In this case, we can still hand out pages from the current super page // allocation. - if (requestedAddress && requestedAddress != superPage) - root->nextSuperPage = 0; - - // We allocated a new super page so update super page metadata. - // First check if this is a new extent or not. - PartitionSuperPageExtentEntry* latestExtent = reinterpret_cast(partitionSuperPageToMetadataArea(superPage)); - PartitionSuperPageExtentEntry* currentExtent = root->currentExtent; - bool isNewExtent = (superPage != requestedAddress); - if (UNLIKELY(isNewExtent)) { - latestExtent->next = 0; - if (UNLIKELY(!currentExtent)) { - root->firstExtent = latestExtent; - } else { - ASSERT(currentExtent->superPageBase); - currentExtent->next = latestExtent; - } - root->currentExtent = latestExtent; - currentExtent = latestExtent; - currentExtent->superPageBase = superPage; - currentExtent->superPagesEnd = superPage + kSuperPageSize; + char* ret = root->nextPartitionPage; + root->nextPartitionPage += totalSize; + return ret; + } + + // Need a new super page. + root->totalSizeOfSuperPages += kSuperPageSize; + if (root->totalSizeOfSuperPages > kMaxPartitionSize) + partitionFull(); + char* requestedAddress = root->nextSuperPage; + char* superPage = reinterpret_cast( + allocPages(requestedAddress, kSuperPageSize, kSuperPageSize)); + if (UNLIKELY(!superPage)) { + if (flags & PartitionAllocReturnNull) + return 0; + partitionOutOfMemory(); + } + root->nextSuperPage = superPage + kSuperPageSize; + char* ret = superPage + kPartitionPageSize; + root->nextPartitionPage = ret + totalSize; + root->nextPartitionPageEnd = root->nextSuperPage - kPartitionPageSize; + // Make the first partition page in the super page a guard page, but leave a + // hole in the middle. + // This is where we put page metadata and also a tiny amount of extent + // metadata. + setSystemPagesInaccessible(superPage, kSystemPageSize); + setSystemPagesInaccessible(superPage + (kSystemPageSize * 2), + kPartitionPageSize - (kSystemPageSize * 2)); + // Also make the last partition page a guard page. + setSystemPagesInaccessible(superPage + (kSuperPageSize - kPartitionPageSize), + kPartitionPageSize); + + // If we were after a specific address, but didn't get it, assume that + // the system chose a lousy address and re-randomize the next + // allocation. + if (requestedAddress && requestedAddress != superPage) + root->nextSuperPage = 0; + + // We allocated a new super page so update super page metadata. + // First check if this is a new extent or not. + PartitionSuperPageExtentEntry* latestExtent = + reinterpret_cast( + partitionSuperPageToMetadataArea(superPage)); + PartitionSuperPageExtentEntry* currentExtent = root->currentExtent; + bool isNewExtent = (superPage != requestedAddress); + if (UNLIKELY(isNewExtent)) { + latestExtent->next = 0; + if (UNLIKELY(!currentExtent)) { + root->firstExtent = latestExtent; } else { - // We allocated next to an existing extent so just nudge the size up a little. - currentExtent->superPagesEnd += kSuperPageSize; - ASSERT(ret >= currentExtent->superPageBase && ret < currentExtent->superPagesEnd); + ASSERT(currentExtent->superPageBase); + currentExtent->next = latestExtent; } - // By storing the root in every extent metadata object, we have a fast way - // to go from a pointer within the partition to the root object. - latestExtent->root = root; - - return ret; + root->currentExtent = latestExtent; + currentExtent = latestExtent; + currentExtent->superPageBase = superPage; + currentExtent->superPagesEnd = superPage + kSuperPageSize; + } else { + // We allocated next to an existing extent so just nudge the size up a + // little. + currentExtent->superPagesEnd += kSuperPageSize; + ASSERT(ret >= currentExtent->superPageBase && + ret < currentExtent->superPagesEnd); + } + // By storing the root in every extent metadata object, we have a fast way + // to go from a pointer within the partition to the root object. + latestExtent->root = root; + + return ret; } -static ALWAYS_INLINE void partitionUnusePage(PartitionRootBase* root, PartitionPage* page) -{ - ASSERT(page->bucket->numSystemPagesPerSlotSpan); - void* addr = partitionPageToPointer(page); - partitionDecommitSystemPages(root, addr, page->bucket->numSystemPagesPerSlotSpan * kSystemPageSize); +static ALWAYS_INLINE void partitionUnusePage(PartitionRootBase* root, + PartitionPage* page) { + ASSERT(page->bucket->numSystemPagesPerSlotSpan); + void* addr = partitionPageToPointer(page); + partitionDecommitSystemPages( + root, addr, page->bucket->numSystemPagesPerSlotSpan * kSystemPageSize); } -static ALWAYS_INLINE size_t partitionBucketSlots(const PartitionBucket* bucket) -{ - return (bucket->numSystemPagesPerSlotSpan * kSystemPageSize) / bucket->slotSize; +static ALWAYS_INLINE size_t +partitionBucketSlots(const PartitionBucket* bucket) { + return (bucket->numSystemPagesPerSlotSpan * kSystemPageSize) / + bucket->slotSize; } -static ALWAYS_INLINE size_t partitionBucketPartitionPages(const PartitionBucket* bucket) -{ - return (bucket->numSystemPagesPerSlotSpan + (kNumSystemPagesPerPartitionPage - 1)) / kNumSystemPagesPerPartitionPage; +static ALWAYS_INLINE size_t +partitionBucketPartitionPages(const PartitionBucket* bucket) { + return (bucket->numSystemPagesPerSlotSpan + + (kNumSystemPagesPerPartitionPage - 1)) / + kNumSystemPagesPerPartitionPage; } -static ALWAYS_INLINE void partitionPageReset(PartitionPage* page, PartitionBucket* bucket) -{ - ASSERT(page != &PartitionRootGeneric::gSeedPage); - page->numAllocatedSlots = 0; - page->numUnprovisionedSlots = partitionBucketSlots(bucket); - ASSERT(page->numUnprovisionedSlots); - page->bucket = bucket; - page->nextPage = 0; - // NULLing the freelist is not strictly necessary but it makes an ASSERT in partitionPageFillFreelist simpler. - page->freelistHead = 0; - page->pageOffset = 0; - page->freeCacheIndex = -1; - size_t numPartitionPages = partitionBucketPartitionPages(bucket); - size_t i; - char* pageCharPtr = reinterpret_cast(page); - for (i = 1; i < numPartitionPages; ++i) { - pageCharPtr += kPageMetadataSize; - PartitionPage* secondaryPage = reinterpret_cast(pageCharPtr); - secondaryPage->pageOffset = i; - } +static ALWAYS_INLINE void partitionPageReset(PartitionPage* page, + PartitionBucket* bucket) { + ASSERT(page != &PartitionRootGeneric::gSeedPage); + page->numAllocatedSlots = 0; + page->numUnprovisionedSlots = partitionBucketSlots(bucket); + ASSERT(page->numUnprovisionedSlots); + page->bucket = bucket; + page->nextPage = 0; + // NULLing the freelist is not strictly necessary but it makes an ASSERT in + // partitionPageFillFreelist simpler. + page->freelistHead = 0; + page->pageOffset = 0; + page->freeCacheIndex = -1; + size_t numPartitionPages = partitionBucketPartitionPages(bucket); + size_t i; + char* pageCharPtr = reinterpret_cast(page); + for (i = 1; i < numPartitionPages; ++i) { + pageCharPtr += kPageMetadataSize; + PartitionPage* secondaryPage = + reinterpret_cast(pageCharPtr); + secondaryPage->pageOffset = i; + } } -static ALWAYS_INLINE char* partitionPageAllocAndFillFreelist(PartitionPage* page) -{ - ASSERT(page != &PartitionRootGeneric::gSeedPage); - size_t numSlots = page->numUnprovisionedSlots; - ASSERT(numSlots); - PartitionBucket* bucket = page->bucket; - // We should only get here when _every_ slot is either used or unprovisioned. - // (The third state is "on the freelist". If we have a non-empty freelist, we should not get here.) - ASSERT(numSlots + page->numAllocatedSlots == partitionBucketSlots(bucket)); - // Similarly, make explicitly sure that the freelist is empty. - ASSERT(!page->freelistHead); - ASSERT(page->numAllocatedSlots >= 0); - - size_t size = bucket->slotSize; - char* base = reinterpret_cast(partitionPageToPointer(page)); - char* returnObject = base + (size * page->numAllocatedSlots); - char* firstFreelistPointer = returnObject + size; - char* firstFreelistPointerExtent = firstFreelistPointer + sizeof(PartitionFreelistEntry*); - // Our goal is to fault as few system pages as possible. We calculate the - // page containing the "end" of the returned slot, and then allow freelist - // pointers to be written up to the end of that page. - char* subPageLimit = reinterpret_cast((reinterpret_cast(firstFreelistPointer) + kSystemPageOffsetMask) & kSystemPageBaseMask); - char* slotsLimit = returnObject + (size * page->numUnprovisionedSlots); - char* freelistLimit = subPageLimit; - if (UNLIKELY(slotsLimit < freelistLimit)) - freelistLimit = slotsLimit; - - size_t numNewFreelistEntries = 0; - if (LIKELY(firstFreelistPointerExtent <= freelistLimit)) { - // Only consider used space in the slot span. If we consider wasted - // space, we may get an off-by-one when a freelist pointer fits in the - // wasted space, but a slot does not. - // We know we can fit at least one freelist pointer. - numNewFreelistEntries = 1; - // Any further entries require space for the whole slot span. - numNewFreelistEntries += (freelistLimit - firstFreelistPointerExtent) / size; - } - - // We always return an object slot -- that's the +1 below. - // We do not neccessarily create any new freelist entries, because we cross sub page boundaries frequently for large bucket sizes. - ASSERT(numNewFreelistEntries + 1 <= numSlots); - numSlots -= (numNewFreelistEntries + 1); - page->numUnprovisionedSlots = numSlots; - page->numAllocatedSlots++; - - if (LIKELY(numNewFreelistEntries)) { - char* freelistPointer = firstFreelistPointer; - PartitionFreelistEntry* entry = reinterpret_cast(freelistPointer); - page->freelistHead = entry; - while (--numNewFreelistEntries) { - freelistPointer += size; - PartitionFreelistEntry* nextEntry = reinterpret_cast(freelistPointer); - entry->next = partitionFreelistMask(nextEntry); - entry = nextEntry; - } - entry->next = partitionFreelistMask(0); - } else { - page->freelistHead = 0; +static ALWAYS_INLINE char* partitionPageAllocAndFillFreelist( + PartitionPage* page) { + ASSERT(page != &PartitionRootGeneric::gSeedPage); + size_t numSlots = page->numUnprovisionedSlots; + ASSERT(numSlots); + PartitionBucket* bucket = page->bucket; + // We should only get here when _every_ slot is either used or unprovisioned. + // (The third state is "on the freelist". If we have a non-empty freelist, we + // should not get here.) + ASSERT(numSlots + page->numAllocatedSlots == partitionBucketSlots(bucket)); + // Similarly, make explicitly sure that the freelist is empty. + ASSERT(!page->freelistHead); + ASSERT(page->numAllocatedSlots >= 0); + + size_t size = bucket->slotSize; + char* base = reinterpret_cast(partitionPageToPointer(page)); + char* returnObject = base + (size * page->numAllocatedSlots); + char* firstFreelistPointer = returnObject + size; + char* firstFreelistPointerExtent = + firstFreelistPointer + sizeof(PartitionFreelistEntry*); + // Our goal is to fault as few system pages as possible. We calculate the + // page containing the "end" of the returned slot, and then allow freelist + // pointers to be written up to the end of that page. + char* subPageLimit = reinterpret_cast( + (reinterpret_cast(firstFreelistPointer) + + kSystemPageOffsetMask) & + kSystemPageBaseMask); + char* slotsLimit = returnObject + (size * page->numUnprovisionedSlots); + char* freelistLimit = subPageLimit; + if (UNLIKELY(slotsLimit < freelistLimit)) + freelistLimit = slotsLimit; + + size_t numNewFreelistEntries = 0; + if (LIKELY(firstFreelistPointerExtent <= freelistLimit)) { + // Only consider used space in the slot span. If we consider wasted + // space, we may get an off-by-one when a freelist pointer fits in the + // wasted space, but a slot does not. + // We know we can fit at least one freelist pointer. + numNewFreelistEntries = 1; + // Any further entries require space for the whole slot span. + numNewFreelistEntries += + (freelistLimit - firstFreelistPointerExtent) / size; + } + + // We always return an object slot -- that's the +1 below. + // We do not neccessarily create any new freelist entries, because we cross + // sub page boundaries frequently for large bucket sizes. + ASSERT(numNewFreelistEntries + 1 <= numSlots); + numSlots -= (numNewFreelistEntries + 1); + page->numUnprovisionedSlots = numSlots; + page->numAllocatedSlots++; + + if (LIKELY(numNewFreelistEntries)) { + char* freelistPointer = firstFreelistPointer; + PartitionFreelistEntry* entry = + reinterpret_cast(freelistPointer); + page->freelistHead = entry; + while (--numNewFreelistEntries) { + freelistPointer += size; + PartitionFreelistEntry* nextEntry = + reinterpret_cast(freelistPointer); + entry->next = partitionFreelistMask(nextEntry); + entry = nextEntry; } - return returnObject; + entry->next = partitionFreelistMask(0); + } else { + page->freelistHead = 0; + } + return returnObject; } // This helper function scans the active page list for a suitable new active @@ -505,480 +550,502 @@ static ALWAYS_INLINE char* partitionPageAllocAndFillFreelist(PartitionPage* page // As potential pages are scanned, they are tidied up according to their state. // Freed pages are swept on to the free page list and full pages are unlinked // from any list. -static ALWAYS_INLINE bool partitionSetNewActivePage(PartitionPage* page) -{ - if (page == &PartitionRootBase::gSeedPage) { - ASSERT(!page->nextPage); - return false; +static ALWAYS_INLINE bool partitionSetNewActivePage(PartitionPage* page) { + if (page == &PartitionRootBase::gSeedPage) { + ASSERT(!page->nextPage); + return false; + } + + PartitionPage* nextPage = 0; + PartitionBucket* bucket = page->bucket; + + for (; page; page = nextPage) { + nextPage = page->nextPage; + ASSERT(page->bucket == bucket); + ASSERT(page != bucket->freePagesHead); + ASSERT(!bucket->freePagesHead || page != bucket->freePagesHead->nextPage); + + // Page is usable if it has something on the freelist, or unprovisioned + // slots that can be turned into a freelist. + if (LIKELY(page->freelistHead != 0) || + LIKELY(page->numUnprovisionedSlots)) { + bucket->activePagesHead = page; + return true; } - PartitionPage* nextPage = 0; - PartitionBucket* bucket = page->bucket; - - for (; page; page = nextPage) { - nextPage = page->nextPage; - ASSERT(page->bucket == bucket); - ASSERT(page != bucket->freePagesHead); - ASSERT(!bucket->freePagesHead || page != bucket->freePagesHead->nextPage); - - // Page is usable if it has something on the freelist, or unprovisioned - // slots that can be turned into a freelist. - if (LIKELY(page->freelistHead != 0) || LIKELY(page->numUnprovisionedSlots)) { - bucket->activePagesHead = page; - return true; - } - - ASSERT(page->numAllocatedSlots >= 0); - if (LIKELY(page->numAllocatedSlots == 0)) { - ASSERT(page->freeCacheIndex == -1); - // We hit a free page, so shepherd it on to the free page list. - page->nextPage = bucket->freePagesHead; - bucket->freePagesHead = page; - } else { - // If we get here, we found a full page. Skip over it too, and also - // tag it as full (via a negative value). We need it tagged so that - // free'ing can tell, and move it back into the active page list. - ASSERT(page->numAllocatedSlots == static_cast(partitionBucketSlots(bucket))); - page->numAllocatedSlots = -page->numAllocatedSlots; - ++bucket->numFullPages; - // numFullPages is a uint16_t for efficient packing so guard against - // overflow to be safe. - RELEASE_ASSERT(bucket->numFullPages); - // Not necessary but might help stop accidents. - page->nextPage = 0; - } + ASSERT(page->numAllocatedSlots >= 0); + if (LIKELY(page->numAllocatedSlots == 0)) { + ASSERT(page->freeCacheIndex == -1); + // We hit a free page, so shepherd it on to the free page list. + page->nextPage = bucket->freePagesHead; + bucket->freePagesHead = page; + } else { + // If we get here, we found a full page. Skip over it too, and also + // tag it as full (via a negative value). We need it tagged so that + // free'ing can tell, and move it back into the active page list. + ASSERT(page->numAllocatedSlots == + static_cast(partitionBucketSlots(bucket))); + page->numAllocatedSlots = -page->numAllocatedSlots; + ++bucket->numFullPages; + // numFullPages is a uint16_t for efficient packing so guard against + // overflow to be safe. + RELEASE_ASSERT(bucket->numFullPages); + // Not necessary but might help stop accidents. + page->nextPage = 0; } + } - bucket->activePagesHead = 0; - return false; + bucket->activePagesHead = 0; + return false; } struct PartitionDirectMapExtent { - size_t mapSize; // Mapped size, not including guard pages and meta-data. + size_t mapSize; // Mapped size, not including guard pages and meta-data. }; -static ALWAYS_INLINE PartitionDirectMapExtent* partitionPageToDirectMapExtent(PartitionPage* page) -{ - ASSERT(partitionBucketIsDirectMapped(page->bucket)); - return reinterpret_cast(reinterpret_cast(page) + 2 * kPageMetadataSize); +static ALWAYS_INLINE PartitionDirectMapExtent* partitionPageToDirectMapExtent( + PartitionPage* page) { + ASSERT(partitionBucketIsDirectMapped(page->bucket)); + return reinterpret_cast( + reinterpret_cast(page) + 2 * kPageMetadataSize); } -static ALWAYS_INLINE void* partitionDirectMap(PartitionRootBase* root, int flags, size_t size) -{ - size = partitionDirectMapSize(size); - - // Because we need to fake looking like a super page, We need to allocate - // a bunch of system pages more than "size": - // - The first few system pages are the partition page in which the super - // page metadata is stored. We fault just one system page out of a partition - // page sized clump. - // - We add a trailing guard page. - size_t mapSize = size + kPartitionPageSize + kSystemPageSize; - // Round up to the allocation granularity. - mapSize += kPageAllocationGranularityOffsetMask; - mapSize &= kPageAllocationGranularityBaseMask; - - // TODO: we may want to let the operating system place these allocations - // where it pleases. On 32-bit, this might limit address space - // fragmentation and on 64-bit, this might have useful savings for TLB - // and page table overhead. - // TODO: if upsizing realloc()s are common on large sizes, we could - // consider over-allocating address space on 64-bit, "just in case". - // TODO: consider pre-populating page tables (e.g. MAP_POPULATE on Linux, - // MADV_WILLNEED on POSIX). - // TODO: these pages will be zero-filled. Consider internalizing an - // allocZeroed() API so we can avoid a memset() entirely in this case. - char* ptr = reinterpret_cast(allocPages(0, mapSize, kSuperPageSize)); - if (!ptr) { - if (flags & PartitionAllocReturnNull) - return 0; - partitionOutOfMemory(); - } - char* ret = ptr + kPartitionPageSize; - // TODO: due to all the guard paging, this arrangement creates 4 mappings. - // We could get it down to three by using read-only for the metadata page, - // or perhaps two by leaving out the trailing guard page on 64-bit. - setSystemPagesInaccessible(ptr, kSystemPageSize); - setSystemPagesInaccessible(ptr + (kSystemPageSize * 2), kPartitionPageSize - (kSystemPageSize * 2)); - setSystemPagesInaccessible(ret + size, kSystemPageSize); - - PartitionSuperPageExtentEntry* extent = reinterpret_cast(partitionSuperPageToMetadataArea(ptr)); - extent->root = root; - PartitionPage* page = partitionPointerToPageNoAlignmentCheck(ret); - PartitionBucket* bucket = reinterpret_cast(reinterpret_cast(page) + kPageMetadataSize); - page->freelistHead = 0; - page->nextPage = 0; - page->bucket = bucket; - page->numAllocatedSlots = 1; - page->numUnprovisionedSlots = 0; - page->pageOffset = 0; - page->freeCacheIndex = 0; - - bucket->activePagesHead = 0; - bucket->freePagesHead = 0; - bucket->slotSize = size; - bucket->numSystemPagesPerSlotSpan = 0; - bucket->numFullPages = 0; - - PartitionDirectMapExtent* mapExtent = partitionPageToDirectMapExtent(page); - mapExtent->mapSize = mapSize - kPartitionPageSize - kSystemPageSize; - - return ret; +static ALWAYS_INLINE void* partitionDirectMap(PartitionRootBase* root, + int flags, + size_t size) { + size = partitionDirectMapSize(size); + + // Because we need to fake looking like a super page, We need to allocate + // a bunch of system pages more than "size": + // - The first few system pages are the partition page in which the super + // page metadata is stored. We fault just one system page out of a partition + // page sized clump. + // - We add a trailing guard page. + size_t mapSize = size + kPartitionPageSize + kSystemPageSize; + // Round up to the allocation granularity. + mapSize += kPageAllocationGranularityOffsetMask; + mapSize &= kPageAllocationGranularityBaseMask; + + // TODO: we may want to let the operating system place these allocations + // where it pleases. On 32-bit, this might limit address space + // fragmentation and on 64-bit, this might have useful savings for TLB + // and page table overhead. + // TODO: if upsizing realloc()s are common on large sizes, we could + // consider over-allocating address space on 64-bit, "just in case". + // TODO: consider pre-populating page tables (e.g. MAP_POPULATE on Linux, + // MADV_WILLNEED on POSIX). + // TODO: these pages will be zero-filled. Consider internalizing an + // allocZeroed() API so we can avoid a memset() entirely in this case. + char* ptr = reinterpret_cast(allocPages(0, mapSize, kSuperPageSize)); + if (!ptr) { + if (flags & PartitionAllocReturnNull) + return 0; + partitionOutOfMemory(); + } + char* ret = ptr + kPartitionPageSize; + // TODO: due to all the guard paging, this arrangement creates 4 mappings. + // We could get it down to three by using read-only for the metadata page, + // or perhaps two by leaving out the trailing guard page on 64-bit. + setSystemPagesInaccessible(ptr, kSystemPageSize); + setSystemPagesInaccessible(ptr + (kSystemPageSize * 2), + kPartitionPageSize - (kSystemPageSize * 2)); + setSystemPagesInaccessible(ret + size, kSystemPageSize); + + PartitionSuperPageExtentEntry* extent = + reinterpret_cast( + partitionSuperPageToMetadataArea(ptr)); + extent->root = root; + PartitionPage* page = partitionPointerToPageNoAlignmentCheck(ret); + PartitionBucket* bucket = reinterpret_cast( + reinterpret_cast(page) + kPageMetadataSize); + page->freelistHead = 0; + page->nextPage = 0; + page->bucket = bucket; + page->numAllocatedSlots = 1; + page->numUnprovisionedSlots = 0; + page->pageOffset = 0; + page->freeCacheIndex = 0; + + bucket->activePagesHead = 0; + bucket->freePagesHead = 0; + bucket->slotSize = size; + bucket->numSystemPagesPerSlotSpan = 0; + bucket->numFullPages = 0; + + PartitionDirectMapExtent* mapExtent = partitionPageToDirectMapExtent(page); + mapExtent->mapSize = mapSize - kPartitionPageSize - kSystemPageSize; + + return ret; } -static ALWAYS_INLINE void partitionDirectUnmap(PartitionPage* page) -{ - size_t unmapSize = partitionPageToDirectMapExtent(page)->mapSize; +static ALWAYS_INLINE void partitionDirectUnmap(PartitionPage* page) { + size_t unmapSize = partitionPageToDirectMapExtent(page)->mapSize; - // Add on the size of the trailing guard page and preceeding partition - // page. - unmapSize += kPartitionPageSize + kSystemPageSize; + // Add on the size of the trailing guard page and preceeding partition + // page. + unmapSize += kPartitionPageSize + kSystemPageSize; - ASSERT(!(unmapSize & kPageAllocationGranularityOffsetMask)); + ASSERT(!(unmapSize & kPageAllocationGranularityOffsetMask)); - char* ptr = reinterpret_cast(partitionPageToPointer(page)); - // Account for the mapping starting a partition page before the actual - // allocation address. - ptr -= kPartitionPageSize; + char* ptr = reinterpret_cast(partitionPageToPointer(page)); + // Account for the mapping starting a partition page before the actual + // allocation address. + ptr -= kPartitionPageSize; - freePages(ptr, unmapSize); + freePages(ptr, unmapSize); } -void* partitionAllocSlowPath(PartitionRootBase* root, int flags, size_t size, PartitionBucket* bucket) -{ - // The slow path is called when the freelist is empty. - ASSERT(!bucket->activePagesHead->freelistHead); - - // For the partitionAllocGeneric API, we have a bunch of buckets marked - // as special cases. We bounce them through to the slow path so that we - // can still have a blazing fast hot path due to lack of corner-case - // branches. - bool returnNull = flags & PartitionAllocReturnNull; - if (UNLIKELY(partitionBucketIsDirectMapped(bucket))) { - ASSERT(size > kGenericMaxBucketed); - ASSERT(bucket == &PartitionRootBase::gPagedBucket); - if (size > kGenericMaxDirectMapped) { - if (returnNull) - return 0; - RELEASE_ASSERT(false); - } - return partitionDirectMap(root, flags, size); +void* partitionAllocSlowPath(PartitionRootBase* root, + int flags, + size_t size, + PartitionBucket* bucket) { + // The slow path is called when the freelist is empty. + ASSERT(!bucket->activePagesHead->freelistHead); + + // For the partitionAllocGeneric API, we have a bunch of buckets marked + // as special cases. We bounce them through to the slow path so that we + // can still have a blazing fast hot path due to lack of corner-case + // branches. + bool returnNull = flags & PartitionAllocReturnNull; + if (UNLIKELY(partitionBucketIsDirectMapped(bucket))) { + ASSERT(size > kGenericMaxBucketed); + ASSERT(bucket == &PartitionRootBase::gPagedBucket); + if (size > kGenericMaxDirectMapped) { + if (returnNull) + return 0; + RELEASE_ASSERT(false); } - - // First, look for a usable page in the existing active pages list. - // Change active page, accepting the current page as a candidate. - if (LIKELY(partitionSetNewActivePage(bucket->activePagesHead))) { - PartitionPage* newPage = bucket->activePagesHead; - if (LIKELY(newPage->freelistHead != 0)) { - PartitionFreelistEntry* ret = newPage->freelistHead; - newPage->freelistHead = partitionFreelistMask(ret->next); - newPage->numAllocatedSlots++; - return ret; - } - ASSERT(newPage->numUnprovisionedSlots); - return partitionPageAllocAndFillFreelist(newPage); + return partitionDirectMap(root, flags, size); + } + + // First, look for a usable page in the existing active pages list. + // Change active page, accepting the current page as a candidate. + if (LIKELY(partitionSetNewActivePage(bucket->activePagesHead))) { + PartitionPage* newPage = bucket->activePagesHead; + if (LIKELY(newPage->freelistHead != 0)) { + PartitionFreelistEntry* ret = newPage->freelistHead; + newPage->freelistHead = partitionFreelistMask(ret->next); + newPage->numAllocatedSlots++; + return ret; } - - // Second, look in our list of freed but reserved pages. - PartitionPage* newPage = bucket->freePagesHead; - if (LIKELY(newPage != 0)) { - ASSERT(newPage != &PartitionRootGeneric::gSeedPage); - ASSERT(!newPage->freelistHead); - ASSERT(!newPage->numAllocatedSlots); - ASSERT(!newPage->numUnprovisionedSlots); - ASSERT(newPage->freeCacheIndex == -1); - bucket->freePagesHead = newPage->nextPage; - void* addr = partitionPageToPointer(newPage); - partitionRecommitSystemPages(root, addr, newPage->bucket->numSystemPagesPerSlotSpan * kSystemPageSize); - } else { - // Third. If we get here, we need a brand new page. - size_t numPartitionPages = partitionBucketPartitionPages(bucket); - void* rawNewPage = partitionAllocPartitionPages(root, flags, numPartitionPages); - if (UNLIKELY(!rawNewPage)) { - ASSERT(returnNull); - return 0; - } - // Skip the alignment check because it depends on page->bucket, which is not yet set. - newPage = partitionPointerToPageNoAlignmentCheck(rawNewPage); - } - - partitionPageReset(newPage, bucket); - bucket->activePagesHead = newPage; + ASSERT(newPage->numUnprovisionedSlots); return partitionPageAllocAndFillFreelist(newPage); + } + + // Second, look in our list of freed but reserved pages. + PartitionPage* newPage = bucket->freePagesHead; + if (LIKELY(newPage != 0)) { + ASSERT(newPage != &PartitionRootGeneric::gSeedPage); + ASSERT(!newPage->freelistHead); + ASSERT(!newPage->numAllocatedSlots); + ASSERT(!newPage->numUnprovisionedSlots); + ASSERT(newPage->freeCacheIndex == -1); + bucket->freePagesHead = newPage->nextPage; + void* addr = partitionPageToPointer(newPage); + partitionRecommitSystemPages( + root, addr, + newPage->bucket->numSystemPagesPerSlotSpan * kSystemPageSize); + } else { + // Third. If we get here, we need a brand new page. + size_t numPartitionPages = partitionBucketPartitionPages(bucket); + void* rawNewPage = + partitionAllocPartitionPages(root, flags, numPartitionPages); + if (UNLIKELY(!rawNewPage)) { + ASSERT(returnNull); + return 0; + } + // Skip the alignment check because it depends on page->bucket, which is not + // yet set. + newPage = partitionPointerToPageNoAlignmentCheck(rawNewPage); + } + + partitionPageReset(newPage, bucket); + bucket->activePagesHead = newPage; + return partitionPageAllocAndFillFreelist(newPage); } -static ALWAYS_INLINE void partitionFreePage(PartitionRootBase* root, PartitionPage* page) -{ - ASSERT(page->freelistHead); - ASSERT(!page->numAllocatedSlots); - partitionUnusePage(root, page); - // We actually leave the freed page in the active list. We'll sweep it on - // to the free page list when we next walk the active page list. Pulling - // this trick enables us to use a singly-linked page list for all cases, - // which is critical in keeping the page metadata structure down to 32 - // bytes in size. - page->freelistHead = 0; - page->numUnprovisionedSlots = 0; +static ALWAYS_INLINE void partitionFreePage(PartitionRootBase* root, + PartitionPage* page) { + ASSERT(page->freelistHead); + ASSERT(!page->numAllocatedSlots); + partitionUnusePage(root, page); + // We actually leave the freed page in the active list. We'll sweep it on + // to the free page list when we next walk the active page list. Pulling + // this trick enables us to use a singly-linked page list for all cases, + // which is critical in keeping the page metadata structure down to 32 + // bytes in size. + page->freelistHead = 0; + page->numUnprovisionedSlots = 0; } -static ALWAYS_INLINE void partitionRegisterEmptyPage(PartitionPage* page) -{ - PartitionRootBase* root = partitionPageToRoot(page); - - // If the page is already registered as empty, give it another life. - if (page->freeCacheIndex != -1) { - ASSERT(page->freeCacheIndex >= 0); - ASSERT(static_cast(page->freeCacheIndex) < kMaxFreeableSpans); - ASSERT(root->globalEmptyPageRing[page->freeCacheIndex] == page); - root->globalEmptyPageRing[page->freeCacheIndex] = 0; - } - - size_t currentIndex = root->globalEmptyPageRingIndex; - PartitionPage* pageToFree = root->globalEmptyPageRing[currentIndex]; - // The page might well have been re-activated, filled up, etc. before we get - // around to looking at it here. - if (pageToFree) { - ASSERT(pageToFree != &PartitionRootBase::gSeedPage); - ASSERT(pageToFree->freeCacheIndex >= 0); - ASSERT(static_cast(pageToFree->freeCacheIndex) < kMaxFreeableSpans); - ASSERT(pageToFree == root->globalEmptyPageRing[pageToFree->freeCacheIndex]); - if (!pageToFree->numAllocatedSlots && pageToFree->freelistHead) { - // The page is still empty, and not freed, so _really_ free it. - partitionFreePage(root, pageToFree); - } - pageToFree->freeCacheIndex = -1; +static ALWAYS_INLINE void partitionRegisterEmptyPage(PartitionPage* page) { + PartitionRootBase* root = partitionPageToRoot(page); + + // If the page is already registered as empty, give it another life. + if (page->freeCacheIndex != -1) { + ASSERT(page->freeCacheIndex >= 0); + ASSERT(static_cast(page->freeCacheIndex) < kMaxFreeableSpans); + ASSERT(root->globalEmptyPageRing[page->freeCacheIndex] == page); + root->globalEmptyPageRing[page->freeCacheIndex] = 0; + } + + size_t currentIndex = root->globalEmptyPageRingIndex; + PartitionPage* pageToFree = root->globalEmptyPageRing[currentIndex]; + // The page might well have been re-activated, filled up, etc. before we get + // around to looking at it here. + if (pageToFree) { + ASSERT(pageToFree != &PartitionRootBase::gSeedPage); + ASSERT(pageToFree->freeCacheIndex >= 0); + ASSERT(static_cast(pageToFree->freeCacheIndex) < + kMaxFreeableSpans); + ASSERT(pageToFree == root->globalEmptyPageRing[pageToFree->freeCacheIndex]); + if (!pageToFree->numAllocatedSlots && pageToFree->freelistHead) { + // The page is still empty, and not freed, so _really_ free it. + partitionFreePage(root, pageToFree); } - - // We put the empty slot span on our global list of "pages that were once - // empty". thus providing it a bit of breathing room to get re-used before - // we really free it. This improves performance, particularly on Mac OS X - // which has subpar memory management performance. - root->globalEmptyPageRing[currentIndex] = page; - page->freeCacheIndex = currentIndex; - ++currentIndex; - if (currentIndex == kMaxFreeableSpans) - currentIndex = 0; - root->globalEmptyPageRingIndex = currentIndex; + pageToFree->freeCacheIndex = -1; + } + + // We put the empty slot span on our global list of "pages that were once + // empty". thus providing it a bit of breathing room to get re-used before + // we really free it. This improves performance, particularly on Mac OS X + // which has subpar memory management performance. + root->globalEmptyPageRing[currentIndex] = page; + page->freeCacheIndex = currentIndex; + ++currentIndex; + if (currentIndex == kMaxFreeableSpans) + currentIndex = 0; + root->globalEmptyPageRingIndex = currentIndex; } -void partitionFreeSlowPath(PartitionPage* page) -{ - PartitionBucket* bucket = page->bucket; - ASSERT(page != &PartitionRootGeneric::gSeedPage); - ASSERT(bucket->activePagesHead != &PartitionRootGeneric::gSeedPage); - if (LIKELY(page->numAllocatedSlots == 0)) { - // Page became fully unused. - if (UNLIKELY(partitionBucketIsDirectMapped(bucket))) { - partitionDirectUnmap(page); - return; - } - // If it's the current active page, attempt to change it. We'd prefer to leave - // the page empty as a gentle force towards defragmentation. - if (LIKELY(page == bucket->activePagesHead) && page->nextPage) { - if (partitionSetNewActivePage(page->nextPage)) { - ASSERT(bucket->activePagesHead != page); - // Link the empty page back in after the new current page, to - // avoid losing a reference to it. - // TODO: consider walking the list to link the empty page after - // all non-empty pages? - PartitionPage* currentPage = bucket->activePagesHead; - page->nextPage = currentPage->nextPage; - currentPage->nextPage = page; - } else { - bucket->activePagesHead = page; - page->nextPage = 0; - } - } - partitionRegisterEmptyPage(page); - } else { - // Ensure that the page is full. That's the only valid case if we - // arrive here. - ASSERT(page->numAllocatedSlots < 0); - // A transition of numAllocatedSlots from 0 to -1 is not legal, and - // likely indicates a double-free. - RELEASE_ASSERT(page->numAllocatedSlots != -1); - page->numAllocatedSlots = -page->numAllocatedSlots - 2; - ASSERT(page->numAllocatedSlots == static_cast(partitionBucketSlots(bucket) - 1)); - // Fully used page became partially used. It must be put back on the - // non-full page list. Also make it the current page to increase the - // chances of it being filled up again. The old current page will be - // the next page. - page->nextPage = bucket->activePagesHead; +void partitionFreeSlowPath(PartitionPage* page) { + PartitionBucket* bucket = page->bucket; + ASSERT(page != &PartitionRootGeneric::gSeedPage); + ASSERT(bucket->activePagesHead != &PartitionRootGeneric::gSeedPage); + if (LIKELY(page->numAllocatedSlots == 0)) { + // Page became fully unused. + if (UNLIKELY(partitionBucketIsDirectMapped(bucket))) { + partitionDirectUnmap(page); + return; + } + // If it's the current active page, attempt to change it. We'd prefer to + // leave the page empty as a gentle force towards defragmentation. + if (LIKELY(page == bucket->activePagesHead) && page->nextPage) { + if (partitionSetNewActivePage(page->nextPage)) { + ASSERT(bucket->activePagesHead != page); + // Link the empty page back in after the new current page, to + // avoid losing a reference to it. + // TODO: consider walking the list to link the empty page after + // all non-empty pages? + PartitionPage* currentPage = bucket->activePagesHead; + page->nextPage = currentPage->nextPage; + currentPage->nextPage = page; + } else { bucket->activePagesHead = page; - --bucket->numFullPages; - // Special case: for a partition page with just a single slot, it may - // now be empty and we want to run it through the empty logic. - if (UNLIKELY(page->numAllocatedSlots == 0)) - partitionFreeSlowPath(page); + page->nextPage = 0; + } } + partitionRegisterEmptyPage(page); + } else { + // Ensure that the page is full. That's the only valid case if we + // arrive here. + ASSERT(page->numAllocatedSlots < 0); + // A transition of numAllocatedSlots from 0 to -1 is not legal, and + // likely indicates a double-free. + RELEASE_ASSERT(page->numAllocatedSlots != -1); + page->numAllocatedSlots = -page->numAllocatedSlots - 2; + ASSERT(page->numAllocatedSlots == + static_cast(partitionBucketSlots(bucket) - 1)); + // Fully used page became partially used. It must be put back on the + // non-full page list. Also make it the current page to increase the + // chances of it being filled up again. The old current page will be + // the next page. + page->nextPage = bucket->activePagesHead; + bucket->activePagesHead = page; + --bucket->numFullPages; + // Special case: for a partition page with just a single slot, it may + // now be empty and we want to run it through the empty logic. + if (UNLIKELY(page->numAllocatedSlots == 0)) + partitionFreeSlowPath(page); + } } -bool partitionReallocDirectMappedInPlace(PartitionRootGeneric* root, PartitionPage* page, size_t newSize) -{ - ASSERT(partitionBucketIsDirectMapped(page->bucket)); +bool partitionReallocDirectMappedInPlace(PartitionRootGeneric* root, + PartitionPage* page, + size_t newSize) { + ASSERT(partitionBucketIsDirectMapped(page->bucket)); - newSize = partitionCookieSizeAdjustAdd(newSize); + newSize = partitionCookieSizeAdjustAdd(newSize); - // Note that the new size might be a bucketed size; this function is called - // whenever we're reallocating a direct mapped allocation. - newSize = partitionDirectMapSize(newSize); - if (newSize < kGenericMinDirectMappedDownsize) - return false; + // Note that the new size might be a bucketed size; this function is called + // whenever we're reallocating a direct mapped allocation. + newSize = partitionDirectMapSize(newSize); + if (newSize < kGenericMinDirectMappedDownsize) + return false; - // bucket->slotSize is the current size of the allocation. - size_t currentSize = page->bucket->slotSize; - if (newSize == currentSize) - return true; + // bucket->slotSize is the current size of the allocation. + size_t currentSize = page->bucket->slotSize; + if (newSize == currentSize) + return true; - char* charPtr = static_cast(partitionPageToPointer(page)); + char* charPtr = static_cast(partitionPageToPointer(page)); - if (newSize < currentSize) { - size_t mapSize = partitionPageToDirectMapExtent(page)->mapSize; + if (newSize < currentSize) { + size_t mapSize = partitionPageToDirectMapExtent(page)->mapSize; - // Don't reallocate in-place if new size is less than 80 % of the full - // map size, to avoid holding on to too much unused address space. - if ((newSize / kSystemPageSize) * 5 < (mapSize / kSystemPageSize) * 4) - return false; + // Don't reallocate in-place if new size is less than 80 % of the full + // map size, to avoid holding on to too much unused address space. + if ((newSize / kSystemPageSize) * 5 < (mapSize / kSystemPageSize) * 4) + return false; - // Shrink by decommitting unneeded pages and making them inaccessible. - size_t decommitSize = currentSize - newSize; - partitionDecommitSystemPages(root, charPtr + newSize, decommitSize); - setSystemPagesInaccessible(charPtr + newSize, decommitSize); - } else if (newSize <= partitionPageToDirectMapExtent(page)->mapSize) { - // Grow within the actually allocated memory. Just need to make the - // pages accessible again. - size_t recommitSize = newSize - currentSize; - setSystemPagesAccessible(charPtr + currentSize, recommitSize); - partitionRecommitSystemPages(root, charPtr + currentSize, recommitSize); + // Shrink by decommitting unneeded pages and making them inaccessible. + size_t decommitSize = currentSize - newSize; + partitionDecommitSystemPages(root, charPtr + newSize, decommitSize); + setSystemPagesInaccessible(charPtr + newSize, decommitSize); + } else if (newSize <= partitionPageToDirectMapExtent(page)->mapSize) { + // Grow within the actually allocated memory. Just need to make the + // pages accessible again. + size_t recommitSize = newSize - currentSize; + setSystemPagesAccessible(charPtr + currentSize, recommitSize); + partitionRecommitSystemPages(root, charPtr + currentSize, recommitSize); #if ENABLE(ASSERT) - memset(charPtr + currentSize, kUninitializedByte, recommitSize); + memset(charPtr + currentSize, kUninitializedByte, recommitSize); #endif - } else { - // We can't perform the realloc in-place. - // TODO: support this too when possible. - return false; - } + } else { + // We can't perform the realloc in-place. + // TODO: support this too when possible. + return false; + } #if ENABLE(ASSERT) - // Write a new trailing cookie. - partitionCookieWriteValue(charPtr + newSize - kCookieSize); + // Write a new trailing cookie. + partitionCookieWriteValue(charPtr + newSize - kCookieSize); #endif - page->bucket->slotSize = newSize; - return true; + page->bucket->slotSize = newSize; + return true; } -void* partitionReallocGeneric(PartitionRootGeneric* root, void* ptr, size_t newSize) -{ +void* partitionReallocGeneric(PartitionRootGeneric* root, + void* ptr, + size_t newSize) { #if defined(MEMORY_TOOL_REPLACES_ALLOCATOR) - return realloc(ptr, newSize); + return realloc(ptr, newSize); #else - if (UNLIKELY(!ptr)) - return partitionAllocGeneric(root, newSize); - if (UNLIKELY(!newSize)) { - partitionFreeGeneric(root, ptr); - return 0; - } - - RELEASE_ASSERT(newSize <= kGenericMaxDirectMapped); - - ASSERT(partitionPointerIsValid(partitionCookieFreePointerAdjust(ptr))); - - PartitionPage* page = partitionPointerToPage(partitionCookieFreePointerAdjust(ptr)); - - if (UNLIKELY(partitionBucketIsDirectMapped(page->bucket))) { - // We may be able to perform the realloc in place by changing the - // accessibility of memory pages and, if reducing the size, decommitting - // them. - if (partitionReallocDirectMappedInPlace(root, page, newSize)) - return ptr; - } - - size_t actualNewSize = partitionAllocActualSize(root, newSize); - size_t actualOldSize = partitionAllocGetSize(ptr); - - // TODO: note that tcmalloc will "ignore" a downsizing realloc() unless the - // new size is a significant percentage smaller. We could do the same if we - // determine it is a win. - if (actualNewSize == actualOldSize) { - // Trying to allocate a block of size newSize would give us a block of - // the same size as the one we've already got, so no point in doing - // anything here. - return ptr; - } - - // This realloc cannot be resized in-place. Sadness. - void* ret = partitionAllocGeneric(root, newSize); - size_t copySize = actualOldSize; - if (newSize < copySize) - copySize = newSize; - - memcpy(ret, ptr, copySize); + if (UNLIKELY(!ptr)) + return partitionAllocGeneric(root, newSize); + if (UNLIKELY(!newSize)) { partitionFreeGeneric(root, ptr); - return ret; + return 0; + } + + RELEASE_ASSERT(newSize <= kGenericMaxDirectMapped); + + ASSERT(partitionPointerIsValid(partitionCookieFreePointerAdjust(ptr))); + + PartitionPage* page = + partitionPointerToPage(partitionCookieFreePointerAdjust(ptr)); + + if (UNLIKELY(partitionBucketIsDirectMapped(page->bucket))) { + // We may be able to perform the realloc in place by changing the + // accessibility of memory pages and, if reducing the size, decommitting + // them. + if (partitionReallocDirectMappedInPlace(root, page, newSize)) + return ptr; + } + + size_t actualNewSize = partitionAllocActualSize(root, newSize); + size_t actualOldSize = partitionAllocGetSize(ptr); + + // TODO: note that tcmalloc will "ignore" a downsizing realloc() unless the + // new size is a significant percentage smaller. We could do the same if we + // determine it is a win. + if (actualNewSize == actualOldSize) { + // Trying to allocate a block of size newSize would give us a block of + // the same size as the one we've already got, so no point in doing + // anything here. + return ptr; + } + + // This realloc cannot be resized in-place. Sadness. + void* ret = partitionAllocGeneric(root, newSize); + size_t copySize = actualOldSize; + if (newSize < copySize) + copySize = newSize; + + memcpy(ret, ptr, copySize); + partitionFreeGeneric(root, ptr); + return ret; #endif } #ifndef NDEBUG -void partitionDumpStats(const PartitionRoot& root) -{ - size_t i; - size_t totalLive = 0; - size_t totalResident = 0; - size_t totalFreeable = 0; - for (i = 0; i < root.numBuckets; ++i) { - const PartitionBucket& bucket = root.buckets()[i]; - if (bucket.activePagesHead == &PartitionRootGeneric::gSeedPage && !bucket.freePagesHead && !bucket.numFullPages) { - // Empty bucket with no freelist or full pages. Skip reporting it. - continue; - } - size_t numFreePages = 0; - PartitionPage* freePages = bucket.freePagesHead; - while (freePages) { - ++numFreePages; - freePages = freePages->nextPage; - } - size_t bucketSlotSize = bucket.slotSize; - size_t bucketNumSlots = partitionBucketSlots(&bucket); - size_t bucketUsefulStorage = bucketSlotSize * bucketNumSlots; - size_t bucketPageSize = bucket.numSystemPagesPerSlotSpan * kSystemPageSize; - size_t bucketWaste = bucketPageSize - bucketUsefulStorage; - size_t numActiveBytes = bucket.numFullPages * bucketUsefulStorage; - size_t numResidentBytes = bucket.numFullPages * bucketPageSize; - size_t numFreeableBytes = 0; - size_t numActivePages = 0; - const PartitionPage* page = bucket.activePagesHead; - while (page) { - ASSERT(page != &PartitionRootGeneric::gSeedPage); - // A page may be on the active list but freed and not yet swept. - if (!page->freelistHead && !page->numUnprovisionedSlots && !page->numAllocatedSlots) { - ++numFreePages; - } else { - ++numActivePages; - numActiveBytes += (page->numAllocatedSlots * bucketSlotSize); - size_t pageBytesResident = (bucketNumSlots - page->numUnprovisionedSlots) * bucketSlotSize; - // Round up to system page size. - pageBytesResident = (pageBytesResident + kSystemPageOffsetMask) & kSystemPageBaseMask; - numResidentBytes += pageBytesResident; - if (!page->numAllocatedSlots) - numFreeableBytes += pageBytesResident; - } - page = page->nextPage; - } - totalLive += numActiveBytes; - totalResident += numResidentBytes; - totalFreeable += numFreeableBytes; - printf("bucket size %zu (pageSize %zu waste %zu): %zu alloc/%zu commit/%zu freeable bytes, %zu/%zu/%zu full/active/free pages\n", bucketSlotSize, bucketPageSize, bucketWaste, numActiveBytes, numResidentBytes, numFreeableBytes, static_cast(bucket.numFullPages), numActivePages, numFreePages); +void partitionDumpStats(const PartitionRoot& root) { + size_t i; + size_t totalLive = 0; + size_t totalResident = 0; + size_t totalFreeable = 0; + for (i = 0; i < root.numBuckets; ++i) { + const PartitionBucket& bucket = root.buckets()[i]; + if (bucket.activePagesHead == &PartitionRootGeneric::gSeedPage && + !bucket.freePagesHead && !bucket.numFullPages) { + // Empty bucket with no freelist or full pages. Skip reporting it. + continue; + } + size_t numFreePages = 0; + PartitionPage* freePages = bucket.freePagesHead; + while (freePages) { + ++numFreePages; + freePages = freePages->nextPage; } - printf("total live: %zu bytes\n", totalLive); - printf("total resident: %zu bytes\n", totalResident); - printf("total freeable: %zu bytes\n", totalFreeable); - fflush(stdout); + size_t bucketSlotSize = bucket.slotSize; + size_t bucketNumSlots = partitionBucketSlots(&bucket); + size_t bucketUsefulStorage = bucketSlotSize * bucketNumSlots; + size_t bucketPageSize = bucket.numSystemPagesPerSlotSpan * kSystemPageSize; + size_t bucketWaste = bucketPageSize - bucketUsefulStorage; + size_t numActiveBytes = bucket.numFullPages * bucketUsefulStorage; + size_t numResidentBytes = bucket.numFullPages * bucketPageSize; + size_t numFreeableBytes = 0; + size_t numActivePages = 0; + const PartitionPage* page = bucket.activePagesHead; + while (page) { + ASSERT(page != &PartitionRootGeneric::gSeedPage); + // A page may be on the active list but freed and not yet swept. + if (!page->freelistHead && !page->numUnprovisionedSlots && + !page->numAllocatedSlots) { + ++numFreePages; + } else { + ++numActivePages; + numActiveBytes += (page->numAllocatedSlots * bucketSlotSize); + size_t pageBytesResident = + (bucketNumSlots - page->numUnprovisionedSlots) * bucketSlotSize; + // Round up to system page size. + pageBytesResident = + (pageBytesResident + kSystemPageOffsetMask) & kSystemPageBaseMask; + numResidentBytes += pageBytesResident; + if (!page->numAllocatedSlots) + numFreeableBytes += pageBytesResident; + } + page = page->nextPage; + } + totalLive += numActiveBytes; + totalResident += numResidentBytes; + totalFreeable += numFreeableBytes; + printf( + "bucket size %zu (pageSize %zu waste %zu): %zu alloc/%zu commit/%zu " + "freeable bytes, %zu/%zu/%zu full/active/free pages\n", + bucketSlotSize, bucketPageSize, bucketWaste, numActiveBytes, + numResidentBytes, numFreeableBytes, + static_cast(bucket.numFullPages), numActivePages, numFreePages); + } + printf("total live: %zu bytes\n", totalLive); + printf("total resident: %zu bytes\n", totalResident); + printf("total freeable: %zu bytes\n", totalFreeable); + fflush(stdout); } -#endif // !NDEBUG - -} // namespace WTF +#endif // !NDEBUG +} // namespace WTF diff --git a/sky/engine/wtf/PartitionAlloc.h b/sky/engine/wtf/PartitionAlloc.h index ff37e24a908f8..6e81d1a1b3287 100644 --- a/sky/engine/wtf/PartitionAlloc.h +++ b/sky/engine/wtf/PartitionAlloc.h @@ -139,7 +139,7 @@ static const size_t kBucketShift = (kAllocationGranularity == 8) ? 3 : 2; // system page of the span. For our current max slot span size of 64k and other // constant values, we pack _all_ partitionAllocGeneric() sizes perfectly up // against the end of a system page. -static const size_t kPartitionPageShift = 14; // 16KB +static const size_t kPartitionPageShift = 14; // 16KB static const size_t kPartitionPageSize = 1 << kPartitionPageShift; static const size_t kPartitionPageOffsetMask = kPartitionPageSize - 1; static const size_t kPartitionPageBaseMask = ~kPartitionPageOffsetMask; @@ -151,21 +151,24 @@ static const size_t kMaxPartitionPagesPerSlotSpan = 4; // (typically 16KB) will freelist pointers right away. Writing freelist // pointers will fault and dirty a private page, which is very wasteful if we // never actually store objects there. -static const size_t kNumSystemPagesPerPartitionPage = kPartitionPageSize / kSystemPageSize; -static const size_t kMaxSystemPagesPerSlotSpan = kNumSystemPagesPerPartitionPage * kMaxPartitionPagesPerSlotSpan; +static const size_t kNumSystemPagesPerPartitionPage = + kPartitionPageSize / kSystemPageSize; +static const size_t kMaxSystemPagesPerSlotSpan = + kNumSystemPagesPerPartitionPage * kMaxPartitionPagesPerSlotSpan; // We reserve virtual address space in 2MB chunks (aligned to 2MB as well). // These chunks are called "super pages". We do this so that we can store // metadata in the first few pages of each 2MB aligned section. This leads to // a very fast free(). We specifically choose 2MB because this virtual address // block represents a full but single PTE allocation on ARM, ia32 and x64. -static const size_t kSuperPageShift = 21; // 2MB +static const size_t kSuperPageShift = 21; // 2MB static const size_t kSuperPageSize = 1 << kSuperPageShift; static const size_t kSuperPageOffsetMask = kSuperPageSize - 1; static const size_t kSuperPageBaseMask = ~kSuperPageOffsetMask; -static const size_t kNumPartitionPagesPerSuperPage = kSuperPageSize / kPartitionPageSize; +static const size_t kNumPartitionPagesPerSuperPage = + kSuperPageSize / kPartitionPageSize; -static const size_t kPageMetadataShift = 5; // 32 bytes per partition page. +static const size_t kPageMetadataShift = 5; // 32 bytes per partition page. static const size_t kPageMetadataSize = 1 << kPageMetadataShift; // The following kGeneric* constants apply to the generic variants of the API. @@ -175,15 +178,26 @@ static const size_t kPageMetadataSize = 1 << kPageMetadataShift; // at index 1 for the least-significant-bit. // In terms of allocation sizes, order 0 covers 0, order 1 covers 1, order 2 // covers 2->3, order 3 covers 4->7, order 4 covers 8->15. -static const size_t kGenericMinBucketedOrder = 4; // 8 bytes. -static const size_t kGenericMaxBucketedOrder = 20; // Largest bucketed order is 1<<(20-1) (storing 512KB -> almost 1MB) -static const size_t kGenericNumBucketedOrders = (kGenericMaxBucketedOrder - kGenericMinBucketedOrder) + 1; -static const size_t kGenericNumBucketsPerOrderBits = 3; // Eight buckets per order (for the higher orders), e.g. order 8 is 128, 144, 160, ..., 240 -static const size_t kGenericNumBucketsPerOrder = 1 << kGenericNumBucketsPerOrderBits; -static const size_t kGenericSmallestBucket = 1 << (kGenericMinBucketedOrder - 1); -static const size_t kGenericMaxBucketSpacing = 1 << ((kGenericMaxBucketedOrder - 1) - kGenericNumBucketsPerOrderBits); -static const size_t kGenericMaxBucketed = (1 << (kGenericMaxBucketedOrder - 1)) + ((kGenericNumBucketsPerOrder - 1) * kGenericMaxBucketSpacing); -static const size_t kGenericMinDirectMappedDownsize = kGenericMaxBucketed + 1; // Limit when downsizing a direct mapping using realloc(). +static const size_t kGenericMinBucketedOrder = 4; // 8 bytes. +static const size_t kGenericMaxBucketedOrder = + 20; // Largest bucketed order is 1<<(20-1) (storing 512KB -> almost 1MB) +static const size_t kGenericNumBucketedOrders = + (kGenericMaxBucketedOrder - kGenericMinBucketedOrder) + 1; +static const size_t kGenericNumBucketsPerOrderBits = + 3; // Eight buckets per order (for the higher orders), e.g. order 8 is 128, + // 144, 160, ..., 240 +static const size_t kGenericNumBucketsPerOrder = + 1 << kGenericNumBucketsPerOrderBits; +static const size_t kGenericSmallestBucket = 1 + << (kGenericMinBucketedOrder - 1); +static const size_t kGenericMaxBucketSpacing = + 1 << ((kGenericMaxBucketedOrder - 1) - kGenericNumBucketsPerOrderBits); +static const size_t kGenericMaxBucketed = + (1 << (kGenericMaxBucketedOrder - 1)) + + ((kGenericNumBucketsPerOrder - 1) * kGenericMaxBucketSpacing); +static const size_t kGenericMinDirectMappedDownsize = + kGenericMaxBucketed + + 1; // Limit when downsizing a direct mapping using realloc(). static const size_t kGenericMaxDirectMapped = INT_MAX - kSystemPageSize; static const size_t kBitsPerSizet = sizeof(void*) * CHAR_BIT; @@ -195,14 +209,15 @@ static const size_t kMaxFreeableSpans = 16; static const unsigned char kUninitializedByte = 0xAB; static const unsigned char kFreedByte = 0xCD; static const uint32_t kCookieValue = 0xDEADBEEFu; -static const size_t kCookieSize = 16; // Handles alignment up to XMM instructions on Intel. +static const size_t kCookieSize = + 16; // Handles alignment up to XMM instructions on Intel. #endif struct PartitionBucket; struct PartitionRootBase; struct PartitionFreelistEntry { - PartitionFreelistEntry* next; + PartitionFreelistEntry* next; }; // Some notes on page states. A page can be in one of three major states: @@ -226,406 +241,429 @@ struct PartitionFreelistEntry { // active list. If there are no suitable active pages found, a free page (if one // exists) will be pulled from the free list on to the active list. struct PartitionPage { - PartitionFreelistEntry* freelistHead; - PartitionPage* nextPage; - PartitionBucket* bucket; - int16_t numAllocatedSlots; // Deliberately signed, -1 for free page, -n for full pages. - uint16_t numUnprovisionedSlots; - uint16_t pageOffset; - int16_t freeCacheIndex; // -1 if not in the free cache. + PartitionFreelistEntry* freelistHead; + PartitionPage* nextPage; + PartitionBucket* bucket; + int16_t numAllocatedSlots; // Deliberately signed, -1 for free page, -n for + // full pages. + uint16_t numUnprovisionedSlots; + uint16_t pageOffset; + int16_t freeCacheIndex; // -1 if not in the free cache. }; struct PartitionBucket { - PartitionPage* activePagesHead; // Accessed most in hot path => goes first. - PartitionPage* freePagesHead; - uint32_t slotSize; - uint16_t numSystemPagesPerSlotSpan; - uint16_t numFullPages; + PartitionPage* activePagesHead; // Accessed most in hot path => goes first. + PartitionPage* freePagesHead; + uint32_t slotSize; + uint16_t numSystemPagesPerSlotSpan; + uint16_t numFullPages; }; // An "extent" is a span of consecutive superpages. We link to the partition's // next extent (if there is one) at the very start of a superpage's metadata // area. struct PartitionSuperPageExtentEntry { - PartitionRootBase* root; - char* superPageBase; - char* superPagesEnd; - PartitionSuperPageExtentEntry* next; + PartitionRootBase* root; + char* superPageBase; + char* superPagesEnd; + PartitionSuperPageExtentEntry* next; }; struct WTF_EXPORT PartitionRootBase { - size_t totalSizeOfCommittedPages; - size_t totalSizeOfSuperPages; - unsigned numBuckets; - unsigned maxAllocation; - bool initialized; - char* nextSuperPage; - char* nextPartitionPage; - char* nextPartitionPageEnd; - PartitionSuperPageExtentEntry* currentExtent; - PartitionSuperPageExtentEntry* firstExtent; - PartitionPage* globalEmptyPageRing[kMaxFreeableSpans]; - size_t globalEmptyPageRingIndex; - uintptr_t invertedSelf; - - static int gInitializedLock; - static bool gInitialized; - static PartitionPage gSeedPage; - static PartitionBucket gPagedBucket; + size_t totalSizeOfCommittedPages; + size_t totalSizeOfSuperPages; + unsigned numBuckets; + unsigned maxAllocation; + bool initialized; + char* nextSuperPage; + char* nextPartitionPage; + char* nextPartitionPageEnd; + PartitionSuperPageExtentEntry* currentExtent; + PartitionSuperPageExtentEntry* firstExtent; + PartitionPage* globalEmptyPageRing[kMaxFreeableSpans]; + size_t globalEmptyPageRingIndex; + uintptr_t invertedSelf; + + static int gInitializedLock; + static bool gInitialized; + static PartitionPage gSeedPage; + static PartitionBucket gPagedBucket; }; // Never instantiate a PartitionRoot directly, instead use PartitionAlloc. struct PartitionRoot : public PartitionRootBase { - // The PartitionAlloc templated class ensures the following is correct. - ALWAYS_INLINE PartitionBucket* buckets() { return reinterpret_cast(this + 1); } - ALWAYS_INLINE const PartitionBucket* buckets() const { return reinterpret_cast(this + 1); } + // The PartitionAlloc templated class ensures the following is correct. + ALWAYS_INLINE PartitionBucket* buckets() { + return reinterpret_cast(this + 1); + } + ALWAYS_INLINE const PartitionBucket* buckets() const { + return reinterpret_cast(this + 1); + } }; -// Never instantiate a PartitionRootGeneric directly, instead use PartitionAllocatorGeneric. +// Never instantiate a PartitionRootGeneric directly, instead use +// PartitionAllocatorGeneric. struct PartitionRootGeneric : public PartitionRootBase { - int lock; - // Some pre-computed constants. - size_t orderIndexShifts[kBitsPerSizet + 1]; - size_t orderSubIndexMasks[kBitsPerSizet + 1]; - // The bucket lookup table lets us map a size_t to a bucket quickly. - // The trailing +1 caters for the overflow case for very large allocation sizes. - // It is one flat array instead of a 2D array because in the 2D world, we'd - // need to index array[blah][max+1] which risks undefined behavior. - PartitionBucket* bucketLookups[((kBitsPerSizet + 1) * kGenericNumBucketsPerOrder) + 1]; - PartitionBucket buckets[kGenericNumBucketedOrders * kGenericNumBucketsPerOrder]; + int lock; + // Some pre-computed constants. + size_t orderIndexShifts[kBitsPerSizet + 1]; + size_t orderSubIndexMasks[kBitsPerSizet + 1]; + // The bucket lookup table lets us map a size_t to a bucket quickly. + // The trailing +1 caters for the overflow case for very large allocation + // sizes. It is one flat array instead of a 2D array because in the 2D world, + // we'd need to index array[blah][max+1] which risks undefined behavior. + PartitionBucket* + bucketLookups[((kBitsPerSizet + 1) * kGenericNumBucketsPerOrder) + 1]; + PartitionBucket + buckets[kGenericNumBucketedOrders * kGenericNumBucketsPerOrder]; }; // Flags for partitionAllocGenericFlags. enum PartitionAllocFlags { - PartitionAllocReturnNull = 1 << 0, + PartitionAllocReturnNull = 1 << 0, }; -WTF_EXPORT void partitionAllocInit(PartitionRoot*, size_t numBuckets, size_t maxAllocation); +WTF_EXPORT void partitionAllocInit(PartitionRoot*, + size_t numBuckets, + size_t maxAllocation); WTF_EXPORT bool partitionAllocShutdown(PartitionRoot*); WTF_EXPORT void partitionAllocGenericInit(PartitionRootGeneric*); WTF_EXPORT bool partitionAllocGenericShutdown(PartitionRootGeneric*); -WTF_EXPORT NEVER_INLINE void* partitionAllocSlowPath(PartitionRootBase*, int, size_t, PartitionBucket*); +WTF_EXPORT NEVER_INLINE void* partitionAllocSlowPath(PartitionRootBase*, + int, + size_t, + PartitionBucket*); WTF_EXPORT NEVER_INLINE void partitionFreeSlowPath(PartitionPage*); -WTF_EXPORT NEVER_INLINE void* partitionReallocGeneric(PartitionRootGeneric*, void*, size_t); +WTF_EXPORT NEVER_INLINE void* partitionReallocGeneric(PartitionRootGeneric*, + void*, + size_t); #ifndef NDEBUG WTF_EXPORT void partitionDumpStats(const PartitionRoot&); #endif -ALWAYS_INLINE PartitionFreelistEntry* partitionFreelistMask(PartitionFreelistEntry* ptr) -{ - // We use bswap on little endian as a fast mask for two reasons: - // 1) If an object is freed and its vtable used where the attacker doesn't - // get the chance to run allocations between the free and use, the vtable - // dereference is likely to fault. - // 2) If the attacker has a linear buffer overflow and elects to try and - // corrupt a freelist pointer, partial pointer overwrite attacks are - // thwarted. - // For big endian, similar guarantees are arrived at with a negation. +ALWAYS_INLINE PartitionFreelistEntry* partitionFreelistMask( + PartitionFreelistEntry* ptr) { + // We use bswap on little endian as a fast mask for two reasons: + // 1) If an object is freed and its vtable used where the attacker doesn't + // get the chance to run allocations between the free and use, the vtable + // dereference is likely to fault. + // 2) If the attacker has a linear buffer overflow and elects to try and + // corrupt a freelist pointer, partial pointer overwrite attacks are + // thwarted. + // For big endian, similar guarantees are arrived at with a negation. #if CPU(BIG_ENDIAN) - uintptr_t masked = ~reinterpret_cast(ptr); + uintptr_t masked = ~reinterpret_cast(ptr); #else - uintptr_t masked = bswapuintptrt(reinterpret_cast(ptr)); + uintptr_t masked = bswapuintptrt(reinterpret_cast(ptr)); #endif - return reinterpret_cast(masked); + return reinterpret_cast(masked); } -ALWAYS_INLINE size_t partitionCookieSizeAdjustAdd(size_t size) -{ +ALWAYS_INLINE size_t partitionCookieSizeAdjustAdd(size_t size) { #if ENABLE(ASSERT) - // Add space for cookies, checking for integer overflow. - ASSERT(size + (2 * kCookieSize) > size); - size += 2 * kCookieSize; + // Add space for cookies, checking for integer overflow. + ASSERT(size + (2 * kCookieSize) > size); + size += 2 * kCookieSize; #endif - return size; + return size; } -ALWAYS_INLINE size_t partitionCookieSizeAdjustSubtract(size_t size) -{ +ALWAYS_INLINE size_t partitionCookieSizeAdjustSubtract(size_t size) { #if ENABLE(ASSERT) - // Remove space for cookies. - ASSERT(size >= 2 * kCookieSize); - size -= 2 * kCookieSize; + // Remove space for cookies. + ASSERT(size >= 2 * kCookieSize); + size -= 2 * kCookieSize; #endif - return size; + return size; } -ALWAYS_INLINE void* partitionCookieFreePointerAdjust(void* ptr) -{ +ALWAYS_INLINE void* partitionCookieFreePointerAdjust(void* ptr) { #if ENABLE(ASSERT) - // The value given to the application is actually just after the cookie. - ptr = static_cast(ptr) - kCookieSize; + // The value given to the application is actually just after the cookie. + ptr = static_cast(ptr) - kCookieSize; #endif - return ptr; + return ptr; } -ALWAYS_INLINE void partitionCookieWriteValue(void* ptr) -{ +ALWAYS_INLINE void partitionCookieWriteValue(void* ptr) { #if ENABLE(ASSERT) - uint32_t* cookiePtr = reinterpret_cast(ptr); - for (size_t i = 0; i < kCookieSize / sizeof(kCookieValue); ++i, ++cookiePtr) - *cookiePtr = kCookieValue; + uint32_t* cookiePtr = reinterpret_cast(ptr); + for (size_t i = 0; i < kCookieSize / sizeof(kCookieValue); ++i, ++cookiePtr) + *cookiePtr = kCookieValue; #endif } -ALWAYS_INLINE void partitionCookieCheckValue(void* ptr) -{ +ALWAYS_INLINE void partitionCookieCheckValue(void* ptr) { #if ENABLE(ASSERT) - uint32_t* cookiePtr = reinterpret_cast(ptr); - for (size_t i = 0; i < kCookieSize / sizeof(kCookieValue); ++i, ++cookiePtr) - ASSERT(*cookiePtr == kCookieValue); + uint32_t* cookiePtr = reinterpret_cast(ptr); + for (size_t i = 0; i < kCookieSize / sizeof(kCookieValue); ++i, ++cookiePtr) + ASSERT(*cookiePtr == kCookieValue); #endif } -ALWAYS_INLINE char* partitionSuperPageToMetadataArea(char* ptr) -{ - uintptr_t pointerAsUint = reinterpret_cast(ptr); - ASSERT(!(pointerAsUint & kSuperPageOffsetMask)); - // The metadata area is exactly one system page (the guard page) into the - // super page. - return reinterpret_cast(pointerAsUint + kSystemPageSize); +ALWAYS_INLINE char* partitionSuperPageToMetadataArea(char* ptr) { + uintptr_t pointerAsUint = reinterpret_cast(ptr); + ASSERT(!(pointerAsUint & kSuperPageOffsetMask)); + // The metadata area is exactly one system page (the guard page) into the + // super page. + return reinterpret_cast(pointerAsUint + kSystemPageSize); } -ALWAYS_INLINE PartitionPage* partitionPointerToPageNoAlignmentCheck(void* ptr) -{ - uintptr_t pointerAsUint = reinterpret_cast(ptr); - char* superPagePtr = reinterpret_cast(pointerAsUint & kSuperPageBaseMask); - uintptr_t partitionPageIndex = (pointerAsUint & kSuperPageOffsetMask) >> kPartitionPageShift; - // Index 0 is invalid because it is the metadata area and the last index is invalid because it is a guard page. - ASSERT(partitionPageIndex); - ASSERT(partitionPageIndex < kNumPartitionPagesPerSuperPage - 1); - PartitionPage* page = reinterpret_cast(partitionSuperPageToMetadataArea(superPagePtr) + (partitionPageIndex << kPageMetadataShift)); - // Many partition pages can share the same page object. Adjust for that. - size_t delta = page->pageOffset << kPageMetadataShift; - page = reinterpret_cast(reinterpret_cast(page) - delta); - return page; +ALWAYS_INLINE PartitionPage* partitionPointerToPageNoAlignmentCheck(void* ptr) { + uintptr_t pointerAsUint = reinterpret_cast(ptr); + char* superPagePtr = + reinterpret_cast(pointerAsUint & kSuperPageBaseMask); + uintptr_t partitionPageIndex = + (pointerAsUint & kSuperPageOffsetMask) >> kPartitionPageShift; + // Index 0 is invalid because it is the metadata area and the last index is + // invalid because it is a guard page. + ASSERT(partitionPageIndex); + ASSERT(partitionPageIndex < kNumPartitionPagesPerSuperPage - 1); + PartitionPage* page = reinterpret_cast( + partitionSuperPageToMetadataArea(superPagePtr) + + (partitionPageIndex << kPageMetadataShift)); + // Many partition pages can share the same page object. Adjust for that. + size_t delta = page->pageOffset << kPageMetadataShift; + page = + reinterpret_cast(reinterpret_cast(page) - delta); + return page; } -ALWAYS_INLINE void* partitionPageToPointer(PartitionPage* page) -{ - uintptr_t pointerAsUint = reinterpret_cast(page); - uintptr_t superPageOffset = (pointerAsUint & kSuperPageOffsetMask); - ASSERT(superPageOffset > kSystemPageSize); - ASSERT(superPageOffset < kSystemPageSize + (kNumPartitionPagesPerSuperPage * kPageMetadataSize)); - uintptr_t partitionPageIndex = (superPageOffset - kSystemPageSize) >> kPageMetadataShift; - // Index 0 is invalid because it is the metadata area and the last index is invalid because it is a guard page. - ASSERT(partitionPageIndex); - ASSERT(partitionPageIndex < kNumPartitionPagesPerSuperPage - 1); - uintptr_t superPageBase = (pointerAsUint & kSuperPageBaseMask); - void* ret = reinterpret_cast(superPageBase + (partitionPageIndex << kPartitionPageShift)); - return ret; +ALWAYS_INLINE void* partitionPageToPointer(PartitionPage* page) { + uintptr_t pointerAsUint = reinterpret_cast(page); + uintptr_t superPageOffset = (pointerAsUint & kSuperPageOffsetMask); + ASSERT(superPageOffset > kSystemPageSize); + ASSERT(superPageOffset < kSystemPageSize + (kNumPartitionPagesPerSuperPage * + kPageMetadataSize)); + uintptr_t partitionPageIndex = + (superPageOffset - kSystemPageSize) >> kPageMetadataShift; + // Index 0 is invalid because it is the metadata area and the last index is + // invalid because it is a guard page. + ASSERT(partitionPageIndex); + ASSERT(partitionPageIndex < kNumPartitionPagesPerSuperPage - 1); + uintptr_t superPageBase = (pointerAsUint & kSuperPageBaseMask); + void* ret = reinterpret_cast( + superPageBase + (partitionPageIndex << kPartitionPageShift)); + return ret; } -ALWAYS_INLINE PartitionPage* partitionPointerToPage(void* ptr) -{ - PartitionPage* page = partitionPointerToPageNoAlignmentCheck(ptr); - // Checks that the pointer is a multiple of bucket size. - ASSERT(!((reinterpret_cast(ptr) - reinterpret_cast(partitionPageToPointer(page))) % page->bucket->slotSize)); - return page; +ALWAYS_INLINE PartitionPage* partitionPointerToPage(void* ptr) { + PartitionPage* page = partitionPointerToPageNoAlignmentCheck(ptr); + // Checks that the pointer is a multiple of bucket size. + ASSERT(!((reinterpret_cast(ptr) - + reinterpret_cast(partitionPageToPointer(page))) % + page->bucket->slotSize)); + return page; } -ALWAYS_INLINE PartitionRootBase* partitionPageToRoot(PartitionPage* page) -{ - PartitionSuperPageExtentEntry* extentEntry = reinterpret_cast(reinterpret_cast(page) & kSystemPageBaseMask); - return extentEntry->root; +ALWAYS_INLINE PartitionRootBase* partitionPageToRoot(PartitionPage* page) { + PartitionSuperPageExtentEntry* extentEntry = + reinterpret_cast( + reinterpret_cast(page) & kSystemPageBaseMask); + return extentEntry->root; } -ALWAYS_INLINE bool partitionPointerIsValid(void* ptr) -{ - PartitionPage* page = partitionPointerToPage(ptr); - PartitionRootBase* root = partitionPageToRoot(page); - return root->invertedSelf == ~reinterpret_cast(root); +ALWAYS_INLINE bool partitionPointerIsValid(void* ptr) { + PartitionPage* page = partitionPointerToPage(ptr); + PartitionRootBase* root = partitionPageToRoot(page); + return root->invertedSelf == ~reinterpret_cast(root); } -ALWAYS_INLINE void* partitionBucketAlloc(PartitionRootBase* root, int flags, size_t size, PartitionBucket* bucket) -{ - PartitionPage* page = bucket->activePagesHead; - ASSERT(page->numAllocatedSlots >= 0); - void* ret = page->freelistHead; - if (LIKELY(ret != 0)) { - // If these asserts fire, you probably corrupted memory. - ASSERT(partitionPointerIsValid(ret)); - PartitionFreelistEntry* newHead = partitionFreelistMask(static_cast(ret)->next); - page->freelistHead = newHead; - ASSERT(!ret || partitionPointerIsValid(ret)); - page->numAllocatedSlots++; - } else { - ret = partitionAllocSlowPath(root, flags, size, bucket); - } +ALWAYS_INLINE void* partitionBucketAlloc(PartitionRootBase* root, + int flags, + size_t size, + PartitionBucket* bucket) { + PartitionPage* page = bucket->activePagesHead; + ASSERT(page->numAllocatedSlots >= 0); + void* ret = page->freelistHead; + if (LIKELY(ret != 0)) { + // If these asserts fire, you probably corrupted memory. + ASSERT(partitionPointerIsValid(ret)); + PartitionFreelistEntry* newHead = + partitionFreelistMask(static_cast(ret)->next); + page->freelistHead = newHead; + ASSERT(!ret || partitionPointerIsValid(ret)); + page->numAllocatedSlots++; + } else { + ret = partitionAllocSlowPath(root, flags, size, bucket); + } #if ENABLE(ASSERT) - if (!ret) - return 0; - // Fill the uninitialized pattern. and write the cookies. - page = partitionPointerToPage(ret); - size_t bucketSize = page->bucket->slotSize; - memset(ret, kUninitializedByte, bucketSize); - partitionCookieWriteValue(ret); - partitionCookieWriteValue(reinterpret_cast(ret) + bucketSize - kCookieSize); - // The value given to the application is actually just after the cookie. - ret = static_cast(ret) + kCookieSize; + if (!ret) + return 0; + // Fill the uninitialized pattern. and write the cookies. + page = partitionPointerToPage(ret); + size_t bucketSize = page->bucket->slotSize; + memset(ret, kUninitializedByte, bucketSize); + partitionCookieWriteValue(ret); + partitionCookieWriteValue(reinterpret_cast(ret) + bucketSize - + kCookieSize); + // The value given to the application is actually just after the cookie. + ret = static_cast(ret) + kCookieSize; #endif - return ret; + return ret; } -ALWAYS_INLINE void* partitionAlloc(PartitionRoot* root, size_t size) -{ +ALWAYS_INLINE void* partitionAlloc(PartitionRoot* root, size_t size) { #if defined(MEMORY_TOOL_REPLACES_ALLOCATOR) - void* result = malloc(size); - RELEASE_ASSERT(result); - return result; + void* result = malloc(size); + RELEASE_ASSERT(result); + return result; #else - size = partitionCookieSizeAdjustAdd(size); - ASSERT(root->initialized); - size_t index = size >> kBucketShift; - ASSERT(index < root->numBuckets); - ASSERT(size == index << kBucketShift); - PartitionBucket* bucket = &root->buckets()[index]; - return partitionBucketAlloc(root, 0, size, bucket); -#endif // defined(MEMORY_TOOL_REPLACES_ALLOCATOR) + size = partitionCookieSizeAdjustAdd(size); + ASSERT(root->initialized); + size_t index = size >> kBucketShift; + ASSERT(index < root->numBuckets); + ASSERT(size == index << kBucketShift); + PartitionBucket* bucket = &root->buckets()[index]; + return partitionBucketAlloc(root, 0, size, bucket); +#endif // defined(MEMORY_TOOL_REPLACES_ALLOCATOR) } -ALWAYS_INLINE void partitionFreeWithPage(void* ptr, PartitionPage* page) -{ - // If these asserts fire, you probably corrupted memory. +ALWAYS_INLINE void partitionFreeWithPage(void* ptr, PartitionPage* page) { + // If these asserts fire, you probably corrupted memory. #if ENABLE(ASSERT) - size_t bucketSize = page->bucket->slotSize; - partitionCookieCheckValue(ptr); - partitionCookieCheckValue(reinterpret_cast(ptr) + bucketSize - kCookieSize); - memset(ptr, kFreedByte, bucketSize); + size_t bucketSize = page->bucket->slotSize; + partitionCookieCheckValue(ptr); + partitionCookieCheckValue(reinterpret_cast(ptr) + bucketSize - + kCookieSize); + memset(ptr, kFreedByte, bucketSize); #endif - ASSERT(page->numAllocatedSlots); - PartitionFreelistEntry* freelistHead = page->freelistHead; - ASSERT(!freelistHead || partitionPointerIsValid(freelistHead)); - RELEASE_ASSERT(ptr != freelistHead); // Catches an immediate double free. - ASSERT(!freelistHead || ptr != partitionFreelistMask(freelistHead->next)); // Look for double free one level deeper in debug. - PartitionFreelistEntry* entry = static_cast(ptr); - entry->next = partitionFreelistMask(freelistHead); - page->freelistHead = entry; - --page->numAllocatedSlots; - if (UNLIKELY(page->numAllocatedSlots <= 0)) - partitionFreeSlowPath(page); + ASSERT(page->numAllocatedSlots); + PartitionFreelistEntry* freelistHead = page->freelistHead; + ASSERT(!freelistHead || partitionPointerIsValid(freelistHead)); + RELEASE_ASSERT(ptr != freelistHead); // Catches an immediate double free. + ASSERT(!freelistHead || + ptr != partitionFreelistMask(freelistHead->next)); // Look for double + // free one level + // deeper in debug. + PartitionFreelistEntry* entry = static_cast(ptr); + entry->next = partitionFreelistMask(freelistHead); + page->freelistHead = entry; + --page->numAllocatedSlots; + if (UNLIKELY(page->numAllocatedSlots <= 0)) + partitionFreeSlowPath(page); } -ALWAYS_INLINE void partitionFree(void* ptr) -{ +ALWAYS_INLINE void partitionFree(void* ptr) { #if defined(MEMORY_TOOL_REPLACES_ALLOCATOR) - free(ptr); + free(ptr); #else - ptr = partitionCookieFreePointerAdjust(ptr); - ASSERT(partitionPointerIsValid(ptr)); - PartitionPage* page = partitionPointerToPage(ptr); - partitionFreeWithPage(ptr, page); + ptr = partitionCookieFreePointerAdjust(ptr); + ASSERT(partitionPointerIsValid(ptr)); + PartitionPage* page = partitionPointerToPage(ptr); + partitionFreeWithPage(ptr, page); #endif } -ALWAYS_INLINE PartitionBucket* partitionGenericSizeToBucket(PartitionRootGeneric* root, size_t size) -{ - size_t order = kBitsPerSizet - countLeadingZerosSizet(size); - // The order index is simply the next few bits after the most significant bit. - size_t orderIndex = (size >> root->orderIndexShifts[order]) & (kGenericNumBucketsPerOrder - 1); - // And if the remaining bits are non-zero we must bump the bucket up. - size_t subOrderIndex = size & root->orderSubIndexMasks[order]; - PartitionBucket* bucket = root->bucketLookups[(order << kGenericNumBucketsPerOrderBits) + orderIndex + !!subOrderIndex]; - ASSERT(!bucket->slotSize || bucket->slotSize >= size); - ASSERT(!(bucket->slotSize % kGenericSmallestBucket)); - return bucket; +ALWAYS_INLINE PartitionBucket* partitionGenericSizeToBucket( + PartitionRootGeneric* root, + size_t size) { + size_t order = kBitsPerSizet - countLeadingZerosSizet(size); + // The order index is simply the next few bits after the most significant bit. + size_t orderIndex = (size >> root->orderIndexShifts[order]) & + (kGenericNumBucketsPerOrder - 1); + // And if the remaining bits are non-zero we must bump the bucket up. + size_t subOrderIndex = size & root->orderSubIndexMasks[order]; + PartitionBucket* bucket = + root->bucketLookups[(order << kGenericNumBucketsPerOrderBits) + + orderIndex + !!subOrderIndex]; + ASSERT(!bucket->slotSize || bucket->slotSize >= size); + ASSERT(!(bucket->slotSize % kGenericSmallestBucket)); + return bucket; } -ALWAYS_INLINE void* partitionAllocGenericFlags(PartitionRootGeneric* root, int flags, size_t size) -{ +ALWAYS_INLINE void* partitionAllocGenericFlags(PartitionRootGeneric* root, + int flags, + size_t size) { #if defined(MEMORY_TOOL_REPLACES_ALLOCATOR) - void* result = malloc(size); - RELEASE_ASSERT(result); - return result; + void* result = malloc(size); + RELEASE_ASSERT(result); + return result; #else - ASSERT(root->initialized); - size = partitionCookieSizeAdjustAdd(size); - PartitionBucket* bucket = partitionGenericSizeToBucket(root, size); - spinLockLock(&root->lock); - void* ret = partitionBucketAlloc(root, flags, size, bucket); - spinLockUnlock(&root->lock); - return ret; + ASSERT(root->initialized); + size = partitionCookieSizeAdjustAdd(size); + PartitionBucket* bucket = partitionGenericSizeToBucket(root, size); + spinLockLock(&root->lock); + void* ret = partitionBucketAlloc(root, flags, size, bucket); + spinLockUnlock(&root->lock); + return ret; #endif } -ALWAYS_INLINE void* partitionAllocGeneric(PartitionRootGeneric* root, size_t size) -{ - return partitionAllocGenericFlags(root, 0, size); +ALWAYS_INLINE void* partitionAllocGeneric(PartitionRootGeneric* root, + size_t size) { + return partitionAllocGenericFlags(root, 0, size); } -ALWAYS_INLINE void partitionFreeGeneric(PartitionRootGeneric* root, void* ptr) -{ +ALWAYS_INLINE void partitionFreeGeneric(PartitionRootGeneric* root, void* ptr) { #if defined(MEMORY_TOOL_REPLACES_ALLOCATOR) - free(ptr); + free(ptr); #else - ASSERT(root->initialized); + ASSERT(root->initialized); - if (UNLIKELY(!ptr)) - return; + if (UNLIKELY(!ptr)) + return; - ptr = partitionCookieFreePointerAdjust(ptr); - ASSERT(partitionPointerIsValid(ptr)); - PartitionPage* page = partitionPointerToPage(ptr); - spinLockLock(&root->lock); - partitionFreeWithPage(ptr, page); - spinLockUnlock(&root->lock); + ptr = partitionCookieFreePointerAdjust(ptr); + ASSERT(partitionPointerIsValid(ptr)); + PartitionPage* page = partitionPointerToPage(ptr); + spinLockLock(&root->lock); + partitionFreeWithPage(ptr, page); + spinLockUnlock(&root->lock); #endif } -ALWAYS_INLINE bool partitionBucketIsDirectMapped(PartitionBucket* bucket) -{ - return !bucket->numSystemPagesPerSlotSpan; +ALWAYS_INLINE bool partitionBucketIsDirectMapped(PartitionBucket* bucket) { + return !bucket->numSystemPagesPerSlotSpan; } -ALWAYS_INLINE size_t partitionDirectMapSize(size_t size) -{ - // Caller must check that the size is not above the kGenericMaxDirectMapped - // limit before calling. This also guards against integer overflow in the - // calculation here. - ASSERT(size <= kGenericMaxDirectMapped); - return (size + kSystemPageOffsetMask) & kSystemPageBaseMask; +ALWAYS_INLINE size_t partitionDirectMapSize(size_t size) { + // Caller must check that the size is not above the kGenericMaxDirectMapped + // limit before calling. This also guards against integer overflow in the + // calculation here. + ASSERT(size <= kGenericMaxDirectMapped); + return (size + kSystemPageOffsetMask) & kSystemPageBaseMask; } -ALWAYS_INLINE size_t partitionAllocActualSize(PartitionRootGeneric* root, size_t size) -{ +ALWAYS_INLINE size_t partitionAllocActualSize(PartitionRootGeneric* root, + size_t size) { #if defined(MEMORY_TOOL_REPLACES_ALLOCATOR) - return size; + return size; #else - ASSERT(root->initialized); - size = partitionCookieSizeAdjustAdd(size); - PartitionBucket* bucket = partitionGenericSizeToBucket(root, size); - if (LIKELY(!partitionBucketIsDirectMapped(bucket))) { - size = bucket->slotSize; - } else if (size > kGenericMaxDirectMapped) { - // Too large to allocate => return the size unchanged. - } else { - ASSERT(bucket == &PartitionRootBase::gPagedBucket); - size = partitionDirectMapSize(size); - } - return partitionCookieSizeAdjustSubtract(size); + ASSERT(root->initialized); + size = partitionCookieSizeAdjustAdd(size); + PartitionBucket* bucket = partitionGenericSizeToBucket(root, size); + if (LIKELY(!partitionBucketIsDirectMapped(bucket))) { + size = bucket->slotSize; + } else if (size > kGenericMaxDirectMapped) { + // Too large to allocate => return the size unchanged. + } else { + ASSERT(bucket == &PartitionRootBase::gPagedBucket); + size = partitionDirectMapSize(size); + } + return partitionCookieSizeAdjustSubtract(size); #endif } -ALWAYS_INLINE bool partitionAllocSupportsGetSize() -{ +ALWAYS_INLINE bool partitionAllocSupportsGetSize() { #if defined(MEMORY_TOOL_REPLACES_ALLOCATOR) - return false; + return false; #else - return true; + return true; #endif } -ALWAYS_INLINE size_t partitionAllocGetSize(void* ptr) -{ - // No need to lock here. Only 'ptr' being freed by another thread could - // cause trouble, and the caller is responsible for that not happening. - ASSERT(partitionAllocSupportsGetSize()); - ptr = partitionCookieFreePointerAdjust(ptr); - ASSERT(partitionPointerIsValid(ptr)); - PartitionPage* page = partitionPointerToPage(ptr); - size_t size = page->bucket->slotSize; - return partitionCookieSizeAdjustSubtract(size); +ALWAYS_INLINE size_t partitionAllocGetSize(void* ptr) { + // No need to lock here. Only 'ptr' being freed by another thread could + // cause trouble, and the caller is responsible for that not happening. + ASSERT(partitionAllocSupportsGetSize()); + ptr = partitionCookieFreePointerAdjust(ptr); + ASSERT(partitionPointerIsValid(ptr)); + PartitionPage* page = partitionPointerToPage(ptr); + size_t size = page->bucket->slotSize; + return partitionCookieSizeAdjustSubtract(size); } // N (or more accurately, N - sizeof(void*)) represents the largest size in @@ -633,40 +671,44 @@ ALWAYS_INLINE size_t partitionAllocGetSize(void* ptr) // Attempts to partitionAlloc() more than this amount will fail. template class SizeSpecificPartitionAllocator { -public: - static const size_t kMaxAllocation = N - kAllocationGranularity; - static const size_t kNumBuckets = N / kAllocationGranularity; - void init() { partitionAllocInit(&m_partitionRoot, kNumBuckets, kMaxAllocation); } - bool shutdown() { return partitionAllocShutdown(&m_partitionRoot); } - ALWAYS_INLINE PartitionRoot* root() { return &m_partitionRoot; } -private: - PartitionRoot m_partitionRoot; - PartitionBucket m_actualBuckets[kNumBuckets]; + public: + static const size_t kMaxAllocation = N - kAllocationGranularity; + static const size_t kNumBuckets = N / kAllocationGranularity; + void init() { + partitionAllocInit(&m_partitionRoot, kNumBuckets, kMaxAllocation); + } + bool shutdown() { return partitionAllocShutdown(&m_partitionRoot); } + ALWAYS_INLINE PartitionRoot* root() { return &m_partitionRoot; } + + private: + PartitionRoot m_partitionRoot; + PartitionBucket m_actualBuckets[kNumBuckets]; }; class PartitionAllocatorGeneric { -public: - void init() { partitionAllocGenericInit(&m_partitionRoot); } - bool shutdown() { return partitionAllocGenericShutdown(&m_partitionRoot); } - ALWAYS_INLINE PartitionRootGeneric* root() { return &m_partitionRoot; } -private: - PartitionRootGeneric m_partitionRoot; + public: + void init() { partitionAllocGenericInit(&m_partitionRoot); } + bool shutdown() { return partitionAllocGenericShutdown(&m_partitionRoot); } + ALWAYS_INLINE PartitionRootGeneric* root() { return &m_partitionRoot; } + + private: + PartitionRootGeneric m_partitionRoot; }; -} // namespace WTF +} // namespace WTF -using WTF::SizeSpecificPartitionAllocator; using WTF::PartitionAllocatorGeneric; using WTF::PartitionRoot; +using WTF::SizeSpecificPartitionAllocator; +using WTF::partitionAlloc; +using WTF::partitionAllocActualSize; +using WTF::partitionAllocGeneric; +using WTF::partitionAllocGetSize; using WTF::partitionAllocInit; using WTF::partitionAllocShutdown; -using WTF::partitionAlloc; +using WTF::partitionAllocSupportsGetSize; using WTF::partitionFree; -using WTF::partitionAllocGeneric; using WTF::partitionFreeGeneric; using WTF::partitionReallocGeneric; -using WTF::partitionAllocActualSize; -using WTF::partitionAllocSupportsGetSize; -using WTF::partitionAllocGetSize; #endif // SKY_ENGINE_WTF_PARTITIONALLOC_H_ diff --git a/sky/engine/wtf/PartitionAllocTest.cpp b/sky/engine/wtf/PartitionAllocTest.cpp index e602addd36e92..6824d1d2557ff 100644 --- a/sky/engine/wtf/PartitionAllocTest.cpp +++ b/sky/engine/wtf/PartitionAllocTest.cpp @@ -43,7 +43,7 @@ #ifndef MAP_ANONYMOUS #define MAP_ANONYMOUS MAP_ANON #endif -#endif // OS(POSIX) +#endif // OS(POSIX) #if !defined(MEMORY_TOOL_REPLACES_ALLOCATOR) @@ -64,1036 +64,1128 @@ static const size_t kExtraAllocSize = WTF::kCookieSize * 2; static const size_t kRealAllocSize = kTestAllocSize + kExtraAllocSize; static const size_t kTestBucketIndex = kRealAllocSize >> WTF::kBucketShift; -static void TestSetup() -{ - allocator.init(); - genericAllocator.init(); +static void TestSetup() { + allocator.init(); + genericAllocator.init(); } -static void TestShutdown() -{ +static void TestShutdown() { #ifndef NDEBUG - // Test that the partition statistic dumping code works. Previously, it - // bitrotted because no test calls it. - partitionDumpStats(*allocator.root()); + // Test that the partition statistic dumping code works. Previously, it + // bitrotted because no test calls it. + partitionDumpStats(*allocator.root()); #endif - // We expect no leaks in the general case. We have a test for leak - // detection. - EXPECT_TRUE(allocator.shutdown()); - EXPECT_TRUE(genericAllocator.shutdown()); + // We expect no leaks in the general case. We have a test for leak + // detection. + EXPECT_TRUE(allocator.shutdown()); + EXPECT_TRUE(genericAllocator.shutdown()); } -static WTF::PartitionPage* GetFullPage(size_t size) -{ - size_t realSize = size + kExtraAllocSize; - size_t bucketIdx = realSize >> WTF::kBucketShift; - WTF::PartitionBucket* bucket = &allocator.root()->buckets()[bucketIdx]; - size_t numSlots = (bucket->numSystemPagesPerSlotSpan * WTF::kSystemPageSize) / realSize; - void* first = 0; - void* last = 0; - size_t i; - for (i = 0; i < numSlots; ++i) { - void* ptr = partitionAlloc(allocator.root(), size); - EXPECT_TRUE(ptr); - if (!i) - first = WTF::partitionCookieFreePointerAdjust(ptr); - else if (i == numSlots - 1) - last = WTF::partitionCookieFreePointerAdjust(ptr); - } - EXPECT_EQ(WTF::partitionPointerToPage(first), WTF::partitionPointerToPage(last)); - if (bucket->numSystemPagesPerSlotSpan == WTF::kNumSystemPagesPerPartitionPage) - EXPECT_EQ(reinterpret_cast(first) & WTF::kPartitionPageBaseMask, reinterpret_cast(last) & WTF::kPartitionPageBaseMask); - EXPECT_EQ(numSlots, static_cast(bucket->activePagesHead->numAllocatedSlots)); - EXPECT_EQ(0, bucket->activePagesHead->freelistHead); - EXPECT_TRUE(bucket->activePagesHead); - EXPECT_TRUE(bucket->activePagesHead != &WTF::PartitionRootGeneric::gSeedPage); - return bucket->activePagesHead; +static WTF::PartitionPage* GetFullPage(size_t size) { + size_t realSize = size + kExtraAllocSize; + size_t bucketIdx = realSize >> WTF::kBucketShift; + WTF::PartitionBucket* bucket = &allocator.root()->buckets()[bucketIdx]; + size_t numSlots = + (bucket->numSystemPagesPerSlotSpan * WTF::kSystemPageSize) / realSize; + void* first = 0; + void* last = 0; + size_t i; + for (i = 0; i < numSlots; ++i) { + void* ptr = partitionAlloc(allocator.root(), size); + EXPECT_TRUE(ptr); + if (!i) + first = WTF::partitionCookieFreePointerAdjust(ptr); + else if (i == numSlots - 1) + last = WTF::partitionCookieFreePointerAdjust(ptr); + } + EXPECT_EQ(WTF::partitionPointerToPage(first), + WTF::partitionPointerToPage(last)); + if (bucket->numSystemPagesPerSlotSpan == WTF::kNumSystemPagesPerPartitionPage) + EXPECT_EQ(reinterpret_cast(first) & WTF::kPartitionPageBaseMask, + reinterpret_cast(last) & WTF::kPartitionPageBaseMask); + EXPECT_EQ(numSlots, + static_cast(bucket->activePagesHead->numAllocatedSlots)); + EXPECT_EQ(0, bucket->activePagesHead->freelistHead); + EXPECT_TRUE(bucket->activePagesHead); + EXPECT_TRUE(bucket->activePagesHead != &WTF::PartitionRootGeneric::gSeedPage); + return bucket->activePagesHead; } -static void FreeFullPage(WTF::PartitionPage* page) -{ - size_t size = page->bucket->slotSize; - size_t numSlots = (page->bucket->numSystemPagesPerSlotSpan * WTF::kSystemPageSize) / size; - EXPECT_EQ(numSlots, static_cast(abs(page->numAllocatedSlots))); - char* ptr = reinterpret_cast(partitionPageToPointer(page)); - size_t i; - for (i = 0; i < numSlots; ++i) { - partitionFree(ptr + kPointerOffset); - ptr += size; - } +static void FreeFullPage(WTF::PartitionPage* page) { + size_t size = page->bucket->slotSize; + size_t numSlots = + (page->bucket->numSystemPagesPerSlotSpan * WTF::kSystemPageSize) / size; + EXPECT_EQ(numSlots, static_cast(abs(page->numAllocatedSlots))); + char* ptr = reinterpret_cast(partitionPageToPointer(page)); + size_t i; + for (i = 0; i < numSlots; ++i) { + partitionFree(ptr + kPointerOffset); + ptr += size; + } } -static void CycleFreeCache(size_t size) -{ - size_t realSize = size + kExtraAllocSize; - size_t bucketIdx = realSize >> WTF::kBucketShift; - WTF::PartitionBucket* bucket = &allocator.root()->buckets()[bucketIdx]; - ASSERT(!bucket->activePagesHead->numAllocatedSlots); - - for (size_t i = 0; i < WTF::kMaxFreeableSpans; ++i) { - void* ptr = partitionAlloc(allocator.root(), size); - EXPECT_EQ(1, bucket->activePagesHead->numAllocatedSlots); - partitionFree(ptr); - EXPECT_EQ(0, bucket->activePagesHead->numAllocatedSlots); - EXPECT_NE(-1, bucket->activePagesHead->freeCacheIndex); - } +static void CycleFreeCache(size_t size) { + size_t realSize = size + kExtraAllocSize; + size_t bucketIdx = realSize >> WTF::kBucketShift; + WTF::PartitionBucket* bucket = &allocator.root()->buckets()[bucketIdx]; + ASSERT(!bucket->activePagesHead->numAllocatedSlots); + + for (size_t i = 0; i < WTF::kMaxFreeableSpans; ++i) { + void* ptr = partitionAlloc(allocator.root(), size); + EXPECT_EQ(1, bucket->activePagesHead->numAllocatedSlots); + partitionFree(ptr); + EXPECT_EQ(0, bucket->activePagesHead->numAllocatedSlots); + EXPECT_NE(-1, bucket->activePagesHead->freeCacheIndex); + } } -static void CycleGenericFreeCache(size_t size) -{ - for (size_t i = 0; i < WTF::kMaxFreeableSpans; ++i) { - void* ptr = partitionAllocGeneric(genericAllocator.root(), size); - WTF::PartitionPage* page = WTF::partitionPointerToPage(WTF::partitionCookieFreePointerAdjust(ptr)); - WTF::PartitionBucket* bucket = page->bucket; - EXPECT_EQ(1, bucket->activePagesHead->numAllocatedSlots); - partitionFreeGeneric(genericAllocator.root(), ptr); - EXPECT_EQ(0, bucket->activePagesHead->numAllocatedSlots); - EXPECT_NE(-1, bucket->activePagesHead->freeCacheIndex); - } +static void CycleGenericFreeCache(size_t size) { + for (size_t i = 0; i < WTF::kMaxFreeableSpans; ++i) { + void* ptr = partitionAllocGeneric(genericAllocator.root(), size); + WTF::PartitionPage* page = + WTF::partitionPointerToPage(WTF::partitionCookieFreePointerAdjust(ptr)); + WTF::PartitionBucket* bucket = page->bucket; + EXPECT_EQ(1, bucket->activePagesHead->numAllocatedSlots); + partitionFreeGeneric(genericAllocator.root(), ptr); + EXPECT_EQ(0, bucket->activePagesHead->numAllocatedSlots); + EXPECT_NE(-1, bucket->activePagesHead->freeCacheIndex); + } } // Check that the most basic of allocate / free pairs work. -TEST(PartitionAllocTest, Basic) -{ - TestSetup(); - WTF::PartitionBucket* bucket = &allocator.root()->buckets()[kTestBucketIndex]; - WTF::PartitionPage* seedPage = &WTF::PartitionRootGeneric::gSeedPage; - - EXPECT_FALSE(bucket->freePagesHead); - EXPECT_EQ(seedPage, bucket->activePagesHead); - EXPECT_EQ(0, bucket->activePagesHead->nextPage); - - void* ptr = partitionAlloc(allocator.root(), kTestAllocSize); - EXPECT_TRUE(ptr); - EXPECT_EQ(kPointerOffset, reinterpret_cast(ptr) & WTF::kPartitionPageOffsetMask); - // Check that the offset appears to include a guard page. - EXPECT_EQ(WTF::kPartitionPageSize + kPointerOffset, reinterpret_cast(ptr) & WTF::kSuperPageOffsetMask); - - partitionFree(ptr); - // Expect that the last active page does not get tossed to the freelist. - EXPECT_FALSE(bucket->freePagesHead); - - TestShutdown(); +TEST(PartitionAllocTest, Basic) { + TestSetup(); + WTF::PartitionBucket* bucket = &allocator.root()->buckets()[kTestBucketIndex]; + WTF::PartitionPage* seedPage = &WTF::PartitionRootGeneric::gSeedPage; + + EXPECT_FALSE(bucket->freePagesHead); + EXPECT_EQ(seedPage, bucket->activePagesHead); + EXPECT_EQ(0, bucket->activePagesHead->nextPage); + + void* ptr = partitionAlloc(allocator.root(), kTestAllocSize); + EXPECT_TRUE(ptr); + EXPECT_EQ(kPointerOffset, + reinterpret_cast(ptr) & WTF::kPartitionPageOffsetMask); + // Check that the offset appears to include a guard page. + EXPECT_EQ(WTF::kPartitionPageSize + kPointerOffset, + reinterpret_cast(ptr) & WTF::kSuperPageOffsetMask); + + partitionFree(ptr); + // Expect that the last active page does not get tossed to the freelist. + EXPECT_FALSE(bucket->freePagesHead); + + TestShutdown(); } // Check that we can detect a memory leak. -TEST(PartitionAllocTest, SimpleLeak) -{ - TestSetup(); - void* leakedPtr = partitionAlloc(allocator.root(), kTestAllocSize); - (void)leakedPtr; - void* leakedPtr2 = partitionAllocGeneric(genericAllocator.root(), kTestAllocSize); - (void)leakedPtr2; - EXPECT_FALSE(allocator.shutdown()); - EXPECT_FALSE(genericAllocator.shutdown()); +TEST(PartitionAllocTest, SimpleLeak) { + TestSetup(); + void* leakedPtr = partitionAlloc(allocator.root(), kTestAllocSize); + (void)leakedPtr; + void* leakedPtr2 = + partitionAllocGeneric(genericAllocator.root(), kTestAllocSize); + (void)leakedPtr2; + EXPECT_FALSE(allocator.shutdown()); + EXPECT_FALSE(genericAllocator.shutdown()); } // Test multiple allocations, and freelist handling. -TEST(PartitionAllocTest, MultiAlloc) -{ - TestSetup(); - - char* ptr1 = reinterpret_cast(partitionAlloc(allocator.root(), kTestAllocSize)); - char* ptr2 = reinterpret_cast(partitionAlloc(allocator.root(), kTestAllocSize)); - EXPECT_TRUE(ptr1); - EXPECT_TRUE(ptr2); - ptrdiff_t diff = ptr2 - ptr1; - EXPECT_EQ(static_cast(kRealAllocSize), diff); - - // Check that we re-use the just-freed slot. - partitionFree(ptr2); - ptr2 = reinterpret_cast(partitionAlloc(allocator.root(), kTestAllocSize)); - EXPECT_TRUE(ptr2); - diff = ptr2 - ptr1; - EXPECT_EQ(static_cast(kRealAllocSize), diff); - partitionFree(ptr1); - ptr1 = reinterpret_cast(partitionAlloc(allocator.root(), kTestAllocSize)); - EXPECT_TRUE(ptr1); - diff = ptr2 - ptr1; - EXPECT_EQ(static_cast(kRealAllocSize), diff); - - char* ptr3 = reinterpret_cast(partitionAlloc(allocator.root(), kTestAllocSize)); - EXPECT_TRUE(ptr3); - diff = ptr3 - ptr1; - EXPECT_EQ(static_cast(kRealAllocSize * 2), diff); - - partitionFree(ptr1); - partitionFree(ptr2); - partitionFree(ptr3); - - TestShutdown(); +TEST(PartitionAllocTest, MultiAlloc) { + TestSetup(); + + char* ptr1 = + reinterpret_cast(partitionAlloc(allocator.root(), kTestAllocSize)); + char* ptr2 = + reinterpret_cast(partitionAlloc(allocator.root(), kTestAllocSize)); + EXPECT_TRUE(ptr1); + EXPECT_TRUE(ptr2); + ptrdiff_t diff = ptr2 - ptr1; + EXPECT_EQ(static_cast(kRealAllocSize), diff); + + // Check that we re-use the just-freed slot. + partitionFree(ptr2); + ptr2 = + reinterpret_cast(partitionAlloc(allocator.root(), kTestAllocSize)); + EXPECT_TRUE(ptr2); + diff = ptr2 - ptr1; + EXPECT_EQ(static_cast(kRealAllocSize), diff); + partitionFree(ptr1); + ptr1 = + reinterpret_cast(partitionAlloc(allocator.root(), kTestAllocSize)); + EXPECT_TRUE(ptr1); + diff = ptr2 - ptr1; + EXPECT_EQ(static_cast(kRealAllocSize), diff); + + char* ptr3 = + reinterpret_cast(partitionAlloc(allocator.root(), kTestAllocSize)); + EXPECT_TRUE(ptr3); + diff = ptr3 - ptr1; + EXPECT_EQ(static_cast(kRealAllocSize * 2), diff); + + partitionFree(ptr1); + partitionFree(ptr2); + partitionFree(ptr3); + + TestShutdown(); } // Test a bucket with multiple pages. -TEST(PartitionAllocTest, MultiPages) -{ - TestSetup(); - WTF::PartitionBucket* bucket = &allocator.root()->buckets()[kTestBucketIndex]; - - WTF::PartitionPage* page = GetFullPage(kTestAllocSize); - FreeFullPage(page); - EXPECT_FALSE(bucket->freePagesHead); - EXPECT_EQ(page, bucket->activePagesHead); - EXPECT_EQ(0, page->nextPage); - EXPECT_EQ(0, page->numAllocatedSlots); - - page = GetFullPage(kTestAllocSize); - WTF::PartitionPage* page2 = GetFullPage(kTestAllocSize); - - EXPECT_EQ(page2, bucket->activePagesHead); - EXPECT_EQ(0, page2->nextPage); - EXPECT_EQ(reinterpret_cast(partitionPageToPointer(page)) & WTF::kSuperPageBaseMask, reinterpret_cast(partitionPageToPointer(page2)) & WTF::kSuperPageBaseMask); - - // Fully free the non-current page. It should not be freelisted because - // there is no other immediately useable page. The other page is full. - FreeFullPage(page); - EXPECT_EQ(0, page->numAllocatedSlots); - EXPECT_FALSE(bucket->freePagesHead); - EXPECT_EQ(page, bucket->activePagesHead); - - // Allocate a new page, it should pull from the freelist. - page = GetFullPage(kTestAllocSize); - EXPECT_FALSE(bucket->freePagesHead); - EXPECT_EQ(page, bucket->activePagesHead); - - FreeFullPage(page); - FreeFullPage(page2); - EXPECT_EQ(0, page->numAllocatedSlots); - EXPECT_EQ(0, page2->numAllocatedSlots); - EXPECT_EQ(0, page2->numUnprovisionedSlots); - EXPECT_NE(-1, page2->freeCacheIndex); - - TestShutdown(); +TEST(PartitionAllocTest, MultiPages) { + TestSetup(); + WTF::PartitionBucket* bucket = &allocator.root()->buckets()[kTestBucketIndex]; + + WTF::PartitionPage* page = GetFullPage(kTestAllocSize); + FreeFullPage(page); + EXPECT_FALSE(bucket->freePagesHead); + EXPECT_EQ(page, bucket->activePagesHead); + EXPECT_EQ(0, page->nextPage); + EXPECT_EQ(0, page->numAllocatedSlots); + + page = GetFullPage(kTestAllocSize); + WTF::PartitionPage* page2 = GetFullPage(kTestAllocSize); + + EXPECT_EQ(page2, bucket->activePagesHead); + EXPECT_EQ(0, page2->nextPage); + EXPECT_EQ(reinterpret_cast(partitionPageToPointer(page)) & + WTF::kSuperPageBaseMask, + reinterpret_cast(partitionPageToPointer(page2)) & + WTF::kSuperPageBaseMask); + + // Fully free the non-current page. It should not be freelisted because + // there is no other immediately useable page. The other page is full. + FreeFullPage(page); + EXPECT_EQ(0, page->numAllocatedSlots); + EXPECT_FALSE(bucket->freePagesHead); + EXPECT_EQ(page, bucket->activePagesHead); + + // Allocate a new page, it should pull from the freelist. + page = GetFullPage(kTestAllocSize); + EXPECT_FALSE(bucket->freePagesHead); + EXPECT_EQ(page, bucket->activePagesHead); + + FreeFullPage(page); + FreeFullPage(page2); + EXPECT_EQ(0, page->numAllocatedSlots); + EXPECT_EQ(0, page2->numAllocatedSlots); + EXPECT_EQ(0, page2->numUnprovisionedSlots); + EXPECT_NE(-1, page2->freeCacheIndex); + + TestShutdown(); } // Test some finer aspects of internal page transitions. -TEST(PartitionAllocTest, PageTransitions) -{ - TestSetup(); - WTF::PartitionBucket* bucket = &allocator.root()->buckets()[kTestBucketIndex]; - - WTF::PartitionPage* page1 = GetFullPage(kTestAllocSize); - EXPECT_EQ(page1, bucket->activePagesHead); - EXPECT_EQ(0, page1->nextPage); - WTF::PartitionPage* page2 = GetFullPage(kTestAllocSize); - EXPECT_EQ(page2, bucket->activePagesHead); - EXPECT_EQ(0, page2->nextPage); - - // Bounce page1 back into the non-full list then fill it up again. - char* ptr = reinterpret_cast(partitionPageToPointer(page1)) + kPointerOffset; - partitionFree(ptr); - EXPECT_EQ(page1, bucket->activePagesHead); - (void) partitionAlloc(allocator.root(), kTestAllocSize); - EXPECT_EQ(page1, bucket->activePagesHead); - EXPECT_EQ(page2, bucket->activePagesHead->nextPage); - - // Allocating another page at this point should cause us to scan over page1 - // (which is both full and NOT our current page), and evict it from the - // freelist. Older code had a O(n^2) condition due to failure to do this. - WTF::PartitionPage* page3 = GetFullPage(kTestAllocSize); - EXPECT_EQ(page3, bucket->activePagesHead); - EXPECT_EQ(0, page3->nextPage); - - // Work out a pointer into page2 and free it. - ptr = reinterpret_cast(partitionPageToPointer(page2)) + kPointerOffset; - partitionFree(ptr); - // Trying to allocate at this time should cause us to cycle around to page2 - // and find the recently freed slot. - char* newPtr = reinterpret_cast(partitionAlloc(allocator.root(), kTestAllocSize)); - EXPECT_EQ(ptr, newPtr); - EXPECT_EQ(page2, bucket->activePagesHead); - EXPECT_EQ(page3, page2->nextPage); - - // Work out a pointer into page1 and free it. This should pull the page - // back into the list of available pages. - ptr = reinterpret_cast(partitionPageToPointer(page1)) + kPointerOffset; - partitionFree(ptr); - // This allocation should be satisfied by page1. - newPtr = reinterpret_cast(partitionAlloc(allocator.root(), kTestAllocSize)); - EXPECT_EQ(ptr, newPtr); - EXPECT_EQ(page1, bucket->activePagesHead); - EXPECT_EQ(page2, page1->nextPage); - - FreeFullPage(page3); - FreeFullPage(page2); - FreeFullPage(page1); - - // Allocating whilst in this state exposed a bug, so keep the test. - ptr = reinterpret_cast(partitionAlloc(allocator.root(), kTestAllocSize)); - partitionFree(ptr); - - TestShutdown(); +TEST(PartitionAllocTest, PageTransitions) { + TestSetup(); + WTF::PartitionBucket* bucket = &allocator.root()->buckets()[kTestBucketIndex]; + + WTF::PartitionPage* page1 = GetFullPage(kTestAllocSize); + EXPECT_EQ(page1, bucket->activePagesHead); + EXPECT_EQ(0, page1->nextPage); + WTF::PartitionPage* page2 = GetFullPage(kTestAllocSize); + EXPECT_EQ(page2, bucket->activePagesHead); + EXPECT_EQ(0, page2->nextPage); + + // Bounce page1 back into the non-full list then fill it up again. + char* ptr = + reinterpret_cast(partitionPageToPointer(page1)) + kPointerOffset; + partitionFree(ptr); + EXPECT_EQ(page1, bucket->activePagesHead); + (void)partitionAlloc(allocator.root(), kTestAllocSize); + EXPECT_EQ(page1, bucket->activePagesHead); + EXPECT_EQ(page2, bucket->activePagesHead->nextPage); + + // Allocating another page at this point should cause us to scan over page1 + // (which is both full and NOT our current page), and evict it from the + // freelist. Older code had a O(n^2) condition due to failure to do this. + WTF::PartitionPage* page3 = GetFullPage(kTestAllocSize); + EXPECT_EQ(page3, bucket->activePagesHead); + EXPECT_EQ(0, page3->nextPage); + + // Work out a pointer into page2 and free it. + ptr = reinterpret_cast(partitionPageToPointer(page2)) + kPointerOffset; + partitionFree(ptr); + // Trying to allocate at this time should cause us to cycle around to page2 + // and find the recently freed slot. + char* newPtr = + reinterpret_cast(partitionAlloc(allocator.root(), kTestAllocSize)); + EXPECT_EQ(ptr, newPtr); + EXPECT_EQ(page2, bucket->activePagesHead); + EXPECT_EQ(page3, page2->nextPage); + + // Work out a pointer into page1 and free it. This should pull the page + // back into the list of available pages. + ptr = reinterpret_cast(partitionPageToPointer(page1)) + kPointerOffset; + partitionFree(ptr); + // This allocation should be satisfied by page1. + newPtr = + reinterpret_cast(partitionAlloc(allocator.root(), kTestAllocSize)); + EXPECT_EQ(ptr, newPtr); + EXPECT_EQ(page1, bucket->activePagesHead); + EXPECT_EQ(page2, page1->nextPage); + + FreeFullPage(page3); + FreeFullPage(page2); + FreeFullPage(page1); + + // Allocating whilst in this state exposed a bug, so keep the test. + ptr = + reinterpret_cast(partitionAlloc(allocator.root(), kTestAllocSize)); + partitionFree(ptr); + + TestShutdown(); } // Test some corner cases relating to page transitions in the internal // free page list metadata bucket. -TEST(PartitionAllocTest, FreePageListPageTransitions) -{ - TestSetup(); - WTF::PartitionBucket* bucket = &allocator.root()->buckets()[kTestBucketIndex]; - - size_t numToFillFreeListPage = WTF::kPartitionPageSize / (sizeof(WTF::PartitionPage) + kExtraAllocSize); - // The +1 is because we need to account for the fact that the current page - // never gets thrown on the freelist. - ++numToFillFreeListPage; - OwnPtr pages = adoptArrayPtr(new WTF::PartitionPage*[numToFillFreeListPage]); - - size_t i; - for (i = 0; i < numToFillFreeListPage; ++i) { - pages[i] = GetFullPage(kTestAllocSize); - } - EXPECT_EQ(pages[numToFillFreeListPage - 1], bucket->activePagesHead); - for (i = 0; i < numToFillFreeListPage; ++i) - FreeFullPage(pages[i]); - EXPECT_EQ(0, bucket->activePagesHead->numAllocatedSlots); - EXPECT_NE(-1, bucket->activePagesHead->nextPage->freeCacheIndex); - EXPECT_EQ(0, bucket->activePagesHead->nextPage->numAllocatedSlots); - EXPECT_EQ(0, bucket->activePagesHead->nextPage->numUnprovisionedSlots); - - // Allocate / free in a different bucket size so we get control of a - // different free page list. We need two pages because one will be the last - // active page and not get freed. - WTF::PartitionPage* page1 = GetFullPage(kTestAllocSize * 2); - WTF::PartitionPage* page2 = GetFullPage(kTestAllocSize * 2); - FreeFullPage(page1); - FreeFullPage(page2); - - // If we re-allocate all kTestAllocSize allocations, we'll pull all the - // free pages and end up freeing the first page for free page objects. - // It's getting a bit tricky but a nice re-entrancy is going on: - // alloc(kTestAllocSize) -> pulls page from free page list -> - // free(PartitionFreepagelistEntry) -> last entry in page freed -> - // alloc(PartitionFreepagelistEntry). - for (i = 0; i < numToFillFreeListPage; ++i) { - pages[i] = GetFullPage(kTestAllocSize); - } - EXPECT_EQ(pages[numToFillFreeListPage - 1], bucket->activePagesHead); - - // As part of the final free-up, we'll test another re-entrancy: - // free(kTestAllocSize) -> last entry in page freed -> - // alloc(PartitionFreepagelistEntry) -> pulls page from free page list -> - // free(PartitionFreepagelistEntry) - for (i = 0; i < numToFillFreeListPage; ++i) - FreeFullPage(pages[i]); - EXPECT_EQ(0, bucket->activePagesHead->numAllocatedSlots); - EXPECT_NE(-1, bucket->activePagesHead->nextPage->freeCacheIndex); - EXPECT_EQ(0, bucket->activePagesHead->nextPage->numAllocatedSlots); - EXPECT_EQ(0, bucket->activePagesHead->nextPage->numUnprovisionedSlots); - - TestShutdown(); +TEST(PartitionAllocTest, FreePageListPageTransitions) { + TestSetup(); + WTF::PartitionBucket* bucket = &allocator.root()->buckets()[kTestBucketIndex]; + + size_t numToFillFreeListPage = + WTF::kPartitionPageSize / (sizeof(WTF::PartitionPage) + kExtraAllocSize); + // The +1 is because we need to account for the fact that the current page + // never gets thrown on the freelist. + ++numToFillFreeListPage; + OwnPtr pages = + adoptArrayPtr(new WTF::PartitionPage*[numToFillFreeListPage]); + + size_t i; + for (i = 0; i < numToFillFreeListPage; ++i) { + pages[i] = GetFullPage(kTestAllocSize); + } + EXPECT_EQ(pages[numToFillFreeListPage - 1], bucket->activePagesHead); + for (i = 0; i < numToFillFreeListPage; ++i) + FreeFullPage(pages[i]); + EXPECT_EQ(0, bucket->activePagesHead->numAllocatedSlots); + EXPECT_NE(-1, bucket->activePagesHead->nextPage->freeCacheIndex); + EXPECT_EQ(0, bucket->activePagesHead->nextPage->numAllocatedSlots); + EXPECT_EQ(0, bucket->activePagesHead->nextPage->numUnprovisionedSlots); + + // Allocate / free in a different bucket size so we get control of a + // different free page list. We need two pages because one will be the last + // active page and not get freed. + WTF::PartitionPage* page1 = GetFullPage(kTestAllocSize * 2); + WTF::PartitionPage* page2 = GetFullPage(kTestAllocSize * 2); + FreeFullPage(page1); + FreeFullPage(page2); + + // If we re-allocate all kTestAllocSize allocations, we'll pull all the + // free pages and end up freeing the first page for free page objects. + // It's getting a bit tricky but a nice re-entrancy is going on: + // alloc(kTestAllocSize) -> pulls page from free page list -> + // free(PartitionFreepagelistEntry) -> last entry in page freed -> + // alloc(PartitionFreepagelistEntry). + for (i = 0; i < numToFillFreeListPage; ++i) { + pages[i] = GetFullPage(kTestAllocSize); + } + EXPECT_EQ(pages[numToFillFreeListPage - 1], bucket->activePagesHead); + + // As part of the final free-up, we'll test another re-entrancy: + // free(kTestAllocSize) -> last entry in page freed -> + // alloc(PartitionFreepagelistEntry) -> pulls page from free page list -> + // free(PartitionFreepagelistEntry) + for (i = 0; i < numToFillFreeListPage; ++i) + FreeFullPage(pages[i]); + EXPECT_EQ(0, bucket->activePagesHead->numAllocatedSlots); + EXPECT_NE(-1, bucket->activePagesHead->nextPage->freeCacheIndex); + EXPECT_EQ(0, bucket->activePagesHead->nextPage->numAllocatedSlots); + EXPECT_EQ(0, bucket->activePagesHead->nextPage->numUnprovisionedSlots); + + TestShutdown(); } // Test a large series of allocations that cross more than one underlying // 64KB super page allocation. -TEST(PartitionAllocTest, MultiPageAllocs) -{ - TestSetup(); - // This is guaranteed to cross a super page boundary because the first - // partition page "slot" will be taken up by a guard page. - size_t numPagesNeeded = WTF::kNumPartitionPagesPerSuperPage; - // The super page should begin and end in a guard so we one less page in - // order to allocate a single page in the new super page. - --numPagesNeeded; - - EXPECT_GT(numPagesNeeded, 1u); - OwnPtr pages; - pages = adoptArrayPtr(new WTF::PartitionPage*[numPagesNeeded]); - uintptr_t firstSuperPageBase = 0; - size_t i; - for (i = 0; i < numPagesNeeded; ++i) { - pages[i] = GetFullPage(kTestAllocSize); - void* storagePtr = partitionPageToPointer(pages[i]); - if (!i) - firstSuperPageBase = reinterpret_cast(storagePtr) & WTF::kSuperPageBaseMask; - if (i == numPagesNeeded - 1) { - uintptr_t secondSuperPageBase = reinterpret_cast(storagePtr) & WTF::kSuperPageBaseMask; - uintptr_t secondSuperPageOffset = reinterpret_cast(storagePtr) & WTF::kSuperPageOffsetMask; - EXPECT_FALSE(secondSuperPageBase == firstSuperPageBase); - // Check that we allocated a guard page for the second page. - EXPECT_EQ(WTF::kPartitionPageSize, secondSuperPageOffset); - } +TEST(PartitionAllocTest, MultiPageAllocs) { + TestSetup(); + // This is guaranteed to cross a super page boundary because the first + // partition page "slot" will be taken up by a guard page. + size_t numPagesNeeded = WTF::kNumPartitionPagesPerSuperPage; + // The super page should begin and end in a guard so we one less page in + // order to allocate a single page in the new super page. + --numPagesNeeded; + + EXPECT_GT(numPagesNeeded, 1u); + OwnPtr pages; + pages = adoptArrayPtr(new WTF::PartitionPage*[numPagesNeeded]); + uintptr_t firstSuperPageBase = 0; + size_t i; + for (i = 0; i < numPagesNeeded; ++i) { + pages[i] = GetFullPage(kTestAllocSize); + void* storagePtr = partitionPageToPointer(pages[i]); + if (!i) + firstSuperPageBase = + reinterpret_cast(storagePtr) & WTF::kSuperPageBaseMask; + if (i == numPagesNeeded - 1) { + uintptr_t secondSuperPageBase = + reinterpret_cast(storagePtr) & WTF::kSuperPageBaseMask; + uintptr_t secondSuperPageOffset = + reinterpret_cast(storagePtr) & WTF::kSuperPageOffsetMask; + EXPECT_FALSE(secondSuperPageBase == firstSuperPageBase); + // Check that we allocated a guard page for the second page. + EXPECT_EQ(WTF::kPartitionPageSize, secondSuperPageOffset); } - for (i = 0; i < numPagesNeeded; ++i) - FreeFullPage(pages[i]); + } + for (i = 0; i < numPagesNeeded; ++i) + FreeFullPage(pages[i]); - TestShutdown(); + TestShutdown(); } // Test the generic allocation functions that can handle arbitrary sizes and // reallocing etc. -TEST(PartitionAllocTest, GenericAlloc) -{ - TestSetup(); - - void* ptr = partitionAllocGeneric(genericAllocator.root(), 1); - EXPECT_TRUE(ptr); - partitionFreeGeneric(genericAllocator.root(), ptr); - ptr = partitionAllocGeneric(genericAllocator.root(), WTF::kGenericMaxBucketed + 1); - EXPECT_TRUE(ptr); - partitionFreeGeneric(genericAllocator.root(), ptr); - - ptr = partitionAllocGeneric(genericAllocator.root(), 1); - EXPECT_TRUE(ptr); - void* origPtr = ptr; - char* charPtr = static_cast(ptr); - *charPtr = 'A'; - - // Change the size of the realloc, remaining inside the same bucket. - void* newPtr = partitionReallocGeneric(genericAllocator.root(), ptr, 2); - EXPECT_EQ(ptr, newPtr); - newPtr = partitionReallocGeneric(genericAllocator.root(), ptr, 1); - EXPECT_EQ(ptr, newPtr); - newPtr = partitionReallocGeneric(genericAllocator.root(), ptr, WTF::kGenericSmallestBucket); - EXPECT_EQ(ptr, newPtr); - - // Change the size of the realloc, switching buckets. - newPtr = partitionReallocGeneric(genericAllocator.root(), ptr, WTF::kGenericSmallestBucket + 1); - EXPECT_NE(newPtr, ptr); - // Check that the realloc copied correctly. - char* newCharPtr = static_cast(newPtr); - EXPECT_EQ(*newCharPtr, 'A'); +TEST(PartitionAllocTest, GenericAlloc) { + TestSetup(); + + void* ptr = partitionAllocGeneric(genericAllocator.root(), 1); + EXPECT_TRUE(ptr); + partitionFreeGeneric(genericAllocator.root(), ptr); + ptr = partitionAllocGeneric(genericAllocator.root(), + WTF::kGenericMaxBucketed + 1); + EXPECT_TRUE(ptr); + partitionFreeGeneric(genericAllocator.root(), ptr); + + ptr = partitionAllocGeneric(genericAllocator.root(), 1); + EXPECT_TRUE(ptr); + void* origPtr = ptr; + char* charPtr = static_cast(ptr); + *charPtr = 'A'; + + // Change the size of the realloc, remaining inside the same bucket. + void* newPtr = partitionReallocGeneric(genericAllocator.root(), ptr, 2); + EXPECT_EQ(ptr, newPtr); + newPtr = partitionReallocGeneric(genericAllocator.root(), ptr, 1); + EXPECT_EQ(ptr, newPtr); + newPtr = partitionReallocGeneric(genericAllocator.root(), ptr, + WTF::kGenericSmallestBucket); + EXPECT_EQ(ptr, newPtr); + + // Change the size of the realloc, switching buckets. + newPtr = partitionReallocGeneric(genericAllocator.root(), ptr, + WTF::kGenericSmallestBucket + 1); + EXPECT_NE(newPtr, ptr); + // Check that the realloc copied correctly. + char* newCharPtr = static_cast(newPtr); + EXPECT_EQ(*newCharPtr, 'A'); #if ENABLE(ASSERT) - // Subtle: this checks for an old bug where we copied too much from the - // source of the realloc. The condition can be detected by a trashing of - // the uninitialized value in the space of the upsized allocation. - EXPECT_EQ(WTF::kUninitializedByte, static_cast(*(newCharPtr + WTF::kGenericSmallestBucket))); + // Subtle: this checks for an old bug where we copied too much from the + // source of the realloc. The condition can be detected by a trashing of + // the uninitialized value in the space of the upsized allocation. + EXPECT_EQ( + WTF::kUninitializedByte, + static_cast(*(newCharPtr + WTF::kGenericSmallestBucket))); #endif - *newCharPtr = 'B'; - // The realloc moved. To check that the old allocation was freed, we can - // do an alloc of the old allocation size and check that the old allocation - // address is at the head of the freelist and reused. - void* reusedPtr = partitionAllocGeneric(genericAllocator.root(), 1); - EXPECT_EQ(reusedPtr, origPtr); - partitionFreeGeneric(genericAllocator.root(), reusedPtr); - - // Downsize the realloc. - ptr = newPtr; - newPtr = partitionReallocGeneric(genericAllocator.root(), ptr, 1); - EXPECT_EQ(newPtr, origPtr); - newCharPtr = static_cast(newPtr); - EXPECT_EQ(*newCharPtr, 'B'); - *newCharPtr = 'C'; - - // Upsize the realloc to outside the partition. - ptr = newPtr; - newPtr = partitionReallocGeneric(genericAllocator.root(), ptr, WTF::kGenericMaxBucketed + 1); - EXPECT_NE(newPtr, ptr); - newCharPtr = static_cast(newPtr); - EXPECT_EQ(*newCharPtr, 'C'); - *newCharPtr = 'D'; - - // Upsize and downsize the realloc, remaining outside the partition. - ptr = newPtr; - newPtr = partitionReallocGeneric(genericAllocator.root(), ptr, WTF::kGenericMaxBucketed * 10); - newCharPtr = static_cast(newPtr); - EXPECT_EQ(*newCharPtr, 'D'); - *newCharPtr = 'E'; - ptr = newPtr; - newPtr = partitionReallocGeneric(genericAllocator.root(), ptr, WTF::kGenericMaxBucketed * 2); - newCharPtr = static_cast(newPtr); - EXPECT_EQ(*newCharPtr, 'E'); - *newCharPtr = 'F'; - - // Downsize the realloc to inside the partition. - ptr = newPtr; - newPtr = partitionReallocGeneric(genericAllocator.root(), ptr, 1); - EXPECT_NE(newPtr, ptr); - EXPECT_EQ(newPtr, origPtr); - newCharPtr = static_cast(newPtr); - EXPECT_EQ(*newCharPtr, 'F'); - - partitionFreeGeneric(genericAllocator.root(), newPtr); - TestShutdown(); + *newCharPtr = 'B'; + // The realloc moved. To check that the old allocation was freed, we can + // do an alloc of the old allocation size and check that the old allocation + // address is at the head of the freelist and reused. + void* reusedPtr = partitionAllocGeneric(genericAllocator.root(), 1); + EXPECT_EQ(reusedPtr, origPtr); + partitionFreeGeneric(genericAllocator.root(), reusedPtr); + + // Downsize the realloc. + ptr = newPtr; + newPtr = partitionReallocGeneric(genericAllocator.root(), ptr, 1); + EXPECT_EQ(newPtr, origPtr); + newCharPtr = static_cast(newPtr); + EXPECT_EQ(*newCharPtr, 'B'); + *newCharPtr = 'C'; + + // Upsize the realloc to outside the partition. + ptr = newPtr; + newPtr = partitionReallocGeneric(genericAllocator.root(), ptr, + WTF::kGenericMaxBucketed + 1); + EXPECT_NE(newPtr, ptr); + newCharPtr = static_cast(newPtr); + EXPECT_EQ(*newCharPtr, 'C'); + *newCharPtr = 'D'; + + // Upsize and downsize the realloc, remaining outside the partition. + ptr = newPtr; + newPtr = partitionReallocGeneric(genericAllocator.root(), ptr, + WTF::kGenericMaxBucketed * 10); + newCharPtr = static_cast(newPtr); + EXPECT_EQ(*newCharPtr, 'D'); + *newCharPtr = 'E'; + ptr = newPtr; + newPtr = partitionReallocGeneric(genericAllocator.root(), ptr, + WTF::kGenericMaxBucketed * 2); + newCharPtr = static_cast(newPtr); + EXPECT_EQ(*newCharPtr, 'E'); + *newCharPtr = 'F'; + + // Downsize the realloc to inside the partition. + ptr = newPtr; + newPtr = partitionReallocGeneric(genericAllocator.root(), ptr, 1); + EXPECT_NE(newPtr, ptr); + EXPECT_EQ(newPtr, origPtr); + newCharPtr = static_cast(newPtr); + EXPECT_EQ(*newCharPtr, 'F'); + + partitionFreeGeneric(genericAllocator.root(), newPtr); + TestShutdown(); } // Test the generic allocation functions can handle some specific sizes of // interest. -TEST(PartitionAllocTest, GenericAllocSizes) -{ - TestSetup(); - - void* ptr = partitionAllocGeneric(genericAllocator.root(), 0); - EXPECT_TRUE(ptr); - partitionFreeGeneric(genericAllocator.root(), ptr); - - // kPartitionPageSize is interesting because it results in just one - // allocation per page, which tripped up some corner cases. - size_t size = WTF::kPartitionPageSize - kExtraAllocSize; - ptr = partitionAllocGeneric(genericAllocator.root(), size); - EXPECT_TRUE(ptr); - void* ptr2 = partitionAllocGeneric(genericAllocator.root(), size); - EXPECT_TRUE(ptr2); - partitionFreeGeneric(genericAllocator.root(), ptr); - // Should be freeable at this point. - WTF::PartitionPage* page = WTF::partitionPointerToPage(WTF::partitionCookieFreePointerAdjust(ptr)); - EXPECT_NE(-1, page->freeCacheIndex); - partitionFreeGeneric(genericAllocator.root(), ptr2); - - size = (((WTF::kPartitionPageSize * WTF::kMaxPartitionPagesPerSlotSpan) - WTF::kSystemPageSize) / 2) - kExtraAllocSize; - ptr = partitionAllocGeneric(genericAllocator.root(), size); - EXPECT_TRUE(ptr); - memset(ptr, 'A', size); - ptr2 = partitionAllocGeneric(genericAllocator.root(), size); - EXPECT_TRUE(ptr2); - void* ptr3 = partitionAllocGeneric(genericAllocator.root(), size); - EXPECT_TRUE(ptr3); - void* ptr4 = partitionAllocGeneric(genericAllocator.root(), size); - EXPECT_TRUE(ptr4); - - page = WTF::partitionPointerToPage(WTF::partitionCookieFreePointerAdjust(ptr)); - WTF::PartitionPage* page2 = WTF::partitionPointerToPage(WTF::partitionCookieFreePointerAdjust(ptr3)); - EXPECT_NE(page, page2); - - partitionFreeGeneric(genericAllocator.root(), ptr); - partitionFreeGeneric(genericAllocator.root(), ptr3); - partitionFreeGeneric(genericAllocator.root(), ptr2); - // Should be freeable at this point. - EXPECT_NE(-1, page->freeCacheIndex); - EXPECT_EQ(0, page->numAllocatedSlots); - EXPECT_EQ(0, page->numUnprovisionedSlots); - void* newPtr = partitionAllocGeneric(genericAllocator.root(), size); - EXPECT_EQ(ptr3, newPtr); - newPtr = partitionAllocGeneric(genericAllocator.root(), size); - EXPECT_EQ(ptr2, newPtr); +TEST(PartitionAllocTest, GenericAllocSizes) { + TestSetup(); + + void* ptr = partitionAllocGeneric(genericAllocator.root(), 0); + EXPECT_TRUE(ptr); + partitionFreeGeneric(genericAllocator.root(), ptr); + + // kPartitionPageSize is interesting because it results in just one + // allocation per page, which tripped up some corner cases. + size_t size = WTF::kPartitionPageSize - kExtraAllocSize; + ptr = partitionAllocGeneric(genericAllocator.root(), size); + EXPECT_TRUE(ptr); + void* ptr2 = partitionAllocGeneric(genericAllocator.root(), size); + EXPECT_TRUE(ptr2); + partitionFreeGeneric(genericAllocator.root(), ptr); + // Should be freeable at this point. + WTF::PartitionPage* page = + WTF::partitionPointerToPage(WTF::partitionCookieFreePointerAdjust(ptr)); + EXPECT_NE(-1, page->freeCacheIndex); + partitionFreeGeneric(genericAllocator.root(), ptr2); + + size = (((WTF::kPartitionPageSize * WTF::kMaxPartitionPagesPerSlotSpan) - + WTF::kSystemPageSize) / + 2) - + kExtraAllocSize; + ptr = partitionAllocGeneric(genericAllocator.root(), size); + EXPECT_TRUE(ptr); + memset(ptr, 'A', size); + ptr2 = partitionAllocGeneric(genericAllocator.root(), size); + EXPECT_TRUE(ptr2); + void* ptr3 = partitionAllocGeneric(genericAllocator.root(), size); + EXPECT_TRUE(ptr3); + void* ptr4 = partitionAllocGeneric(genericAllocator.root(), size); + EXPECT_TRUE(ptr4); + + page = + WTF::partitionPointerToPage(WTF::partitionCookieFreePointerAdjust(ptr)); + WTF::PartitionPage* page2 = + WTF::partitionPointerToPage(WTF::partitionCookieFreePointerAdjust(ptr3)); + EXPECT_NE(page, page2); + + partitionFreeGeneric(genericAllocator.root(), ptr); + partitionFreeGeneric(genericAllocator.root(), ptr3); + partitionFreeGeneric(genericAllocator.root(), ptr2); + // Should be freeable at this point. + EXPECT_NE(-1, page->freeCacheIndex); + EXPECT_EQ(0, page->numAllocatedSlots); + EXPECT_EQ(0, page->numUnprovisionedSlots); + void* newPtr = partitionAllocGeneric(genericAllocator.root(), size); + EXPECT_EQ(ptr3, newPtr); + newPtr = partitionAllocGeneric(genericAllocator.root(), size); + EXPECT_EQ(ptr2, newPtr); #if OS(LINUX) && !ENABLE(ASSERT) - // On Linux, we have a guarantee that freelisting a page should cause its - // contents to be nulled out. We check for null here to detect an bug we - // had where a large slot size was causing us to not properly free all - // resources back to the system. - // We only run the check when asserts are disabled because when they are - // enabled, the allocated area is overwritten with an "uninitialized" - // byte pattern. - EXPECT_EQ(0, *(reinterpret_cast(newPtr) + (size - 1))); + // On Linux, we have a guarantee that freelisting a page should cause its + // contents to be nulled out. We check for null here to detect an bug we + // had where a large slot size was causing us to not properly free all + // resources back to the system. + // We only run the check when asserts are disabled because when they are + // enabled, the allocated area is overwritten with an "uninitialized" + // byte pattern. + EXPECT_EQ(0, *(reinterpret_cast(newPtr) + (size - 1))); #endif - partitionFreeGeneric(genericAllocator.root(), newPtr); - partitionFreeGeneric(genericAllocator.root(), ptr3); - partitionFreeGeneric(genericAllocator.root(), ptr4); - - // Can we allocate a massive (512MB) size? - ptr = partitionAllocGeneric(genericAllocator.root(), 512 * 1024 * 1024); - partitionFreeGeneric(genericAllocator.root(), ptr); - - // Check a more reasonable, but still direct mapped, size. - // Chop a system page and a byte off to test for rounding errors. - size = 20 * 1024 * 1024; - size -= WTF::kSystemPageSize; - size -= 1; - ptr = partitionAllocGeneric(genericAllocator.root(), size); - char* charPtr = reinterpret_cast(ptr); - *(charPtr + (size - 1)) = 'A'; - partitionFreeGeneric(genericAllocator.root(), ptr); - - // Can we free null? - partitionFreeGeneric(genericAllocator.root(), 0); - - // Do we correctly get a null for a failed allocation? - EXPECT_EQ(0, partitionAllocGenericFlags(genericAllocator.root(), WTF::PartitionAllocReturnNull, 3u * 1024 * 1024 * 1024)); - - TestShutdown(); + partitionFreeGeneric(genericAllocator.root(), newPtr); + partitionFreeGeneric(genericAllocator.root(), ptr3); + partitionFreeGeneric(genericAllocator.root(), ptr4); + + // Can we allocate a massive (512MB) size? + ptr = partitionAllocGeneric(genericAllocator.root(), 512 * 1024 * 1024); + partitionFreeGeneric(genericAllocator.root(), ptr); + + // Check a more reasonable, but still direct mapped, size. + // Chop a system page and a byte off to test for rounding errors. + size = 20 * 1024 * 1024; + size -= WTF::kSystemPageSize; + size -= 1; + ptr = partitionAllocGeneric(genericAllocator.root(), size); + char* charPtr = reinterpret_cast(ptr); + *(charPtr + (size - 1)) = 'A'; + partitionFreeGeneric(genericAllocator.root(), ptr); + + // Can we free null? + partitionFreeGeneric(genericAllocator.root(), 0); + + // Do we correctly get a null for a failed allocation? + EXPECT_EQ(0, partitionAllocGenericFlags(genericAllocator.root(), + WTF::PartitionAllocReturnNull, + 3u * 1024 * 1024 * 1024)); + + TestShutdown(); } // Test that we can fetch the real allocated size after an allocation. -TEST(PartitionAllocTest, GenericAllocGetSize) -{ - TestSetup(); - - void* ptr; - size_t requestedSize, actualSize, predictedSize; - - EXPECT_TRUE(partitionAllocSupportsGetSize()); - - // Allocate something small. - requestedSize = 511 - kExtraAllocSize; - predictedSize = partitionAllocActualSize(genericAllocator.root(), requestedSize); - ptr = partitionAllocGeneric(genericAllocator.root(), requestedSize); - EXPECT_TRUE(ptr); - actualSize = partitionAllocGetSize(ptr); - EXPECT_EQ(predictedSize, actualSize); - EXPECT_LT(requestedSize, actualSize); - partitionFreeGeneric(genericAllocator.root(), ptr); - - // Allocate a size that should be a perfect match for a bucket, because it - // is an exact power of 2. - requestedSize = (256 * 1024) - kExtraAllocSize; - predictedSize = partitionAllocActualSize(genericAllocator.root(), requestedSize); - ptr = partitionAllocGeneric(genericAllocator.root(), requestedSize); - EXPECT_TRUE(ptr); - actualSize = partitionAllocGetSize(ptr); - EXPECT_EQ(predictedSize, actualSize); - EXPECT_EQ(requestedSize, actualSize); - partitionFreeGeneric(genericAllocator.root(), ptr); - - // Allocate a size that is a system page smaller than a bucket. GetSize() - // should return a larger size than we asked for now. - requestedSize = (256 * 1024) - WTF::kSystemPageSize - kExtraAllocSize; - predictedSize = partitionAllocActualSize(genericAllocator.root(), requestedSize); - ptr = partitionAllocGeneric(genericAllocator.root(), requestedSize); - EXPECT_TRUE(ptr); - actualSize = partitionAllocGetSize(ptr); - EXPECT_EQ(predictedSize, actualSize); - EXPECT_EQ(requestedSize + WTF::kSystemPageSize, actualSize); - // Check that we can write at the end of the reported size too. - char* charPtr = reinterpret_cast(ptr); - *(charPtr + (actualSize - 1)) = 'A'; - partitionFreeGeneric(genericAllocator.root(), ptr); - - // Allocate something very large, and uneven. - requestedSize = 512 * 1024 * 1024 - 1; - predictedSize = partitionAllocActualSize(genericAllocator.root(), requestedSize); - ptr = partitionAllocGeneric(genericAllocator.root(), requestedSize); - EXPECT_TRUE(ptr); - actualSize = partitionAllocGetSize(ptr); - EXPECT_EQ(predictedSize, actualSize); - EXPECT_LT(requestedSize, actualSize); - partitionFreeGeneric(genericAllocator.root(), ptr); - - // Too large allocation. - requestedSize = INT_MAX; - predictedSize = partitionAllocActualSize(genericAllocator.root(), requestedSize); - EXPECT_EQ(requestedSize, predictedSize); - - TestShutdown(); +TEST(PartitionAllocTest, GenericAllocGetSize) { + TestSetup(); + + void* ptr; + size_t requestedSize, actualSize, predictedSize; + + EXPECT_TRUE(partitionAllocSupportsGetSize()); + + // Allocate something small. + requestedSize = 511 - kExtraAllocSize; + predictedSize = + partitionAllocActualSize(genericAllocator.root(), requestedSize); + ptr = partitionAllocGeneric(genericAllocator.root(), requestedSize); + EXPECT_TRUE(ptr); + actualSize = partitionAllocGetSize(ptr); + EXPECT_EQ(predictedSize, actualSize); + EXPECT_LT(requestedSize, actualSize); + partitionFreeGeneric(genericAllocator.root(), ptr); + + // Allocate a size that should be a perfect match for a bucket, because it + // is an exact power of 2. + requestedSize = (256 * 1024) - kExtraAllocSize; + predictedSize = + partitionAllocActualSize(genericAllocator.root(), requestedSize); + ptr = partitionAllocGeneric(genericAllocator.root(), requestedSize); + EXPECT_TRUE(ptr); + actualSize = partitionAllocGetSize(ptr); + EXPECT_EQ(predictedSize, actualSize); + EXPECT_EQ(requestedSize, actualSize); + partitionFreeGeneric(genericAllocator.root(), ptr); + + // Allocate a size that is a system page smaller than a bucket. GetSize() + // should return a larger size than we asked for now. + requestedSize = (256 * 1024) - WTF::kSystemPageSize - kExtraAllocSize; + predictedSize = + partitionAllocActualSize(genericAllocator.root(), requestedSize); + ptr = partitionAllocGeneric(genericAllocator.root(), requestedSize); + EXPECT_TRUE(ptr); + actualSize = partitionAllocGetSize(ptr); + EXPECT_EQ(predictedSize, actualSize); + EXPECT_EQ(requestedSize + WTF::kSystemPageSize, actualSize); + // Check that we can write at the end of the reported size too. + char* charPtr = reinterpret_cast(ptr); + *(charPtr + (actualSize - 1)) = 'A'; + partitionFreeGeneric(genericAllocator.root(), ptr); + + // Allocate something very large, and uneven. + requestedSize = 512 * 1024 * 1024 - 1; + predictedSize = + partitionAllocActualSize(genericAllocator.root(), requestedSize); + ptr = partitionAllocGeneric(genericAllocator.root(), requestedSize); + EXPECT_TRUE(ptr); + actualSize = partitionAllocGetSize(ptr); + EXPECT_EQ(predictedSize, actualSize); + EXPECT_LT(requestedSize, actualSize); + partitionFreeGeneric(genericAllocator.root(), ptr); + + // Too large allocation. + requestedSize = INT_MAX; + predictedSize = + partitionAllocActualSize(genericAllocator.root(), requestedSize); + EXPECT_EQ(requestedSize, predictedSize); + + TestShutdown(); } // Test the realloc() contract. -TEST(PartitionAllocTest, Realloc) -{ - TestSetup(); - - // realloc(0, size) should be equivalent to malloc(). - void* ptr = partitionReallocGeneric(genericAllocator.root(), 0, kTestAllocSize); - memset(ptr, 'A', kTestAllocSize); - WTF::PartitionPage* page = WTF::partitionPointerToPage(WTF::partitionCookieFreePointerAdjust(ptr)); - // realloc(ptr, 0) should be equivalent to free(). - void* ptr2 = partitionReallocGeneric(genericAllocator.root(), ptr, 0); - EXPECT_EQ(0, ptr2); - EXPECT_EQ(WTF::partitionCookieFreePointerAdjust(ptr), page->freelistHead); - - // Test that growing an allocation with realloc() copies everything from the - // old allocation. - size_t size = WTF::kSystemPageSize - kExtraAllocSize; - EXPECT_EQ(size, partitionAllocActualSize(genericAllocator.root(), size)); - ptr = partitionAllocGeneric(genericAllocator.root(), size); - memset(ptr, 'A', size); - ptr2 = partitionReallocGeneric(genericAllocator.root(), ptr, size + 1); - EXPECT_NE(ptr, ptr2); - char* charPtr2 = static_cast(ptr2); - EXPECT_EQ('A', charPtr2[0]); - EXPECT_EQ('A', charPtr2[size - 1]); +TEST(PartitionAllocTest, Realloc) { + TestSetup(); + + // realloc(0, size) should be equivalent to malloc(). + void* ptr = + partitionReallocGeneric(genericAllocator.root(), 0, kTestAllocSize); + memset(ptr, 'A', kTestAllocSize); + WTF::PartitionPage* page = + WTF::partitionPointerToPage(WTF::partitionCookieFreePointerAdjust(ptr)); + // realloc(ptr, 0) should be equivalent to free(). + void* ptr2 = partitionReallocGeneric(genericAllocator.root(), ptr, 0); + EXPECT_EQ(0, ptr2); + EXPECT_EQ(WTF::partitionCookieFreePointerAdjust(ptr), page->freelistHead); + + // Test that growing an allocation with realloc() copies everything from the + // old allocation. + size_t size = WTF::kSystemPageSize - kExtraAllocSize; + EXPECT_EQ(size, partitionAllocActualSize(genericAllocator.root(), size)); + ptr = partitionAllocGeneric(genericAllocator.root(), size); + memset(ptr, 'A', size); + ptr2 = partitionReallocGeneric(genericAllocator.root(), ptr, size + 1); + EXPECT_NE(ptr, ptr2); + char* charPtr2 = static_cast(ptr2); + EXPECT_EQ('A', charPtr2[0]); + EXPECT_EQ('A', charPtr2[size - 1]); #if ENABLE(ASSERT) - EXPECT_EQ(WTF::kUninitializedByte, static_cast(charPtr2[size])); + EXPECT_EQ(WTF::kUninitializedByte, + static_cast(charPtr2[size])); #endif - // Test that shrinking an allocation with realloc() also copies everything - // from the old allocation. - ptr = partitionReallocGeneric(genericAllocator.root(), ptr2, size - 1); - EXPECT_NE(ptr2, ptr); - char* charPtr = static_cast(ptr); - EXPECT_EQ('A', charPtr[0]); - EXPECT_EQ('A', charPtr[size - 2]); + // Test that shrinking an allocation with realloc() also copies everything + // from the old allocation. + ptr = partitionReallocGeneric(genericAllocator.root(), ptr2, size - 1); + EXPECT_NE(ptr2, ptr); + char* charPtr = static_cast(ptr); + EXPECT_EQ('A', charPtr[0]); + EXPECT_EQ('A', charPtr[size - 2]); #if ENABLE(ASSERT) - EXPECT_EQ(WTF::kUninitializedByte, static_cast(charPtr[size - 1])); + EXPECT_EQ(WTF::kUninitializedByte, + static_cast(charPtr[size - 1])); #endif - partitionFreeGeneric(genericAllocator.root(), ptr); - - // Test that shrinking a direct mapped allocation happens in-place. - size = WTF::kGenericMaxBucketed + 16 * WTF::kSystemPageSize; - ptr = partitionAllocGeneric(genericAllocator.root(), size); - size_t actualSize = partitionAllocGetSize(ptr); - ptr2 = partitionReallocGeneric(genericAllocator.root(), ptr, WTF::kGenericMaxBucketed + 8 * WTF::kSystemPageSize); - EXPECT_EQ(ptr, ptr2); - EXPECT_EQ(actualSize - 8 * WTF::kSystemPageSize, partitionAllocGetSize(ptr2)); - - // Test that a previously in-place shrunk direct mapped allocation can be - // expanded up again within its original size. - ptr = partitionReallocGeneric(genericAllocator.root(), ptr2, size - WTF::kSystemPageSize); - EXPECT_EQ(ptr2, ptr); - EXPECT_EQ(actualSize - WTF::kSystemPageSize, partitionAllocGetSize(ptr)); - - // Test that a direct mapped allocation is performed not in-place when the - // new size is small enough. - ptr2 = partitionReallocGeneric(genericAllocator.root(), ptr, WTF::kSystemPageSize); - EXPECT_NE(ptr, ptr2); - - partitionFreeGeneric(genericAllocator.root(), ptr2); - - TestShutdown(); + partitionFreeGeneric(genericAllocator.root(), ptr); + + // Test that shrinking a direct mapped allocation happens in-place. + size = WTF::kGenericMaxBucketed + 16 * WTF::kSystemPageSize; + ptr = partitionAllocGeneric(genericAllocator.root(), size); + size_t actualSize = partitionAllocGetSize(ptr); + ptr2 = partitionReallocGeneric( + genericAllocator.root(), ptr, + WTF::kGenericMaxBucketed + 8 * WTF::kSystemPageSize); + EXPECT_EQ(ptr, ptr2); + EXPECT_EQ(actualSize - 8 * WTF::kSystemPageSize, partitionAllocGetSize(ptr2)); + + // Test that a previously in-place shrunk direct mapped allocation can be + // expanded up again within its original size. + ptr = partitionReallocGeneric(genericAllocator.root(), ptr2, + size - WTF::kSystemPageSize); + EXPECT_EQ(ptr2, ptr); + EXPECT_EQ(actualSize - WTF::kSystemPageSize, partitionAllocGetSize(ptr)); + + // Test that a direct mapped allocation is performed not in-place when the + // new size is small enough. + ptr2 = partitionReallocGeneric(genericAllocator.root(), ptr, + WTF::kSystemPageSize); + EXPECT_NE(ptr, ptr2); + + partitionFreeGeneric(genericAllocator.root(), ptr2); + + TestShutdown(); } // Tests the handing out of freelists for partial pages. -TEST(PartitionAllocTest, PartialPageFreelists) -{ - TestSetup(); - - size_t bigSize = allocator.root()->maxAllocation - kExtraAllocSize; - EXPECT_EQ(WTF::kSystemPageSize - WTF::kAllocationGranularity, bigSize + kExtraAllocSize); - size_t bucketIdx = (bigSize + kExtraAllocSize) >> WTF::kBucketShift; - WTF::PartitionBucket* bucket = &allocator.root()->buckets()[bucketIdx]; - EXPECT_EQ(0, bucket->freePagesHead); - - void* ptr = partitionAlloc(allocator.root(), bigSize); - EXPECT_TRUE(ptr); - - WTF::PartitionPage* page = WTF::partitionPointerToPage(WTF::partitionCookieFreePointerAdjust(ptr)); - size_t totalSlots = (page->bucket->numSystemPagesPerSlotSpan * WTF::kSystemPageSize) / (bigSize + kExtraAllocSize); - EXPECT_EQ(4u, totalSlots); - // The freelist should have one entry, because we were able to exactly fit - // one object slot and one freelist pointer (the null that the head points - // to) into a system page. - EXPECT_TRUE(page->freelistHead); - EXPECT_EQ(1, page->numAllocatedSlots); - EXPECT_EQ(2, page->numUnprovisionedSlots); - - void* ptr2 = partitionAlloc(allocator.root(), bigSize); - EXPECT_TRUE(ptr2); - EXPECT_FALSE(page->freelistHead); - EXPECT_EQ(2, page->numAllocatedSlots); - EXPECT_EQ(2, page->numUnprovisionedSlots); - - void* ptr3 = partitionAlloc(allocator.root(), bigSize); - EXPECT_TRUE(ptr3); - EXPECT_TRUE(page->freelistHead); - EXPECT_EQ(3, page->numAllocatedSlots); - EXPECT_EQ(0, page->numUnprovisionedSlots); - - void* ptr4 = partitionAlloc(allocator.root(), bigSize); - EXPECT_TRUE(ptr4); - EXPECT_FALSE(page->freelistHead); - EXPECT_EQ(4, page->numAllocatedSlots); - EXPECT_EQ(0, page->numUnprovisionedSlots); - - void* ptr5 = partitionAlloc(allocator.root(), bigSize); - EXPECT_TRUE(ptr5); - - WTF::PartitionPage* page2 = WTF::partitionPointerToPage(WTF::partitionCookieFreePointerAdjust(ptr5)); - EXPECT_EQ(1, page2->numAllocatedSlots); - - // Churn things a little whilst there's a partial page freelist. - partitionFree(ptr); - ptr = partitionAlloc(allocator.root(), bigSize); - void* ptr6 = partitionAlloc(allocator.root(), bigSize); - - partitionFree(ptr); - partitionFree(ptr2); - partitionFree(ptr3); - partitionFree(ptr4); - partitionFree(ptr5); - partitionFree(ptr6); - EXPECT_NE(-1, page->freeCacheIndex); - EXPECT_NE(-1, page2->freeCacheIndex); - EXPECT_TRUE(page2->freelistHead); - EXPECT_EQ(0, page2->numAllocatedSlots); - - // And test a couple of sizes that do not cross kSystemPageSize with a single allocation. - size_t mediumSize = (WTF::kSystemPageSize / 2) - kExtraAllocSize; - bucketIdx = (mediumSize + kExtraAllocSize) >> WTF::kBucketShift; - bucket = &allocator.root()->buckets()[bucketIdx]; - EXPECT_EQ(0, bucket->freePagesHead); - - ptr = partitionAlloc(allocator.root(), mediumSize); - EXPECT_TRUE(ptr); - page = WTF::partitionPointerToPage(WTF::partitionCookieFreePointerAdjust(ptr)); - EXPECT_EQ(1, page->numAllocatedSlots); - totalSlots = (page->bucket->numSystemPagesPerSlotSpan * WTF::kSystemPageSize) / (mediumSize + kExtraAllocSize); - size_t firstPageSlots = WTF::kSystemPageSize / (mediumSize + kExtraAllocSize); - EXPECT_EQ(2u, firstPageSlots); - EXPECT_EQ(totalSlots - firstPageSlots, page->numUnprovisionedSlots); - - partitionFree(ptr); - - size_t smallSize = (WTF::kSystemPageSize / 4) - kExtraAllocSize; - bucketIdx = (smallSize + kExtraAllocSize) >> WTF::kBucketShift; - bucket = &allocator.root()->buckets()[bucketIdx]; - EXPECT_EQ(0, bucket->freePagesHead); - - ptr = partitionAlloc(allocator.root(), smallSize); - EXPECT_TRUE(ptr); - page = WTF::partitionPointerToPage(WTF::partitionCookieFreePointerAdjust(ptr)); - EXPECT_EQ(1, page->numAllocatedSlots); - totalSlots = (page->bucket->numSystemPagesPerSlotSpan * WTF::kSystemPageSize) / (smallSize + kExtraAllocSize); - firstPageSlots = WTF::kSystemPageSize / (smallSize + kExtraAllocSize); - EXPECT_EQ(totalSlots - firstPageSlots, page->numUnprovisionedSlots); - - partitionFree(ptr); - EXPECT_TRUE(page->freelistHead); - EXPECT_EQ(0, page->numAllocatedSlots); - - size_t verySmallSize = 32 - kExtraAllocSize; - bucketIdx = (verySmallSize + kExtraAllocSize) >> WTF::kBucketShift; - bucket = &allocator.root()->buckets()[bucketIdx]; - EXPECT_EQ(0, bucket->freePagesHead); - - ptr = partitionAlloc(allocator.root(), verySmallSize); - EXPECT_TRUE(ptr); - page = WTF::partitionPointerToPage(WTF::partitionCookieFreePointerAdjust(ptr)); - EXPECT_EQ(1, page->numAllocatedSlots); - totalSlots = (page->bucket->numSystemPagesPerSlotSpan * WTF::kSystemPageSize) / (verySmallSize + kExtraAllocSize); - firstPageSlots = WTF::kSystemPageSize / (verySmallSize + kExtraAllocSize); - EXPECT_EQ(totalSlots - firstPageSlots, page->numUnprovisionedSlots); - - partitionFree(ptr); - EXPECT_TRUE(page->freelistHead); - EXPECT_EQ(0, page->numAllocatedSlots); - - // And try an allocation size (against the generic allocator) that is - // larger than a system page. - size_t pageAndAHalfSize = (WTF::kSystemPageSize + (WTF::kSystemPageSize / 2)) - kExtraAllocSize; - ptr = partitionAllocGeneric(genericAllocator.root(), pageAndAHalfSize); - EXPECT_TRUE(ptr); - page = WTF::partitionPointerToPage(WTF::partitionCookieFreePointerAdjust(ptr)); - EXPECT_EQ(1, page->numAllocatedSlots); - EXPECT_TRUE(page->freelistHead); - totalSlots = (page->bucket->numSystemPagesPerSlotSpan * WTF::kSystemPageSize) / (pageAndAHalfSize + kExtraAllocSize); - EXPECT_EQ(totalSlots - 2, page->numUnprovisionedSlots); - partitionFreeGeneric(genericAllocator.root(), ptr); - - // And then make sure than exactly the page size only faults one page. - size_t pageSize = WTF::kSystemPageSize - kExtraAllocSize; - ptr = partitionAllocGeneric(genericAllocator.root(), pageSize); - EXPECT_TRUE(ptr); - page = WTF::partitionPointerToPage(WTF::partitionCookieFreePointerAdjust(ptr)); - EXPECT_EQ(1, page->numAllocatedSlots); - EXPECT_FALSE(page->freelistHead); - totalSlots = (page->bucket->numSystemPagesPerSlotSpan * WTF::kSystemPageSize) / (pageSize + kExtraAllocSize); - EXPECT_EQ(totalSlots - 1, page->numUnprovisionedSlots); - partitionFreeGeneric(genericAllocator.root(), ptr); - - TestShutdown(); +TEST(PartitionAllocTest, PartialPageFreelists) { + TestSetup(); + + size_t bigSize = allocator.root()->maxAllocation - kExtraAllocSize; + EXPECT_EQ(WTF::kSystemPageSize - WTF::kAllocationGranularity, + bigSize + kExtraAllocSize); + size_t bucketIdx = (bigSize + kExtraAllocSize) >> WTF::kBucketShift; + WTF::PartitionBucket* bucket = &allocator.root()->buckets()[bucketIdx]; + EXPECT_EQ(0, bucket->freePagesHead); + + void* ptr = partitionAlloc(allocator.root(), bigSize); + EXPECT_TRUE(ptr); + + WTF::PartitionPage* page = + WTF::partitionPointerToPage(WTF::partitionCookieFreePointerAdjust(ptr)); + size_t totalSlots = + (page->bucket->numSystemPagesPerSlotSpan * WTF::kSystemPageSize) / + (bigSize + kExtraAllocSize); + EXPECT_EQ(4u, totalSlots); + // The freelist should have one entry, because we were able to exactly fit + // one object slot and one freelist pointer (the null that the head points + // to) into a system page. + EXPECT_TRUE(page->freelistHead); + EXPECT_EQ(1, page->numAllocatedSlots); + EXPECT_EQ(2, page->numUnprovisionedSlots); + + void* ptr2 = partitionAlloc(allocator.root(), bigSize); + EXPECT_TRUE(ptr2); + EXPECT_FALSE(page->freelistHead); + EXPECT_EQ(2, page->numAllocatedSlots); + EXPECT_EQ(2, page->numUnprovisionedSlots); + + void* ptr3 = partitionAlloc(allocator.root(), bigSize); + EXPECT_TRUE(ptr3); + EXPECT_TRUE(page->freelistHead); + EXPECT_EQ(3, page->numAllocatedSlots); + EXPECT_EQ(0, page->numUnprovisionedSlots); + + void* ptr4 = partitionAlloc(allocator.root(), bigSize); + EXPECT_TRUE(ptr4); + EXPECT_FALSE(page->freelistHead); + EXPECT_EQ(4, page->numAllocatedSlots); + EXPECT_EQ(0, page->numUnprovisionedSlots); + + void* ptr5 = partitionAlloc(allocator.root(), bigSize); + EXPECT_TRUE(ptr5); + + WTF::PartitionPage* page2 = + WTF::partitionPointerToPage(WTF::partitionCookieFreePointerAdjust(ptr5)); + EXPECT_EQ(1, page2->numAllocatedSlots); + + // Churn things a little whilst there's a partial page freelist. + partitionFree(ptr); + ptr = partitionAlloc(allocator.root(), bigSize); + void* ptr6 = partitionAlloc(allocator.root(), bigSize); + + partitionFree(ptr); + partitionFree(ptr2); + partitionFree(ptr3); + partitionFree(ptr4); + partitionFree(ptr5); + partitionFree(ptr6); + EXPECT_NE(-1, page->freeCacheIndex); + EXPECT_NE(-1, page2->freeCacheIndex); + EXPECT_TRUE(page2->freelistHead); + EXPECT_EQ(0, page2->numAllocatedSlots); + + // And test a couple of sizes that do not cross kSystemPageSize with a single + // allocation. + size_t mediumSize = (WTF::kSystemPageSize / 2) - kExtraAllocSize; + bucketIdx = (mediumSize + kExtraAllocSize) >> WTF::kBucketShift; + bucket = &allocator.root()->buckets()[bucketIdx]; + EXPECT_EQ(0, bucket->freePagesHead); + + ptr = partitionAlloc(allocator.root(), mediumSize); + EXPECT_TRUE(ptr); + page = + WTF::partitionPointerToPage(WTF::partitionCookieFreePointerAdjust(ptr)); + EXPECT_EQ(1, page->numAllocatedSlots); + totalSlots = + (page->bucket->numSystemPagesPerSlotSpan * WTF::kSystemPageSize) / + (mediumSize + kExtraAllocSize); + size_t firstPageSlots = WTF::kSystemPageSize / (mediumSize + kExtraAllocSize); + EXPECT_EQ(2u, firstPageSlots); + EXPECT_EQ(totalSlots - firstPageSlots, page->numUnprovisionedSlots); + + partitionFree(ptr); + + size_t smallSize = (WTF::kSystemPageSize / 4) - kExtraAllocSize; + bucketIdx = (smallSize + kExtraAllocSize) >> WTF::kBucketShift; + bucket = &allocator.root()->buckets()[bucketIdx]; + EXPECT_EQ(0, bucket->freePagesHead); + + ptr = partitionAlloc(allocator.root(), smallSize); + EXPECT_TRUE(ptr); + page = + WTF::partitionPointerToPage(WTF::partitionCookieFreePointerAdjust(ptr)); + EXPECT_EQ(1, page->numAllocatedSlots); + totalSlots = + (page->bucket->numSystemPagesPerSlotSpan * WTF::kSystemPageSize) / + (smallSize + kExtraAllocSize); + firstPageSlots = WTF::kSystemPageSize / (smallSize + kExtraAllocSize); + EXPECT_EQ(totalSlots - firstPageSlots, page->numUnprovisionedSlots); + + partitionFree(ptr); + EXPECT_TRUE(page->freelistHead); + EXPECT_EQ(0, page->numAllocatedSlots); + + size_t verySmallSize = 32 - kExtraAllocSize; + bucketIdx = (verySmallSize + kExtraAllocSize) >> WTF::kBucketShift; + bucket = &allocator.root()->buckets()[bucketIdx]; + EXPECT_EQ(0, bucket->freePagesHead); + + ptr = partitionAlloc(allocator.root(), verySmallSize); + EXPECT_TRUE(ptr); + page = + WTF::partitionPointerToPage(WTF::partitionCookieFreePointerAdjust(ptr)); + EXPECT_EQ(1, page->numAllocatedSlots); + totalSlots = + (page->bucket->numSystemPagesPerSlotSpan * WTF::kSystemPageSize) / + (verySmallSize + kExtraAllocSize); + firstPageSlots = WTF::kSystemPageSize / (verySmallSize + kExtraAllocSize); + EXPECT_EQ(totalSlots - firstPageSlots, page->numUnprovisionedSlots); + + partitionFree(ptr); + EXPECT_TRUE(page->freelistHead); + EXPECT_EQ(0, page->numAllocatedSlots); + + // And try an allocation size (against the generic allocator) that is + // larger than a system page. + size_t pageAndAHalfSize = + (WTF::kSystemPageSize + (WTF::kSystemPageSize / 2)) - kExtraAllocSize; + ptr = partitionAllocGeneric(genericAllocator.root(), pageAndAHalfSize); + EXPECT_TRUE(ptr); + page = + WTF::partitionPointerToPage(WTF::partitionCookieFreePointerAdjust(ptr)); + EXPECT_EQ(1, page->numAllocatedSlots); + EXPECT_TRUE(page->freelistHead); + totalSlots = + (page->bucket->numSystemPagesPerSlotSpan * WTF::kSystemPageSize) / + (pageAndAHalfSize + kExtraAllocSize); + EXPECT_EQ(totalSlots - 2, page->numUnprovisionedSlots); + partitionFreeGeneric(genericAllocator.root(), ptr); + + // And then make sure than exactly the page size only faults one page. + size_t pageSize = WTF::kSystemPageSize - kExtraAllocSize; + ptr = partitionAllocGeneric(genericAllocator.root(), pageSize); + EXPECT_TRUE(ptr); + page = + WTF::partitionPointerToPage(WTF::partitionCookieFreePointerAdjust(ptr)); + EXPECT_EQ(1, page->numAllocatedSlots); + EXPECT_FALSE(page->freelistHead); + totalSlots = + (page->bucket->numSystemPagesPerSlotSpan * WTF::kSystemPageSize) / + (pageSize + kExtraAllocSize); + EXPECT_EQ(totalSlots - 1, page->numUnprovisionedSlots); + partitionFreeGeneric(genericAllocator.root(), ptr); + + TestShutdown(); } // Test some of the fragmentation-resistant properties of the allocator. -TEST(PartitionAllocTest, PageRefilling) -{ - TestSetup(); - WTF::PartitionBucket* bucket = &allocator.root()->buckets()[kTestBucketIndex]; - - // Grab two full pages and a non-full page. - WTF::PartitionPage* page1 = GetFullPage(kTestAllocSize); - WTF::PartitionPage* page2 = GetFullPage(kTestAllocSize); - void* ptr = partitionAlloc(allocator.root(), kTestAllocSize); - EXPECT_TRUE(ptr); - EXPECT_NE(page1, bucket->activePagesHead); - EXPECT_NE(page2, bucket->activePagesHead); - WTF::PartitionPage* page = WTF::partitionPointerToPage(WTF::partitionCookieFreePointerAdjust(ptr)); - EXPECT_EQ(1, page->numAllocatedSlots); - - // Work out a pointer into page2 and free it; and then page1 and free it. - char* ptr2 = reinterpret_cast(WTF::partitionPageToPointer(page1)) + kPointerOffset; - partitionFree(ptr2); - ptr2 = reinterpret_cast(WTF::partitionPageToPointer(page2)) + kPointerOffset; - partitionFree(ptr2); - - // If we perform two allocations from the same bucket now, we expect to - // refill both the nearly full pages. - (void) partitionAlloc(allocator.root(), kTestAllocSize); - (void) partitionAlloc(allocator.root(), kTestAllocSize); - EXPECT_EQ(1, page->numAllocatedSlots); - - FreeFullPage(page2); - FreeFullPage(page1); - partitionFree(ptr); - - TestShutdown(); +TEST(PartitionAllocTest, PageRefilling) { + TestSetup(); + WTF::PartitionBucket* bucket = &allocator.root()->buckets()[kTestBucketIndex]; + + // Grab two full pages and a non-full page. + WTF::PartitionPage* page1 = GetFullPage(kTestAllocSize); + WTF::PartitionPage* page2 = GetFullPage(kTestAllocSize); + void* ptr = partitionAlloc(allocator.root(), kTestAllocSize); + EXPECT_TRUE(ptr); + EXPECT_NE(page1, bucket->activePagesHead); + EXPECT_NE(page2, bucket->activePagesHead); + WTF::PartitionPage* page = + WTF::partitionPointerToPage(WTF::partitionCookieFreePointerAdjust(ptr)); + EXPECT_EQ(1, page->numAllocatedSlots); + + // Work out a pointer into page2 and free it; and then page1 and free it. + char* ptr2 = reinterpret_cast(WTF::partitionPageToPointer(page1)) + + kPointerOffset; + partitionFree(ptr2); + ptr2 = reinterpret_cast(WTF::partitionPageToPointer(page2)) + + kPointerOffset; + partitionFree(ptr2); + + // If we perform two allocations from the same bucket now, we expect to + // refill both the nearly full pages. + (void)partitionAlloc(allocator.root(), kTestAllocSize); + (void)partitionAlloc(allocator.root(), kTestAllocSize); + EXPECT_EQ(1, page->numAllocatedSlots); + + FreeFullPage(page2); + FreeFullPage(page1); + partitionFree(ptr); + + TestShutdown(); } // Basic tests to ensure that allocations work for partial page buckets. -TEST(PartitionAllocTest, PartialPages) -{ - TestSetup(); - - // Find a size that is backed by a partial partition page. - size_t size = sizeof(void*); - WTF::PartitionBucket* bucket = 0; - while (size < kTestMaxAllocation) { - bucket = &allocator.root()->buckets()[size >> WTF::kBucketShift]; - if (bucket->numSystemPagesPerSlotSpan % WTF::kNumSystemPagesPerPartitionPage) - break; - size += sizeof(void*); - } - EXPECT_LT(size, kTestMaxAllocation); - - WTF::PartitionPage* page1 = GetFullPage(size); - WTF::PartitionPage* page2 = GetFullPage(size); - FreeFullPage(page2); - FreeFullPage(page1); - - TestShutdown(); +TEST(PartitionAllocTest, PartialPages) { + TestSetup(); + + // Find a size that is backed by a partial partition page. + size_t size = sizeof(void*); + WTF::PartitionBucket* bucket = 0; + while (size < kTestMaxAllocation) { + bucket = &allocator.root()->buckets()[size >> WTF::kBucketShift]; + if (bucket->numSystemPagesPerSlotSpan % + WTF::kNumSystemPagesPerPartitionPage) + break; + size += sizeof(void*); + } + EXPECT_LT(size, kTestMaxAllocation); + + WTF::PartitionPage* page1 = GetFullPage(size); + WTF::PartitionPage* page2 = GetFullPage(size); + FreeFullPage(page2); + FreeFullPage(page1); + + TestShutdown(); } // Test correct handling if our mapping collides with another. -TEST(PartitionAllocTest, MappingCollision) -{ - TestSetup(); - // The -2 is because the first and last partition pages in a super page are - // guard pages. - size_t numPartitionPagesNeeded = WTF::kNumPartitionPagesPerSuperPage - 2; - OwnPtr firstSuperPagePages = adoptArrayPtr(new WTF::PartitionPage*[numPartitionPagesNeeded]); - OwnPtr secondSuperPagePages = adoptArrayPtr(new WTF::PartitionPage*[numPartitionPagesNeeded]); - - size_t i; - for (i = 0; i < numPartitionPagesNeeded; ++i) - firstSuperPagePages[i] = GetFullPage(kTestAllocSize); - - char* pageBase = reinterpret_cast(WTF::partitionPageToPointer(firstSuperPagePages[0])); - EXPECT_EQ(WTF::kPartitionPageSize, reinterpret_cast(pageBase) & WTF::kSuperPageOffsetMask); - pageBase -= WTF::kPartitionPageSize; - // Map a single system page either side of the mapping for our allocations, - // with the goal of tripping up alignment of the next mapping. - void* map1 = WTF::allocPages(pageBase - WTF::kPageAllocationGranularity, WTF::kPageAllocationGranularity, WTF::kPageAllocationGranularity); - EXPECT_TRUE(map1); - void* map2 = WTF::allocPages(pageBase + WTF::kSuperPageSize, WTF::kPageAllocationGranularity, WTF::kPageAllocationGranularity); - EXPECT_TRUE(map2); - WTF::setSystemPagesInaccessible(map1, WTF::kPageAllocationGranularity); - WTF::setSystemPagesInaccessible(map2, WTF::kPageAllocationGranularity); - - for (i = 0; i < numPartitionPagesNeeded; ++i) - secondSuperPagePages[i] = GetFullPage(kTestAllocSize); - - WTF::freePages(map1, WTF::kPageAllocationGranularity); - WTF::freePages(map2, WTF::kPageAllocationGranularity); - - pageBase = reinterpret_cast(partitionPageToPointer(secondSuperPagePages[0])); - EXPECT_EQ(WTF::kPartitionPageSize, reinterpret_cast(pageBase) & WTF::kSuperPageOffsetMask); - pageBase -= WTF::kPartitionPageSize; - // Map a single system page either side of the mapping for our allocations, - // with the goal of tripping up alignment of the next mapping. - map1 = WTF::allocPages(pageBase - WTF::kPageAllocationGranularity, WTF::kPageAllocationGranularity, WTF::kPageAllocationGranularity); - EXPECT_TRUE(map1); - map2 = WTF::allocPages(pageBase + WTF::kSuperPageSize, WTF::kPageAllocationGranularity, WTF::kPageAllocationGranularity); - EXPECT_TRUE(map2); - WTF::setSystemPagesInaccessible(map1, WTF::kPageAllocationGranularity); - WTF::setSystemPagesInaccessible(map2, WTF::kPageAllocationGranularity); - - WTF::PartitionPage* pageInThirdSuperPage = GetFullPage(kTestAllocSize); - WTF::freePages(map1, WTF::kPageAllocationGranularity); - WTF::freePages(map2, WTF::kPageAllocationGranularity); - - EXPECT_EQ(0u, reinterpret_cast(partitionPageToPointer(pageInThirdSuperPage)) & WTF::kPartitionPageOffsetMask); - - // And make sure we really did get a page in a new superpage. - EXPECT_NE(reinterpret_cast(partitionPageToPointer(firstSuperPagePages[0])) & WTF::kSuperPageBaseMask, reinterpret_cast(partitionPageToPointer(pageInThirdSuperPage)) & WTF::kSuperPageBaseMask); - EXPECT_NE(reinterpret_cast(partitionPageToPointer(secondSuperPagePages[0])) & WTF::kSuperPageBaseMask, reinterpret_cast(partitionPageToPointer(pageInThirdSuperPage)) & WTF::kSuperPageBaseMask); - - FreeFullPage(pageInThirdSuperPage); - for (i = 0; i < numPartitionPagesNeeded; ++i) { - FreeFullPage(firstSuperPagePages[i]); - FreeFullPage(secondSuperPagePages[i]); - } - - TestShutdown(); +TEST(PartitionAllocTest, MappingCollision) { + TestSetup(); + // The -2 is because the first and last partition pages in a super page are + // guard pages. + size_t numPartitionPagesNeeded = WTF::kNumPartitionPagesPerSuperPage - 2; + OwnPtr firstSuperPagePages = + adoptArrayPtr(new WTF::PartitionPage*[numPartitionPagesNeeded]); + OwnPtr secondSuperPagePages = + adoptArrayPtr(new WTF::PartitionPage*[numPartitionPagesNeeded]); + + size_t i; + for (i = 0; i < numPartitionPagesNeeded; ++i) + firstSuperPagePages[i] = GetFullPage(kTestAllocSize); + + char* pageBase = reinterpret_cast( + WTF::partitionPageToPointer(firstSuperPagePages[0])); + EXPECT_EQ(WTF::kPartitionPageSize, + reinterpret_cast(pageBase) & WTF::kSuperPageOffsetMask); + pageBase -= WTF::kPartitionPageSize; + // Map a single system page either side of the mapping for our allocations, + // with the goal of tripping up alignment of the next mapping. + void* map1 = WTF::allocPages(pageBase - WTF::kPageAllocationGranularity, + WTF::kPageAllocationGranularity, + WTF::kPageAllocationGranularity); + EXPECT_TRUE(map1); + void* map2 = WTF::allocPages(pageBase + WTF::kSuperPageSize, + WTF::kPageAllocationGranularity, + WTF::kPageAllocationGranularity); + EXPECT_TRUE(map2); + WTF::setSystemPagesInaccessible(map1, WTF::kPageAllocationGranularity); + WTF::setSystemPagesInaccessible(map2, WTF::kPageAllocationGranularity); + + for (i = 0; i < numPartitionPagesNeeded; ++i) + secondSuperPagePages[i] = GetFullPage(kTestAllocSize); + + WTF::freePages(map1, WTF::kPageAllocationGranularity); + WTF::freePages(map2, WTF::kPageAllocationGranularity); + + pageBase = + reinterpret_cast(partitionPageToPointer(secondSuperPagePages[0])); + EXPECT_EQ(WTF::kPartitionPageSize, + reinterpret_cast(pageBase) & WTF::kSuperPageOffsetMask); + pageBase -= WTF::kPartitionPageSize; + // Map a single system page either side of the mapping for our allocations, + // with the goal of tripping up alignment of the next mapping. + map1 = WTF::allocPages(pageBase - WTF::kPageAllocationGranularity, + WTF::kPageAllocationGranularity, + WTF::kPageAllocationGranularity); + EXPECT_TRUE(map1); + map2 = WTF::allocPages(pageBase + WTF::kSuperPageSize, + WTF::kPageAllocationGranularity, + WTF::kPageAllocationGranularity); + EXPECT_TRUE(map2); + WTF::setSystemPagesInaccessible(map1, WTF::kPageAllocationGranularity); + WTF::setSystemPagesInaccessible(map2, WTF::kPageAllocationGranularity); + + WTF::PartitionPage* pageInThirdSuperPage = GetFullPage(kTestAllocSize); + WTF::freePages(map1, WTF::kPageAllocationGranularity); + WTF::freePages(map2, WTF::kPageAllocationGranularity); + + EXPECT_EQ(0u, reinterpret_cast( + partitionPageToPointer(pageInThirdSuperPage)) & + WTF::kPartitionPageOffsetMask); + + // And make sure we really did get a page in a new superpage. + EXPECT_NE(reinterpret_cast( + partitionPageToPointer(firstSuperPagePages[0])) & + WTF::kSuperPageBaseMask, + reinterpret_cast( + partitionPageToPointer(pageInThirdSuperPage)) & + WTF::kSuperPageBaseMask); + EXPECT_NE(reinterpret_cast( + partitionPageToPointer(secondSuperPagePages[0])) & + WTF::kSuperPageBaseMask, + reinterpret_cast( + partitionPageToPointer(pageInThirdSuperPage)) & + WTF::kSuperPageBaseMask); + + FreeFullPage(pageInThirdSuperPage); + for (i = 0; i < numPartitionPagesNeeded; ++i) { + FreeFullPage(firstSuperPagePages[i]); + FreeFullPage(secondSuperPagePages[i]); + } + + TestShutdown(); } // Tests that pages in the free page cache do get freed as appropriate. -TEST(PartitionAllocTest, FreeCache) -{ - TestSetup(); - - EXPECT_EQ(0U, allocator.root()->totalSizeOfCommittedPages); - - size_t bigSize = allocator.root()->maxAllocation - kExtraAllocSize; - size_t bucketIdx = (bigSize + kExtraAllocSize) >> WTF::kBucketShift; - WTF::PartitionBucket* bucket = &allocator.root()->buckets()[bucketIdx]; - - void* ptr = partitionAlloc(allocator.root(), bigSize); - EXPECT_TRUE(ptr); - WTF::PartitionPage* page = WTF::partitionPointerToPage(WTF::partitionCookieFreePointerAdjust(ptr)); - EXPECT_EQ(0, bucket->freePagesHead); - EXPECT_EQ(1, page->numAllocatedSlots); - EXPECT_EQ(WTF::kPartitionPageSize, allocator.root()->totalSizeOfCommittedPages); - partitionFree(ptr); - EXPECT_EQ(0, page->numAllocatedSlots); - EXPECT_NE(-1, page->freeCacheIndex); - EXPECT_TRUE(page->freelistHead); - - CycleFreeCache(kTestAllocSize); - - // Flushing the cache should have really freed the unused page. - EXPECT_FALSE(page->freelistHead); - EXPECT_EQ(-1, page->freeCacheIndex); - EXPECT_EQ(0, page->numAllocatedSlots); - WTF::PartitionBucket* cycleFreeCacheBucket = &allocator.root()->buckets()[kTestBucketIndex]; - EXPECT_EQ(cycleFreeCacheBucket->numSystemPagesPerSlotSpan * WTF::kSystemPageSize, allocator.root()->totalSizeOfCommittedPages); - - // Check that an allocation works ok whilst in this state (a free'd page - // as the active pages head). +TEST(PartitionAllocTest, FreeCache) { + TestSetup(); + + EXPECT_EQ(0U, allocator.root()->totalSizeOfCommittedPages); + + size_t bigSize = allocator.root()->maxAllocation - kExtraAllocSize; + size_t bucketIdx = (bigSize + kExtraAllocSize) >> WTF::kBucketShift; + WTF::PartitionBucket* bucket = &allocator.root()->buckets()[bucketIdx]; + + void* ptr = partitionAlloc(allocator.root(), bigSize); + EXPECT_TRUE(ptr); + WTF::PartitionPage* page = + WTF::partitionPointerToPage(WTF::partitionCookieFreePointerAdjust(ptr)); + EXPECT_EQ(0, bucket->freePagesHead); + EXPECT_EQ(1, page->numAllocatedSlots); + EXPECT_EQ(WTF::kPartitionPageSize, + allocator.root()->totalSizeOfCommittedPages); + partitionFree(ptr); + EXPECT_EQ(0, page->numAllocatedSlots); + EXPECT_NE(-1, page->freeCacheIndex); + EXPECT_TRUE(page->freelistHead); + + CycleFreeCache(kTestAllocSize); + + // Flushing the cache should have really freed the unused page. + EXPECT_FALSE(page->freelistHead); + EXPECT_EQ(-1, page->freeCacheIndex); + EXPECT_EQ(0, page->numAllocatedSlots); + WTF::PartitionBucket* cycleFreeCacheBucket = + &allocator.root()->buckets()[kTestBucketIndex]; + EXPECT_EQ( + cycleFreeCacheBucket->numSystemPagesPerSlotSpan * WTF::kSystemPageSize, + allocator.root()->totalSizeOfCommittedPages); + + // Check that an allocation works ok whilst in this state (a free'd page + // as the active pages head). + ptr = partitionAlloc(allocator.root(), bigSize); + EXPECT_FALSE(bucket->freePagesHead); + partitionFree(ptr); + + // Also check that a page that is bouncing immediately between empty and + // used does not get freed. + for (size_t i = 0; i < WTF::kMaxFreeableSpans * 2; ++i) { ptr = partitionAlloc(allocator.root(), bigSize); - EXPECT_FALSE(bucket->freePagesHead); + EXPECT_TRUE(page->freelistHead); partitionFree(ptr); - - // Also check that a page that is bouncing immediately between empty and - // used does not get freed. - for (size_t i = 0; i < WTF::kMaxFreeableSpans * 2; ++i) { - ptr = partitionAlloc(allocator.root(), bigSize); - EXPECT_TRUE(page->freelistHead); - partitionFree(ptr); - EXPECT_TRUE(page->freelistHead); - } - EXPECT_EQ(WTF::kPartitionPageSize, allocator.root()->totalSizeOfCommittedPages); - TestShutdown(); + EXPECT_TRUE(page->freelistHead); + } + EXPECT_EQ(WTF::kPartitionPageSize, + allocator.root()->totalSizeOfCommittedPages); + TestShutdown(); } // Tests for a bug we had with losing references to free pages. -TEST(PartitionAllocTest, LostFreePagesBug) -{ - TestSetup(); +TEST(PartitionAllocTest, LostFreePagesBug) { + TestSetup(); - size_t size = WTF::kPartitionPageSize - kExtraAllocSize; + size_t size = WTF::kPartitionPageSize - kExtraAllocSize; - void* ptr = partitionAllocGeneric(genericAllocator.root(), size); - EXPECT_TRUE(ptr); - void* ptr2 = partitionAllocGeneric(genericAllocator.root(), size); - EXPECT_TRUE(ptr2); + void* ptr = partitionAllocGeneric(genericAllocator.root(), size); + EXPECT_TRUE(ptr); + void* ptr2 = partitionAllocGeneric(genericAllocator.root(), size); + EXPECT_TRUE(ptr2); - WTF::PartitionPage* page = WTF::partitionPointerToPage(WTF::partitionCookieFreePointerAdjust(ptr)); - WTF::PartitionPage* page2 = WTF::partitionPointerToPage(WTF::partitionCookieFreePointerAdjust(ptr2)); - WTF::PartitionBucket* bucket = page->bucket; + WTF::PartitionPage* page = + WTF::partitionPointerToPage(WTF::partitionCookieFreePointerAdjust(ptr)); + WTF::PartitionPage* page2 = + WTF::partitionPointerToPage(WTF::partitionCookieFreePointerAdjust(ptr2)); + WTF::PartitionBucket* bucket = page->bucket; - EXPECT_EQ(0, bucket->freePagesHead); - EXPECT_EQ(-1, page->numAllocatedSlots); - EXPECT_EQ(1, page2->numAllocatedSlots); + EXPECT_EQ(0, bucket->freePagesHead); + EXPECT_EQ(-1, page->numAllocatedSlots); + EXPECT_EQ(1, page2->numAllocatedSlots); - partitionFreeGeneric(genericAllocator.root(), ptr); - partitionFreeGeneric(genericAllocator.root(), ptr2); + partitionFreeGeneric(genericAllocator.root(), ptr); + partitionFreeGeneric(genericAllocator.root(), ptr2); - EXPECT_EQ(0, bucket->freePagesHead); - EXPECT_EQ(0, page->numAllocatedSlots); - EXPECT_EQ(0, page2->numAllocatedSlots); - EXPECT_TRUE(page->freelistHead); - EXPECT_TRUE(page2->freelistHead); + EXPECT_EQ(0, bucket->freePagesHead); + EXPECT_EQ(0, page->numAllocatedSlots); + EXPECT_EQ(0, page2->numAllocatedSlots); + EXPECT_TRUE(page->freelistHead); + EXPECT_TRUE(page2->freelistHead); - CycleGenericFreeCache(kTestAllocSize); + CycleGenericFreeCache(kTestAllocSize); - EXPECT_FALSE(page->freelistHead); - EXPECT_FALSE(page2->freelistHead); + EXPECT_FALSE(page->freelistHead); + EXPECT_FALSE(page2->freelistHead); - EXPECT_FALSE(bucket->freePagesHead); - EXPECT_TRUE(bucket->activePagesHead); - EXPECT_TRUE(bucket->activePagesHead->nextPage); + EXPECT_FALSE(bucket->freePagesHead); + EXPECT_TRUE(bucket->activePagesHead); + EXPECT_TRUE(bucket->activePagesHead->nextPage); - // At this moment, we have two freed pages, on the freelist. + // At this moment, we have two freed pages, on the freelist. - ptr = partitionAllocGeneric(genericAllocator.root(), size); - EXPECT_TRUE(ptr); - partitionFreeGeneric(genericAllocator.root(), ptr); + ptr = partitionAllocGeneric(genericAllocator.root(), size); + EXPECT_TRUE(ptr); + partitionFreeGeneric(genericAllocator.root(), ptr); - EXPECT_TRUE(bucket->activePagesHead); - EXPECT_TRUE(bucket->freePagesHead); + EXPECT_TRUE(bucket->activePagesHead); + EXPECT_TRUE(bucket->freePagesHead); - CycleGenericFreeCache(kTestAllocSize); + CycleGenericFreeCache(kTestAllocSize); - // We're now set up to trigger the bug by scanning over the active pages - // list, where the current active page is freed, and there exists at least - // one freed page in the free pages list. - ptr = partitionAllocGeneric(genericAllocator.root(), size); - EXPECT_TRUE(ptr); - partitionFreeGeneric(genericAllocator.root(), ptr); + // We're now set up to trigger the bug by scanning over the active pages + // list, where the current active page is freed, and there exists at least + // one freed page in the free pages list. + ptr = partitionAllocGeneric(genericAllocator.root(), size); + EXPECT_TRUE(ptr); + partitionFreeGeneric(genericAllocator.root(), ptr); - EXPECT_TRUE(bucket->activePagesHead); - EXPECT_TRUE(bucket->freePagesHead); + EXPECT_TRUE(bucket->activePagesHead); + EXPECT_TRUE(bucket->freePagesHead); - TestShutdown(); + TestShutdown(); } #if !OS(ANDROID) && !OS(IOS) && !OS(FUCHSIA) @@ -1101,97 +1193,97 @@ TEST(PartitionAllocTest, LostFreePagesBug) // Make sure that malloc(-1) dies. // In the past, we had an integer overflow that would alias malloc(-1) to // malloc(0), which is not good. -TEST(PartitionAllocDeathTest, LargeAllocs) -{ - TestSetup(); - // Largest alloc. - EXPECT_DEATH(partitionAllocGeneric(genericAllocator.root(), static_cast(-1)), ""); - // And the smallest allocation we expect to die. - EXPECT_DEATH(partitionAllocGeneric(genericAllocator.root(), static_cast(INT_MAX) + 1), ""); - - TestShutdown(); +TEST(PartitionAllocDeathTest, LargeAllocs) { + TestSetup(); + // Largest alloc. + EXPECT_DEATH( + partitionAllocGeneric(genericAllocator.root(), static_cast(-1)), + ""); + // And the smallest allocation we expect to die. + EXPECT_DEATH(partitionAllocGeneric(genericAllocator.root(), + static_cast(INT_MAX) + 1), + ""); + + TestShutdown(); } // Check that our immediate double-free detection works. -TEST(PartitionAllocDeathTest, ImmediateDoubleFree) -{ - TestSetup(); +TEST(PartitionAllocDeathTest, ImmediateDoubleFree) { + TestSetup(); - void* ptr = partitionAllocGeneric(genericAllocator.root(), kTestAllocSize); - EXPECT_TRUE(ptr); - partitionFreeGeneric(genericAllocator.root(), ptr); + void* ptr = partitionAllocGeneric(genericAllocator.root(), kTestAllocSize); + EXPECT_TRUE(ptr); + partitionFreeGeneric(genericAllocator.root(), ptr); - EXPECT_DEATH(partitionFreeGeneric(genericAllocator.root(), ptr), ""); + EXPECT_DEATH(partitionFreeGeneric(genericAllocator.root(), ptr), ""); - TestShutdown(); + TestShutdown(); } // Check that our refcount-based double-free detection works. -TEST(PartitionAllocDeathTest, RefcountDoubleFree) -{ - TestSetup(); - - void* ptr = partitionAllocGeneric(genericAllocator.root(), kTestAllocSize); - EXPECT_TRUE(ptr); - void* ptr2 = partitionAllocGeneric(genericAllocator.root(), kTestAllocSize); - EXPECT_TRUE(ptr2); - partitionFreeGeneric(genericAllocator.root(), ptr); - partitionFreeGeneric(genericAllocator.root(), ptr2); - // This is not an immediate double-free so our immediate detection won't - // fire. However, it does take the "refcount" of the partition page to -1, - // which is illegal and should be trapped. - EXPECT_DEATH(partitionFreeGeneric(genericAllocator.root(), ptr), ""); - - TestShutdown(); +TEST(PartitionAllocDeathTest, RefcountDoubleFree) { + TestSetup(); + + void* ptr = partitionAllocGeneric(genericAllocator.root(), kTestAllocSize); + EXPECT_TRUE(ptr); + void* ptr2 = partitionAllocGeneric(genericAllocator.root(), kTestAllocSize); + EXPECT_TRUE(ptr2); + partitionFreeGeneric(genericAllocator.root(), ptr); + partitionFreeGeneric(genericAllocator.root(), ptr2); + // This is not an immediate double-free so our immediate detection won't + // fire. However, it does take the "refcount" of the partition page to -1, + // which is illegal and should be trapped. + EXPECT_DEATH(partitionFreeGeneric(genericAllocator.root(), ptr), ""); + + TestShutdown(); } // Check that guard pages are present where expected. -TEST(PartitionAllocDeathTest, GuardPages) -{ - TestSetup(); +TEST(PartitionAllocDeathTest, GuardPages) { + TestSetup(); - // This large size will result in a direct mapped allocation with guard - // pages at either end. - size_t size = (WTF::kGenericMaxBucketed + WTF::kSystemPageSize) - kExtraAllocSize; - void* ptr = partitionAllocGeneric(genericAllocator.root(), size); - EXPECT_TRUE(ptr); - char* charPtr = reinterpret_cast(ptr) - kPointerOffset; + // This large size will result in a direct mapped allocation with guard + // pages at either end. + size_t size = + (WTF::kGenericMaxBucketed + WTF::kSystemPageSize) - kExtraAllocSize; + void* ptr = partitionAllocGeneric(genericAllocator.root(), size); + EXPECT_TRUE(ptr); + char* charPtr = reinterpret_cast(ptr) - kPointerOffset; - EXPECT_DEATH(*(charPtr - 1) = 'A', ""); - EXPECT_DEATH(*(charPtr + size + kExtraAllocSize) = 'A', ""); + EXPECT_DEATH(*(charPtr - 1) = 'A', ""); + EXPECT_DEATH(*(charPtr + size + kExtraAllocSize) = 'A', ""); - partitionFreeGeneric(genericAllocator.root(), ptr); + partitionFreeGeneric(genericAllocator.root(), ptr); - TestShutdown(); + TestShutdown(); } -#endif // !OS(ANDROID) +#endif // !OS(ANDROID) // Tests that the countLeadingZeros() functions work to our satisfaction. // It doesn't seem worth the overhead of a whole new file for these tests, so // we'll put them here since partitionAllocGeneric will depend heavily on these // functions working correctly. -TEST(PartitionAllocTest, CLZWorks) -{ - EXPECT_EQ(32u, WTF::countLeadingZeros32(0u)); - EXPECT_EQ(31u, WTF::countLeadingZeros32(1u)); - EXPECT_EQ(1u, WTF::countLeadingZeros32(1u << 30)); - EXPECT_EQ(0u, WTF::countLeadingZeros32(1u << 31)); +TEST(PartitionAllocTest, CLZWorks) { + EXPECT_EQ(32u, WTF::countLeadingZeros32(0u)); + EXPECT_EQ(31u, WTF::countLeadingZeros32(1u)); + EXPECT_EQ(1u, WTF::countLeadingZeros32(1u << 30)); + EXPECT_EQ(0u, WTF::countLeadingZeros32(1u << 31)); #if CPU(64BIT) - EXPECT_EQ(64u, WTF::countLeadingZerosSizet(0ull)); - EXPECT_EQ(63u, WTF::countLeadingZerosSizet(1ull)); - EXPECT_EQ(32u, WTF::countLeadingZerosSizet(1ull << 31)); - EXPECT_EQ(1u, WTF::countLeadingZerosSizet(1ull << 62)); - EXPECT_EQ(0u, WTF::countLeadingZerosSizet(1ull << 63)); + EXPECT_EQ(64u, WTF::countLeadingZerosSizet(0ull)); + EXPECT_EQ(63u, WTF::countLeadingZerosSizet(1ull)); + EXPECT_EQ(32u, WTF::countLeadingZerosSizet(1ull << 31)); + EXPECT_EQ(1u, WTF::countLeadingZerosSizet(1ull << 62)); + EXPECT_EQ(0u, WTF::countLeadingZerosSizet(1ull << 63)); #else - EXPECT_EQ(32u, WTF::countLeadingZerosSizet(0u)); - EXPECT_EQ(31u, WTF::countLeadingZerosSizet(1u)); - EXPECT_EQ(1u, WTF::countLeadingZerosSizet(1u << 30)); - EXPECT_EQ(0u, WTF::countLeadingZerosSizet(1u << 31)); + EXPECT_EQ(32u, WTF::countLeadingZerosSizet(0u)); + EXPECT_EQ(31u, WTF::countLeadingZerosSizet(1u)); + EXPECT_EQ(1u, WTF::countLeadingZerosSizet(1u << 30)); + EXPECT_EQ(0u, WTF::countLeadingZerosSizet(1u << 31)); #endif } -} // namespace +} // namespace -#endif // !defined(MEMORY_TOOL_REPLACES_ALLOCATOR) +#endif // !defined(MEMORY_TOOL_REPLACES_ALLOCATOR) diff --git a/sky/engine/wtf/PassOwnPtr.h b/sky/engine/wtf/PassOwnPtr.h index 59c14dadc8ed0..96594d93690fa 100644 --- a/sky/engine/wtf/PassOwnPtr.h +++ b/sky/engine/wtf/PassOwnPtr.h @@ -32,119 +32,161 @@ namespace WTF { - template class OwnPtr; - template class PassOwnPtr; - template PassOwnPtr adoptPtr(T*); - template PassOwnPtr adoptArrayPtr(T*); - - template class PassOwnPtr { - WTF_DISALLOW_CONSTRUCTION_FROM_ZERO(PassOwnPtr); - public: - typedef typename RemoveExtent::Type ValueType; - typedef ValueType* PtrType; - - PassOwnPtr() : m_ptr(0) { } - PassOwnPtr(std::nullptr_t) : m_ptr(0) { } - - // It somewhat breaks the type system to allow transfer of ownership out of - // a const PassOwnPtr. However, it makes it much easier to work with PassOwnPtr - // temporaries, and we don't have a need to use real const PassOwnPtrs anyway. - PassOwnPtr(const PassOwnPtr& o) : m_ptr(o.leakPtr()) { } - template PassOwnPtr(const PassOwnPtr&, EnsurePtrConvertibleArgDecl(U, T)); - - ~PassOwnPtr() { OwnedPtrDeleter::deletePtr(m_ptr); } - - PtrType get() const { return m_ptr; } - - PtrType leakPtr() const WARN_UNUSED_RETURN; - - ValueType& operator*() const { ASSERT(m_ptr); return *m_ptr; } - PtrType operator->() const { ASSERT(m_ptr); return m_ptr; } - - bool operator!() const { return !m_ptr; } - - // This conversion operator allows implicit conversion to bool but not to other integer types. - typedef PtrType PassOwnPtr::*UnspecifiedBoolType; - operator UnspecifiedBoolType() const { return m_ptr ? &PassOwnPtr::m_ptr : 0; } - - template friend PassOwnPtr adoptPtr(U*); - template friend PassOwnPtr adoptArrayPtr(U*); - template friend class OwnPtr; - - private: - explicit PassOwnPtr(PtrType ptr) : m_ptr(ptr) { } - - PassOwnPtr& operator=(const PassOwnPtr&) { COMPILE_ASSERT(!sizeof(T*), PassOwnPtr_should_never_be_assigned_to); return *this; } - - // We should never have two OwnPtrs for the same underlying object (otherwise we'll get - // double-destruction), so these equality operators should never be needed. - template bool operator==(const PassOwnPtr&) const { COMPILE_ASSERT(!sizeof(U*), OwnPtrs_should_never_be_equal); return false; } - template bool operator!=(const PassOwnPtr&) const { COMPILE_ASSERT(!sizeof(U*), OwnPtrs_should_never_be_equal); return false; } - template bool operator==(const OwnPtr&) const { COMPILE_ASSERT(!sizeof(U*), OwnPtrs_should_never_be_equal); return false; } - template bool operator!=(const OwnPtr&) const { COMPILE_ASSERT(!sizeof(U*), OwnPtrs_should_never_be_equal); return false; } - - mutable PtrType m_ptr; - }; - - template template inline PassOwnPtr::PassOwnPtr(const PassOwnPtr& o, EnsurePtrConvertibleArgDefn(U, T)) - : m_ptr(o.leakPtr()) - { - COMPILE_ASSERT(!IsArray::value, Pointers_to_array_must_never_be_converted); - } - - template inline typename PassOwnPtr::PtrType PassOwnPtr::leakPtr() const - { - PtrType ptr = m_ptr; - m_ptr = 0; - return ptr; - } - - template inline bool operator==(const PassOwnPtr& a, U* b) - { - return a.get() == b; - } - - template inline bool operator==(T* a, const PassOwnPtr& b) - { - return a == b.get(); - } - - template inline bool operator!=(const PassOwnPtr& a, U* b) - { - return a.get() != b; - } - - template inline bool operator!=(T* a, const PassOwnPtr& b) - { - return a != b.get(); - } - - template inline PassOwnPtr adoptPtr(T* ptr) - { - return PassOwnPtr(ptr); - } - - template inline PassOwnPtr adoptArrayPtr(T* ptr) - { - return PassOwnPtr(ptr); - } - - template inline PassOwnPtr static_pointer_cast(const PassOwnPtr& p) - { - COMPILE_ASSERT(!IsArray::value, Pointers_to_array_must_never_be_converted); - return adoptPtr(static_cast(p.leakPtr())); - } - - template inline T* getPtr(const PassOwnPtr& p) - { - return p.get(); - } - -} // namespace WTF +template +class OwnPtr; +template +class PassOwnPtr; +template +PassOwnPtr adoptPtr(T*); +template +PassOwnPtr adoptArrayPtr(T*); + +template +class PassOwnPtr { + WTF_DISALLOW_CONSTRUCTION_FROM_ZERO(PassOwnPtr); + + public: + typedef typename RemoveExtent::Type ValueType; + typedef ValueType* PtrType; + + PassOwnPtr() : m_ptr(0) {} + PassOwnPtr(std::nullptr_t) : m_ptr(0) {} + + // It somewhat breaks the type system to allow transfer of ownership out of + // a const PassOwnPtr. However, it makes it much easier to work with + // PassOwnPtr temporaries, and we don't have a need to use real const + // PassOwnPtrs anyway. + PassOwnPtr(const PassOwnPtr& o) : m_ptr(o.leakPtr()) {} + template + PassOwnPtr(const PassOwnPtr&, EnsurePtrConvertibleArgDecl(U, T)); + + ~PassOwnPtr() { OwnedPtrDeleter::deletePtr(m_ptr); } + + PtrType get() const { return m_ptr; } + + PtrType leakPtr() const WARN_UNUSED_RETURN; + + ValueType& operator*() const { + ASSERT(m_ptr); + return *m_ptr; + } + PtrType operator->() const { + ASSERT(m_ptr); + return m_ptr; + } + + bool operator!() const { return !m_ptr; } + + // This conversion operator allows implicit conversion to bool but not to + // other integer types. + typedef PtrType PassOwnPtr::*UnspecifiedBoolType; + operator UnspecifiedBoolType() const { + return m_ptr ? &PassOwnPtr::m_ptr : 0; + } + + template + friend PassOwnPtr adoptPtr(U*); + template + friend PassOwnPtr adoptArrayPtr(U*); + template + friend class OwnPtr; + + private: + explicit PassOwnPtr(PtrType ptr) : m_ptr(ptr) {} + + PassOwnPtr& operator=(const PassOwnPtr&) { + COMPILE_ASSERT(!sizeof(T*), PassOwnPtr_should_never_be_assigned_to); + return *this; + } + + // We should never have two OwnPtrs for the same underlying object (otherwise + // we'll get double-destruction), so these equality operators should never be + // needed. + template + bool operator==(const PassOwnPtr&) const { + COMPILE_ASSERT(!sizeof(U*), OwnPtrs_should_never_be_equal); + return false; + } + template + bool operator!=(const PassOwnPtr&) const { + COMPILE_ASSERT(!sizeof(U*), OwnPtrs_should_never_be_equal); + return false; + } + template + bool operator==(const OwnPtr&) const { + COMPILE_ASSERT(!sizeof(U*), OwnPtrs_should_never_be_equal); + return false; + } + template + bool operator!=(const OwnPtr&) const { + COMPILE_ASSERT(!sizeof(U*), OwnPtrs_should_never_be_equal); + return false; + } + + mutable PtrType m_ptr; +}; + +template +template +inline PassOwnPtr::PassOwnPtr(const PassOwnPtr& o, + EnsurePtrConvertibleArgDefn(U, T)) + : m_ptr(o.leakPtr()) { + COMPILE_ASSERT(!IsArray::value, Pointers_to_array_must_never_be_converted); +} + +template +inline typename PassOwnPtr::PtrType PassOwnPtr::leakPtr() const { + PtrType ptr = m_ptr; + m_ptr = 0; + return ptr; +} + +template +inline bool operator==(const PassOwnPtr& a, U* b) { + return a.get() == b; +} + +template +inline bool operator==(T* a, const PassOwnPtr& b) { + return a == b.get(); +} + +template +inline bool operator!=(const PassOwnPtr& a, U* b) { + return a.get() != b; +} + +template +inline bool operator!=(T* a, const PassOwnPtr& b) { + return a != b.get(); +} + +template +inline PassOwnPtr adoptPtr(T* ptr) { + return PassOwnPtr(ptr); +} + +template +inline PassOwnPtr adoptArrayPtr(T* ptr) { + return PassOwnPtr(ptr); +} + +template +inline PassOwnPtr static_pointer_cast(const PassOwnPtr& p) { + COMPILE_ASSERT(!IsArray::value, Pointers_to_array_must_never_be_converted); + return adoptPtr(static_cast(p.leakPtr())); +} + +template +inline T* getPtr(const PassOwnPtr& p) { + return p.get(); +} + +} // namespace WTF using WTF::PassOwnPtr; -using WTF::adoptPtr; using WTF::adoptArrayPtr; +using WTF::adoptPtr; using WTF::static_pointer_cast; #endif // SKY_ENGINE_WTF_PASSOWNPTR_H_ diff --git a/sky/engine/wtf/PassRefPtr.h b/sky/engine/wtf/PassRefPtr.h index d90a94be125e7..42b0918f29066 100644 --- a/sky/engine/wtf/PassRefPtr.h +++ b/sky/engine/wtf/PassRefPtr.h @@ -1,5 +1,6 @@ /* - * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. + * All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -29,178 +30,199 @@ namespace WTF { - template class RefPtr; - template class PassRefPtr; - template PassRefPtr adoptRef(T*); - - inline void adopted(const void*) { } - - // requireAdoption() is not overloaded for WTF::RefCounted, which has a - // built-in assumption that adoption is required. requireAdoption() is - // for bootstrapping alternate reference count classes that are compatible - // with ReftPtr/PassRefPtr but cannot have adoption checks enabled - // by default, such as skia's SkRefCnt. The purpose of requireAdoption() - // is to enable adoption checks only once it is known that the object will - // be used with RefPtr/PassRefPtr. - inline void requireAdoption(const void*) { } - - template ALWAYS_INLINE void refIfNotNull(T* ptr) - { - if (LIKELY(ptr != 0)) { - requireAdoption(ptr); - ptr->ref(); - } - } - - template ALWAYS_INLINE void derefIfNotNull(T* ptr) - { - if (LIKELY(ptr != 0)) - ptr->deref(); - } - - template class PassRefPtr { - WTF_DISALLOW_CONSTRUCTION_FROM_ZERO(PassRefPtr); - public: - PassRefPtr() : m_ptr(0) { } - PassRefPtr(std::nullptr_t) : m_ptr(0) { } - PassRefPtr(T* ptr) : m_ptr(ptr) { refIfNotNull(ptr); } - template PassRefPtr(const RawPtr& ptr, EnsurePtrConvertibleArgDecl(U, T)) : m_ptr(ptr.get()) { refIfNotNull(m_ptr); } - explicit PassRefPtr(T& ptr) : m_ptr(&ptr) { m_ptr->ref(); } - // It somewhat breaks the type system to allow transfer of ownership out of - // a const PassRefPtr. However, it makes it much easier to work with PassRefPtr - // temporaries, and we don't have a need to use real const PassRefPtrs anyway. - PassRefPtr(const PassRefPtr& o) : m_ptr(o.leakRef()) { } - template PassRefPtr(const PassRefPtr& o, EnsurePtrConvertibleArgDecl(U, T)) : m_ptr(o.leakRef()) { } - - ALWAYS_INLINE ~PassRefPtr() { derefIfNotNull(m_ptr); } - - template PassRefPtr(const RefPtr&, EnsurePtrConvertibleArgDecl(U, T)); - - T* get() const { return m_ptr; } - - T* leakRef() const WARN_UNUSED_RETURN; - - T& operator*() const { return *m_ptr; } - T* operator->() const { return m_ptr; } - - bool operator!() const { return !m_ptr; } - - // This conversion operator allows implicit conversion to bool but not to other integer types. - typedef T* (PassRefPtr::*UnspecifiedBoolType); - operator UnspecifiedBoolType() const { return m_ptr ? &PassRefPtr::m_ptr : 0; } - - friend PassRefPtr adoptRef(T*); - - private: - enum AdoptRefTag { AdoptRef }; - PassRefPtr(T* ptr, AdoptRefTag) : m_ptr(ptr) { } - - PassRefPtr& operator=(const PassRefPtr&) { COMPILE_ASSERT(!sizeof(T*), PassRefPtr_should_never_be_assigned_to); return *this; } - - mutable T* m_ptr; - }; - - template template inline PassRefPtr::PassRefPtr(const RefPtr& o, EnsurePtrConvertibleArgDefn(U, T)) - : m_ptr(o.get()) - { - T* ptr = m_ptr; - refIfNotNull(ptr); - } - - template inline T* PassRefPtr::leakRef() const - { - T* ptr = m_ptr; - m_ptr = 0; - return ptr; - } - - template inline bool operator==(const PassRefPtr& a, const PassRefPtr& b) - { - return a.get() == b.get(); - } - - template inline bool operator==(const PassRefPtr& a, const RefPtr& b) - { - return a.get() == b.get(); - } - - template inline bool operator==(const RefPtr& a, const PassRefPtr& b) - { - return a.get() == b.get(); - } - - template inline bool operator==(const PassRefPtr& a, U* b) - { - return a.get() == b; - } - - template inline bool operator==(T* a, const PassRefPtr& b) - { - return a == b.get(); - } - - template inline bool operator==(const PassRefPtr& a, const RawPtr& b) - { - return a.get() == b.get(); - } - - template inline bool operator==(const RawPtr& a, const PassRefPtr& b) - { - return a.get() == b.get(); - } - - template inline bool operator!=(const PassRefPtr& a, const PassRefPtr& b) - { - return a.get() != b.get(); - } - - template inline bool operator!=(const PassRefPtr& a, const RefPtr& b) - { - return a.get() != b.get(); - } - - template inline bool operator!=(const RefPtr& a, const PassRefPtr& b) - { - return a.get() != b.get(); - } - - template inline bool operator!=(const PassRefPtr& a, U* b) - { - return a.get() != b; - } - - template inline bool operator!=(T* a, const PassRefPtr& b) - { - return a != b.get(); - } - - template inline bool operator!=(const PassRefPtr& a, const RawPtr& b) - { - return a.get() != b.get(); - } - - template inline bool operator!=(const RawPtr& a, const PassRefPtr& b) - { - return a.get() != b.get(); - } - - template PassRefPtr adoptRef(T* p) - { - adopted(p); - return PassRefPtr(p, PassRefPtr::AdoptRef); - } - - template inline PassRefPtr static_pointer_cast(const PassRefPtr& p) - { - return adoptRef(static_cast(p.leakRef())); - } - - template inline T* getPtr(const PassRefPtr& p) - { - return p.get(); - } - -} // namespace WTF +template +class RefPtr; +template +class PassRefPtr; +template +PassRefPtr adoptRef(T*); + +inline void adopted(const void*) {} + +// requireAdoption() is not overloaded for WTF::RefCounted, which has a +// built-in assumption that adoption is required. requireAdoption() is +// for bootstrapping alternate reference count classes that are compatible +// with ReftPtr/PassRefPtr but cannot have adoption checks enabled +// by default, such as skia's SkRefCnt. The purpose of requireAdoption() +// is to enable adoption checks only once it is known that the object will +// be used with RefPtr/PassRefPtr. +inline void requireAdoption(const void*) {} + +template +ALWAYS_INLINE void refIfNotNull(T* ptr) { + if (LIKELY(ptr != 0)) { + requireAdoption(ptr); + ptr->ref(); + } +} + +template +ALWAYS_INLINE void derefIfNotNull(T* ptr) { + if (LIKELY(ptr != 0)) + ptr->deref(); +} + +template +class PassRefPtr { + WTF_DISALLOW_CONSTRUCTION_FROM_ZERO(PassRefPtr); + + public: + PassRefPtr() : m_ptr(0) {} + PassRefPtr(std::nullptr_t) : m_ptr(0) {} + PassRefPtr(T* ptr) : m_ptr(ptr) { refIfNotNull(ptr); } + template + PassRefPtr(const RawPtr& ptr, EnsurePtrConvertibleArgDecl(U, T)) + : m_ptr(ptr.get()) { + refIfNotNull(m_ptr); + } + explicit PassRefPtr(T& ptr) : m_ptr(&ptr) { m_ptr->ref(); } + // It somewhat breaks the type system to allow transfer of ownership out of + // a const PassRefPtr. However, it makes it much easier to work with + // PassRefPtr temporaries, and we don't have a need to use real const + // PassRefPtrs anyway. + PassRefPtr(const PassRefPtr& o) : m_ptr(o.leakRef()) {} + template + PassRefPtr(const PassRefPtr& o, EnsurePtrConvertibleArgDecl(U, T)) + : m_ptr(o.leakRef()) {} + + ALWAYS_INLINE ~PassRefPtr() { derefIfNotNull(m_ptr); } + + template + PassRefPtr(const RefPtr&, EnsurePtrConvertibleArgDecl(U, T)); + + T* get() const { return m_ptr; } + + T* leakRef() const WARN_UNUSED_RETURN; + + T& operator*() const { return *m_ptr; } + T* operator->() const { return m_ptr; } + + bool operator!() const { return !m_ptr; } + + // This conversion operator allows implicit conversion to bool but not to + // other integer types. + typedef T*(PassRefPtr::*UnspecifiedBoolType); + operator UnspecifiedBoolType() const { + return m_ptr ? &PassRefPtr::m_ptr : 0; + } + + friend PassRefPtr adoptRef(T*); + + private: + enum AdoptRefTag { AdoptRef }; + PassRefPtr(T* ptr, AdoptRefTag) : m_ptr(ptr) {} + + PassRefPtr& operator=(const PassRefPtr&) { + COMPILE_ASSERT(!sizeof(T*), PassRefPtr_should_never_be_assigned_to); + return *this; + } + + mutable T* m_ptr; +}; + +template +template +inline PassRefPtr::PassRefPtr(const RefPtr& o, + EnsurePtrConvertibleArgDefn(U, T)) + : m_ptr(o.get()) { + T* ptr = m_ptr; + refIfNotNull(ptr); +} + +template +inline T* PassRefPtr::leakRef() const { + T* ptr = m_ptr; + m_ptr = 0; + return ptr; +} + +template +inline bool operator==(const PassRefPtr& a, const PassRefPtr& b) { + return a.get() == b.get(); +} + +template +inline bool operator==(const PassRefPtr& a, const RefPtr& b) { + return a.get() == b.get(); +} + +template +inline bool operator==(const RefPtr& a, const PassRefPtr& b) { + return a.get() == b.get(); +} + +template +inline bool operator==(const PassRefPtr& a, U* b) { + return a.get() == b; +} + +template +inline bool operator==(T* a, const PassRefPtr& b) { + return a == b.get(); +} + +template +inline bool operator==(const PassRefPtr& a, const RawPtr& b) { + return a.get() == b.get(); +} + +template +inline bool operator==(const RawPtr& a, const PassRefPtr& b) { + return a.get() == b.get(); +} + +template +inline bool operator!=(const PassRefPtr& a, const PassRefPtr& b) { + return a.get() != b.get(); +} + +template +inline bool operator!=(const PassRefPtr& a, const RefPtr& b) { + return a.get() != b.get(); +} + +template +inline bool operator!=(const RefPtr& a, const PassRefPtr& b) { + return a.get() != b.get(); +} + +template +inline bool operator!=(const PassRefPtr& a, U* b) { + return a.get() != b; +} + +template +inline bool operator!=(T* a, const PassRefPtr& b) { + return a != b.get(); +} + +template +inline bool operator!=(const PassRefPtr& a, const RawPtr& b) { + return a.get() != b.get(); +} + +template +inline bool operator!=(const RawPtr& a, const PassRefPtr& b) { + return a.get() != b.get(); +} + +template +PassRefPtr adoptRef(T* p) { + adopted(p); + return PassRefPtr(p, PassRefPtr::AdoptRef); +} + +template +inline PassRefPtr static_pointer_cast(const PassRefPtr& p) { + return adoptRef(static_cast(p.leakRef())); +} + +template +inline T* getPtr(const PassRefPtr& p) { + return p.get(); +} + +} // namespace WTF using WTF::PassRefPtr; using WTF::adoptRef; diff --git a/sky/engine/wtf/PassTraits.h b/sky/engine/wtf/PassTraits.h index 278098d229bfe..68b67f407a29c 100644 --- a/sky/engine/wtf/PassTraits.h +++ b/sky/engine/wtf/PassTraits.h @@ -38,25 +38,28 @@ namespace WTF { -template struct PassTraits { - typedef T Type; - typedef T PassType; - static Type& transfer(Type& value) { return value; } +template +struct PassTraits { + typedef T Type; + typedef T PassType; + static Type& transfer(Type& value) { return value; } }; -template struct PassTraits > { - typedef OwnPtr Type; - typedef PassOwnPtr PassType; - static PassType transfer(Type& value) { return value.release(); } +template +struct PassTraits> { + typedef OwnPtr Type; + typedef PassOwnPtr PassType; + static PassType transfer(Type& value) { return value.release(); } }; -template struct PassTraits > { - typedef RefPtr Type; - typedef PassRefPtr PassType; - static PassType transfer(Type& value) { return value.release(); } +template +struct PassTraits> { + typedef RefPtr Type; + typedef PassRefPtr PassType; + static PassType transfer(Type& value) { return value.release(); } }; -} // namespace WTF +} // namespace WTF using WTF::PassTraits; diff --git a/sky/engine/wtf/PrintStream.cpp b/sky/engine/wtf/PrintStream.cpp index 8520a87e41b45..1b5f53ed362b8 100644 --- a/sky/engine/wtf/PrintStream.cpp +++ b/sky/engine/wtf/PrintStream.cpp @@ -31,88 +31,71 @@ namespace WTF { -PrintStream::PrintStream() { } -PrintStream::~PrintStream() { } // Force the vtable to be in this module - -void PrintStream::printf(const char* format, ...) -{ - va_list argList; - va_start(argList, format); - vprintf(format, argList); - va_end(argList); +PrintStream::PrintStream() {} +PrintStream::~PrintStream() {} // Force the vtable to be in this module + +void PrintStream::printf(const char* format, ...) { + va_list argList; + va_start(argList, format); + vprintf(format, argList); + va_end(argList); } -void PrintStream::flush() -{ -} +void PrintStream::flush() {} -void printInternal(PrintStream& out, const char* string) -{ - out.printf("%s", string); +void printInternal(PrintStream& out, const char* string) { + out.printf("%s", string); } -void printInternal(PrintStream& out, const CString& string) -{ - out.print(string.data()); +void printInternal(PrintStream& out, const CString& string) { + out.print(string.data()); } -void printInternal(PrintStream& out, const String& string) -{ - out.print(string.utf8()); +void printInternal(PrintStream& out, const String& string) { + out.print(string.utf8()); } -void printInternal(PrintStream& out, bool value) -{ - if (value) - out.print("true"); - else - out.print("false"); +void printInternal(PrintStream& out, bool value) { + if (value) + out.print("true"); + else + out.print("false"); } -void printInternal(PrintStream& out, int value) -{ - out.printf("%d", value); +void printInternal(PrintStream& out, int value) { + out.printf("%d", value); } -void printInternal(PrintStream& out, unsigned value) -{ - out.printf("%u", value); +void printInternal(PrintStream& out, unsigned value) { + out.printf("%u", value); } -void printInternal(PrintStream& out, long value) -{ - out.printf("%ld", value); +void printInternal(PrintStream& out, long value) { + out.printf("%ld", value); } -void printInternal(PrintStream& out, unsigned long value) -{ - out.printf("%lu", value); +void printInternal(PrintStream& out, unsigned long value) { + out.printf("%lu", value); } -void printInternal(PrintStream& out, long long value) -{ - out.printf("%lld", value); +void printInternal(PrintStream& out, long long value) { + out.printf("%lld", value); } -void printInternal(PrintStream& out, unsigned long long value) -{ - out.printf("%llu", value); +void printInternal(PrintStream& out, unsigned long long value) { + out.printf("%llu", value); } -void printInternal(PrintStream& out, float value) -{ - out.print(static_cast(value)); +void printInternal(PrintStream& out, float value) { + out.print(static_cast(value)); } -void printInternal(PrintStream& out, double value) -{ - out.printf("%lf", value); +void printInternal(PrintStream& out, double value) { + out.printf("%lf", value); } -void dumpCharacter(PrintStream& out, char value) -{ - out.printf("%c", value); +void dumpCharacter(PrintStream& out, char value) { + out.printf("%c", value); } -} // namespace WTF - +} // namespace WTF diff --git a/sky/engine/wtf/PrintStream.h b/sky/engine/wtf/PrintStream.h index 556605453b3bb..59a5c1123d487 100644 --- a/sky/engine/wtf/PrintStream.h +++ b/sky/engine/wtf/PrintStream.h @@ -38,181 +38,320 @@ class CString; class String; class WTF_EXPORT PrintStream { - WTF_MAKE_FAST_ALLOCATED; WTF_MAKE_NONCOPYABLE(PrintStream); -public: - PrintStream(); - virtual ~PrintStream(); - - void printf(const char* format, ...) WTF_ATTRIBUTE_PRINTF(2, 3); - virtual void vprintf(const char* format, va_list) WTF_ATTRIBUTE_PRINTF(2, 0) = 0; - - // Typically a no-op for many subclasses of PrintStream, this is a hint that - // the implementation should flush its buffers if it had not done so already. - virtual void flush(); - - template - void print(const T& value) - { - printInternal(*this, value); - } - - template - void print(const T1& value1, const T2& value2) - { - print(value1); - print(value2); - } - - template - void print(const T1& value1, const T2& value2, const T3& value3) - { - print(value1); - print(value2); - print(value3); - } - - template - void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4) - { - print(value1); - print(value2); - print(value3); - print(value4); - } - - template - void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5) - { - print(value1); - print(value2); - print(value3); - print(value4); - print(value5); - } - - template - void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6) - { - print(value1); - print(value2); - print(value3); - print(value4); - print(value5); - print(value6); - } - - template - void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7) - { - print(value1); - print(value2); - print(value3); - print(value4); - print(value5); - print(value6); - print(value7); - } - - template - void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8) - { - print(value1); - print(value2); - print(value3); - print(value4); - print(value5); - print(value6); - print(value7); - print(value8); - } - - template - void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9) - { - print(value1); - print(value2); - print(value3); - print(value4); - print(value5); - print(value6); - print(value7); - print(value8); - print(value9); - } - - template - void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9, const T10& value10) - { - print(value1); - print(value2); - print(value3); - print(value4); - print(value5); - print(value6); - print(value7); - print(value8); - print(value9); - print(value10); - } - - template - void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9, const T10& value10, const T11& value11) - { - print(value1); - print(value2); - print(value3); - print(value4); - print(value5); - print(value6); - print(value7); - print(value8); - print(value9); - print(value10); - print(value11); - } - - template - void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9, const T10& value10, const T11& value11, const T12& value12) - { - print(value1); - print(value2); - print(value3); - print(value4); - print(value5); - print(value6); - print(value7); - print(value8); - print(value9); - print(value10); - print(value11); - print(value12); - } - - template - void print(const T1& value1, const T2& value2, const T3& value3, const T4& value4, const T5& value5, const T6& value6, const T7& value7, const T8& value8, const T9& value9, const T10& value10, const T11& value11, const T12& value12, const T13& value13) - { - print(value1); - print(value2); - print(value3); - print(value4); - print(value5); - print(value6); - print(value7); - print(value8); - print(value9); - print(value10); - print(value11); - print(value12); - print(value13); - } + WTF_MAKE_FAST_ALLOCATED; + WTF_MAKE_NONCOPYABLE(PrintStream); + + public: + PrintStream(); + virtual ~PrintStream(); + + void printf(const char* format, ...) WTF_ATTRIBUTE_PRINTF(2, 3); + virtual void vprintf(const char* format, va_list) + WTF_ATTRIBUTE_PRINTF(2, 0) = 0; + + // Typically a no-op for many subclasses of PrintStream, this is a hint that + // the implementation should flush its buffers if it had not done so already. + virtual void flush(); + + template + void print(const T& value) { + printInternal(*this, value); + } + + template + void print(const T1& value1, const T2& value2) { + print(value1); + print(value2); + } + + template + void print(const T1& value1, const T2& value2, const T3& value3) { + print(value1); + print(value2); + print(value3); + } + + template + void print(const T1& value1, + const T2& value2, + const T3& value3, + const T4& value4) { + print(value1); + print(value2); + print(value3); + print(value4); + } + + template + void print(const T1& value1, + const T2& value2, + const T3& value3, + const T4& value4, + const T5& value5) { + print(value1); + print(value2); + print(value3); + print(value4); + print(value5); + } + + template + void print(const T1& value1, + const T2& value2, + const T3& value3, + const T4& value4, + const T5& value5, + const T6& value6) { + print(value1); + print(value2); + print(value3); + print(value4); + print(value5); + print(value6); + } + + template + void print(const T1& value1, + const T2& value2, + const T3& value3, + const T4& value4, + const T5& value5, + const T6& value6, + const T7& value7) { + print(value1); + print(value2); + print(value3); + print(value4); + print(value5); + print(value6); + print(value7); + } + + template + void print(const T1& value1, + const T2& value2, + const T3& value3, + const T4& value4, + const T5& value5, + const T6& value6, + const T7& value7, + const T8& value8) { + print(value1); + print(value2); + print(value3); + print(value4); + print(value5); + print(value6); + print(value7); + print(value8); + } + + template + void print(const T1& value1, + const T2& value2, + const T3& value3, + const T4& value4, + const T5& value5, + const T6& value6, + const T7& value7, + const T8& value8, + const T9& value9) { + print(value1); + print(value2); + print(value3); + print(value4); + print(value5); + print(value6); + print(value7); + print(value8); + print(value9); + } + + template + void print(const T1& value1, + const T2& value2, + const T3& value3, + const T4& value4, + const T5& value5, + const T6& value6, + const T7& value7, + const T8& value8, + const T9& value9, + const T10& value10) { + print(value1); + print(value2); + print(value3); + print(value4); + print(value5); + print(value6); + print(value7); + print(value8); + print(value9); + print(value10); + } + + template + void print(const T1& value1, + const T2& value2, + const T3& value3, + const T4& value4, + const T5& value5, + const T6& value6, + const T7& value7, + const T8& value8, + const T9& value9, + const T10& value10, + const T11& value11) { + print(value1); + print(value2); + print(value3); + print(value4); + print(value5); + print(value6); + print(value7); + print(value8); + print(value9); + print(value10); + print(value11); + } + + template + void print(const T1& value1, + const T2& value2, + const T3& value3, + const T4& value4, + const T5& value5, + const T6& value6, + const T7& value7, + const T8& value8, + const T9& value9, + const T10& value10, + const T11& value11, + const T12& value12) { + print(value1); + print(value2); + print(value3); + print(value4); + print(value5); + print(value6); + print(value7); + print(value8); + print(value9); + print(value10); + print(value11); + print(value12); + } + + template + void print(const T1& value1, + const T2& value2, + const T3& value3, + const T4& value4, + const T5& value5, + const T6& value6, + const T7& value7, + const T8& value8, + const T9& value9, + const T10& value10, + const T11& value11, + const T12& value12, + const T13& value13) { + print(value1); + print(value2); + print(value3); + print(value4); + print(value5); + print(value6); + print(value7); + print(value8); + print(value9); + print(value10); + print(value11); + print(value12); + print(value13); + } }; WTF_EXPORT void printInternal(PrintStream&, const char*); WTF_EXPORT void printInternal(PrintStream&, const CString&); WTF_EXPORT void printInternal(PrintStream&, const String&); -inline void printInternal(PrintStream& out, char* value) { printInternal(out, static_cast(value)); } -inline void printInternal(PrintStream& out, CString& value) { printInternal(out, static_cast(value)); } -inline void printInternal(PrintStream& out, String& value) { printInternal(out, static_cast(value)); } +inline void printInternal(PrintStream& out, char* value) { + printInternal(out, static_cast(value)); +} +inline void printInternal(PrintStream& out, CString& value) { + printInternal(out, static_cast(value)); +} +inline void printInternal(PrintStream& out, String& value) { + printInternal(out, static_cast(value)); +} WTF_EXPORT void printInternal(PrintStream&, bool); WTF_EXPORT void printInternal(PrintStream&, int); WTF_EXPORT void printInternal(PrintStream&, unsigned); @@ -223,45 +362,34 @@ WTF_EXPORT void printInternal(PrintStream&, unsigned long long); WTF_EXPORT void printInternal(PrintStream&, float); WTF_EXPORT void printInternal(PrintStream&, double); -template -void printInternal(PrintStream& out, const T& value) -{ - value.dump(out); +template +void printInternal(PrintStream& out, const T& value) { + value.dump(out); } -#define MAKE_PRINT_ADAPTOR(Name, Type, function) \ - class Name { \ - public: \ - Name(const Type& value) \ - : m_value(value) \ - { \ - } \ - void dump(PrintStream& out) const \ - { \ - function(out, m_value); \ - } \ - private: \ - Type m_value; \ - } - -#define MAKE_PRINT_METHOD_ADAPTOR(Name, Type, method) \ - class Name { \ - public: \ - Name(const Type& value) \ - : m_value(value) \ - { \ - } \ - void dump(PrintStream& out) const \ - { \ - m_value.method(out); \ - } \ - private: \ - const Type& m_value; \ - } - -#define MAKE_PRINT_METHOD(Type, dumpMethod, method) \ - MAKE_PRINT_METHOD_ADAPTOR(DumperFor_##method, Type, dumpMethod); \ - DumperFor_##method method() const { return DumperFor_##method(*this); } +#define MAKE_PRINT_ADAPTOR(Name, Type, function) \ + class Name { \ + public: \ + Name(const Type& value) : m_value(value) {} \ + void dump(PrintStream& out) const { function(out, m_value); } \ + \ + private: \ + Type m_value; \ + } + +#define MAKE_PRINT_METHOD_ADAPTOR(Name, Type, method) \ + class Name { \ + public: \ + Name(const Type& value) : m_value(value) {} \ + void dump(PrintStream& out) const { m_value.method(out); } \ + \ + private: \ + const Type& m_value; \ + } + +#define MAKE_PRINT_METHOD(Type, dumpMethod, method) \ + MAKE_PRINT_METHOD_ADAPTOR(DumperFor_##method, Type, dumpMethod); \ + DumperFor_##method method() const { return DumperFor_##method(*this); } // Use an adaptor-based dumper for characters to avoid situations where // you've "compressed" an integer to a character and it ends up printing @@ -269,29 +397,28 @@ void printInternal(PrintStream& out, const T& value) void dumpCharacter(PrintStream&, char); MAKE_PRINT_ADAPTOR(CharacterDump, char, dumpCharacter); -template +template class PointerDump { -public: - PointerDump(const T* ptr) - : m_ptr(ptr) - { - } - - void dump(PrintStream& out) const - { - if (m_ptr) - printInternal(out, *m_ptr); - else - out.print("(null)"); - } -private: - const T* m_ptr; + public: + PointerDump(const T* ptr) : m_ptr(ptr) {} + + void dump(PrintStream& out) const { + if (m_ptr) + printInternal(out, *m_ptr); + else + out.print("(null)"); + } + + private: + const T* m_ptr; }; -template -PointerDump pointerDump(const T* ptr) { return PointerDump(ptr); } +template +PointerDump pointerDump(const T* ptr) { + return PointerDump(ptr); +} -} // namespace WTF +} // namespace WTF using WTF::CharacterDump; using WTF::PointerDump; @@ -299,4 +426,3 @@ using WTF::PrintStream; using WTF::pointerDump; #endif // SKY_ENGINE_WTF_PRINTSTREAM_H_ - diff --git a/sky/engine/wtf/RawPtr.h b/sky/engine/wtf/RawPtr.h index 62906568255db..509623a237f31 100644 --- a/sky/engine/wtf/RawPtr.h +++ b/sky/engine/wtf/RawPtr.h @@ -45,105 +45,93 @@ namespace WTF { -template +template class RawPtr { - WTF_DISALLOW_CONSTRUCTION_FROM_ZERO(RawPtr); - WTF_DISALLOW_ZERO_ASSIGNMENT(RawPtr); -public: - RawPtr() - { + WTF_DISALLOW_CONSTRUCTION_FROM_ZERO(RawPtr); + WTF_DISALLOW_ZERO_ASSIGNMENT(RawPtr); + + public: + RawPtr() { #if ENABLE(ASSERT) - m_ptr = reinterpret_cast(rawPtrZapValue); + m_ptr = reinterpret_cast(rawPtrZapValue); #endif - } - RawPtr(std::nullptr_t) : m_ptr(0) { } - RawPtr(T* ptr) : m_ptr(ptr) { } - explicit RawPtr(T& reference) : m_ptr(&reference) { } - RawPtr(const RawPtr& other) - : m_ptr(other.get()) - { - } - - template - RawPtr(const RawPtr& other) - : m_ptr(other.get()) - { - } - - // Hash table deleted values, which are only constructed and never copied or destroyed. - RawPtr(HashTableDeletedValueType) : m_ptr(hashTableDeletedValue()) { } - bool isHashTableDeletedValue() const { return m_ptr == hashTableDeletedValue(); } - - T* get() const { return m_ptr; } - void clear() { m_ptr = 0; } - // FIXME: oilpan: Remove release and leakRef once we remove RefPtr. - RawPtr release() - { - RawPtr tmp = m_ptr; - m_ptr = 0; - return tmp; - } - T* leakRef() - { - T* ptr = m_ptr; - m_ptr = 0; - return ptr; - } - T* leakPtr() - { - T* ptr = m_ptr; - m_ptr = 0; - return ptr; - } - - template - RawPtr& operator=(U* ptr) - { - m_ptr = ptr; - return *this; - } - - template - RawPtr& operator=(RawPtr ptr) - { - m_ptr = ptr.get(); - return *this; - } - - RawPtr& operator=(std::nullptr_t) - { - m_ptr = 0; - return *this; - } - - operator T*() const { return m_ptr; } - T& operator*() const { return *m_ptr; } - T* operator->() const { return m_ptr; } - bool operator!() const { return !m_ptr; } - - void swap(RawPtr& o) - { - std::swap(m_ptr, o.m_ptr); - } - - static T* hashTableDeletedValue() { return reinterpret_cast(-1); } - -private: - static const uintptr_t rawPtrZapValue = 0x3a3a3a3a; - T* m_ptr; + } + RawPtr(std::nullptr_t) : m_ptr(0) {} + RawPtr(T* ptr) : m_ptr(ptr) {} + explicit RawPtr(T& reference) : m_ptr(&reference) {} + RawPtr(const RawPtr& other) : m_ptr(other.get()) {} + + template + RawPtr(const RawPtr& other) : m_ptr(other.get()) {} + + // Hash table deleted values, which are only constructed and never copied or + // destroyed. + RawPtr(HashTableDeletedValueType) : m_ptr(hashTableDeletedValue()) {} + bool isHashTableDeletedValue() const { + return m_ptr == hashTableDeletedValue(); + } + + T* get() const { return m_ptr; } + void clear() { m_ptr = 0; } + // FIXME: oilpan: Remove release and leakRef once we remove RefPtr. + RawPtr release() { + RawPtr tmp = m_ptr; + m_ptr = 0; + return tmp; + } + T* leakRef() { + T* ptr = m_ptr; + m_ptr = 0; + return ptr; + } + T* leakPtr() { + T* ptr = m_ptr; + m_ptr = 0; + return ptr; + } + + template + RawPtr& operator=(U* ptr) { + m_ptr = ptr; + return *this; + } + + template + RawPtr& operator=(RawPtr ptr) { + m_ptr = ptr.get(); + return *this; + } + + RawPtr& operator=(std::nullptr_t) { + m_ptr = 0; + return *this; + } + + operator T*() const { return m_ptr; } + T& operator*() const { return *m_ptr; } + T* operator->() const { return m_ptr; } + bool operator!() const { return !m_ptr; } + + void swap(RawPtr& o) { std::swap(m_ptr, o.m_ptr); } + + static T* hashTableDeletedValue() { return reinterpret_cast(-1); } + + private: + static const uintptr_t rawPtrZapValue = 0x3a3a3a3a; + T* m_ptr; }; -template inline RawPtr static_pointer_cast(const RawPtr& p) -{ - return RawPtr(static_cast(p.get())); +template +inline RawPtr static_pointer_cast(const RawPtr& p) { + return RawPtr(static_cast(p.get())); } -template inline T* getPtr(const RawPtr& p) -{ - return p.get(); +template +inline T* getPtr(const RawPtr& p) { + return p.get(); } -} // namespace WTF +} // namespace WTF using WTF::RawPtr; diff --git a/sky/engine/wtf/RefCounted.h b/sky/engine/wtf/RefCounted.h index 99baa14dedbdb..ef6da7d60ef52 100644 --- a/sky/engine/wtf/RefCounted.h +++ b/sky/engine/wtf/RefCounted.h @@ -40,157 +40,142 @@ namespace WTF { // The RefCounted class inherits from it reducing the template bloat // generated by the compiler (technique called template hoisting). class WTF_EXPORT RefCountedBase { -public: - void ref() - { + public: + void ref() { #if CHECK_REF_COUNTED_LIFECYCLE - // Start thread verification as soon as the ref count gets to 2. This - // heuristic reflects the fact that items are often created on one thread - // and then given to another thread to be used. - // FIXME: Make this restriction tigher. Especially as we move to more - // common methods for sharing items across threads like CrossThreadCopier.h - // We should be able to add a "detachFromThread" method to make this explicit. - if (m_refCount == 1) - m_verifier.setShared(true); - // If this assert fires, it either indicates a thread safety issue or - // that the verification needs to change. See ThreadRestrictionVerifier for - // the different modes. - ASSERT(m_verifier.isSafeToUse()); - ASSERT(!m_adoptionIsRequired); -#endif - ASSERT_WITH_SECURITY_IMPLICATION(!m_deletionHasBegun); - ++m_refCount; - } - - bool hasOneRef() const - { - ASSERT_WITH_SECURITY_IMPLICATION(!m_deletionHasBegun); + // Start thread verification as soon as the ref count gets to 2. This + // heuristic reflects the fact that items are often created on one thread + // and then given to another thread to be used. + // FIXME: Make this restriction tigher. Especially as we move to more + // common methods for sharing items across threads like CrossThreadCopier.h + // We should be able to add a "detachFromThread" method to make this + // explicit. + if (m_refCount == 1) + m_verifier.setShared(true); + // If this assert fires, it either indicates a thread safety issue or + // that the verification needs to change. See ThreadRestrictionVerifier for + // the different modes. + ASSERT(m_verifier.isSafeToUse()); + ASSERT(!m_adoptionIsRequired); +#endif + ASSERT_WITH_SECURITY_IMPLICATION(!m_deletionHasBegun); + ++m_refCount; + } + + bool hasOneRef() const { + ASSERT_WITH_SECURITY_IMPLICATION(!m_deletionHasBegun); #if CHECK_REF_COUNTED_LIFECYCLE - ASSERT(m_verifier.isSafeToUse()); + ASSERT(m_verifier.isSafeToUse()); #endif - return m_refCount == 1; - } + return m_refCount == 1; + } - int refCount() const - { + int refCount() const { #if CHECK_REF_COUNTED_LIFECYCLE - ASSERT(m_verifier.isSafeToUse()); + ASSERT(m_verifier.isSafeToUse()); #endif - return m_refCount; - } + return m_refCount; + } -protected: - RefCountedBase() - : m_refCount(1) + protected: + RefCountedBase() + : m_refCount(1) #if ENABLE(SECURITY_ASSERT) - , m_deletionHasBegun(false) + , + m_deletionHasBegun(false) #endif #if CHECK_REF_COUNTED_LIFECYCLE - , m_adoptionIsRequired(true) + , + m_adoptionIsRequired(true) #endif - { - } + { + } - ~RefCountedBase() - { - ASSERT_WITH_SECURITY_IMPLICATION(m_deletionHasBegun); + ~RefCountedBase() { + ASSERT_WITH_SECURITY_IMPLICATION(m_deletionHasBegun); #if CHECK_REF_COUNTED_LIFECYCLE - ASSERT(!m_adoptionIsRequired); + ASSERT(!m_adoptionIsRequired); #endif - } + } - // Returns whether the pointer should be freed or not. - bool derefBase() - { - ASSERT_WITH_SECURITY_IMPLICATION(!m_deletionHasBegun); + // Returns whether the pointer should be freed or not. + bool derefBase() { + ASSERT_WITH_SECURITY_IMPLICATION(!m_deletionHasBegun); #if CHECK_REF_COUNTED_LIFECYCLE - ASSERT(m_verifier.isSafeToUse()); - ASSERT(!m_adoptionIsRequired); + ASSERT(m_verifier.isSafeToUse()); + ASSERT(!m_adoptionIsRequired); #endif - ASSERT(m_refCount > 0); - --m_refCount; - if (!m_refCount) { + ASSERT(m_refCount > 0); + --m_refCount; + if (!m_refCount) { #if ENABLE(SECURITY_ASSERT) - m_deletionHasBegun = true; + m_deletionHasBegun = true; #endif - return true; - } + return true; + } #if CHECK_REF_COUNTED_LIFECYCLE - // Stop thread verification when the ref goes to 1 because it - // is safe to be passed to another thread at this point. - if (m_refCount == 1) - m_verifier.setShared(false); + // Stop thread verification when the ref goes to 1 because it + // is safe to be passed to another thread at this point. + if (m_refCount == 1) + m_verifier.setShared(false); #endif - return false; - } + return false; + } #if CHECK_REF_COUNTED_LIFECYCLE - bool deletionHasBegun() const - { - return m_deletionHasBegun; - } + bool deletionHasBegun() const { return m_deletionHasBegun; } #endif -private: - + private: #if CHECK_REF_COUNTED_LIFECYCLE || ENABLE(SECURITY_ASSERT) - friend void adopted(RefCountedBase*); + friend void adopted(RefCountedBase*); #endif - int m_refCount; + int m_refCount; #if ENABLE(SECURITY_ASSERT) - bool m_deletionHasBegun; + bool m_deletionHasBegun; #endif #if CHECK_REF_COUNTED_LIFECYCLE - bool m_adoptionIsRequired; - ThreadRestrictionVerifier m_verifier; + bool m_adoptionIsRequired; + ThreadRestrictionVerifier m_verifier; #endif }; #if CHECK_REF_COUNTED_LIFECYCLE || ENABLE(SECURITY_ASSERT) -inline void adopted(RefCountedBase* object) -{ - if (!object) - return; - ASSERT_WITH_SECURITY_IMPLICATION(!object->m_deletionHasBegun); +inline void adopted(RefCountedBase* object) { + if (!object) + return; + ASSERT_WITH_SECURITY_IMPLICATION(!object->m_deletionHasBegun); #if CHECK_REF_COUNTED_LIFECYCLE - object->m_adoptionIsRequired = false; + object->m_adoptionIsRequired = false; #endif } #endif -template class RefCounted : public RefCountedBase { - WTF_MAKE_NONCOPYABLE(RefCounted); - WTF_MAKE_FAST_ALLOCATED; +template +class RefCounted : public RefCountedBase { + WTF_MAKE_NONCOPYABLE(RefCounted); + WTF_MAKE_FAST_ALLOCATED; -public: - void deref() - { - if (derefBase()) - delete static_cast(this); - } + public: + void deref() { + if (derefBase()) + delete static_cast(this); + } -protected: + protected: #ifdef ENABLE_INSTANCE_COUNTER - RefCounted() - { - incrementInstanceCount(static_cast(this)); - } + RefCounted() { incrementInstanceCount(static_cast(this)); } - ~RefCounted() - { - decrementInstanceCount(static_cast(this)); - } + ~RefCounted() { decrementInstanceCount(static_cast(this)); } #else - RefCounted() - { - } + RefCounted() {} #endif }; -} // namespace WTF +} // namespace WTF using WTF::RefCounted; diff --git a/sky/engine/wtf/RefCountedLeakCounter.cpp b/sky/engine/wtf/RefCountedLeakCounter.cpp index ef16a34dd1324..a0ffc2062c00b 100644 --- a/sky/engine/wtf/RefCountedLeakCounter.cpp +++ b/sky/engine/wtf/RefCountedLeakCounter.cpp @@ -18,8 +18,8 @@ * */ -#include "flutter/sky/engine/wtf/Assertions.h" #include "flutter/sky/engine/wtf/RefCountedLeakCounter.h" +#include "flutter/sky/engine/wtf/Assertions.h" #if ENABLE(ASSERT) #include "flutter/sky/engine/wtf/Atomics.h" @@ -30,66 +30,62 @@ namespace WTF { #if !ENABLE(ASSERT) -void RefCountedLeakCounter::suppressMessages(const char*) { } -void RefCountedLeakCounter::cancelMessageSuppression(const char*) { } +void RefCountedLeakCounter::suppressMessages(const char*) {} +void RefCountedLeakCounter::cancelMessageSuppression(const char*) {} -RefCountedLeakCounter::RefCountedLeakCounter(const char*) { } -RefCountedLeakCounter::~RefCountedLeakCounter() { } +RefCountedLeakCounter::RefCountedLeakCounter(const char*) {} +RefCountedLeakCounter::~RefCountedLeakCounter() {} -void RefCountedLeakCounter::increment() { } -void RefCountedLeakCounter::decrement() { } +void RefCountedLeakCounter::increment() {} +void RefCountedLeakCounter::decrement() {} #else #define LOG_CHANNEL_PREFIX Log -static WTFLogChannel LogRefCountedLeaks = { WTFLogChannelOn }; +static WTFLogChannel LogRefCountedLeaks = {WTFLogChannelOn}; -typedef HashCountedSet > ReasonSet; +typedef HashCountedSet> ReasonSet; static ReasonSet* leakMessageSuppressionReasons; -void RefCountedLeakCounter::suppressMessages(const char* reason) -{ - if (!leakMessageSuppressionReasons) - leakMessageSuppressionReasons = new ReasonSet; - leakMessageSuppressionReasons->add(reason); +void RefCountedLeakCounter::suppressMessages(const char* reason) { + if (!leakMessageSuppressionReasons) + leakMessageSuppressionReasons = new ReasonSet; + leakMessageSuppressionReasons->add(reason); } -void RefCountedLeakCounter::cancelMessageSuppression(const char* reason) -{ - ASSERT(leakMessageSuppressionReasons); - ASSERT(leakMessageSuppressionReasons->contains(reason)); - leakMessageSuppressionReasons->remove(reason); +void RefCountedLeakCounter::cancelMessageSuppression(const char* reason) { + ASSERT(leakMessageSuppressionReasons); + ASSERT(leakMessageSuppressionReasons->contains(reason)); + leakMessageSuppressionReasons->remove(reason); } RefCountedLeakCounter::RefCountedLeakCounter(const char* description) - : m_description(description) -{ -} - -RefCountedLeakCounter::~RefCountedLeakCounter() -{ - static bool loggedSuppressionReason; - if (m_count) { - if (!leakMessageSuppressionReasons || leakMessageSuppressionReasons->isEmpty()) - WTF_LOG(RefCountedLeaks, "LEAK: %u %s", m_count, m_description); - else if (!loggedSuppressionReason) { - // This logs only one reason. Later we could change it so we log all the reasons. - WTF_LOG(RefCountedLeaks, "No leak checking done: %s", leakMessageSuppressionReasons->begin()->key); - loggedSuppressionReason = true; - } + : m_description(description) {} + +RefCountedLeakCounter::~RefCountedLeakCounter() { + static bool loggedSuppressionReason; + if (m_count) { + if (!leakMessageSuppressionReasons || + leakMessageSuppressionReasons->isEmpty()) + WTF_LOG(RefCountedLeaks, "LEAK: %u %s", m_count, m_description); + else if (!loggedSuppressionReason) { + // This logs only one reason. Later we could change it so we log all the + // reasons. + WTF_LOG(RefCountedLeaks, "No leak checking done: %s", + leakMessageSuppressionReasons->begin()->key); + loggedSuppressionReason = true; } + } } -void RefCountedLeakCounter::increment() -{ - atomicIncrement(&m_count); +void RefCountedLeakCounter::increment() { + atomicIncrement(&m_count); } -void RefCountedLeakCounter::decrement() -{ - atomicDecrement(&m_count); +void RefCountedLeakCounter::decrement() { + atomicDecrement(&m_count); } #endif -} // namespace WTF +} // namespace WTF diff --git a/sky/engine/wtf/RefCountedLeakCounter.h b/sky/engine/wtf/RefCountedLeakCounter.h index 741e40243bf4a..34d17768645bf 100644 --- a/sky/engine/wtf/RefCountedLeakCounter.h +++ b/sky/engine/wtf/RefCountedLeakCounter.h @@ -26,22 +26,22 @@ namespace WTF { - struct WTF_EXPORT RefCountedLeakCounter { - static void suppressMessages(const char*); - static void cancelMessageSuppression(const char*); +struct WTF_EXPORT RefCountedLeakCounter { + static void suppressMessages(const char*); + static void cancelMessageSuppression(const char*); - explicit RefCountedLeakCounter(const char* description); - ~RefCountedLeakCounter(); + explicit RefCountedLeakCounter(const char* description); + ~RefCountedLeakCounter(); - void increment(); - void decrement(); + void increment(); + void decrement(); #if ENABLE(ASSERT) - private: - volatile int m_count; - const char* m_description; + private: + volatile int m_count; + const char* m_description; #endif - }; +}; } // namespace WTF diff --git a/sky/engine/wtf/RefPtr.h b/sky/engine/wtf/RefPtr.h index 13bc4003c6860..38c0caa2d10fc 100644 --- a/sky/engine/wtf/RefPtr.h +++ b/sky/engine/wtf/RefPtr.h @@ -1,5 +1,6 @@ /* - * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2013 Apple Inc. + * All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -30,189 +31,227 @@ namespace WTF { - template class PassRefPtr; - - template class RefPtr { - WTF_DISALLOW_CONSTRUCTION_FROM_ZERO(RefPtr); - WTF_DISALLOW_ZERO_ASSIGNMENT(RefPtr); - public: - ALWAYS_INLINE RefPtr() : m_ptr(0) { } - ALWAYS_INLINE RefPtr(std::nullptr_t) : m_ptr(0) { } - ALWAYS_INLINE RefPtr(T* ptr) : m_ptr(ptr) { refIfNotNull(ptr); } - template RefPtr(const RawPtr& ptr, EnsurePtrConvertibleArgDecl(U, T)) : m_ptr(ptr.get()) { refIfNotNull(m_ptr); } - ALWAYS_INLINE explicit RefPtr(T& ref) : m_ptr(&ref) { m_ptr->ref(); } - ALWAYS_INLINE RefPtr(const RefPtr& o) : m_ptr(o.m_ptr) { refIfNotNull(m_ptr); } - template RefPtr(const RefPtr& o, EnsurePtrConvertibleArgDecl(U, T)) : m_ptr(o.get()) { refIfNotNull(m_ptr); } - - RefPtr(RefPtr&& o) : m_ptr(o.m_ptr) { o.m_ptr = 0; } - RefPtr& operator=(RefPtr&&); - - // See comments in PassRefPtr.h for an explanation of why this takes a const reference. - template RefPtr(const PassRefPtr&, EnsurePtrConvertibleArgDecl(U, T)); - - // Hash table deleted values, which are only constructed and never copied or destroyed. - RefPtr(HashTableDeletedValueType) : m_ptr(hashTableDeletedValue()) { } - bool isHashTableDeletedValue() const { return m_ptr == hashTableDeletedValue(); } - - ALWAYS_INLINE ~RefPtr() { derefIfNotNull(m_ptr); } - - ALWAYS_INLINE T* get() const { return m_ptr; } - - void clear(); - PassRefPtr release() { PassRefPtr tmp = adoptRef(m_ptr); m_ptr = 0; return tmp; } - - T& operator*() const { return *m_ptr; } - ALWAYS_INLINE T* operator->() const { return m_ptr; } - - bool operator!() const { return !m_ptr; } - - // This conversion operator allows implicit conversion to bool but not to other integer types. - typedef T* (RefPtr::*UnspecifiedBoolType); - operator UnspecifiedBoolType() const { return m_ptr ? &RefPtr::m_ptr : 0; } - - RefPtr& operator=(const RefPtr&); - RefPtr& operator=(T*); - RefPtr& operator=(const PassRefPtr&); - RefPtr& operator=(std::nullptr_t) { clear(); return *this; } - - template RefPtr& operator=(const RefPtr&); - template RefPtr& operator=(const PassRefPtr&); - template RefPtr& operator=(const RawPtr&); - - void swap(RefPtr&); - - static T* hashTableDeletedValue() { return reinterpret_cast(-1); } - - private: - T* m_ptr; - }; - - template template inline RefPtr::RefPtr(const PassRefPtr& o, EnsurePtrConvertibleArgDefn(U, T)) - : m_ptr(o.leakRef()) - { - } - - template inline void RefPtr::clear() - { - T* ptr = m_ptr; - m_ptr = 0; - derefIfNotNull(ptr); - } - - template inline RefPtr& RefPtr::operator=(const RefPtr& o) - { - RefPtr ptr = o; - swap(ptr); - return *this; - } - - template inline RefPtr& RefPtr::operator=(RefPtr&& o) - { - // FIXME: Instead of explicitly casting to RefPtr&& here, we should use std::move, but that requires us to - // have a standard library that supports move semantics. - RefPtr ptr = static_cast(o); - swap(ptr); - return *this; - } - - template template inline RefPtr& RefPtr::operator=(const RefPtr& o) - { - RefPtr ptr = o; - swap(ptr); - return *this; - } - - template inline RefPtr& RefPtr::operator=(T* optr) - { - RefPtr ptr = optr; - swap(ptr); - return *this; - } - - template inline RefPtr& RefPtr::operator=(const PassRefPtr& o) - { - RefPtr ptr = o; - swap(ptr); - return *this; - } - - template template inline RefPtr& RefPtr::operator=(const PassRefPtr& o) - { - RefPtr ptr = o; - swap(ptr); - return *this; - } - - template template inline RefPtr& RefPtr::operator=(const RawPtr& o) - { - RefPtr ptr = o.get(); - swap(ptr); - return *this; - } - - template inline void RefPtr::swap(RefPtr& o) - { - std::swap(m_ptr, o.m_ptr); - } - - template inline void swap(RefPtr& a, RefPtr& b) - { - a.swap(b); - } - - template inline bool operator==(const RefPtr& a, const RefPtr& b) - { - return a.get() == b.get(); - } - - template inline bool operator==(const RefPtr& a, U* b) - { - return a.get() == b; - } - - template inline bool operator==(T* a, const RefPtr& b) - { - return a == b.get(); - } - - template inline bool operator!=(const RefPtr& a, const RefPtr& b) - { - return a.get() != b.get(); - } - - template inline bool operator!=(const RefPtr& a, U* b) - { - return a.get() != b; - } - - template inline bool operator!=(T* a, const RefPtr& b) - { - return a != b.get(); - } - - template inline RefPtr static_pointer_cast(const RefPtr& p) - { - return RefPtr(static_cast(p.get())); - } - - template inline T* getPtr(const RefPtr& p) - { - return p.get(); - } - - template class RefPtrValuePeeker { - public: - ALWAYS_INLINE RefPtrValuePeeker(T* p): m_ptr(p) { } - ALWAYS_INLINE RefPtrValuePeeker(std::nullptr_t): m_ptr(0) { } - template RefPtrValuePeeker(const RefPtr& p): m_ptr(p.get()) { } - template RefPtrValuePeeker(const PassRefPtr& p): m_ptr(p.get()) { } - - ALWAYS_INLINE operator T*() const { return m_ptr; } - private: - T* m_ptr; - }; - -} // namespace WTF +template +class PassRefPtr; + +template +class RefPtr { + WTF_DISALLOW_CONSTRUCTION_FROM_ZERO(RefPtr); + WTF_DISALLOW_ZERO_ASSIGNMENT(RefPtr); + + public: + ALWAYS_INLINE RefPtr() : m_ptr(0) {} + ALWAYS_INLINE RefPtr(std::nullptr_t) : m_ptr(0) {} + ALWAYS_INLINE RefPtr(T* ptr) : m_ptr(ptr) { refIfNotNull(ptr); } + template + RefPtr(const RawPtr& ptr, EnsurePtrConvertibleArgDecl(U, T)) + : m_ptr(ptr.get()) { + refIfNotNull(m_ptr); + } + ALWAYS_INLINE explicit RefPtr(T& ref) : m_ptr(&ref) { m_ptr->ref(); } + ALWAYS_INLINE RefPtr(const RefPtr& o) : m_ptr(o.m_ptr) { + refIfNotNull(m_ptr); + } + template + RefPtr(const RefPtr& o, EnsurePtrConvertibleArgDecl(U, T)) + : m_ptr(o.get()) { + refIfNotNull(m_ptr); + } + + RefPtr(RefPtr&& o) : m_ptr(o.m_ptr) { o.m_ptr = 0; } + RefPtr& operator=(RefPtr&&); + + // See comments in PassRefPtr.h for an explanation of why this takes a const + // reference. + template + RefPtr(const PassRefPtr&, EnsurePtrConvertibleArgDecl(U, T)); + + // Hash table deleted values, which are only constructed and never copied or + // destroyed. + RefPtr(HashTableDeletedValueType) : m_ptr(hashTableDeletedValue()) {} + bool isHashTableDeletedValue() const { + return m_ptr == hashTableDeletedValue(); + } + + ALWAYS_INLINE ~RefPtr() { derefIfNotNull(m_ptr); } + + ALWAYS_INLINE T* get() const { return m_ptr; } + + void clear(); + PassRefPtr release() { + PassRefPtr tmp = adoptRef(m_ptr); + m_ptr = 0; + return tmp; + } + + T& operator*() const { return *m_ptr; } + ALWAYS_INLINE T* operator->() const { return m_ptr; } + + bool operator!() const { return !m_ptr; } + + // This conversion operator allows implicit conversion to bool but not to + // other integer types. + typedef T*(RefPtr::*UnspecifiedBoolType); + operator UnspecifiedBoolType() const { return m_ptr ? &RefPtr::m_ptr : 0; } + + RefPtr& operator=(const RefPtr&); + RefPtr& operator=(T*); + RefPtr& operator=(const PassRefPtr&); + RefPtr& operator=(std::nullptr_t) { + clear(); + return *this; + } + + template + RefPtr& operator=(const RefPtr&); + template + RefPtr& operator=(const PassRefPtr&); + template + RefPtr& operator=(const RawPtr&); + + void swap(RefPtr&); + + static T* hashTableDeletedValue() { return reinterpret_cast(-1); } + + private: + T* m_ptr; +}; + +template +template +inline RefPtr::RefPtr(const PassRefPtr& o, + EnsurePtrConvertibleArgDefn(U, T)) + : m_ptr(o.leakRef()) {} + +template +inline void RefPtr::clear() { + T* ptr = m_ptr; + m_ptr = 0; + derefIfNotNull(ptr); +} + +template +inline RefPtr& RefPtr::operator=(const RefPtr& o) { + RefPtr ptr = o; + swap(ptr); + return *this; +} + +template +inline RefPtr& RefPtr::operator=(RefPtr&& o) { + // FIXME: Instead of explicitly casting to RefPtr&& here, we should use + // std::move, but that requires us to have a standard library that supports + // move semantics. + RefPtr ptr = static_cast(o); + swap(ptr); + return *this; +} + +template +template +inline RefPtr& RefPtr::operator=(const RefPtr& o) { + RefPtr ptr = o; + swap(ptr); + return *this; +} + +template +inline RefPtr& RefPtr::operator=(T* optr) { + RefPtr ptr = optr; + swap(ptr); + return *this; +} + +template +inline RefPtr& RefPtr::operator=(const PassRefPtr& o) { + RefPtr ptr = o; + swap(ptr); + return *this; +} + +template +template +inline RefPtr& RefPtr::operator=(const PassRefPtr& o) { + RefPtr ptr = o; + swap(ptr); + return *this; +} + +template +template +inline RefPtr& RefPtr::operator=(const RawPtr& o) { + RefPtr ptr = o.get(); + swap(ptr); + return *this; +} + +template +inline void RefPtr::swap(RefPtr& o) { + std::swap(m_ptr, o.m_ptr); +} + +template +inline void swap(RefPtr& a, RefPtr& b) { + a.swap(b); +} + +template +inline bool operator==(const RefPtr& a, const RefPtr& b) { + return a.get() == b.get(); +} + +template +inline bool operator==(const RefPtr& a, U* b) { + return a.get() == b; +} + +template +inline bool operator==(T* a, const RefPtr& b) { + return a == b.get(); +} + +template +inline bool operator!=(const RefPtr& a, const RefPtr& b) { + return a.get() != b.get(); +} + +template +inline bool operator!=(const RefPtr& a, U* b) { + return a.get() != b; +} + +template +inline bool operator!=(T* a, const RefPtr& b) { + return a != b.get(); +} + +template +inline RefPtr static_pointer_cast(const RefPtr& p) { + return RefPtr(static_cast(p.get())); +} + +template +inline T* getPtr(const RefPtr& p) { + return p.get(); +} + +template +class RefPtrValuePeeker { + public: + ALWAYS_INLINE RefPtrValuePeeker(T* p) : m_ptr(p) {} + ALWAYS_INLINE RefPtrValuePeeker(std::nullptr_t) : m_ptr(0) {} + template + RefPtrValuePeeker(const RefPtr& p) : m_ptr(p.get()) {} + template + RefPtrValuePeeker(const PassRefPtr& p) : m_ptr(p.get()) {} + + ALWAYS_INLINE operator T*() const { return m_ptr; } + + private: + T* m_ptr; +}; + +} // namespace WTF using WTF::RefPtr; using WTF::static_pointer_cast; diff --git a/sky/engine/wtf/RefPtrTest.cpp b/sky/engine/wtf/RefPtrTest.cpp index 336a788448251..89d3887dcc8b9 100644 --- a/sky/engine/wtf/RefPtrTest.cpp +++ b/sky/engine/wtf/RefPtrTest.cpp @@ -9,25 +9,24 @@ namespace { -TEST(RefPtrTest, Basic) -{ - RefPtr string; - EXPECT_TRUE(!string); - string = StringImpl::create("test"); - EXPECT_TRUE(!!string); - string.clear(); - EXPECT_TRUE(!string); +TEST(RefPtrTest, Basic) { + RefPtr string; + EXPECT_TRUE(!string); + string = StringImpl::create("test"); + EXPECT_TRUE(!!string); + string.clear(); + EXPECT_TRUE(!string); } -TEST(RefPtrTest, MoveAssignmentOperator) -{ - RefPtr a = StringImpl::create("a"); - RefPtr b = StringImpl::create("b"); - // FIXME: Instead of explicitly casting to RefPtr&& here, we should use std::move, but that - // requires us to have a standard library that supports move semantics. - b = static_cast&&>(a); - EXPECT_TRUE(!!b); - EXPECT_TRUE(!a); +TEST(RefPtrTest, MoveAssignmentOperator) { + RefPtr a = StringImpl::create("a"); + RefPtr b = StringImpl::create("b"); + // FIXME: Instead of explicitly casting to RefPtr&& here, we + // should use std::move, but that requires us to have a standard library that + // supports move semantics. + b = static_cast&&>(a); + EXPECT_TRUE(!!b); + EXPECT_TRUE(!a); } -} +} // namespace diff --git a/sky/engine/wtf/RefVector.h b/sky/engine/wtf/RefVector.h index 2e10c97c351b4..954815324e05a 100644 --- a/sky/engine/wtf/RefVector.h +++ b/sky/engine/wtf/RefVector.h @@ -12,29 +12,29 @@ namespace blink { template -class RefVector : public RefCounted > { -public: - static PassRefPtr create() { return adoptRef(new RefVector); } - PassRefPtr copy() { return adoptRef(new RefVector(*this)); } - - const T& operator[](size_t i) const { return m_vector[i]; } - T& operator[](size_t i) { return m_vector[i]; } - const T& at(size_t i) const { return m_vector.at(i); } - T& at(size_t i) { return m_vector.at(i); } - - bool operator==(const RefVector& o) const { return m_vector == o.m_vector; } - bool operator!=(const RefVector& o) const { return m_vector != o.m_vector; } - - size_t size() const { return m_vector.size(); } - void append(const T& decoration) { m_vector.append(decoration); } - const Vector& vector() const { return m_vector; } - -private: - Vector m_vector; - RefVector() { } - RefVector(const RefVector& o) : m_vector(o.m_vector) { } +class RefVector : public RefCounted> { + public: + static PassRefPtr create() { return adoptRef(new RefVector); } + PassRefPtr copy() { return adoptRef(new RefVector(*this)); } + + const T& operator[](size_t i) const { return m_vector[i]; } + T& operator[](size_t i) { return m_vector[i]; } + const T& at(size_t i) const { return m_vector.at(i); } + T& at(size_t i) { return m_vector.at(i); } + + bool operator==(const RefVector& o) const { return m_vector == o.m_vector; } + bool operator!=(const RefVector& o) const { return m_vector != o.m_vector; } + + size_t size() const { return m_vector.size(); } + void append(const T& decoration) { m_vector.append(decoration); } + const Vector& vector() const { return m_vector; } + + private: + Vector m_vector; + RefVector() {} + RefVector(const RefVector& o) : m_vector(o.m_vector) {} }; -} // namespace blink +} // namespace blink #endif // SKY_ENGINE_WTF_REFVECTOR_H_ diff --git a/sky/engine/wtf/SaturatedArithmetic.h b/sky/engine/wtf/SaturatedArithmetic.h index 4079613afb9e8..b2a45140d65d4 100644 --- a/sky/engine/wtf/SaturatedArithmetic.h +++ b/sky/engine/wtf/SaturatedArithmetic.h @@ -43,77 +43,70 @@ #else -ALWAYS_INLINE int32_t saturatedAddition(int32_t a, int32_t b) -{ - uint32_t ua = a; - uint32_t ub = b; - uint32_t result = ua + ub; +ALWAYS_INLINE int32_t saturatedAddition(int32_t a, int32_t b) { + uint32_t ua = a; + uint32_t ub = b; + uint32_t result = ua + ub; - // Can only overflow if the signed bit of the two values match. If the - // signed bit of the result and one of the values differ it overflowed. + // Can only overflow if the signed bit of the two values match. If the + // signed bit of the result and one of the values differ it overflowed. - if (~(ua ^ ub) & (result ^ ua) & (1 << 31)) - return std::numeric_limits::max() + (ua >> 31); + if (~(ua ^ ub) & (result ^ ua) & (1 << 31)) + return std::numeric_limits::max() + (ua >> 31); - return result; + return result; } -ALWAYS_INLINE int32_t saturatedSubtraction(int32_t a, int32_t b) -{ - uint32_t ua = a; - uint32_t ub = b; - uint32_t result = ua - ub; +ALWAYS_INLINE int32_t saturatedSubtraction(int32_t a, int32_t b) { + uint32_t ua = a; + uint32_t ub = b; + uint32_t result = ua - ub; - // Can only overflow if the signed bit of the two input values differ. If - // the signed bit of the result and the first value differ it overflowed. + // Can only overflow if the signed bit of the two input values differ. If + // the signed bit of the result and the first value differ it overflowed. - if ((ua ^ ub) & (result ^ ua) & (1 << 31)) - return std::numeric_limits::max() + (ua >> 31); + if ((ua ^ ub) & (result ^ ua) & (1 << 31)) + return std::numeric_limits::max() + (ua >> 31); - return result; + return result; } -inline int getMaxSaturatedSetResultForTesting(int FractionalShift) -{ - // For C version the set function maxes out to max int, this differs from - // the ARM asm version, see SaturatedArithmetiARM.h for the equivalent asm - // version. - return std::numeric_limits::max(); +inline int getMaxSaturatedSetResultForTesting(int FractionalShift) { + // For C version the set function maxes out to max int, this differs from + // the ARM asm version, see SaturatedArithmetiARM.h for the equivalent asm + // version. + return std::numeric_limits::max(); } -inline int getMinSaturatedSetResultForTesting(int FractionalShift) -{ - return std::numeric_limits::min(); +inline int getMinSaturatedSetResultForTesting(int FractionalShift) { + return std::numeric_limits::min(); } -ALWAYS_INLINE int saturatedSet(int value, int FractionalShift) -{ - const int intMaxForLayoutUnit = - std::numeric_limits::max() >> FractionalShift; +ALWAYS_INLINE int saturatedSet(int value, int FractionalShift) { + const int intMaxForLayoutUnit = + std::numeric_limits::max() >> FractionalShift; - const int intMinForLayoutUnit = - std::numeric_limits::min() >> FractionalShift; + const int intMinForLayoutUnit = + std::numeric_limits::min() >> FractionalShift; - if (value > intMaxForLayoutUnit) - return std::numeric_limits::max(); + if (value > intMaxForLayoutUnit) + return std::numeric_limits::max(); - if (value < intMinForLayoutUnit) - return std::numeric_limits::min(); + if (value < intMinForLayoutUnit) + return std::numeric_limits::min(); - return value << FractionalShift; + return value << FractionalShift; } +ALWAYS_INLINE int saturatedSet(unsigned value, int FractionalShift) { + const unsigned intMaxForLayoutUnit = + std::numeric_limits::max() >> FractionalShift; -ALWAYS_INLINE int saturatedSet(unsigned value, int FractionalShift) -{ - const unsigned intMaxForLayoutUnit = - std::numeric_limits::max() >> FractionalShift; - - if (value >= intMaxForLayoutUnit) - return std::numeric_limits::max(); + if (value >= intMaxForLayoutUnit) + return std::numeric_limits::max(); - return value << FractionalShift; + return value << FractionalShift; } -#endif // CPU(ARM) && COMPILER(GCC) +#endif // CPU(ARM) && COMPILER(GCC) #endif // SKY_ENGINE_WTF_SATURATEDARITHMETIC_H_ diff --git a/sky/engine/wtf/SaturatedArithmeticTest.cpp b/sky/engine/wtf/SaturatedArithmeticTest.cpp index 61de2e54d7401..08a13ad436e56 100644 --- a/sky/engine/wtf/SaturatedArithmeticTest.cpp +++ b/sky/engine/wtf/SaturatedArithmeticTest.cpp @@ -28,129 +28,123 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #include #include #include "flutter/sky/engine/wtf/SaturatedArithmetic.h" namespace { -TEST(SaturatedArithmeticTest, Addition) -{ - EXPECT_EQ(0, saturatedAddition(0, 0)); - EXPECT_EQ(1, saturatedAddition(0, 1)); - EXPECT_EQ(100, saturatedAddition(0, 100)); - EXPECT_EQ(150, saturatedAddition(100, 50)); - - EXPECT_EQ(-1, saturatedAddition(0, -1)); - EXPECT_EQ(0, saturatedAddition(1, -1)); - EXPECT_EQ(50, saturatedAddition(100, -50)); - EXPECT_EQ(-50, saturatedAddition(50, -100)); - - EXPECT_EQ(INT_MAX - 1, saturatedAddition(INT_MAX - 1, 0)); - EXPECT_EQ(INT_MAX, saturatedAddition(INT_MAX - 1, 1)); - EXPECT_EQ(INT_MAX, saturatedAddition(INT_MAX - 1, 2)); - EXPECT_EQ(INT_MAX - 1, saturatedAddition(0, INT_MAX - 1)); - EXPECT_EQ(INT_MAX, saturatedAddition(1, INT_MAX - 1)); - EXPECT_EQ(INT_MAX, saturatedAddition(2, INT_MAX - 1)); - EXPECT_EQ(INT_MAX, saturatedAddition(INT_MAX - 1, INT_MAX - 1)); - EXPECT_EQ(INT_MAX, saturatedAddition(INT_MAX, INT_MAX)); - - EXPECT_EQ(INT_MIN, saturatedAddition(INT_MIN, 0)); - EXPECT_EQ(INT_MIN + 1, saturatedAddition(INT_MIN + 1, 0)); - EXPECT_EQ(INT_MIN + 2, saturatedAddition(INT_MIN + 1, 1)); - EXPECT_EQ(INT_MIN + 3, saturatedAddition(INT_MIN + 1, 2)); - EXPECT_EQ(INT_MIN, saturatedAddition(INT_MIN + 1, -1)); - EXPECT_EQ(INT_MIN, saturatedAddition(INT_MIN + 1, -2)); - EXPECT_EQ(INT_MIN + 1, saturatedAddition(0, INT_MIN + 1)); - EXPECT_EQ(INT_MIN, saturatedAddition(-1, INT_MIN + 1)); - EXPECT_EQ(INT_MIN, saturatedAddition(-2, INT_MIN + 1)); - - EXPECT_EQ(INT_MAX / 2 + 10000, saturatedAddition(INT_MAX / 2, 10000)); - EXPECT_EQ(INT_MAX, saturatedAddition(INT_MAX / 2 + 1, INT_MAX / 2 + 1)); - EXPECT_EQ(-1, saturatedAddition(INT_MIN, INT_MAX)); +TEST(SaturatedArithmeticTest, Addition) { + EXPECT_EQ(0, saturatedAddition(0, 0)); + EXPECT_EQ(1, saturatedAddition(0, 1)); + EXPECT_EQ(100, saturatedAddition(0, 100)); + EXPECT_EQ(150, saturatedAddition(100, 50)); + + EXPECT_EQ(-1, saturatedAddition(0, -1)); + EXPECT_EQ(0, saturatedAddition(1, -1)); + EXPECT_EQ(50, saturatedAddition(100, -50)); + EXPECT_EQ(-50, saturatedAddition(50, -100)); + + EXPECT_EQ(INT_MAX - 1, saturatedAddition(INT_MAX - 1, 0)); + EXPECT_EQ(INT_MAX, saturatedAddition(INT_MAX - 1, 1)); + EXPECT_EQ(INT_MAX, saturatedAddition(INT_MAX - 1, 2)); + EXPECT_EQ(INT_MAX - 1, saturatedAddition(0, INT_MAX - 1)); + EXPECT_EQ(INT_MAX, saturatedAddition(1, INT_MAX - 1)); + EXPECT_EQ(INT_MAX, saturatedAddition(2, INT_MAX - 1)); + EXPECT_EQ(INT_MAX, saturatedAddition(INT_MAX - 1, INT_MAX - 1)); + EXPECT_EQ(INT_MAX, saturatedAddition(INT_MAX, INT_MAX)); + + EXPECT_EQ(INT_MIN, saturatedAddition(INT_MIN, 0)); + EXPECT_EQ(INT_MIN + 1, saturatedAddition(INT_MIN + 1, 0)); + EXPECT_EQ(INT_MIN + 2, saturatedAddition(INT_MIN + 1, 1)); + EXPECT_EQ(INT_MIN + 3, saturatedAddition(INT_MIN + 1, 2)); + EXPECT_EQ(INT_MIN, saturatedAddition(INT_MIN + 1, -1)); + EXPECT_EQ(INT_MIN, saturatedAddition(INT_MIN + 1, -2)); + EXPECT_EQ(INT_MIN + 1, saturatedAddition(0, INT_MIN + 1)); + EXPECT_EQ(INT_MIN, saturatedAddition(-1, INT_MIN + 1)); + EXPECT_EQ(INT_MIN, saturatedAddition(-2, INT_MIN + 1)); + + EXPECT_EQ(INT_MAX / 2 + 10000, saturatedAddition(INT_MAX / 2, 10000)); + EXPECT_EQ(INT_MAX, saturatedAddition(INT_MAX / 2 + 1, INT_MAX / 2 + 1)); + EXPECT_EQ(-1, saturatedAddition(INT_MIN, INT_MAX)); } -TEST(SaturatedArithmeticTest, Subtraction) -{ - EXPECT_EQ(0, saturatedSubtraction(0, 0)); - EXPECT_EQ(-1, saturatedSubtraction(0, 1)); - EXPECT_EQ(-100, saturatedSubtraction(0, 100)); - EXPECT_EQ(50, saturatedSubtraction(100, 50)); - - EXPECT_EQ(1, saturatedSubtraction(0, -1)); - EXPECT_EQ(2, saturatedSubtraction(1, -1)); - EXPECT_EQ(150, saturatedSubtraction(100, -50)); - EXPECT_EQ(150, saturatedSubtraction(50, -100)); - - EXPECT_EQ(INT_MAX, saturatedSubtraction(INT_MAX, 0)); - EXPECT_EQ(INT_MAX - 1, saturatedSubtraction(INT_MAX, 1)); - EXPECT_EQ(INT_MAX - 1, saturatedSubtraction(INT_MAX - 1, 0)); - EXPECT_EQ(INT_MAX, saturatedSubtraction(INT_MAX - 1, -1)); - EXPECT_EQ(INT_MAX, saturatedSubtraction(INT_MAX - 1, -2)); - EXPECT_EQ(-INT_MAX + 1, saturatedSubtraction(0, INT_MAX - 1)); - EXPECT_EQ(-INT_MAX, saturatedSubtraction(-1, INT_MAX - 1)); - EXPECT_EQ(-INT_MAX - 1, saturatedSubtraction(-2, INT_MAX - 1)); - EXPECT_EQ(-INT_MAX - 1, saturatedSubtraction(-3, INT_MAX - 1)); - - EXPECT_EQ(INT_MIN, saturatedSubtraction(INT_MIN, 0)); - EXPECT_EQ(INT_MIN + 1, saturatedSubtraction(INT_MIN + 1, 0)); - EXPECT_EQ(INT_MIN, saturatedSubtraction(INT_MIN + 1, 1)); - EXPECT_EQ(INT_MIN, saturatedSubtraction(INT_MIN + 1, 2)); - - EXPECT_EQ(0, saturatedSubtraction(INT_MIN, INT_MIN)); - EXPECT_EQ(0, saturatedSubtraction(INT_MAX, INT_MAX)); - EXPECT_EQ(INT_MAX, saturatedSubtraction(INT_MAX, INT_MIN)); +TEST(SaturatedArithmeticTest, Subtraction) { + EXPECT_EQ(0, saturatedSubtraction(0, 0)); + EXPECT_EQ(-1, saturatedSubtraction(0, 1)); + EXPECT_EQ(-100, saturatedSubtraction(0, 100)); + EXPECT_EQ(50, saturatedSubtraction(100, 50)); + + EXPECT_EQ(1, saturatedSubtraction(0, -1)); + EXPECT_EQ(2, saturatedSubtraction(1, -1)); + EXPECT_EQ(150, saturatedSubtraction(100, -50)); + EXPECT_EQ(150, saturatedSubtraction(50, -100)); + + EXPECT_EQ(INT_MAX, saturatedSubtraction(INT_MAX, 0)); + EXPECT_EQ(INT_MAX - 1, saturatedSubtraction(INT_MAX, 1)); + EXPECT_EQ(INT_MAX - 1, saturatedSubtraction(INT_MAX - 1, 0)); + EXPECT_EQ(INT_MAX, saturatedSubtraction(INT_MAX - 1, -1)); + EXPECT_EQ(INT_MAX, saturatedSubtraction(INT_MAX - 1, -2)); + EXPECT_EQ(-INT_MAX + 1, saturatedSubtraction(0, INT_MAX - 1)); + EXPECT_EQ(-INT_MAX, saturatedSubtraction(-1, INT_MAX - 1)); + EXPECT_EQ(-INT_MAX - 1, saturatedSubtraction(-2, INT_MAX - 1)); + EXPECT_EQ(-INT_MAX - 1, saturatedSubtraction(-3, INT_MAX - 1)); + + EXPECT_EQ(INT_MIN, saturatedSubtraction(INT_MIN, 0)); + EXPECT_EQ(INT_MIN + 1, saturatedSubtraction(INT_MIN + 1, 0)); + EXPECT_EQ(INT_MIN, saturatedSubtraction(INT_MIN + 1, 1)); + EXPECT_EQ(INT_MIN, saturatedSubtraction(INT_MIN + 1, 2)); + + EXPECT_EQ(0, saturatedSubtraction(INT_MIN, INT_MIN)); + EXPECT_EQ(0, saturatedSubtraction(INT_MAX, INT_MAX)); + EXPECT_EQ(INT_MAX, saturatedSubtraction(INT_MAX, INT_MIN)); } -TEST(SaturatedArithmeticTest, SetSigned) -{ - const int kFractionBits = 6; - const int intMaxForLayoutUnit = INT_MAX >> kFractionBits; - const int intMinForLayoutUnit = INT_MIN >> kFractionBits; +TEST(SaturatedArithmeticTest, SetSigned) { + const int kFractionBits = 6; + const int intMaxForLayoutUnit = INT_MAX >> kFractionBits; + const int intMinForLayoutUnit = INT_MIN >> kFractionBits; - EXPECT_EQ(0, saturatedSet(0, kFractionBits)); + EXPECT_EQ(0, saturatedSet(0, kFractionBits)); - // Internally the max number we can represent (without saturating) - // is all the (non-sign) bits set except for the bottom n fraction bits - const int maxInternalRepresentation = INT_MAX ^ ((1 << kFractionBits)-1); - EXPECT_EQ(maxInternalRepresentation, - saturatedSet(intMaxForLayoutUnit, kFractionBits)); + // Internally the max number we can represent (without saturating) + // is all the (non-sign) bits set except for the bottom n fraction bits + const int maxInternalRepresentation = INT_MAX ^ ((1 << kFractionBits) - 1); + EXPECT_EQ(maxInternalRepresentation, + saturatedSet(intMaxForLayoutUnit, kFractionBits)); - EXPECT_EQ(getMaxSaturatedSetResultForTesting(kFractionBits), - saturatedSet(intMaxForLayoutUnit + 100, kFractionBits)); + EXPECT_EQ(getMaxSaturatedSetResultForTesting(kFractionBits), + saturatedSet(intMaxForLayoutUnit + 100, kFractionBits)); - EXPECT_EQ((intMaxForLayoutUnit - 100) << kFractionBits, - saturatedSet(intMaxForLayoutUnit - 100, kFractionBits)); + EXPECT_EQ((intMaxForLayoutUnit - 100) << kFractionBits, + saturatedSet(intMaxForLayoutUnit - 100, kFractionBits)); - EXPECT_EQ(getMinSaturatedSetResultForTesting(kFractionBits), - saturatedSet(intMinForLayoutUnit, kFractionBits)); + EXPECT_EQ(getMinSaturatedSetResultForTesting(kFractionBits), + saturatedSet(intMinForLayoutUnit, kFractionBits)); - EXPECT_EQ(getMinSaturatedSetResultForTesting(kFractionBits), - saturatedSet(intMinForLayoutUnit - 100, kFractionBits)); + EXPECT_EQ(getMinSaturatedSetResultForTesting(kFractionBits), + saturatedSet(intMinForLayoutUnit - 100, kFractionBits)); - EXPECT_EQ((intMinForLayoutUnit + 100) << kFractionBits, - saturatedSet(intMinForLayoutUnit + 100, kFractionBits)); + EXPECT_EQ((intMinForLayoutUnit + 100) << kFractionBits, + saturatedSet(intMinForLayoutUnit + 100, kFractionBits)); } -TEST(SaturatedArithmeticTest, SetUnsigned) -{ - const int kFractionBits = 6; - const int intMaxForLayoutUnit = INT_MAX >> kFractionBits; +TEST(SaturatedArithmeticTest, SetUnsigned) { + const int kFractionBits = 6; + const int intMaxForLayoutUnit = INT_MAX >> kFractionBits; - EXPECT_EQ(0, saturatedSet((unsigned)0, kFractionBits)); + EXPECT_EQ(0, saturatedSet((unsigned)0, kFractionBits)); - EXPECT_EQ(getMaxSaturatedSetResultForTesting(kFractionBits), - saturatedSet((unsigned)intMaxForLayoutUnit, kFractionBits)); + EXPECT_EQ(getMaxSaturatedSetResultForTesting(kFractionBits), + saturatedSet((unsigned)intMaxForLayoutUnit, kFractionBits)); - const unsigned kOverflowed = intMaxForLayoutUnit + 100; - EXPECT_EQ(getMaxSaturatedSetResultForTesting(kFractionBits), - saturatedSet(kOverflowed, kFractionBits)); + const unsigned kOverflowed = intMaxForLayoutUnit + 100; + EXPECT_EQ(getMaxSaturatedSetResultForTesting(kFractionBits), + saturatedSet(kOverflowed, kFractionBits)); - const unsigned kNotOverflowed = intMaxForLayoutUnit - 100; - EXPECT_EQ((intMaxForLayoutUnit - 100) << kFractionBits, - saturatedSet(kNotOverflowed, kFractionBits)); + const unsigned kNotOverflowed = intMaxForLayoutUnit - 100; + EXPECT_EQ((intMaxForLayoutUnit - 100) << kFractionBits, + saturatedSet(kNotOverflowed, kFractionBits)); } - -} // namespace +} // namespace diff --git a/sky/engine/wtf/SizeLimits.cpp b/sky/engine/wtf/SizeLimits.cpp index 031457e7611bf..48f3a6fef945e 100644 --- a/sky/engine/wtf/SizeLimits.cpp +++ b/sky/engine/wtf/SizeLimits.cpp @@ -28,7 +28,6 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #include "flutter/sky/engine/wtf/Assertions.h" #include "flutter/sky/engine/wtf/OwnPtr.h" #include "flutter/sky/engine/wtf/PassRefPtr.h" @@ -44,45 +43,57 @@ namespace WTF { #if ENABLE(ASSERT) || ENABLE(SECURITY_ASSERT) // The debug/assertion version may get bigger. struct SameSizeAsRefCounted { - int a; + int a; #if ENABLE(SECURITY_ASSERT) - bool b; + bool b; #endif #if ENABLE(ASSERT) - bool c; - ThreadRestrictionVerifier d; + bool c; + ThreadRestrictionVerifier d; #endif }; #else struct SameSizeAsRefCounted { - int a; - // Don't add anything here because this should stay small. + int a; + // Don't add anything here because this should stay small. }; #endif -template +template struct SameSizeAsVectorWithInlineCapacity; -template +template struct SameSizeAsVectorWithInlineCapacity { - void* bufferPointer; - unsigned capacity; - unsigned size; + void* bufferPointer; + unsigned capacity; + unsigned size; }; -template +template struct SameSizeAsVectorWithInlineCapacity { - SameSizeAsVectorWithInlineCapacity baseCapacity; - AlignedBuffer inlineBuffer; + SameSizeAsVectorWithInlineCapacity baseCapacity; + AlignedBuffer inlineBuffer; }; COMPILE_ASSERT(sizeof(OwnPtr) == sizeof(int*), OwnPtr_should_stay_small); -COMPILE_ASSERT(sizeof(PassRefPtr >) == sizeof(int*), PassRefPtr_should_stay_small); -COMPILE_ASSERT(sizeof(RefCounted) == sizeof(SameSizeAsRefCounted), RefCounted_should_stay_small); -COMPILE_ASSERT(sizeof(RefPtr >) == sizeof(int*), RefPtr_should_stay_small); +COMPILE_ASSERT(sizeof(PassRefPtr>) == sizeof(int*), + PassRefPtr_should_stay_small); +COMPILE_ASSERT(sizeof(RefCounted) == sizeof(SameSizeAsRefCounted), + RefCounted_should_stay_small); +COMPILE_ASSERT(sizeof(RefPtr>) == sizeof(int*), + RefPtr_should_stay_small); COMPILE_ASSERT(sizeof(String) == sizeof(int*), String_should_stay_small); -COMPILE_ASSERT(sizeof(AtomicString) == sizeof(String), AtomicString_should_stay_small); -COMPILE_ASSERT(sizeof(Vector) == sizeof(SameSizeAsVectorWithInlineCapacity), Vector_should_stay_small); -COMPILE_ASSERT(sizeof(Vector) == sizeof(SameSizeAsVectorWithInlineCapacity), Vector_should_stay_small); -COMPILE_ASSERT(sizeof(Vector) == sizeof(SameSizeAsVectorWithInlineCapacity), Vector_should_stay_small); -COMPILE_ASSERT(sizeof(Vector) == sizeof(SameSizeAsVectorWithInlineCapacity), Vector_should_stay_small); -} +COMPILE_ASSERT(sizeof(AtomicString) == sizeof(String), + AtomicString_should_stay_small); +COMPILE_ASSERT(sizeof(Vector) == + sizeof(SameSizeAsVectorWithInlineCapacity), + Vector_should_stay_small); +COMPILE_ASSERT(sizeof(Vector) == + sizeof(SameSizeAsVectorWithInlineCapacity), + Vector_should_stay_small); +COMPILE_ASSERT(sizeof(Vector) == + sizeof(SameSizeAsVectorWithInlineCapacity), + Vector_should_stay_small); +COMPILE_ASSERT(sizeof(Vector) == + sizeof(SameSizeAsVectorWithInlineCapacity), + Vector_should_stay_small); +} // namespace WTF diff --git a/sky/engine/wtf/SpinLock.h b/sky/engine/wtf/SpinLock.h index e3305d108a1d5..1fae148ff9916 100644 --- a/sky/engine/wtf/SpinLock.h +++ b/sky/engine/wtf/SpinLock.h @@ -40,19 +40,18 @@ namespace WTF { -ALWAYS_INLINE void spinLockLock(int volatile* lock) -{ - while (UNLIKELY(atomicTestAndSetToOne(lock))) { - while (*lock) { } // Spin without spamming locked instructions. - } +ALWAYS_INLINE void spinLockLock(int volatile* lock) { + while (UNLIKELY(atomicTestAndSetToOne(lock))) { + while (*lock) { + } // Spin without spamming locked instructions. + } } -ALWAYS_INLINE void spinLockUnlock(int volatile* lock) -{ - atomicSetOneToZero(lock); +ALWAYS_INLINE void spinLockUnlock(int volatile* lock) { + atomicSetOneToZero(lock); } -} // namespace WTF +} // namespace WTF using WTF::spinLockLock; using WTF::spinLockUnlock; diff --git a/sky/engine/wtf/StaticConstructors.h b/sky/engine/wtf/StaticConstructors.h index 9d36b8db48e92..d18baf8a0e440 100644 --- a/sky/engine/wtf/StaticConstructors.h +++ b/sky/engine/wtf/StaticConstructors.h @@ -30,9 +30,10 @@ // load, while a real global could be referenced directly by absolute or // relative addressing. -// Use an array of pointers instead of an array of char in case there is some alignment issue. -#define DEFINE_GLOBAL(type, name, ...) \ - void* name##Storage[(sizeof(type) + sizeof(void *) - 1) / sizeof(void *)]; \ - const type& name = *reinterpret_cast(&name##Storage); +// Use an array of pointers instead of an array of char in case there is some +// alignment issue. +#define DEFINE_GLOBAL(type, name, ...) \ + void* name##Storage[(sizeof(type) + sizeof(void*) - 1) / sizeof(void*)]; \ + const type& name = *reinterpret_cast(&name##Storage); #endif // SKY_ENGINE_WTF_STATICCONSTRUCTORS_H_ diff --git a/sky/engine/wtf/StdLibExtras.h b/sky/engine/wtf/StdLibExtras.h index e0d4d4f8e3494..0544726332255 100644 --- a/sky/engine/wtf/StdLibExtras.h +++ b/sky/engine/wtf/StdLibExtras.h @@ -34,36 +34,37 @@ // it is leaked so that its destructors are not called at exit. #ifndef DEFINE_STATIC_LOCAL #define DEFINE_STATIC_LOCAL(type, name, arguments) \ - static type& name = *new type arguments + static type& name = *new type arguments #endif -// Use this to declare and define a static local pointer to a ref-counted object so that -// it is leaked so that the object's destructors are not called at exit. -// This macro should be used with ref-counted objects rather than DEFINE_STATIC_LOCAL macro, -// as this macro does not lead to an extra memory allocation. +// Use this to declare and define a static local pointer to a ref-counted object +// so that it is leaked so that the object's destructors are not called at exit. +// This macro should be used with ref-counted objects rather than +// DEFINE_STATIC_LOCAL macro, as this macro does not lead to an extra memory +// allocation. #ifndef DEFINE_STATIC_REF #define DEFINE_STATIC_REF(type, name, arguments) \ - static type* name = PassRefPtr(arguments).leakRef(); + static type* name = PassRefPtr(arguments).leakRef(); #endif -// Use this macro to declare and define a debug-only global variable that may have a -// non-trivial constructor and destructor. When building with clang, this will suppress -// warnings about global constructors and exit-time destructors. +// Use this macro to declare and define a debug-only global variable that may +// have a non-trivial constructor and destructor. When building with clang, this +// will suppress warnings about global constructors and exit-time destructors. #ifndef NDEBUG #if COMPILER(CLANG) -#define DEFINE_DEBUG_ONLY_GLOBAL(type, name, arguments) \ - _Pragma("clang diagnostic push") \ - _Pragma("clang diagnostic ignored \"-Wglobal-constructors\"") \ - _Pragma("clang diagnostic ignored \"-Wexit-time-destructors\"") \ - static type name arguments; \ - _Pragma("clang diagnostic pop") +#define DEFINE_DEBUG_ONLY_GLOBAL(type, name, arguments) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wglobal-constructors\"") _Pragma( \ + "clang diagnostic ignored \"-Wexit-time-destructors\"") static type \ + name arguments; \ + _Pragma("clang diagnostic pop") #else #define DEFINE_DEBUG_ONLY_GLOBAL(type, name, arguments) \ - static type name arguments; -#endif // COMPILER(CLANG) + static type name arguments; +#endif // COMPILER(CLANG) #else #define DEFINE_DEBUG_ONLY_GLOBAL(type, name, arguments) -#endif // NDEBUG +#endif // NDEBUG /* * The reinterpret_cast([pointer to Type2]) expressions - where @@ -76,30 +77,26 @@ * - http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43976 */ #if CPU(ARM) && COMPILER(GCC) -template -bool isPointerTypeAlignmentOkay(Type* ptr) -{ - return !(reinterpret_cast(ptr) % __alignof__(Type)); +template +bool isPointerTypeAlignmentOkay(Type* ptr) { + return !(reinterpret_cast(ptr) % __alignof__(Type)); } -template -TypePtr reinterpret_cast_ptr(void* ptr) -{ - ASSERT(isPointerTypeAlignmentOkay(reinterpret_cast(ptr))); - return reinterpret_cast(ptr); +template +TypePtr reinterpret_cast_ptr(void* ptr) { + ASSERT(isPointerTypeAlignmentOkay(reinterpret_cast(ptr))); + return reinterpret_cast(ptr); } -template -TypePtr reinterpret_cast_ptr(const void* ptr) -{ - ASSERT(isPointerTypeAlignmentOkay(reinterpret_cast(ptr))); - return reinterpret_cast(ptr); +template +TypePtr reinterpret_cast_ptr(const void* ptr) { + ASSERT(isPointerTypeAlignmentOkay(reinterpret_cast(ptr))); + return reinterpret_cast(ptr); } #else -template -bool isPointerTypeAlignmentOkay(Type*) -{ - return true; +template +bool isPointerTypeAlignmentOkay(Type*) { + return true; } #define reinterpret_cast_ptr reinterpret_cast #endif @@ -109,41 +106,42 @@ namespace WTF { /* * C++'s idea of a reinterpret_cast lacks sufficient cojones. */ -template -inline TO bitwise_cast(FROM from) -{ - COMPILE_ASSERT(sizeof(TO) == sizeof(FROM), WTF_bitwise_cast_sizeof_casted_types_is_equal); - union { - FROM from; - TO to; - } u; - u.from = from; - return u.to; +template +inline TO bitwise_cast(FROM from) { + COMPILE_ASSERT(sizeof(TO) == sizeof(FROM), + WTF_bitwise_cast_sizeof_casted_types_is_equal); + union { + FROM from; + TO to; + } u; + u.from = from; + return u.to; } -template -inline To safeCast(From value) -{ - ASSERT(isInBounds(value)); - return static_cast(value); +template +inline To safeCast(From value) { + ASSERT(isInBounds(value)); + return static_cast(value); } -// Macro that returns a compile time constant with the length of an array, but gives an error if passed a non-array. -template char (&ArrayLengthHelperFunction(T (&)[Size]))[Size]; +// Macro that returns a compile time constant with the length of an array, but +// gives an error if passed a non-array. +template +char (&ArrayLengthHelperFunction(T (&)[Size]))[Size]; // GCC needs some help to deduce a 0 length array. #if COMPILER(GCC) -template char (&ArrayLengthHelperFunction(T (&)[0]))[0]; +template +char (&ArrayLengthHelperFunction(T (&)[0]))[0]; #endif #define WTF_ARRAY_LENGTH(array) sizeof(::WTF::ArrayLengthHelperFunction(array)) -} // namespace WTF +} // namespace WTF // This version of placement new omits a 0 check. enum NotNullTag { NotNull }; -inline void* operator new(size_t, NotNullTag, void* location) -{ - ASSERT(location); - return location; +inline void* operator new(size_t, NotNullTag, void* location) { + ASSERT(location); + return location; } using WTF::bitwise_cast; diff --git a/sky/engine/wtf/StreamBuffer.h b/sky/engine/wtf/StreamBuffer.h index f719dcaca5983..534a28a2d1319 100644 --- a/sky/engine/wtf/StreamBuffer.h +++ b/sky/engine/wtf/StreamBuffer.h @@ -1,32 +1,32 @@ /* -* Copyright (C) 2012 Google Inc. All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are -* met: -* -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above -* copyright notice, this list of conditions and the following disclaimer -* in the documentation and/or other materials provided with the -* distribution. -* * Neither the name of Google Inc. nor the names of its -* contributors may be used to endorse or promote products derived from -* this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ + * Copyright (C) 2012 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ #ifndef SKY_ENGINE_WTF_STREAMBUFFER_H_ #define SKY_ENGINE_WTF_STREAMBUFFER_H_ @@ -36,81 +36,73 @@ namespace WTF { -template class StreamBuffer { -private: - typedef Vector Block; -public: - StreamBuffer() - : m_size(0) - , m_readOffset(0) - { - } +template +class StreamBuffer { + private: + typedef Vector Block; - ~StreamBuffer() - { - } + public: + StreamBuffer() : m_size(0), m_readOffset(0) {} - bool isEmpty() const { return !size(); } - - void append(const T* data, size_t size) - { - if (!size) - return; - - m_size += size; - while (size) { - if (!m_buffer.size() || m_buffer.last()->size() == BlockSize) - m_buffer.append(adoptPtr(new Block)); - size_t appendSize = std::min(BlockSize - m_buffer.last()->size(), size); - m_buffer.last()->append(data, appendSize); - data += appendSize; - size -= appendSize; - } - } + ~StreamBuffer() {} - // This function consume data in the fist block. - // Specified size must be less than over equal to firstBlockSize(). - void consume(size_t size) - { - ASSERT(m_size >= size); - if (!m_size) - return; - - ASSERT(m_buffer.size() > 0); - ASSERT(m_readOffset + size <= m_buffer.first()->size()); - m_readOffset += size; - m_size -= size; - if (m_readOffset >= m_buffer.first()->size()) { - m_readOffset = 0; - m_buffer.removeFirst(); - } - } + bool isEmpty() const { return !size(); } - size_t size() const { return m_size; } + void append(const T* data, size_t size) { + if (!size) + return; - const T* firstBlockData() const - { - if (!m_size) - return 0; - ASSERT(m_buffer.size() > 0); - return &m_buffer.first()->data()[m_readOffset]; + m_size += size; + while (size) { + if (!m_buffer.size() || m_buffer.last()->size() == BlockSize) + m_buffer.append(adoptPtr(new Block)); + size_t appendSize = std::min(BlockSize - m_buffer.last()->size(), size); + m_buffer.last()->append(data, appendSize); + data += appendSize; + size -= appendSize; } - - size_t firstBlockSize() const - { - if (!m_size) - return 0; - ASSERT(m_buffer.size() > 0); - return m_buffer.first()->size() - m_readOffset; + } + + // This function consume data in the fist block. + // Specified size must be less than over equal to firstBlockSize(). + void consume(size_t size) { + ASSERT(m_size >= size); + if (!m_size) + return; + + ASSERT(m_buffer.size() > 0); + ASSERT(m_readOffset + size <= m_buffer.first()->size()); + m_readOffset += size; + m_size -= size; + if (m_readOffset >= m_buffer.first()->size()) { + m_readOffset = 0; + m_buffer.removeFirst(); } - -private: - size_t m_size; - size_t m_readOffset; - Deque > m_buffer; + } + + size_t size() const { return m_size; } + + const T* firstBlockData() const { + if (!m_size) + return 0; + ASSERT(m_buffer.size() > 0); + return &m_buffer.first()->data()[m_readOffset]; + } + + size_t firstBlockSize() const { + if (!m_size) + return 0; + ASSERT(m_buffer.size() > 0); + return m_buffer.first()->size() - m_readOffset; + } + + private: + size_t m_size; + size_t m_readOffset; + Deque> m_buffer; }; -} // namespace WTF +} // namespace WTF using WTF::StreamBuffer; diff --git a/sky/engine/wtf/StringExtras.h b/sky/engine/wtf/StringExtras.h index ea511b2d2bf70..d02458394cdc3 100644 --- a/sky/engine/wtf/StringExtras.h +++ b/sky/engine/wtf/StringExtras.h @@ -34,16 +34,19 @@ #if !HAVE(STRNSTR) -inline char* strnstr(const char* buffer, const char* target, size_t bufferLength) -{ - size_t targetLength = strlen(target); - if (targetLength == 0) - return const_cast(buffer); - for (const char* start = buffer; *start && start + targetLength <= buffer + bufferLength; start++) { - if (*start == *target && strncmp(start + 1, target + 1, targetLength - 1) == 0) - return const_cast(start); - } - return 0; +inline char* strnstr(const char* buffer, + const char* target, + size_t bufferLength) { + size_t targetLength = strlen(target); + if (targetLength == 0) + return const_cast(buffer); + for (const char* start = buffer; + *start && start + targetLength <= buffer + bufferLength; start++) { + if (*start == *target && + strncmp(start + 1, target + 1, targetLength - 1) == 0) + return const_cast(start); + } + return 0; } #endif diff --git a/sky/engine/wtf/StringExtrasTest.cpp b/sky/engine/wtf/StringExtrasTest.cpp index 5634b129e4bd2..b87170197a9c0 100644 --- a/sky/engine/wtf/StringExtrasTest.cpp +++ b/sky/engine/wtf/StringExtrasTest.cpp @@ -23,7 +23,6 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ - #include #include #include "flutter/sky/engine/wtf/StringExtras.h" @@ -32,105 +31,128 @@ namespace { -template struct PrintfFormatTrait { static const char format[]; }; +template +struct PrintfFormatTrait { + static const char format[]; +}; -template<> struct PrintfFormatTrait { static const char format[]; }; +template <> +struct PrintfFormatTrait { + static const char format[]; +}; const char PrintfFormatTrait::format[] = "%hd"; -template<> struct PrintfFormatTrait { static const char format[]; }; +template <> +struct PrintfFormatTrait { + static const char format[]; +}; const char PrintfFormatTrait::format[] = "%d"; -template<> struct PrintfFormatTrait { static const char format[]; }; +template <> +struct PrintfFormatTrait { + static const char format[]; +}; const char PrintfFormatTrait::format[] = "%ld"; -template<> struct PrintfFormatTrait { static const char format[]; }; +template <> +struct PrintfFormatTrait { + static const char format[]; +}; #if OS(WIN) const char PrintfFormatTrait::format[] = "%I64i"; #else const char PrintfFormatTrait::format[] = "%lli"; -#endif // OS(WIN) +#endif // OS(WIN) -template<> struct PrintfFormatTrait { static const char format[]; }; +template <> +struct PrintfFormatTrait { + static const char format[]; +}; const char PrintfFormatTrait::format[] = "%hu"; -template<> struct PrintfFormatTrait { static const char format[]; }; +template <> +struct PrintfFormatTrait { + static const char format[]; +}; const char PrintfFormatTrait::format[] = "%u"; -template<> struct PrintfFormatTrait { static const char format[]; }; +template <> +struct PrintfFormatTrait { + static const char format[]; +}; const char PrintfFormatTrait::format[] = "%lu"; -template<> struct PrintfFormatTrait { static const char format[]; }; +template <> +struct PrintfFormatTrait { + static const char format[]; +}; #if OS(WIN) const char PrintfFormatTrait::format[] = "%I64u"; #else const char PrintfFormatTrait::format[] = "%llu"; -#endif // OS(WIN) - +#endif // OS(WIN) // FIXME: use snprintf from StringExtras.h -template -void testBoundaries() -{ - const unsigned bufferSize = 256; - Vector buffer; - buffer.resize(bufferSize); - - const IntegerType min = std::numeric_limits::min(); - CString minStringData = String::number(min).latin1(); - snprintf(buffer.data(), bufferSize, PrintfFormatTrait::format, min); - EXPECT_STREQ(buffer.data(), minStringData.data()); - - const IntegerType max = std::numeric_limits::max(); - CString maxStringData = String::number(max).latin1(); - snprintf(buffer.data(), bufferSize, PrintfFormatTrait::format, max); - EXPECT_STREQ(buffer.data(), maxStringData.data()); +template +void testBoundaries() { + const unsigned bufferSize = 256; + Vector buffer; + buffer.resize(bufferSize); + + const IntegerType min = std::numeric_limits::min(); + CString minStringData = String::number(min).latin1(); + snprintf(buffer.data(), bufferSize, PrintfFormatTrait::format, + min); + EXPECT_STREQ(buffer.data(), minStringData.data()); + + const IntegerType max = std::numeric_limits::max(); + CString maxStringData = String::number(max).latin1(); + snprintf(buffer.data(), bufferSize, PrintfFormatTrait::format, + max); + EXPECT_STREQ(buffer.data(), maxStringData.data()); } -template -void testNumbers() -{ - const unsigned bufferSize = 256; - Vector buffer; - buffer.resize(bufferSize); - - for (int i = -100; i < 100; ++i) { - const IntegerType number = static_cast(i); - CString numberStringData = String::number(number).latin1(); - snprintf(buffer.data(), bufferSize, PrintfFormatTrait::format, number); - EXPECT_STREQ(buffer.data(), numberStringData.data()); - } +template +void testNumbers() { + const unsigned bufferSize = 256; + Vector buffer; + buffer.resize(bufferSize); + + for (int i = -100; i < 100; ++i) { + const IntegerType number = static_cast(i); + CString numberStringData = String::number(number).latin1(); + snprintf(buffer.data(), bufferSize, PrintfFormatTrait::format, + number); + EXPECT_STREQ(buffer.data(), numberStringData.data()); + } } -TEST(StringExtraTest, IntegerToStringConversionSignedIntegerBoundaries) -{ - testBoundaries(); - testBoundaries(); - testBoundaries(); - testBoundaries(); +TEST(StringExtraTest, IntegerToStringConversionSignedIntegerBoundaries) { + testBoundaries(); + testBoundaries(); + testBoundaries(); + testBoundaries(); } -TEST(StringExtraTest, IntegerToStringConversionSignedIntegerRegularNumbers) -{ - testNumbers(); - testNumbers(); - testNumbers(); - testNumbers(); +TEST(StringExtraTest, IntegerToStringConversionSignedIntegerRegularNumbers) { + testNumbers(); + testNumbers(); + testNumbers(); + testNumbers(); } -TEST(StringExtraTest, IntegerToStringConversionUnsignedIntegerBoundaries) -{ - testBoundaries(); - testBoundaries(); - testBoundaries(); - testBoundaries(); +TEST(StringExtraTest, IntegerToStringConversionUnsignedIntegerBoundaries) { + testBoundaries(); + testBoundaries(); + testBoundaries(); + testBoundaries(); } -TEST(StringExtraTest, IntegerToStringConversionUnsignedIntegerRegularNumbers) -{ - testNumbers(); - testNumbers(); - testNumbers(); - testNumbers(); +TEST(StringExtraTest, IntegerToStringConversionUnsignedIntegerRegularNumbers) { + testNumbers(); + testNumbers(); + testNumbers(); + testNumbers(); } -} // namespace +} // namespace diff --git a/sky/engine/wtf/StringHasher.h b/sky/engine/wtf/StringHasher.h index bd984c8d46061..c2d7158592f42 100644 --- a/sky/engine/wtf/StringHasher.h +++ b/sky/engine/wtf/StringHasher.h @@ -31,208 +31,197 @@ namespace WTF { // LChar data is interpreted as Latin-1-encoded (zero extended to 16 bits). -// NOTE: The hash computation here must stay in sync with the create_hash_table script in -// JavaScriptCore and the CodeGeneratorJS.pm script in WebCore. +// NOTE: The hash computation here must stay in sync with the create_hash_table +// script in JavaScriptCore and the CodeGeneratorJS.pm script in WebCore. -// Golden ratio. Arbitrary start value to avoid mapping all zeros to a hash value of zero. +// Golden ratio. Arbitrary start value to avoid mapping all zeros to a hash +// value of zero. static const unsigned stringHashingStartValue = 0x9E3779B9U; class StringHasher { -public: - static const unsigned flagCount = 8; // Save 8 bits for StringImpl to use as flags. - - StringHasher() - : m_hash(stringHashingStartValue) - , m_hasPendingCharacter(false) - , m_pendingCharacter(0) - { - } - - // The hasher hashes two characters at a time, and thus an "aligned" hasher is one - // where an even number of characters have been added. Callers that always add - // characters two at a time can use the "assuming aligned" functions. - void addCharactersAssumingAligned(UChar a, UChar b) - { - ASSERT(!m_hasPendingCharacter); - m_hash += a; - m_hash = (m_hash << 16) ^ ((b << 11) ^ m_hash); - m_hash += m_hash >> 11; - } - - void addCharacter(UChar character) - { - if (m_hasPendingCharacter) { - m_hasPendingCharacter = false; - addCharactersAssumingAligned(m_pendingCharacter, character); - return; - } - - m_pendingCharacter = character; - m_hasPendingCharacter = true; - } - - void addCharacters(UChar a, UChar b) - { - if (m_hasPendingCharacter) { + public: + static const unsigned flagCount = + 8; // Save 8 bits for StringImpl to use as flags. + + StringHasher() + : m_hash(stringHashingStartValue), + m_hasPendingCharacter(false), + m_pendingCharacter(0) {} + + // The hasher hashes two characters at a time, and thus an "aligned" hasher is + // one where an even number of characters have been added. Callers that always + // add characters two at a time can use the "assuming aligned" functions. + void addCharactersAssumingAligned(UChar a, UChar b) { + ASSERT(!m_hasPendingCharacter); + m_hash += a; + m_hash = (m_hash << 16) ^ ((b << 11) ^ m_hash); + m_hash += m_hash >> 11; + } + + void addCharacter(UChar character) { + if (m_hasPendingCharacter) { + m_hasPendingCharacter = false; + addCharactersAssumingAligned(m_pendingCharacter, character); + return; + } + + m_pendingCharacter = character; + m_hasPendingCharacter = true; + } + + void addCharacters(UChar a, UChar b) { + if (m_hasPendingCharacter) { #if ENABLE(ASSERT) - m_hasPendingCharacter = false; + m_hasPendingCharacter = false; #endif - addCharactersAssumingAligned(m_pendingCharacter, a); - m_pendingCharacter = b; + addCharactersAssumingAligned(m_pendingCharacter, a); + m_pendingCharacter = b; #if ENABLE(ASSERT) - m_hasPendingCharacter = true; + m_hasPendingCharacter = true; #endif - return; - } - - addCharactersAssumingAligned(a, b); - } - - template void addCharactersAssumingAligned(const T* data, unsigned length) - { - ASSERT(!m_hasPendingCharacter); - - bool remainder = length & 1; - length >>= 1; - - while (length--) { - addCharactersAssumingAligned(Converter(data[0]), Converter(data[1])); - data += 2; - } - - if (remainder) - addCharacter(Converter(*data)); - } - - template void addCharactersAssumingAligned(const T* data, unsigned length) - { - addCharactersAssumingAligned(data, length); - } - - template void addCharacters(const T* data, unsigned length) - { - if (m_hasPendingCharacter && length) { - m_hasPendingCharacter = false; - addCharactersAssumingAligned(m_pendingCharacter, Converter(*data++)); - --length; - } - addCharactersAssumingAligned(data, length); - } - - template void addCharacters(const T* data, unsigned length) - { - addCharacters(data, length); - } - - unsigned hashWithTop8BitsMasked() const - { - unsigned result = avalancheBits(); - - // Reserving space from the high bits for flags preserves most of the hash's - // value, since hash lookup typically masks out the high bits anyway. - result &= (1U << (sizeof(result) * 8 - flagCount)) - 1; - - // This avoids ever returning a hash code of 0, since that is used to - // signal "hash not computed yet". Setting the high bit maintains - // reasonable fidelity to a hash code of 0 because it is likely to yield - // exactly 0 when hash lookup masks out the high bits. - if (!result) - result = 0x80000000 >> flagCount; - - return result; - } - - unsigned hash() const - { - unsigned result = avalancheBits(); - - // This avoids ever returning a hash code of 0, since that is used to - // signal "hash not computed yet". Setting the high bit maintains - // reasonable fidelity to a hash code of 0 because it is likely to yield - // exactly 0 when hash lookup masks out the high bits. - if (!result) - result = 0x80000000; - - return result; - } - - template static unsigned computeHashAndMaskTop8Bits(const T* data, unsigned length) - { - StringHasher hasher; - hasher.addCharactersAssumingAligned(data, length); - return hasher.hashWithTop8BitsMasked(); - } - - template static unsigned computeHashAndMaskTop8Bits(const T* data, unsigned length) - { - return computeHashAndMaskTop8Bits(data, length); - } - - template static unsigned computeHash(const T* data, unsigned length) - { - StringHasher hasher; - hasher.addCharactersAssumingAligned(data, length); - return hasher.hash(); - } - - template static unsigned computeHash(const T* data, unsigned length) - { - return computeHash(data, length); - } - - static unsigned hashMemory(const void* data, unsigned length) - { - // FIXME: Why does this function use the version of the hash that drops the top 8 bits? - // We want that for all string hashing so we can use those bits in StringImpl and hash - // strings consistently, but I don't see why we'd want that for general memory hashing. - ASSERT(!(length % 2)); - return computeHashAndMaskTop8Bits(static_cast(data), length / sizeof(UChar)); - } - - template static unsigned hashMemory(const void* data) - { - COMPILE_ASSERT(!(length % 2), length_must_be_a_multiple_of_two); - return hashMemory(data, length); - } - -private: - static UChar defaultConverter(UChar character) - { - return character; - } - - static UChar defaultConverter(LChar character) - { - return character; - } - - unsigned avalancheBits() const - { - unsigned result = m_hash; - - // Handle end case. - if (m_hasPendingCharacter) { - result += m_pendingCharacter; - result ^= result << 11; - result += result >> 17; - } - - // Force "avalanching" of final 31 bits. - result ^= result << 3; - result += result >> 5; - result ^= result << 2; - result += result >> 15; - result ^= result << 10; - - return result; - } - - unsigned m_hash; - bool m_hasPendingCharacter; - UChar m_pendingCharacter; + return; + } + + addCharactersAssumingAligned(a, b); + } + + template + void addCharactersAssumingAligned(const T* data, unsigned length) { + ASSERT(!m_hasPendingCharacter); + + bool remainder = length & 1; + length >>= 1; + + while (length--) { + addCharactersAssumingAligned(Converter(data[0]), Converter(data[1])); + data += 2; + } + + if (remainder) + addCharacter(Converter(*data)); + } + + template + void addCharactersAssumingAligned(const T* data, unsigned length) { + addCharactersAssumingAligned(data, length); + } + + template + void addCharacters(const T* data, unsigned length) { + if (m_hasPendingCharacter && length) { + m_hasPendingCharacter = false; + addCharactersAssumingAligned(m_pendingCharacter, Converter(*data++)); + --length; + } + addCharactersAssumingAligned(data, length); + } + + template + void addCharacters(const T* data, unsigned length) { + addCharacters(data, length); + } + + unsigned hashWithTop8BitsMasked() const { + unsigned result = avalancheBits(); + + // Reserving space from the high bits for flags preserves most of the hash's + // value, since hash lookup typically masks out the high bits anyway. + result &= (1U << (sizeof(result) * 8 - flagCount)) - 1; + + // This avoids ever returning a hash code of 0, since that is used to + // signal "hash not computed yet". Setting the high bit maintains + // reasonable fidelity to a hash code of 0 because it is likely to yield + // exactly 0 when hash lookup masks out the high bits. + if (!result) + result = 0x80000000 >> flagCount; + + return result; + } + + unsigned hash() const { + unsigned result = avalancheBits(); + + // This avoids ever returning a hash code of 0, since that is used to + // signal "hash not computed yet". Setting the high bit maintains + // reasonable fidelity to a hash code of 0 because it is likely to yield + // exactly 0 when hash lookup masks out the high bits. + if (!result) + result = 0x80000000; + + return result; + } + + template + static unsigned computeHashAndMaskTop8Bits(const T* data, unsigned length) { + StringHasher hasher; + hasher.addCharactersAssumingAligned(data, length); + return hasher.hashWithTop8BitsMasked(); + } + + template + static unsigned computeHashAndMaskTop8Bits(const T* data, unsigned length) { + return computeHashAndMaskTop8Bits(data, length); + } + + template + static unsigned computeHash(const T* data, unsigned length) { + StringHasher hasher; + hasher.addCharactersAssumingAligned(data, length); + return hasher.hash(); + } + + template + static unsigned computeHash(const T* data, unsigned length) { + return computeHash(data, length); + } + + static unsigned hashMemory(const void* data, unsigned length) { + // FIXME: Why does this function use the version of the hash that drops the + // top 8 bits? We want that for all string hashing so we can use those bits + // in StringImpl and hash strings consistently, but I don't see why we'd + // want that for general memory hashing. + ASSERT(!(length % 2)); + return computeHashAndMaskTop8Bits(static_cast(data), + length / sizeof(UChar)); + } + + template + static unsigned hashMemory(const void* data) { + COMPILE_ASSERT(!(length % 2), length_must_be_a_multiple_of_two); + return hashMemory(data, length); + } + + private: + static UChar defaultConverter(UChar character) { return character; } + + static UChar defaultConverter(LChar character) { return character; } + + unsigned avalancheBits() const { + unsigned result = m_hash; + + // Handle end case. + if (m_hasPendingCharacter) { + result += m_pendingCharacter; + result ^= result << 11; + result += result >> 17; + } + + // Force "avalanching" of final 31 bits. + result ^= result << 3; + result += result >> 5; + result ^= result << 2; + result += result >> 15; + result ^= result << 10; + + return result; + } + + unsigned m_hash; + bool m_hasPendingCharacter; + UChar m_pendingCharacter; }; -} // namespace WTF +} // namespace WTF using WTF::StringHasher; diff --git a/sky/engine/wtf/StringHasherTest.cpp b/sky/engine/wtf/StringHasherTest.cpp index 715d8b46a4ab3..174d70f03495f 100644 --- a/sky/engine/wtf/StringHasherTest.cpp +++ b/sky/engine/wtf/StringHasherTest.cpp @@ -23,21 +23,20 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ - #include #include "flutter/sky/engine/wtf/StringHasher.h" namespace { -static const LChar nullLChars[2] = { 0, 0 }; -static const UChar nullUChars[2] = { 0, 0 }; +static const LChar nullLChars[2] = {0, 0}; +static const UChar nullUChars[2] = {0, 0}; static const unsigned emptyStringHash = 0x4EC889EU; static const unsigned singleNullCharacterHash = 0x3D3ABF44U; -static const LChar testALChars[6] = { 0x41, 0x95, 0xFF, 0x50, 0x01, 0 }; -static const UChar testAUChars[6] = { 0x41, 0x95, 0xFF, 0x50, 0x01, 0 }; -static const UChar testBUChars[6] = { 0x41, 0x95, 0xFFFF, 0x1080, 0x01, 0 }; +static const LChar testALChars[6] = {0x41, 0x95, 0xFF, 0x50, 0x01, 0}; +static const UChar testAUChars[6] = {0x41, 0x95, 0xFF, 0x50, 0x01, 0}; +static const UChar testBUChars[6] = {0x41, 0x95, 0xFFFF, 0x1080, 0x01, 0}; static const unsigned testAHash1 = 0xEA32B004; static const unsigned testAHash2 = 0x93F0F71E; @@ -51,358 +50,374 @@ static const unsigned testBHash3 = 0x59EB1B2C; static const unsigned testBHash4 = 0xA7BCCC0A; static const unsigned testBHash5 = 0x79201649; -TEST(StringHasherTest, StringHasher) -{ - StringHasher hasher; +TEST(StringHasherTest, StringHasher) { + StringHasher hasher; - // The initial state of the hasher. - EXPECT_EQ(emptyStringHash, hasher.hash()); - EXPECT_EQ(emptyStringHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + // The initial state of the hasher. + EXPECT_EQ(emptyStringHash, hasher.hash()); + EXPECT_EQ(emptyStringHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); } -TEST(StringHasherTest, StringHasher_addCharacter) -{ - StringHasher hasher; - - // Hashing a single character. - hasher = StringHasher(); - hasher.addCharacter(0); - EXPECT_EQ(singleNullCharacterHash, hasher.hash()); - EXPECT_EQ(singleNullCharacterHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - - // Hashing five characters, checking the intermediate state after each is added. - hasher = StringHasher(); - hasher.addCharacter(testAUChars[0]); - EXPECT_EQ(testAHash1, hasher.hash()); - EXPECT_EQ(testAHash1 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher.addCharacter(testAUChars[1]); - EXPECT_EQ(testAHash2, hasher.hash()); - EXPECT_EQ(testAHash2 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher.addCharacter(testAUChars[2]); - EXPECT_EQ(testAHash3, hasher.hash()); - EXPECT_EQ(testAHash3 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher.addCharacter(testAUChars[3]); - EXPECT_EQ(testAHash4, hasher.hash()); - EXPECT_EQ(testAHash4 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher.addCharacter(testAUChars[4]); - EXPECT_EQ(testAHash5, hasher.hash()); - EXPECT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - - // Hashing a second set of five characters, including non-Latin-1 characters. - hasher = StringHasher(); - hasher.addCharacter(testBUChars[0]); - EXPECT_EQ(testBHash1, hasher.hash()); - EXPECT_EQ(testBHash1 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher.addCharacter(testBUChars[1]); - EXPECT_EQ(testBHash2, hasher.hash()); - EXPECT_EQ(testBHash2 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher.addCharacter(testBUChars[2]); - EXPECT_EQ(testBHash3, hasher.hash()); - EXPECT_EQ(testBHash3 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher.addCharacter(testBUChars[3]); - EXPECT_EQ(testBHash4, hasher.hash()); - EXPECT_EQ(testBHash4 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher.addCharacter(testBUChars[4]); - EXPECT_EQ(testBHash5, hasher.hash()); - EXPECT_EQ(testBHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); +TEST(StringHasherTest, StringHasher_addCharacter) { + StringHasher hasher; + + // Hashing a single character. + hasher = StringHasher(); + hasher.addCharacter(0); + EXPECT_EQ(singleNullCharacterHash, hasher.hash()); + EXPECT_EQ(singleNullCharacterHash & 0xFFFFFF, + hasher.hashWithTop8BitsMasked()); + + // Hashing five characters, checking the intermediate state after each is + // added. + hasher = StringHasher(); + hasher.addCharacter(testAUChars[0]); + EXPECT_EQ(testAHash1, hasher.hash()); + EXPECT_EQ(testAHash1 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharacter(testAUChars[1]); + EXPECT_EQ(testAHash2, hasher.hash()); + EXPECT_EQ(testAHash2 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharacter(testAUChars[2]); + EXPECT_EQ(testAHash3, hasher.hash()); + EXPECT_EQ(testAHash3 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharacter(testAUChars[3]); + EXPECT_EQ(testAHash4, hasher.hash()); + EXPECT_EQ(testAHash4 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharacter(testAUChars[4]); + EXPECT_EQ(testAHash5, hasher.hash()); + EXPECT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + + // Hashing a second set of five characters, including non-Latin-1 characters. + hasher = StringHasher(); + hasher.addCharacter(testBUChars[0]); + EXPECT_EQ(testBHash1, hasher.hash()); + EXPECT_EQ(testBHash1 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharacter(testBUChars[1]); + EXPECT_EQ(testBHash2, hasher.hash()); + EXPECT_EQ(testBHash2 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharacter(testBUChars[2]); + EXPECT_EQ(testBHash3, hasher.hash()); + EXPECT_EQ(testBHash3 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharacter(testBUChars[3]); + EXPECT_EQ(testBHash4, hasher.hash()); + EXPECT_EQ(testBHash4 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharacter(testBUChars[4]); + EXPECT_EQ(testBHash5, hasher.hash()); + EXPECT_EQ(testBHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); } -TEST(StringHasherTest, StringHasher_addCharacters) -{ - StringHasher hasher; - - // Hashing zero characters. - hasher = StringHasher(); - hasher.addCharacters(static_cast(0), 0); - EXPECT_EQ(emptyStringHash, hasher.hash()); - EXPECT_EQ(emptyStringHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher = StringHasher(); - hasher.addCharacters(nullLChars, 0); - EXPECT_EQ(emptyStringHash, hasher.hash()); - EXPECT_EQ(emptyStringHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher = StringHasher(); - hasher.addCharacters(static_cast(0), 0); - EXPECT_EQ(emptyStringHash, hasher.hash()); - EXPECT_EQ(emptyStringHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher = StringHasher(); - hasher.addCharacters(nullUChars, 0); - EXPECT_EQ(emptyStringHash, hasher.hash()); - EXPECT_EQ(emptyStringHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - - // Hashing one character. - hasher = StringHasher(); - hasher.addCharacters(nullLChars, 1); - EXPECT_EQ(singleNullCharacterHash, hasher.hash()); - EXPECT_EQ(singleNullCharacterHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher = StringHasher(); - hasher.addCharacters(nullUChars, 1); - EXPECT_EQ(singleNullCharacterHash, hasher.hash()); - EXPECT_EQ(singleNullCharacterHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - - // Hashing five characters, all at once. - hasher = StringHasher(); - hasher.addCharacters(testALChars, 5); - EXPECT_EQ(testAHash5, hasher.hash()); - EXPECT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher = StringHasher(); - hasher.addCharacters(testAUChars, 5); - EXPECT_EQ(testAHash5, hasher.hash()); - EXPECT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher = StringHasher(); - hasher.addCharacters(testBUChars, 5); - EXPECT_EQ(testBHash5, hasher.hash()); - EXPECT_EQ(testBHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - - // Hashing five characters, in groups of two, then the last one. - hasher = StringHasher(); - hasher.addCharacters(testALChars, 2); - EXPECT_EQ(testAHash2, hasher.hash()); - EXPECT_EQ(testAHash2 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher.addCharacters(testALChars + 2, 2); - EXPECT_EQ(testAHash4, hasher.hash()); - EXPECT_EQ(testAHash4 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher.addCharacters(testALChars + 4, 1); - EXPECT_EQ(testAHash5, hasher.hash()); - EXPECT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher = StringHasher(); - hasher.addCharacters(testALChars, 2); - hasher.addCharacters(testALChars + 2, 2); - hasher.addCharacters(testALChars + 4, 1); - EXPECT_EQ(testAHash5, hasher.hash()); - EXPECT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher = StringHasher(); - hasher.addCharacters(testAUChars, 2); - EXPECT_EQ(testAHash2, hasher.hash()); - EXPECT_EQ(testAHash2 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher.addCharacters(testAUChars + 2, 2); - EXPECT_EQ(testAHash4, hasher.hash()); - EXPECT_EQ(testAHash4 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher.addCharacters(testAUChars + 4, 1); - EXPECT_EQ(testAHash5, hasher.hash()); - EXPECT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher = StringHasher(); - hasher.addCharacters(testAUChars, 2); - hasher.addCharacters(testAUChars + 2, 2); - hasher.addCharacters(testAUChars + 4, 1); - EXPECT_EQ(testAHash5, hasher.hash()); - EXPECT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher = StringHasher(); - hasher.addCharacters(testBUChars, 2); - EXPECT_EQ(testBHash2, hasher.hash()); - EXPECT_EQ(testBHash2 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher.addCharacters(testBUChars + 2, 2); - EXPECT_EQ(testBHash4, hasher.hash()); - EXPECT_EQ(testBHash4 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher.addCharacters(testBUChars + 4, 1); - EXPECT_EQ(testBHash5, hasher.hash()); - EXPECT_EQ(testBHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher = StringHasher(); - hasher.addCharacters(testBUChars, 2); - hasher.addCharacters(testBUChars + 2, 2); - hasher.addCharacters(testBUChars + 4, 1); - EXPECT_EQ(testBHash5, hasher.hash()); - EXPECT_EQ(testBHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - - // Hashing five characters, the first three, then the last two. - hasher = StringHasher(); - hasher.addCharacters(testALChars, 3); - EXPECT_EQ(testAHash3, hasher.hash()); - EXPECT_EQ(testAHash3 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher.addCharacters(testALChars + 3, 2); - EXPECT_EQ(testAHash5, hasher.hash()); - EXPECT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher = StringHasher(); - hasher.addCharacters(testALChars, 3); - EXPECT_EQ(testAHash3, hasher.hash()); - EXPECT_EQ(testAHash3 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher.addCharacters(testALChars + 3, 2); - EXPECT_EQ(testAHash5, hasher.hash()); - EXPECT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher = StringHasher(); - hasher.addCharacters(testAUChars, 3); - EXPECT_EQ(testAHash3, hasher.hash()); - EXPECT_EQ(testAHash3 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher.addCharacters(testAUChars + 3, 2); - EXPECT_EQ(testAHash5, hasher.hash()); - EXPECT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher = StringHasher(); - hasher.addCharacters(testAUChars, 3); - EXPECT_EQ(testAHash3, hasher.hash()); - EXPECT_EQ(testAHash3 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher.addCharacters(testAUChars + 3, 2); - EXPECT_EQ(testAHash5, hasher.hash()); - EXPECT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher = StringHasher(); - hasher.addCharacters(testBUChars, 3); - EXPECT_EQ(testBHash3, hasher.hash()); - EXPECT_EQ(testBHash3 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher.addCharacters(testBUChars + 3, 2); - EXPECT_EQ(testBHash5, hasher.hash()); - EXPECT_EQ(testBHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher = StringHasher(); - hasher.addCharacters(testBUChars, 3); - hasher.addCharacters(testBUChars + 3, 2); - EXPECT_EQ(testBHash5, hasher.hash()); - EXPECT_EQ(testBHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); +TEST(StringHasherTest, StringHasher_addCharacters) { + StringHasher hasher; + + // Hashing zero characters. + hasher = StringHasher(); + hasher.addCharacters(static_cast(0), 0); + EXPECT_EQ(emptyStringHash, hasher.hash()); + EXPECT_EQ(emptyStringHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharacters(nullLChars, 0); + EXPECT_EQ(emptyStringHash, hasher.hash()); + EXPECT_EQ(emptyStringHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharacters(static_cast(0), 0); + EXPECT_EQ(emptyStringHash, hasher.hash()); + EXPECT_EQ(emptyStringHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharacters(nullUChars, 0); + EXPECT_EQ(emptyStringHash, hasher.hash()); + EXPECT_EQ(emptyStringHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + + // Hashing one character. + hasher = StringHasher(); + hasher.addCharacters(nullLChars, 1); + EXPECT_EQ(singleNullCharacterHash, hasher.hash()); + EXPECT_EQ(singleNullCharacterHash & 0xFFFFFF, + hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharacters(nullUChars, 1); + EXPECT_EQ(singleNullCharacterHash, hasher.hash()); + EXPECT_EQ(singleNullCharacterHash & 0xFFFFFF, + hasher.hashWithTop8BitsMasked()); + + // Hashing five characters, all at once. + hasher = StringHasher(); + hasher.addCharacters(testALChars, 5); + EXPECT_EQ(testAHash5, hasher.hash()); + EXPECT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharacters(testAUChars, 5); + EXPECT_EQ(testAHash5, hasher.hash()); + EXPECT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharacters(testBUChars, 5); + EXPECT_EQ(testBHash5, hasher.hash()); + EXPECT_EQ(testBHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + + // Hashing five characters, in groups of two, then the last one. + hasher = StringHasher(); + hasher.addCharacters(testALChars, 2); + EXPECT_EQ(testAHash2, hasher.hash()); + EXPECT_EQ(testAHash2 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharacters(testALChars + 2, 2); + EXPECT_EQ(testAHash4, hasher.hash()); + EXPECT_EQ(testAHash4 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharacters(testALChars + 4, 1); + EXPECT_EQ(testAHash5, hasher.hash()); + EXPECT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharacters(testALChars, 2); + hasher.addCharacters(testALChars + 2, 2); + hasher.addCharacters(testALChars + 4, 1); + EXPECT_EQ(testAHash5, hasher.hash()); + EXPECT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharacters(testAUChars, 2); + EXPECT_EQ(testAHash2, hasher.hash()); + EXPECT_EQ(testAHash2 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharacters(testAUChars + 2, 2); + EXPECT_EQ(testAHash4, hasher.hash()); + EXPECT_EQ(testAHash4 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharacters(testAUChars + 4, 1); + EXPECT_EQ(testAHash5, hasher.hash()); + EXPECT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharacters(testAUChars, 2); + hasher.addCharacters(testAUChars + 2, 2); + hasher.addCharacters(testAUChars + 4, 1); + EXPECT_EQ(testAHash5, hasher.hash()); + EXPECT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharacters(testBUChars, 2); + EXPECT_EQ(testBHash2, hasher.hash()); + EXPECT_EQ(testBHash2 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharacters(testBUChars + 2, 2); + EXPECT_EQ(testBHash4, hasher.hash()); + EXPECT_EQ(testBHash4 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharacters(testBUChars + 4, 1); + EXPECT_EQ(testBHash5, hasher.hash()); + EXPECT_EQ(testBHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharacters(testBUChars, 2); + hasher.addCharacters(testBUChars + 2, 2); + hasher.addCharacters(testBUChars + 4, 1); + EXPECT_EQ(testBHash5, hasher.hash()); + EXPECT_EQ(testBHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + + // Hashing five characters, the first three, then the last two. + hasher = StringHasher(); + hasher.addCharacters(testALChars, 3); + EXPECT_EQ(testAHash3, hasher.hash()); + EXPECT_EQ(testAHash3 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharacters(testALChars + 3, 2); + EXPECT_EQ(testAHash5, hasher.hash()); + EXPECT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharacters(testALChars, 3); + EXPECT_EQ(testAHash3, hasher.hash()); + EXPECT_EQ(testAHash3 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharacters(testALChars + 3, 2); + EXPECT_EQ(testAHash5, hasher.hash()); + EXPECT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharacters(testAUChars, 3); + EXPECT_EQ(testAHash3, hasher.hash()); + EXPECT_EQ(testAHash3 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharacters(testAUChars + 3, 2); + EXPECT_EQ(testAHash5, hasher.hash()); + EXPECT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharacters(testAUChars, 3); + EXPECT_EQ(testAHash3, hasher.hash()); + EXPECT_EQ(testAHash3 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharacters(testAUChars + 3, 2); + EXPECT_EQ(testAHash5, hasher.hash()); + EXPECT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharacters(testBUChars, 3); + EXPECT_EQ(testBHash3, hasher.hash()); + EXPECT_EQ(testBHash3 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharacters(testBUChars + 3, 2); + EXPECT_EQ(testBHash5, hasher.hash()); + EXPECT_EQ(testBHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharacters(testBUChars, 3); + hasher.addCharacters(testBUChars + 3, 2); + EXPECT_EQ(testBHash5, hasher.hash()); + EXPECT_EQ(testBHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); } -TEST(StringHasherTest, StringHasher_addCharactersAssumingAligned) -{ - StringHasher hasher; - - // Hashing zero characters. - hasher = StringHasher(); - hasher.addCharactersAssumingAligned(static_cast(0), 0); - EXPECT_EQ(emptyStringHash, hasher.hash()); - EXPECT_EQ(emptyStringHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher = StringHasher(); - hasher.addCharactersAssumingAligned(nullLChars, 0); - EXPECT_EQ(emptyStringHash, hasher.hash()); - EXPECT_EQ(emptyStringHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher = StringHasher(); - hasher.addCharactersAssumingAligned(static_cast(0), 0); - EXPECT_EQ(emptyStringHash, hasher.hash()); - EXPECT_EQ(emptyStringHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher = StringHasher(); - hasher.addCharactersAssumingAligned(nullUChars, 0); - EXPECT_EQ(emptyStringHash, hasher.hash()); - EXPECT_EQ(emptyStringHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - - // Hashing one character. - hasher = StringHasher(); - hasher.addCharactersAssumingAligned(nullLChars, 1); - EXPECT_EQ(singleNullCharacterHash, hasher.hash()); - EXPECT_EQ(singleNullCharacterHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher = StringHasher(); - hasher.addCharactersAssumingAligned(nullUChars, 1); - EXPECT_EQ(singleNullCharacterHash, hasher.hash()); - EXPECT_EQ(singleNullCharacterHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - - // Hashing five characters, all at once. - hasher = StringHasher(); - hasher.addCharactersAssumingAligned(testALChars, 5); - EXPECT_EQ(testAHash5, hasher.hash()); - EXPECT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher = StringHasher(); - hasher.addCharactersAssumingAligned(testAUChars, 5); - EXPECT_EQ(testAHash5, hasher.hash()); - EXPECT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher = StringHasher(); - hasher.addCharactersAssumingAligned(testBUChars, 5); - EXPECT_EQ(testBHash5, hasher.hash()); - EXPECT_EQ(testBHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - - // Hashing five characters, in groups of two, then the last one. - hasher = StringHasher(); - hasher.addCharactersAssumingAligned(testALChars, 2); - EXPECT_EQ(testAHash2, hasher.hash()); - EXPECT_EQ(testAHash2 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher.addCharactersAssumingAligned(testALChars + 2, 2); - EXPECT_EQ(testAHash4, hasher.hash()); - EXPECT_EQ(testAHash4 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher.addCharactersAssumingAligned(testALChars + 4, 1); - EXPECT_EQ(testAHash5, hasher.hash()); - EXPECT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher = StringHasher(); - hasher.addCharactersAssumingAligned(testALChars, 2); - hasher.addCharactersAssumingAligned(testALChars + 2, 2); - hasher.addCharactersAssumingAligned(testALChars + 4, 1); - EXPECT_EQ(testAHash5, hasher.hash()); - EXPECT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher = StringHasher(); - hasher.addCharactersAssumingAligned(testAUChars, 2); - EXPECT_EQ(testAHash2, hasher.hash()); - EXPECT_EQ(testAHash2 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher.addCharactersAssumingAligned(testAUChars + 2, 2); - EXPECT_EQ(testAHash4, hasher.hash()); - EXPECT_EQ(testAHash4 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher.addCharactersAssumingAligned(testAUChars + 4, 1); - EXPECT_EQ(testAHash5, hasher.hash()); - EXPECT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher = StringHasher(); - hasher.addCharactersAssumingAligned(testAUChars, 2); - hasher.addCharactersAssumingAligned(testAUChars + 2, 2); - hasher.addCharactersAssumingAligned(testAUChars + 4, 1); - EXPECT_EQ(testAHash5, hasher.hash()); - EXPECT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher = StringHasher(); - hasher.addCharactersAssumingAligned(testBUChars, 2); - EXPECT_EQ(testBHash2, hasher.hash()); - EXPECT_EQ(testBHash2 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher.addCharactersAssumingAligned(testBUChars + 2, 2); - EXPECT_EQ(testBHash4, hasher.hash()); - EXPECT_EQ(testBHash4 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher.addCharactersAssumingAligned(testBUChars + 4, 1); - EXPECT_EQ(testBHash5, hasher.hash()); - EXPECT_EQ(testBHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher = StringHasher(); - hasher.addCharactersAssumingAligned(testBUChars, 2); - hasher.addCharactersAssumingAligned(testBUChars + 2, 2); - hasher.addCharactersAssumingAligned(testBUChars + 4, 1); - EXPECT_EQ(testBHash5, hasher.hash()); - EXPECT_EQ(testBHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - - // Hashing five characters, first two characters one at a time, - // then two more, then the last one. - hasher = StringHasher(); - hasher.addCharacter(testBUChars[0]); - EXPECT_EQ(testBHash1, hasher.hash()); - EXPECT_EQ(testBHash1 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher.addCharacter(testBUChars[1]); - EXPECT_EQ(testBHash2, hasher.hash()); - EXPECT_EQ(testBHash2 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher.addCharactersAssumingAligned(testBUChars[2], testBUChars[3]); - EXPECT_EQ(testBHash4, hasher.hash()); - EXPECT_EQ(testBHash4 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); - hasher.addCharactersAssumingAligned(testBUChars + 4, 1); - EXPECT_EQ(testBHash5, hasher.hash()); - EXPECT_EQ(testBHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); +TEST(StringHasherTest, StringHasher_addCharactersAssumingAligned) { + StringHasher hasher; + + // Hashing zero characters. + hasher = StringHasher(); + hasher.addCharactersAssumingAligned(static_cast(0), 0); + EXPECT_EQ(emptyStringHash, hasher.hash()); + EXPECT_EQ(emptyStringHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharactersAssumingAligned(nullLChars, 0); + EXPECT_EQ(emptyStringHash, hasher.hash()); + EXPECT_EQ(emptyStringHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharactersAssumingAligned(static_cast(0), 0); + EXPECT_EQ(emptyStringHash, hasher.hash()); + EXPECT_EQ(emptyStringHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharactersAssumingAligned(nullUChars, 0); + EXPECT_EQ(emptyStringHash, hasher.hash()); + EXPECT_EQ(emptyStringHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + + // Hashing one character. + hasher = StringHasher(); + hasher.addCharactersAssumingAligned(nullLChars, 1); + EXPECT_EQ(singleNullCharacterHash, hasher.hash()); + EXPECT_EQ(singleNullCharacterHash & 0xFFFFFF, + hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharactersAssumingAligned(nullUChars, 1); + EXPECT_EQ(singleNullCharacterHash, hasher.hash()); + EXPECT_EQ(singleNullCharacterHash & 0xFFFFFF, + hasher.hashWithTop8BitsMasked()); + + // Hashing five characters, all at once. + hasher = StringHasher(); + hasher.addCharactersAssumingAligned(testALChars, 5); + EXPECT_EQ(testAHash5, hasher.hash()); + EXPECT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharactersAssumingAligned(testAUChars, 5); + EXPECT_EQ(testAHash5, hasher.hash()); + EXPECT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharactersAssumingAligned(testBUChars, 5); + EXPECT_EQ(testBHash5, hasher.hash()); + EXPECT_EQ(testBHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + + // Hashing five characters, in groups of two, then the last one. + hasher = StringHasher(); + hasher.addCharactersAssumingAligned(testALChars, 2); + EXPECT_EQ(testAHash2, hasher.hash()); + EXPECT_EQ(testAHash2 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharactersAssumingAligned(testALChars + 2, 2); + EXPECT_EQ(testAHash4, hasher.hash()); + EXPECT_EQ(testAHash4 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharactersAssumingAligned(testALChars + 4, 1); + EXPECT_EQ(testAHash5, hasher.hash()); + EXPECT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharactersAssumingAligned(testALChars, 2); + hasher.addCharactersAssumingAligned(testALChars + 2, 2); + hasher.addCharactersAssumingAligned(testALChars + 4, 1); + EXPECT_EQ(testAHash5, hasher.hash()); + EXPECT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharactersAssumingAligned(testAUChars, 2); + EXPECT_EQ(testAHash2, hasher.hash()); + EXPECT_EQ(testAHash2 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharactersAssumingAligned(testAUChars + 2, 2); + EXPECT_EQ(testAHash4, hasher.hash()); + EXPECT_EQ(testAHash4 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharactersAssumingAligned(testAUChars + 4, 1); + EXPECT_EQ(testAHash5, hasher.hash()); + EXPECT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharactersAssumingAligned(testAUChars, 2); + hasher.addCharactersAssumingAligned(testAUChars + 2, 2); + hasher.addCharactersAssumingAligned(testAUChars + 4, 1); + EXPECT_EQ(testAHash5, hasher.hash()); + EXPECT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharactersAssumingAligned(testBUChars, 2); + EXPECT_EQ(testBHash2, hasher.hash()); + EXPECT_EQ(testBHash2 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharactersAssumingAligned(testBUChars + 2, 2); + EXPECT_EQ(testBHash4, hasher.hash()); + EXPECT_EQ(testBHash4 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharactersAssumingAligned(testBUChars + 4, 1); + EXPECT_EQ(testBHash5, hasher.hash()); + EXPECT_EQ(testBHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharactersAssumingAligned(testBUChars, 2); + hasher.addCharactersAssumingAligned(testBUChars + 2, 2); + hasher.addCharactersAssumingAligned(testBUChars + 4, 1); + EXPECT_EQ(testBHash5, hasher.hash()); + EXPECT_EQ(testBHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + + // Hashing five characters, first two characters one at a time, + // then two more, then the last one. + hasher = StringHasher(); + hasher.addCharacter(testBUChars[0]); + EXPECT_EQ(testBHash1, hasher.hash()); + EXPECT_EQ(testBHash1 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharacter(testBUChars[1]); + EXPECT_EQ(testBHash2, hasher.hash()); + EXPECT_EQ(testBHash2 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharactersAssumingAligned(testBUChars[2], testBUChars[3]); + EXPECT_EQ(testBHash4, hasher.hash()); + EXPECT_EQ(testBHash4 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharactersAssumingAligned(testBUChars + 4, 1); + EXPECT_EQ(testBHash5, hasher.hash()); + EXPECT_EQ(testBHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); } -TEST(StringHasherTest, StringHasher_computeHash) -{ - EXPECT_EQ(emptyStringHash, StringHasher::computeHash(static_cast(0), 0)); - EXPECT_EQ(emptyStringHash, StringHasher::computeHash(nullLChars, 0)); - EXPECT_EQ(emptyStringHash, StringHasher::computeHash(static_cast(0), 0)); - EXPECT_EQ(emptyStringHash, StringHasher::computeHash(nullUChars, 0)); +TEST(StringHasherTest, StringHasher_computeHash) { + EXPECT_EQ(emptyStringHash, + StringHasher::computeHash(static_cast(0), 0)); + EXPECT_EQ(emptyStringHash, StringHasher::computeHash(nullLChars, 0)); + EXPECT_EQ(emptyStringHash, + StringHasher::computeHash(static_cast(0), 0)); + EXPECT_EQ(emptyStringHash, StringHasher::computeHash(nullUChars, 0)); - EXPECT_EQ(singleNullCharacterHash, StringHasher::computeHash(nullLChars, 1)); - EXPECT_EQ(singleNullCharacterHash, StringHasher::computeHash(nullUChars, 1)); + EXPECT_EQ(singleNullCharacterHash, StringHasher::computeHash(nullLChars, 1)); + EXPECT_EQ(singleNullCharacterHash, StringHasher::computeHash(nullUChars, 1)); - EXPECT_EQ(testAHash5, StringHasher::computeHash(testALChars, 5)); - EXPECT_EQ(testAHash5, StringHasher::computeHash(testAUChars, 5)); - EXPECT_EQ(testBHash5, StringHasher::computeHash(testBUChars, 5)); + EXPECT_EQ(testAHash5, StringHasher::computeHash(testALChars, 5)); + EXPECT_EQ(testAHash5, StringHasher::computeHash(testAUChars, 5)); + EXPECT_EQ(testBHash5, StringHasher::computeHash(testBUChars, 5)); } -TEST(StringHasherTest, StringHasher_computeHashAndMaskTop8Bits) -{ - EXPECT_EQ(emptyStringHash & 0xFFFFFF, StringHasher::computeHashAndMaskTop8Bits(static_cast(0), 0)); - EXPECT_EQ(emptyStringHash & 0xFFFFFF, StringHasher::computeHashAndMaskTop8Bits(nullLChars, 0)); - EXPECT_EQ(emptyStringHash & 0xFFFFFF, StringHasher::computeHashAndMaskTop8Bits(static_cast(0), 0)); - EXPECT_EQ(emptyStringHash & 0xFFFFFF, StringHasher::computeHashAndMaskTop8Bits(nullUChars, 0)); - - EXPECT_EQ(singleNullCharacterHash & 0xFFFFFF, StringHasher::computeHashAndMaskTop8Bits(nullLChars, 1)); - EXPECT_EQ(singleNullCharacterHash & 0xFFFFFF, StringHasher::computeHashAndMaskTop8Bits(nullUChars, 1)); - - EXPECT_EQ(testAHash5 & 0xFFFFFF, StringHasher::computeHashAndMaskTop8Bits(testALChars, 5)); - EXPECT_EQ(testAHash5 & 0xFFFFFF, StringHasher::computeHashAndMaskTop8Bits(testAUChars, 5)); - EXPECT_EQ(testBHash5 & 0xFFFFFF, StringHasher::computeHashAndMaskTop8Bits(testBUChars, 5)); +TEST(StringHasherTest, StringHasher_computeHashAndMaskTop8Bits) { + EXPECT_EQ( + emptyStringHash & 0xFFFFFF, + StringHasher::computeHashAndMaskTop8Bits(static_cast(0), 0)); + EXPECT_EQ(emptyStringHash & 0xFFFFFF, + StringHasher::computeHashAndMaskTop8Bits(nullLChars, 0)); + EXPECT_EQ( + emptyStringHash & 0xFFFFFF, + StringHasher::computeHashAndMaskTop8Bits(static_cast(0), 0)); + EXPECT_EQ(emptyStringHash & 0xFFFFFF, + StringHasher::computeHashAndMaskTop8Bits(nullUChars, 0)); + + EXPECT_EQ(singleNullCharacterHash & 0xFFFFFF, + StringHasher::computeHashAndMaskTop8Bits(nullLChars, 1)); + EXPECT_EQ(singleNullCharacterHash & 0xFFFFFF, + StringHasher::computeHashAndMaskTop8Bits(nullUChars, 1)); + + EXPECT_EQ(testAHash5 & 0xFFFFFF, + StringHasher::computeHashAndMaskTop8Bits(testALChars, 5)); + EXPECT_EQ(testAHash5 & 0xFFFFFF, + StringHasher::computeHashAndMaskTop8Bits(testAUChars, 5)); + EXPECT_EQ(testBHash5 & 0xFFFFFF, + StringHasher::computeHashAndMaskTop8Bits(testBUChars, 5)); } -TEST(StringHasherTest, StringHasher_hashMemory) -{ - EXPECT_EQ(emptyStringHash & 0xFFFFFF, StringHasher::hashMemory(0, 0)); - EXPECT_EQ(emptyStringHash & 0xFFFFFF, StringHasher::hashMemory(nullUChars, 0)); - EXPECT_EQ(emptyStringHash & 0xFFFFFF, StringHasher::hashMemory<0>(0)); - EXPECT_EQ(emptyStringHash & 0xFFFFFF, StringHasher::hashMemory<0>(nullUChars)); - - EXPECT_EQ(singleNullCharacterHash & 0xFFFFFF, StringHasher::hashMemory(nullUChars, 2)); - EXPECT_EQ(singleNullCharacterHash & 0xFFFFFF, StringHasher::hashMemory<2>(nullUChars)); - - EXPECT_EQ(testAHash5 & 0xFFFFFF, StringHasher::hashMemory(testAUChars, 10)); - EXPECT_EQ(testAHash5 & 0xFFFFFF, StringHasher::hashMemory<10>(testAUChars)); - EXPECT_EQ(testBHash5 & 0xFFFFFF, StringHasher::hashMemory(testBUChars, 10)); - EXPECT_EQ(testBHash5 & 0xFFFFFF, StringHasher::hashMemory<10>(testBUChars)); +TEST(StringHasherTest, StringHasher_hashMemory) { + EXPECT_EQ(emptyStringHash & 0xFFFFFF, StringHasher::hashMemory(0, 0)); + EXPECT_EQ(emptyStringHash & 0xFFFFFF, + StringHasher::hashMemory(nullUChars, 0)); + EXPECT_EQ(emptyStringHash & 0xFFFFFF, StringHasher::hashMemory<0>(0)); + EXPECT_EQ(emptyStringHash & 0xFFFFFF, + StringHasher::hashMemory<0>(nullUChars)); + + EXPECT_EQ(singleNullCharacterHash & 0xFFFFFF, + StringHasher::hashMemory(nullUChars, 2)); + EXPECT_EQ(singleNullCharacterHash & 0xFFFFFF, + StringHasher::hashMemory<2>(nullUChars)); + + EXPECT_EQ(testAHash5 & 0xFFFFFF, StringHasher::hashMemory(testAUChars, 10)); + EXPECT_EQ(testAHash5 & 0xFFFFFF, StringHasher::hashMemory<10>(testAUChars)); + EXPECT_EQ(testBHash5 & 0xFFFFFF, StringHasher::hashMemory(testBUChars, 10)); + EXPECT_EQ(testBHash5 & 0xFFFFFF, StringHasher::hashMemory<10>(testBUChars)); } -} // namespace +} // namespace diff --git a/sky/engine/wtf/TemporaryChange.h b/sky/engine/wtf/TemporaryChange.h index 91c98aef6f398..75ace2fa528bf 100644 --- a/sky/engine/wtf/TemporaryChange.h +++ b/sky/engine/wtf/TemporaryChange.h @@ -30,38 +30,33 @@ namespace WTF { -// TemporaryChange<> is useful for setting a variable to a new value only within a -// particular scope. An TemporaryChange<> object changes a variable to its original -// value upon destruction, making it an alternative to writing "var = false;" -// or "var = oldVal;" at all of a block's exit points. +// TemporaryChange<> is useful for setting a variable to a new value only within +// a particular scope. An TemporaryChange<> object changes a variable to its +// original value upon destruction, making it an alternative to writing "var = +// false;" or "var = oldVal;" at all of a block's exit points. // -// This should be obvious, but note that an TemporaryChange<> instance should have a -// shorter lifetime than its scopedVariable, to prevent invalid memory writes -// when the TemporaryChange<> object is destroyed. +// This should be obvious, but note that an TemporaryChange<> instance should +// have a shorter lifetime than its scopedVariable, to prevent invalid memory +// writes when the TemporaryChange<> object is destroyed. -template +template class TemporaryChange { - WTF_MAKE_NONCOPYABLE(TemporaryChange); -public: - TemporaryChange(T& scopedVariable, T newValue) - : m_scopedVariable(scopedVariable) - , m_originalValue(scopedVariable) - { - m_scopedVariable = newValue; - } + WTF_MAKE_NONCOPYABLE(TemporaryChange); - ~TemporaryChange() - { - m_scopedVariable = m_originalValue; - } + public: + TemporaryChange(T& scopedVariable, T newValue) + : m_scopedVariable(scopedVariable), m_originalValue(scopedVariable) { + m_scopedVariable = newValue; + } + ~TemporaryChange() { m_scopedVariable = m_originalValue; } -private: - T& m_scopedVariable; - T m_originalValue; + private: + T& m_scopedVariable; + T m_originalValue; }; -} +} // namespace WTF using WTF::TemporaryChange; diff --git a/sky/engine/wtf/TemporaryChangeTest.cpp b/sky/engine/wtf/TemporaryChangeTest.cpp index 623823d304624..9dc17d58f0a77 100644 --- a/sky/engine/wtf/TemporaryChangeTest.cpp +++ b/sky/engine/wtf/TemporaryChangeTest.cpp @@ -23,25 +23,23 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ - #include #include "flutter/sky/engine/wtf/TemporaryChange.h" namespace { -TEST(TemporaryChangeTest, Nested) -{ - bool originallyFalse = false; +TEST(TemporaryChangeTest, Nested) { + bool originallyFalse = false; + { + TemporaryChange change1OriginallyFalse(originallyFalse, true); + EXPECT_TRUE(originallyFalse); { - TemporaryChange change1OriginallyFalse(originallyFalse, true); - EXPECT_TRUE(originallyFalse); - { - TemporaryChange change2OriginallyFalse(originallyFalse, false); - EXPECT_FALSE(originallyFalse); - } - EXPECT_TRUE(originallyFalse); + TemporaryChange change2OriginallyFalse(originallyFalse, false); + EXPECT_FALSE(originallyFalse); } - EXPECT_FALSE(originallyFalse); + EXPECT_TRUE(originallyFalse); + } + EXPECT_FALSE(originallyFalse); } -} // namespace +} // namespace diff --git a/sky/engine/wtf/ThreadIdentifierDataPthreads.cpp b/sky/engine/wtf/ThreadIdentifierDataPthreads.cpp index 6e5b67e577880..d35e0b283dc6d 100644 --- a/sky/engine/wtf/ThreadIdentifierDataPthreads.cpp +++ b/sky/engine/wtf/ThreadIdentifierDataPthreads.cpp @@ -37,10 +37,11 @@ #if USE(PTHREADS) -// PTHREAD_KEYS_MAX might be not defined (e.g. in certain bionic versions), so explicitly define it here. +// PTHREAD_KEYS_MAX might be not defined (e.g. in certain bionic versions), so +// explicitly define it here. #ifndef PTHREAD_KEYS_MAX #define PTHREAD_KEYS_MAX 1024 -#endif // PTHREAD_KEYS_MAX +#endif // PTHREAD_KEYS_MAX namespace WTF { @@ -48,49 +49,47 @@ pthread_key_t ThreadIdentifierData::m_key = PTHREAD_KEYS_MAX; void threadDidExit(ThreadIdentifier); -ThreadIdentifierData::~ThreadIdentifierData() -{ - threadDidExit(m_identifier); +ThreadIdentifierData::~ThreadIdentifierData() { + threadDidExit(m_identifier); } -void ThreadIdentifierData::initializeOnce() -{ - if (pthread_key_create(&m_key, destruct)) - CRASH(); +void ThreadIdentifierData::initializeOnce() { + if (pthread_key_create(&m_key, destruct)) + CRASH(); } -ThreadIdentifier ThreadIdentifierData::identifier() -{ - ASSERT(m_key != PTHREAD_KEYS_MAX); - ThreadIdentifierData* threadIdentifierData = static_cast(pthread_getspecific(m_key)); +ThreadIdentifier ThreadIdentifierData::identifier() { + ASSERT(m_key != PTHREAD_KEYS_MAX); + ThreadIdentifierData* threadIdentifierData = + static_cast(pthread_getspecific(m_key)); - return threadIdentifierData ? threadIdentifierData->m_identifier : 0; + return threadIdentifierData ? threadIdentifierData->m_identifier : 0; } -void ThreadIdentifierData::initialize(ThreadIdentifier id) -{ - ASSERT(!identifier()); - pthread_setspecific(m_key, new ThreadIdentifierData(id)); +void ThreadIdentifierData::initialize(ThreadIdentifier id) { + ASSERT(!identifier()); + pthread_setspecific(m_key, new ThreadIdentifierData(id)); } -void ThreadIdentifierData::destruct(void* data) -{ - if (isShutdown()) - return; +void ThreadIdentifierData::destruct(void* data) { + if (isShutdown()) + return; - ThreadIdentifierData* threadIdentifierData = static_cast(data); - ASSERT(threadIdentifierData); + ThreadIdentifierData* threadIdentifierData = + static_cast(data); + ASSERT(threadIdentifierData); - if (threadIdentifierData->m_isDestroyedOnce) { - delete threadIdentifierData; - return; - } + if (threadIdentifierData->m_isDestroyedOnce) { + delete threadIdentifierData; + return; + } - threadIdentifierData->m_isDestroyedOnce = true; - // Re-setting the value for key causes another destruct() call after all other thread-specific destructors were called. - pthread_setspecific(m_key, threadIdentifierData); + threadIdentifierData->m_isDestroyedOnce = true; + // Re-setting the value for key causes another destruct() call after all other + // thread-specific destructors were called. + pthread_setspecific(m_key, threadIdentifierData); } -} // namespace WTF +} // namespace WTF -#endif // USE(PTHREADS) +#endif // USE(PTHREADS) diff --git a/sky/engine/wtf/ThreadIdentifierDataPthreads.h b/sky/engine/wtf/ThreadIdentifierDataPthreads.h index 2b66994876cd6..2125b10e714fc 100644 --- a/sky/engine/wtf/ThreadIdentifierDataPthreads.h +++ b/sky/engine/wtf/ThreadIdentifierDataPthreads.h @@ -37,44 +37,45 @@ namespace WTF { -// Holds ThreadIdentifier in the thread-specific storage and employs pthreads-specific 2-pass destruction to reliably remove -// ThreadIdentifier from threadMap. It assumes regular ThreadSpecific types don't use multiple-pass destruction. +// Holds ThreadIdentifier in the thread-specific storage and employs +// pthreads-specific 2-pass destruction to reliably remove ThreadIdentifier from +// threadMap. It assumes regular ThreadSpecific types don't use multiple-pass +// destruction. class ThreadIdentifierData { - WTF_MAKE_NONCOPYABLE(ThreadIdentifierData); -public: - ~ThreadIdentifierData(); + WTF_MAKE_NONCOPYABLE(ThreadIdentifierData); - // One time initialization for this class as a whole. - // This method must be called before initialize() and it is not thread-safe. - static void initializeOnce(); + public: + ~ThreadIdentifierData(); - // Creates and puts an instance of ThreadIdentifierData into thread-specific storage. - static void initialize(ThreadIdentifier identifier); + // One time initialization for this class as a whole. + // This method must be called before initialize() and it is not thread-safe. + static void initializeOnce(); - // Returns 0 if thread-specific storage was not initialized. - static ThreadIdentifier identifier(); + // Creates and puts an instance of ThreadIdentifierData into thread-specific + // storage. + static void initialize(ThreadIdentifier identifier); -private: - ThreadIdentifierData(ThreadIdentifier identifier) - : m_identifier(identifier) - , m_isDestroyedOnce(false) - { - } + // Returns 0 if thread-specific storage was not initialized. + static ThreadIdentifier identifier(); - // This thread-specific destructor is called 2 times when thread terminates: - // - first, when all the other thread-specific destructors are called, it simply remembers it was 'destroyed once' - // and re-sets itself into the thread-specific slot to make Pthreads to call it again later. - // - second, after all thread-specific destructors were invoked, it gets called again - this time, we remove the - // ThreadIdentifier from the threadMap, completing the cleanup. - static void destruct(void* data); + private: + ThreadIdentifierData(ThreadIdentifier identifier) + : m_identifier(identifier), m_isDestroyedOnce(false) {} - ThreadIdentifier m_identifier; - bool m_isDestroyedOnce; - static pthread_key_t m_key; + // This thread-specific destructor is called 2 times when thread terminates: + // - first, when all the other thread-specific destructors are called, it + // simply remembers it was 'destroyed once' and re-sets itself into the + // thread-specific slot to make Pthreads to call it again later. + // - second, after all thread-specific destructors were invoked, it gets + // called again - this time, we remove the ThreadIdentifier from the + // threadMap, completing the cleanup. + static void destruct(void* data); + + ThreadIdentifier m_identifier; + bool m_isDestroyedOnce; + static pthread_key_t m_key; }; -} // namespace WTF +} // namespace WTF #endif // SKY_ENGINE_WTF_THREADIDENTIFIERDATAPTHREADS_H_ - - diff --git a/sky/engine/wtf/ThreadRestrictionVerifier.h b/sky/engine/wtf/ThreadRestrictionVerifier.h index c169a82268d3c..135347d210371 100644 --- a/sky/engine/wtf/ThreadRestrictionVerifier.h +++ b/sky/engine/wtf/ThreadRestrictionVerifier.h @@ -39,49 +39,45 @@ namespace WTF { -// Verifies that a class is used in a way that respects its lack of thread-safety. -// The default mode is to verify that the object will only be used on a single thread. The -// thread gets captured when setShared(true) is called. -// The mode may be changed by calling useMutexMode (or turnOffVerification). +// Verifies that a class is used in a way that respects its lack of +// thread-safety. The default mode is to verify that the object will only be +// used on a single thread. The thread gets captured when setShared(true) is +// called. The mode may be changed by calling useMutexMode (or +// turnOffVerification). class ThreadRestrictionVerifier { -public: - ThreadRestrictionVerifier() - : m_shared(false) - , m_owningThread(0) - { - } + public: + ThreadRestrictionVerifier() : m_shared(false), m_owningThread(0) {} - // Indicates that the object may (or may not) be owned by more than one place. - void setShared(bool shared) - { - bool previouslyShared = m_shared; - m_shared = shared; + // Indicates that the object may (or may not) be owned by more than one place. + void setShared(bool shared) { + bool previouslyShared = m_shared; + m_shared = shared; - if (!m_shared) - return; + if (!m_shared) + return; - ASSERT(shared != previouslyShared); - // Capture the current thread to verify that subsequent ref/deref happen on this thread. - m_owningThread = currentThread(); - } + ASSERT(shared != previouslyShared); + // Capture the current thread to verify that subsequent ref/deref happen on + // this thread. + m_owningThread = currentThread(); + } - // Is it OK to use the object at this moment on the current thread? - bool isSafeToUse() const - { - if (!m_shared) - return true; + // Is it OK to use the object at this moment on the current thread? + bool isSafeToUse() const { + if (!m_shared) + return true; - return m_owningThread == currentThread(); - } + return m_owningThread == currentThread(); + } -private: - bool m_shared; + private: + bool m_shared; - // Used by SingleThreadVerificationMode - ThreadIdentifier m_owningThread; + // Used by SingleThreadVerificationMode + ThreadIdentifier m_owningThread; }; -} +} // namespace WTF -#endif // ENABLE(ASSERT) +#endif // ENABLE(ASSERT) #endif // SKY_ENGINE_WTF_THREADRESTRICTIONVERIFIER_H_ diff --git a/sky/engine/wtf/ThreadSafeRefCounted.h b/sky/engine/wtf/ThreadSafeRefCounted.h index a7dfd283ff075..ba2f7f55a9b45 100644 --- a/sky/engine/wtf/ThreadSafeRefCounted.h +++ b/sky/engine/wtf/ThreadSafeRefCounted.h @@ -39,60 +39,47 @@ namespace WTF { class WTF_EXPORT ThreadSafeRefCountedBase { - WTF_MAKE_NONCOPYABLE(ThreadSafeRefCountedBase); - WTF_MAKE_FAST_ALLOCATED; -public: - ThreadSafeRefCountedBase(int initialRefCount = 1) - : m_refCount(initialRefCount) - { - } + WTF_MAKE_NONCOPYABLE(ThreadSafeRefCountedBase); + WTF_MAKE_FAST_ALLOCATED; - void ref() - { - atomicIncrement(&m_refCount); - } + public: + ThreadSafeRefCountedBase(int initialRefCount = 1) + : m_refCount(initialRefCount) {} - bool hasOneRef() - { - return refCount() == 1; - } + void ref() { atomicIncrement(&m_refCount); } - int refCount() const - { - return static_cast(m_refCount); - } + bool hasOneRef() { return refCount() == 1; } -protected: - // Returns whether the pointer should be freed or not. - bool derefBase() - { - WTF_ANNOTATE_HAPPENS_BEFORE(&m_refCount); - if (atomicDecrement(&m_refCount) <= 0) { - WTF_ANNOTATE_HAPPENS_AFTER(&m_refCount); - return true; - } - return false; + int refCount() const { return static_cast(m_refCount); } + + protected: + // Returns whether the pointer should be freed or not. + bool derefBase() { + WTF_ANNOTATE_HAPPENS_BEFORE(&m_refCount); + if (atomicDecrement(&m_refCount) <= 0) { + WTF_ANNOTATE_HAPPENS_AFTER(&m_refCount); + return true; } + return false; + } -private: - int m_refCount; + private: + int m_refCount; }; -template class ThreadSafeRefCounted : public ThreadSafeRefCountedBase { -public: - void deref() - { - if (derefBase()) - delete static_cast(this); - } +template +class ThreadSafeRefCounted : public ThreadSafeRefCountedBase { + public: + void deref() { + if (derefBase()) + delete static_cast(this); + } -protected: - ThreadSafeRefCounted() - { - } + protected: + ThreadSafeRefCounted() {} }; -} // namespace WTF +} // namespace WTF using WTF::ThreadSafeRefCounted; diff --git a/sky/engine/wtf/ThreadSpecific.h b/sky/engine/wtf/ThreadSpecific.h index 5b835c3b684a4..cff93adb5258a 100644 --- a/sky/engine/wtf/ThreadSpecific.h +++ b/sky/engine/wtf/ThreadSpecific.h @@ -54,38 +54,43 @@ namespace WTF { -template class ThreadSpecific { - WTF_MAKE_NONCOPYABLE(ThreadSpecific); -public: - ThreadSpecific(); - bool isSet(); // Useful as a fast check to see if this thread has set this value. - T* operator->(); - operator T*(); - T& operator*(); - -private: - - // Not implemented. It's technically possible to destroy a thread specific key, but one would need - // to make sure that all values have been destroyed already (usually, that all threads that used it - // have exited). It's unlikely that any user of this call will be in that situation - and having - // a destructor defined can be confusing, given that it has such strong pre-requisites to work correctly. - ~ThreadSpecific(); - - T* get(); - void set(T*); - void static destroy(void* ptr); - - struct Data { - WTF_MAKE_NONCOPYABLE(Data); - public: - Data(T* value, ThreadSpecific* owner) : value(value), owner(owner) {} - - T* value; - ThreadSpecific* owner; - }; +template +class ThreadSpecific { + WTF_MAKE_NONCOPYABLE(ThreadSpecific); + + public: + ThreadSpecific(); + bool + isSet(); // Useful as a fast check to see if this thread has set this value. + T* operator->(); + operator T*(); + T& operator*(); + + private: + // Not implemented. It's technically possible to destroy a thread specific + // key, but one would need to make sure that all values have been destroyed + // already (usually, that all threads that used it have exited). It's unlikely + // that any user of this call will be in that situation - and having a + // destructor defined can be confusing, given that it has such strong + // pre-requisites to work correctly. + ~ThreadSpecific(); + + T* get(); + void set(T*); + void static destroy(void* ptr); + + struct Data { + WTF_MAKE_NONCOPYABLE(Data); + + public: + Data(T* value, ThreadSpecific* owner) : value(value), owner(owner) {} + + T* value; + ThreadSpecific* owner; + }; #if USE(PTHREADS) - pthread_key_t m_key; + pthread_key_t m_key; #endif }; @@ -93,115 +98,106 @@ template class ThreadSpecific { typedef pthread_key_t ThreadSpecificKey; -inline void threadSpecificKeyCreate(ThreadSpecificKey* key, void (*destructor)(void *)) -{ - int error = pthread_key_create(key, destructor); - if (error) - CRASH(); +inline void threadSpecificKeyCreate(ThreadSpecificKey* key, + void (*destructor)(void*)) { + int error = pthread_key_create(key, destructor); + if (error) + CRASH(); } -inline void threadSpecificKeyDelete(ThreadSpecificKey key) -{ - int error = pthread_key_delete(key); - if (error) - CRASH(); +inline void threadSpecificKeyDelete(ThreadSpecificKey key) { + int error = pthread_key_delete(key); + if (error) + CRASH(); } -inline void threadSpecificSet(ThreadSpecificKey key, void* value) -{ - pthread_setspecific(key, value); +inline void threadSpecificSet(ThreadSpecificKey key, void* value) { + pthread_setspecific(key, value); } -inline void* threadSpecificGet(ThreadSpecificKey key) -{ - return pthread_getspecific(key); +inline void* threadSpecificGet(ThreadSpecificKey key) { + return pthread_getspecific(key); } -template -inline ThreadSpecific::ThreadSpecific() -{ - int error = pthread_key_create(&m_key, destroy); - if (error) - CRASH(); +template +inline ThreadSpecific::ThreadSpecific() { + int error = pthread_key_create(&m_key, destroy); + if (error) + CRASH(); } -template -inline T* ThreadSpecific::get() -{ - Data* data = static_cast(pthread_getspecific(m_key)); - return data ? data->value : 0; +template +inline T* ThreadSpecific::get() { + Data* data = static_cast(pthread_getspecific(m_key)); + return data ? data->value : 0; } -template -inline void ThreadSpecific::set(T* ptr) -{ - ASSERT(!get()); - pthread_setspecific(m_key, new Data(ptr, this)); +template +inline void ThreadSpecific::set(T* ptr) { + ASSERT(!get()); + pthread_setspecific(m_key, new Data(ptr, this)); } #else #error ThreadSpecific is not implemented for this platform. #endif -template -inline void ThreadSpecific::destroy(void* ptr) -{ - if (isShutdown()) - return; +template +inline void ThreadSpecific::destroy(void* ptr) { + if (isShutdown()) + return; - Data* data = static_cast(ptr); + Data* data = static_cast(ptr); #if USE(PTHREADS) - // We want get() to keep working while data destructor works, because it can be called indirectly by the destructor. - // Some pthreads implementations zero out the pointer before calling destroy(), so we temporarily reset it. - pthread_setspecific(data->owner->m_key, ptr); + // We want get() to keep working while data destructor works, because it can + // be called indirectly by the destructor. Some pthreads implementations zero + // out the pointer before calling destroy(), so we temporarily reset it. + pthread_setspecific(data->owner->m_key, ptr); #endif - data->value->~T(); - fastFree(data->value); + data->value->~T(); + fastFree(data->value); #if USE(PTHREADS) - pthread_setspecific(data->owner->m_key, 0); + pthread_setspecific(data->owner->m_key, 0); #else #error ThreadSpecific is not implemented for this platform. #endif - delete data; + delete data; } -template -inline bool ThreadSpecific::isSet() -{ - return !!get(); +template +inline bool ThreadSpecific::isSet() { + return !!get(); } -template -inline ThreadSpecific::operator T*() -{ - T* ptr = static_cast(get()); - if (!ptr) { - // Set up thread-specific value's memory pointer before invoking constructor, in case any function it calls - // needs to access the value, to avoid recursion. - ptr = static_cast(fastZeroedMalloc(sizeof(T))); - set(ptr); - new (NotNull, ptr) T; - } - return ptr; +template +inline ThreadSpecific::operator T*() { + T* ptr = static_cast(get()); + if (!ptr) { + // Set up thread-specific value's memory pointer before invoking + // constructor, in case any function it calls needs to access the value, to + // avoid recursion. + ptr = static_cast(fastZeroedMalloc(sizeof(T))); + set(ptr); + new (NotNull, ptr) T; + } + return ptr; } -template -inline T* ThreadSpecific::operator->() -{ - return operator T*(); +template +inline T* ThreadSpecific::operator->() { + return operator T*(); } -template -inline T& ThreadSpecific::operator*() -{ - return *operator T*(); +template +inline T& ThreadSpecific::operator*() { + return *operator T*(); } -} // namespace WTF +} // namespace WTF using WTF::ThreadSpecific; diff --git a/sky/engine/wtf/Threading.h b/sky/engine/wtf/Threading.h index 78bfaf2403e83..35fbf5969a1b6 100644 --- a/sky/engine/wtf/Threading.h +++ b/sky/engine/wtf/Threading.h @@ -33,18 +33,20 @@ #include #include "flutter/sky/engine/wtf/WTFExport.h" -// For portability, we do not use thread-safe statics natively supported by some compilers (e.g. gcc). -#define AtomicallyInitializedStatic(T, name) \ - WTF::lockAtomicallyInitializedStaticMutex(); \ - static T name; \ - WTF::unlockAtomicallyInitializedStaticMutex(); +// For portability, we do not use thread-safe statics natively supported by some +// compilers (e.g. gcc). +#define AtomicallyInitializedStatic(T, name) \ + WTF::lockAtomicallyInitializedStaticMutex(); \ + static T name; \ + WTF::unlockAtomicallyInitializedStaticMutex(); namespace WTF { typedef uint32_t ThreadIdentifier; // Called in the thread during initialization. -// Helpful for platforms where the thread name must be set from within the thread. +// Helpful for platforms where the thread name must be set from within the +// thread. WTF_EXPORT void initializeCurrentThreadInternal(const char* threadName); WTF_EXPORT ThreadIdentifier currentThread(); @@ -52,7 +54,7 @@ WTF_EXPORT ThreadIdentifier currentThread(); WTF_EXPORT void lockAtomicallyInitializedStaticMutex(); WTF_EXPORT void unlockAtomicallyInitializedStaticMutex(); -} // namespace WTF +} // namespace WTF using WTF::ThreadIdentifier; using WTF::currentThread; diff --git a/sky/engine/wtf/ThreadingPrimitives.h b/sky/engine/wtf/ThreadingPrimitives.h index 162d504b53740..77cd43890df63 100644 --- a/sky/engine/wtf/ThreadingPrimitives.h +++ b/sky/engine/wtf/ThreadingPrimitives.h @@ -45,9 +45,9 @@ namespace WTF { #if USE(PTHREADS) struct PlatformMutex { - pthread_mutex_t m_internalMutex; + pthread_mutex_t m_internalMutex; #if ENABLE(ASSERT) - size_t m_recursionCount; + size_t m_recursionCount; #endif }; typedef pthread_cond_t PlatformCondition; @@ -57,62 +57,64 @@ typedef void* PlatformCondition; #endif class WTF_EXPORT MutexBase { - WTF_MAKE_NONCOPYABLE(MutexBase); WTF_MAKE_FAST_ALLOCATED; -public: - ~MutexBase(); + WTF_MAKE_NONCOPYABLE(MutexBase); + WTF_MAKE_FAST_ALLOCATED; - void lock(); - void unlock(); + public: + ~MutexBase(); + + void lock(); + void unlock(); #if ENABLE(ASSERT) - bool locked() { return m_mutex.m_recursionCount > 0; } + bool locked() { return m_mutex.m_recursionCount > 0; } #endif -public: - PlatformMutex& impl() { return m_mutex; } + public: + PlatformMutex& impl() { return m_mutex; } -protected: - MutexBase(bool recursive); + protected: + MutexBase(bool recursive); - PlatformMutex m_mutex; + PlatformMutex m_mutex; }; class WTF_EXPORT Mutex : public MutexBase { -public: - Mutex() : MutexBase(false) { } - bool tryLock(); + public: + Mutex() : MutexBase(false) {} + bool tryLock(); }; class WTF_EXPORT RecursiveMutex : public MutexBase { -public: - RecursiveMutex() : MutexBase(true) { } - bool tryLock(); + public: + RecursiveMutex() : MutexBase(true) {} + bool tryLock(); }; typedef Locker MutexLocker; class MutexTryLocker { - WTF_MAKE_NONCOPYABLE(MutexTryLocker); -public: - MutexTryLocker(Mutex& mutex) : m_mutex(mutex), m_locked(mutex.tryLock()) { } - ~MutexTryLocker() - { - if (m_locked) - m_mutex.unlock(); - } - - bool locked() const { return m_locked; } - -private: - Mutex& m_mutex; - bool m_locked; + WTF_MAKE_NONCOPYABLE(MutexTryLocker); + + public: + MutexTryLocker(Mutex& mutex) : m_mutex(mutex), m_locked(mutex.tryLock()) {} + ~MutexTryLocker() { + if (m_locked) + m_mutex.unlock(); + } + + bool locked() const { return m_locked; } + + private: + Mutex& m_mutex; + bool m_locked; }; -} // namespace WTF +} // namespace WTF -using WTF::MutexBase; using WTF::Mutex; -using WTF::RecursiveMutex; +using WTF::MutexBase; using WTF::MutexLocker; using WTF::MutexTryLocker; +using WTF::RecursiveMutex; #endif // SKY_ENGINE_WTF_THREADINGPRIMITIVES_H_ diff --git a/sky/engine/wtf/ThreadingPthreads.cpp b/sky/engine/wtf/ThreadingPthreads.cpp index bd33538a76510..5b16c2fdd42b4 100644 --- a/sky/engine/wtf/ThreadingPthreads.cpp +++ b/sky/engine/wtf/ThreadingPthreads.cpp @@ -51,41 +51,44 @@ namespace WTF { class PthreadState { - WTF_MAKE_FAST_ALLOCATED; -public: - enum JoinableState { - Joinable, // The default thread state. The thread can be joined on. - - Joined, // Somebody waited on this thread to exit and this thread finally exited. This state is here because there can be a - // period of time between when the thread exits (which causes pthread_join to return and the remainder of waitOnThreadCompletion to run) - // and when threadDidExit is called. We need threadDidExit to take charge and delete the thread data since there's - // nobody else to pick up the slack in this case (since waitOnThreadCompletion has already returned). - - Detached // The thread has been detached and can no longer be joined on. At this point, the thread must take care of cleaning up after itself. - }; - - // Currently all threads created by WTF start out as joinable. - PthreadState(pthread_t handle) - : m_joinableState(Joinable) - , m_didExit(false) - , m_pthreadHandle(handle) - { - } - - JoinableState joinableState() { return m_joinableState; } - pthread_t pthreadHandle() { return m_pthreadHandle; } - void didBecomeDetached() { m_joinableState = Detached; } - void didExit() { m_didExit = true; } - void didJoin() { m_joinableState = Joined; } - bool hasExited() { return m_didExit; } - -private: - JoinableState m_joinableState; - bool m_didExit; - pthread_t m_pthreadHandle; + WTF_MAKE_FAST_ALLOCATED; + + public: + enum JoinableState { + Joinable, // The default thread state. The thread can be joined on. + + Joined, // Somebody waited on this thread to exit and this thread finally + // exited. This state is here because there can be a period of time + // between when the thread exits (which causes pthread_join to + // return and the remainder of waitOnThreadCompletion to run) and + // when threadDidExit is called. We need threadDidExit to take + // charge and delete the thread data since there's nobody else to + // pick up the slack in this case (since waitOnThreadCompletion has + // already returned). + + Detached // The thread has been detached and can no longer be joined on. At + // this point, the thread must take care of cleaning up after + // itself. + }; + + // Currently all threads created by WTF start out as joinable. + PthreadState(pthread_t handle) + : m_joinableState(Joinable), m_didExit(false), m_pthreadHandle(handle) {} + + JoinableState joinableState() { return m_joinableState; } + pthread_t pthreadHandle() { return m_pthreadHandle; } + void didBecomeDetached() { m_joinableState = Detached; } + void didExit() { m_didExit = true; } + void didJoin() { m_joinableState = Joined; } + bool hasExited() { return m_didExit; } + + private: + JoinableState m_joinableState; + bool m_didExit; + pthread_t m_pthreadHandle; }; -typedef HashMap > ThreadMap; +typedef HashMap> ThreadMap; static Mutex* atomicallyInitializedStaticMutex; @@ -93,135 +96,125 @@ void unsafeThreadWasDetached(ThreadIdentifier); void threadDidExit(ThreadIdentifier); void threadWasJoined(ThreadIdentifier); -static Mutex& threadMapMutex() -{ - DEFINE_STATIC_LOCAL(Mutex, mutex, ()); - return mutex; +static Mutex& threadMapMutex() { + DEFINE_STATIC_LOCAL(Mutex, mutex, ()); + return mutex; } -void initializeThreading() -{ - // This should only be called once. - ASSERT(!atomicallyInitializedStaticMutex); - - // StringImpl::empty() does not construct its static string in a threadsafe fashion, - // so ensure it has been initialized from here. - StringImpl::empty(); - atomicallyInitializedStaticMutex = new Mutex; - threadMapMutex(); - ThreadIdentifierData::initializeOnce(); - wtfThreadData(); - s_dtoaP5Mutex = new Mutex; +void initializeThreading() { + // This should only be called once. + ASSERT(!atomicallyInitializedStaticMutex); + + // StringImpl::empty() does not construct its static string in a threadsafe + // fashion, so ensure it has been initialized from here. + StringImpl::empty(); + atomicallyInitializedStaticMutex = new Mutex; + threadMapMutex(); + ThreadIdentifierData::initializeOnce(); + wtfThreadData(); + s_dtoaP5Mutex = new Mutex; } -void lockAtomicallyInitializedStaticMutex() -{ - ASSERT(atomicallyInitializedStaticMutex); - atomicallyInitializedStaticMutex->lock(); +void lockAtomicallyInitializedStaticMutex() { + ASSERT(atomicallyInitializedStaticMutex); + atomicallyInitializedStaticMutex->lock(); } -void unlockAtomicallyInitializedStaticMutex() -{ - atomicallyInitializedStaticMutex->unlock(); +void unlockAtomicallyInitializedStaticMutex() { + atomicallyInitializedStaticMutex->unlock(); } -static ThreadMap& threadMap() -{ - DEFINE_STATIC_LOCAL(ThreadMap, map, ()); - return map; +static ThreadMap& threadMap() { + DEFINE_STATIC_LOCAL(ThreadMap, map, ()); + return map; } -static ThreadIdentifier identifierByPthreadHandle(const pthread_t& pthreadHandle) -{ - MutexLocker locker(threadMapMutex()); +static ThreadIdentifier identifierByPthreadHandle( + const pthread_t& pthreadHandle) { + MutexLocker locker(threadMapMutex()); - ThreadMap::iterator i = threadMap().begin(); - for (; i != threadMap().end(); ++i) { - if (pthread_equal(i->value->pthreadHandle(), pthreadHandle) && !i->value->hasExited()) - return i->key; - } + ThreadMap::iterator i = threadMap().begin(); + for (; i != threadMap().end(); ++i) { + if (pthread_equal(i->value->pthreadHandle(), pthreadHandle) && + !i->value->hasExited()) + return i->key; + } - return 0; + return 0; } -static ThreadIdentifier establishIdentifierForPthreadHandle(const pthread_t& pthreadHandle) -{ - ASSERT(!identifierByPthreadHandle(pthreadHandle)); - MutexLocker locker(threadMapMutex()); - static ThreadIdentifier identifierCount = 1; - threadMap().add(identifierCount, adoptPtr(new PthreadState(pthreadHandle))); - return identifierCount++; +static ThreadIdentifier establishIdentifierForPthreadHandle( + const pthread_t& pthreadHandle) { + ASSERT(!identifierByPthreadHandle(pthreadHandle)); + MutexLocker locker(threadMapMutex()); + static ThreadIdentifier identifierCount = 1; + threadMap().add(identifierCount, adoptPtr(new PthreadState(pthreadHandle))); + return identifierCount++; } -void initializeCurrentThreadInternal(const char* threadName) -{ - ThreadIdentifier id = identifierByPthreadHandle(pthread_self()); - ASSERT(id); - ThreadIdentifierData::initialize(id); +void initializeCurrentThreadInternal(const char* threadName) { + ThreadIdentifier id = identifierByPthreadHandle(pthread_self()); + ASSERT(id); + ThreadIdentifierData::initialize(id); } -void threadDidExit(ThreadIdentifier threadID) -{ - MutexLocker locker(threadMapMutex()); - PthreadState* state = threadMap().get(threadID); - ASSERT(state); +void threadDidExit(ThreadIdentifier threadID) { + MutexLocker locker(threadMapMutex()); + PthreadState* state = threadMap().get(threadID); + ASSERT(state); - state->didExit(); + state->didExit(); - if (state->joinableState() != PthreadState::Joinable) - threadMap().remove(threadID); + if (state->joinableState() != PthreadState::Joinable) + threadMap().remove(threadID); } -ThreadIdentifier currentThread() -{ - ThreadIdentifier id = ThreadIdentifierData::identifier(); - if (id) - return id; - - // Not a WTF-created thread, ThreadIdentifier is not established yet. - id = establishIdentifierForPthreadHandle(pthread_self()); - ThreadIdentifierData::initialize(id); +ThreadIdentifier currentThread() { + ThreadIdentifier id = ThreadIdentifierData::identifier(); + if (id) return id; + + // Not a WTF-created thread, ThreadIdentifier is not established yet. + id = establishIdentifierForPthreadHandle(pthread_self()); + ThreadIdentifierData::initialize(id); + return id; } -MutexBase::MutexBase(bool recursive) -{ - pthread_mutexattr_t attr; - pthread_mutexattr_init(&attr); - pthread_mutexattr_settype(&attr, recursive ? PTHREAD_MUTEX_RECURSIVE : PTHREAD_MUTEX_NORMAL); +MutexBase::MutexBase(bool recursive) { + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype( + &attr, recursive ? PTHREAD_MUTEX_RECURSIVE : PTHREAD_MUTEX_NORMAL); - int result = pthread_mutex_init(&m_mutex.m_internalMutex, &attr); - ASSERT_UNUSED(result, !result); + int result = pthread_mutex_init(&m_mutex.m_internalMutex, &attr); + ASSERT_UNUSED(result, !result); #if ENABLE(ASSERT) - m_mutex.m_recursionCount = 0; + m_mutex.m_recursionCount = 0; #endif - pthread_mutexattr_destroy(&attr); + pthread_mutexattr_destroy(&attr); } -MutexBase::~MutexBase() -{ - int result = pthread_mutex_destroy(&m_mutex.m_internalMutex); - ASSERT_UNUSED(result, !result); +MutexBase::~MutexBase() { + int result = pthread_mutex_destroy(&m_mutex.m_internalMutex); + ASSERT_UNUSED(result, !result); } -void MutexBase::lock() -{ - int result = pthread_mutex_lock(&m_mutex.m_internalMutex); - ASSERT_UNUSED(result, !result); +void MutexBase::lock() { + int result = pthread_mutex_lock(&m_mutex.m_internalMutex); + ASSERT_UNUSED(result, !result); #if ENABLE(ASSERT) - ++m_mutex.m_recursionCount; + ++m_mutex.m_recursionCount; #endif } -void MutexBase::unlock() -{ +void MutexBase::unlock() { #if ENABLE(ASSERT) - ASSERT(m_mutex.m_recursionCount); - --m_mutex.m_recursionCount; + ASSERT(m_mutex.m_recursionCount); + --m_mutex.m_recursionCount; #endif - int result = pthread_mutex_unlock(&m_mutex.m_internalMutex); - ASSERT_UNUSED(result, !result); + int result = pthread_mutex_unlock(&m_mutex.m_internalMutex); + ASSERT_UNUSED(result, !result); } // There is a separate tryLock implementation for the Mutex and the @@ -229,41 +222,39 @@ void MutexBase::unlock() // succeed or not for the non-recursive mutex. On Linux the two implementations // are equal except we can assert the recursion count is always zero for the // non-recursive mutex. -bool Mutex::tryLock() -{ - int result = pthread_mutex_trylock(&m_mutex.m_internalMutex); - if (result == 0) { +bool Mutex::tryLock() { + int result = pthread_mutex_trylock(&m_mutex.m_internalMutex); + if (result == 0) { #if ENABLE(ASSERT) - // The Mutex class is not recursive, so the recursionCount should be - // zero after getting the lock. - ASSERT(!m_mutex.m_recursionCount); - ++m_mutex.m_recursionCount; + // The Mutex class is not recursive, so the recursionCount should be + // zero after getting the lock. + ASSERT(!m_mutex.m_recursionCount); + ++m_mutex.m_recursionCount; #endif - return true; - } - if (result == EBUSY) - return false; - - ASSERT_NOT_REACHED(); + return true; + } + if (result == EBUSY) return false; + + ASSERT_NOT_REACHED(); + return false; } -bool RecursiveMutex::tryLock() -{ - int result = pthread_mutex_trylock(&m_mutex.m_internalMutex); - if (result == 0) { +bool RecursiveMutex::tryLock() { + int result = pthread_mutex_trylock(&m_mutex.m_internalMutex); + if (result == 0) { #if ENABLE(ASSERT) - ++m_mutex.m_recursionCount; + ++m_mutex.m_recursionCount; #endif - return true; - } - if (result == EBUSY) - return false; - - ASSERT_NOT_REACHED(); + return true; + } + if (result == EBUSY) return false; + + ASSERT_NOT_REACHED(); + return false; } -} // namespace WTF +} // namespace WTF -#endif // USE(PTHREADS) +#endif // USE(PTHREADS) diff --git a/sky/engine/wtf/TypeTraits.cpp b/sky/engine/wtf/TypeTraits.cpp index f56f2ccb67ab0..b7dd729fad9fb 100644 --- a/sky/engine/wtf/TypeTraits.cpp +++ b/sky/engine/wtf/TypeTraits.cpp @@ -1,4 +1,4 @@ - /* +/* * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. * Copyright (C) 2009, 2010 Google Inc. All rights reserved. * @@ -28,25 +28,32 @@ namespace WTF { COMPILE_ASSERT(IsInteger::value, WTF_IsInteger_bool_true); COMPILE_ASSERT(IsInteger::value, WTF_IsInteger_char_true); COMPILE_ASSERT(IsInteger::value, WTF_IsInteger_signed_char_true); -COMPILE_ASSERT(IsInteger::value, WTF_IsInteger_unsigned_char_true); +COMPILE_ASSERT(IsInteger::value, + WTF_IsInteger_unsigned_char_true); COMPILE_ASSERT(IsInteger::value, WTF_IsInteger_short_true); -COMPILE_ASSERT(IsInteger::value, WTF_IsInteger_unsigned_short_true); +COMPILE_ASSERT(IsInteger::value, + WTF_IsInteger_unsigned_short_true); COMPILE_ASSERT(IsInteger::value, WTF_IsInteger_int_true); COMPILE_ASSERT(IsInteger::value, WTF_IsInteger_unsigned_int_true); COMPILE_ASSERT(IsInteger::value, WTF_IsInteger_long_true); -COMPILE_ASSERT(IsInteger::value, WTF_IsInteger_unsigned_long_true); +COMPILE_ASSERT(IsInteger::value, + WTF_IsInteger_unsigned_long_true); COMPILE_ASSERT(IsInteger::value, WTF_IsInteger_long_long_true); -COMPILE_ASSERT(IsInteger::value, WTF_IsInteger_unsigned_long_long_true); +COMPILE_ASSERT(IsInteger::value, + WTF_IsInteger_unsigned_long_long_true); COMPILE_ASSERT(IsInteger::value, WTF_IsInteger_wchar_t_true); COMPILE_ASSERT(!IsInteger::value, WTF_IsInteger_char_pointer_false); -COMPILE_ASSERT(!IsInteger::value, WTF_IsInteger_const_char_pointer_false); -COMPILE_ASSERT(!IsInteger::value, WTF_IsInteger_volatile_char_pointer_false); +COMPILE_ASSERT(!IsInteger::value, + WTF_IsInteger_const_char_pointer_false); +COMPILE_ASSERT(!IsInteger::value, + WTF_IsInteger_volatile_char_pointer_false); COMPILE_ASSERT(!IsInteger::value, WTF_IsInteger_double_false); COMPILE_ASSERT(!IsInteger::value, WTF_IsInteger_float_false); COMPILE_ASSERT(IsFloatingPoint::value, WTF_IsFloatingPoint_float_true); COMPILE_ASSERT(IsFloatingPoint::value, WTF_IsFloatingPoint_double_true); -COMPILE_ASSERT(IsFloatingPoint::value, WTF_IsFloatingPoint_long_double_true); +COMPILE_ASSERT(IsFloatingPoint::value, + WTF_IsFloatingPoint_long_double_true); COMPILE_ASSERT(!IsFloatingPoint::value, WTF_IsFloatingPoint_int_false); COMPILE_ASSERT(IsPod::value, WTF_IsPod_bool_true); @@ -60,93 +67,153 @@ COMPILE_ASSERT(IsPod::value, WTF_IsPod_unsigned_int_true); COMPILE_ASSERT(IsPod::value, WTF_IsPod_long_true); COMPILE_ASSERT(IsPod::value, WTF_IsPod_unsigned_long_true); COMPILE_ASSERT(IsPod::value, WTF_IsPod_long_long_true); -COMPILE_ASSERT(IsPod::value, WTF_IsPod_unsigned_long_long_true); +COMPILE_ASSERT(IsPod::value, + WTF_IsPod_unsigned_long_long_true); COMPILE_ASSERT(IsPod::value, WTF_IsPod_wchar_t_true); COMPILE_ASSERT(IsPod::value, WTF_IsPod_char_pointer_true); COMPILE_ASSERT(IsPod::value, WTF_IsPod_const_char_pointer_true); -COMPILE_ASSERT(IsPod::value, WTF_IsPod_volatile_char_pointer_true); +COMPILE_ASSERT(IsPod::value, + WTF_IsPod_volatile_char_pointer_true); COMPILE_ASSERT(IsPod::value, WTF_IsPod_double_true); COMPILE_ASSERT(IsPod::value, WTF_IsPod_long_double_true); COMPILE_ASSERT(IsPod::value, WTF_IsPod_float_true); -COMPILE_ASSERT(!IsPod >::value, WTF_IsPod_struct_false); - -enum IsConvertibleToIntegerCheck { }; -COMPILE_ASSERT(IsConvertibleToInteger::value, WTF_IsConvertibleToInteger_enum_true); -COMPILE_ASSERT(IsConvertibleToInteger::value, WTF_IsConvertibleToInteger_bool_true); -COMPILE_ASSERT(IsConvertibleToInteger::value, WTF_IsConvertibleToInteger_char_true); -COMPILE_ASSERT(IsConvertibleToInteger::value, WTF_IsConvertibleToInteger_signed_char_true); -COMPILE_ASSERT(IsConvertibleToInteger::value, WTF_IsConvertibleToInteger_unsigned_char_true); -COMPILE_ASSERT(IsConvertibleToInteger::value, WTF_IsConvertibleToInteger_short_true); -COMPILE_ASSERT(IsConvertibleToInteger::value, WTF_IsConvertibleToInteger_unsigned_short_true); -COMPILE_ASSERT(IsConvertibleToInteger::value, WTF_IsConvertibleToInteger_int_true); -COMPILE_ASSERT(IsConvertibleToInteger::value, WTF_IsConvertibleToInteger_unsigned_int_true); -COMPILE_ASSERT(IsConvertibleToInteger::value, WTF_IsConvertibleToInteger_long_true); -COMPILE_ASSERT(IsConvertibleToInteger::value, WTF_IsConvertibleToInteger_unsigned_long_true); -COMPILE_ASSERT(IsConvertibleToInteger::value, WTF_IsConvertibleToInteger_long_long_true); -COMPILE_ASSERT(IsConvertibleToInteger::value, WTF_IsConvertibleToInteger_unsigned_long_long_true); -COMPILE_ASSERT(IsConvertibleToInteger::value, WTF_IsConvertibleToInteger_wchar_t_true); -COMPILE_ASSERT(IsConvertibleToInteger::value, WTF_IsConvertibleToInteger_double_true); -COMPILE_ASSERT(IsConvertibleToInteger::value, WTF_IsConvertibleToInteger_long_double_true); -COMPILE_ASSERT(IsConvertibleToInteger::value, WTF_IsConvertibleToInteger_float_true); -COMPILE_ASSERT(!IsConvertibleToInteger::value, WTF_IsConvertibleToInteger_char_pointer_false); -COMPILE_ASSERT(!IsConvertibleToInteger::value, WTF_IsConvertibleToInteger_const_char_pointer_false); -COMPILE_ASSERT(!IsConvertibleToInteger::value, WTF_IsConvertibleToInteger_volatile_char_pointer_false); -COMPILE_ASSERT(!IsConvertibleToInteger >::value, WTF_IsConvertibleToInteger_struct_false); - -COMPILE_ASSERT((IsPointerConvertible::Value), WTF_IsPointerConvertible_same_type_true); -COMPILE_ASSERT((!IsPointerConvertible::Value), WTF_IsPointerConvertible_int_to_unsigned_false); -COMPILE_ASSERT((IsPointerConvertible::Value), WTF_IsPointerConvertible_int_to_const_int_true); -COMPILE_ASSERT((!IsPointerConvertible::Value), WTF_IsPointerConvertible_const_int_to_int_false); -COMPILE_ASSERT((IsPointerConvertible::Value), WTF_IsPointerConvertible_int_to_volatile_int_true); +COMPILE_ASSERT(!IsPod>::value, WTF_IsPod_struct_false); + +enum IsConvertibleToIntegerCheck {}; +COMPILE_ASSERT(IsConvertibleToInteger::value, + WTF_IsConvertibleToInteger_enum_true); +COMPILE_ASSERT(IsConvertibleToInteger::value, + WTF_IsConvertibleToInteger_bool_true); +COMPILE_ASSERT(IsConvertibleToInteger::value, + WTF_IsConvertibleToInteger_char_true); +COMPILE_ASSERT(IsConvertibleToInteger::value, + WTF_IsConvertibleToInteger_signed_char_true); +COMPILE_ASSERT(IsConvertibleToInteger::value, + WTF_IsConvertibleToInteger_unsigned_char_true); +COMPILE_ASSERT(IsConvertibleToInteger::value, + WTF_IsConvertibleToInteger_short_true); +COMPILE_ASSERT(IsConvertibleToInteger::value, + WTF_IsConvertibleToInteger_unsigned_short_true); +COMPILE_ASSERT(IsConvertibleToInteger::value, + WTF_IsConvertibleToInteger_int_true); +COMPILE_ASSERT(IsConvertibleToInteger::value, + WTF_IsConvertibleToInteger_unsigned_int_true); +COMPILE_ASSERT(IsConvertibleToInteger::value, + WTF_IsConvertibleToInteger_long_true); +COMPILE_ASSERT(IsConvertibleToInteger::value, + WTF_IsConvertibleToInteger_unsigned_long_true); +COMPILE_ASSERT(IsConvertibleToInteger::value, + WTF_IsConvertibleToInteger_long_long_true); +COMPILE_ASSERT(IsConvertibleToInteger::value, + WTF_IsConvertibleToInteger_unsigned_long_long_true); +COMPILE_ASSERT(IsConvertibleToInteger::value, + WTF_IsConvertibleToInteger_wchar_t_true); +COMPILE_ASSERT(IsConvertibleToInteger::value, + WTF_IsConvertibleToInteger_double_true); +COMPILE_ASSERT(IsConvertibleToInteger::value, + WTF_IsConvertibleToInteger_long_double_true); +COMPILE_ASSERT(IsConvertibleToInteger::value, + WTF_IsConvertibleToInteger_float_true); +COMPILE_ASSERT(!IsConvertibleToInteger::value, + WTF_IsConvertibleToInteger_char_pointer_false); +COMPILE_ASSERT(!IsConvertibleToInteger::value, + WTF_IsConvertibleToInteger_const_char_pointer_false); +COMPILE_ASSERT(!IsConvertibleToInteger::value, + WTF_IsConvertibleToInteger_volatile_char_pointer_false); +COMPILE_ASSERT(!IsConvertibleToInteger>::value, + WTF_IsConvertibleToInteger_struct_false); + +COMPILE_ASSERT((IsPointerConvertible::Value), + WTF_IsPointerConvertible_same_type_true); +COMPILE_ASSERT((!IsPointerConvertible::Value), + WTF_IsPointerConvertible_int_to_unsigned_false); +COMPILE_ASSERT((IsPointerConvertible::Value), + WTF_IsPointerConvertible_int_to_const_int_true); +COMPILE_ASSERT((!IsPointerConvertible::Value), + WTF_IsPointerConvertible_const_int_to_int_false); +COMPILE_ASSERT((IsPointerConvertible::Value), + WTF_IsPointerConvertible_int_to_volatile_int_true); COMPILE_ASSERT((IsSameType::value), WTF_IsSameType_bool_true); -COMPILE_ASSERT((IsSameType::value), WTF_IsSameType_int_pointer_true); -COMPILE_ASSERT((!IsSameType::value), WTF_IsSameType_int_int_pointer_false); -COMPILE_ASSERT((!IsSameType::value), WTF_IsSameType_const_change_false); -COMPILE_ASSERT((!IsSameType::value), WTF_IsSameType_volatile_change_false); +COMPILE_ASSERT((IsSameType::value), + WTF_IsSameType_int_pointer_true); +COMPILE_ASSERT((!IsSameType::value), + WTF_IsSameType_int_int_pointer_false); +COMPILE_ASSERT((!IsSameType::value), + WTF_IsSameType_const_change_false); +COMPILE_ASSERT((!IsSameType::value), + WTF_IsSameType_volatile_change_false); template -class TestBaseClass { -}; - -class TestDerivedClass : public TestBaseClass { -}; - -COMPILE_ASSERT((IsSubclass >::value), WTF_Test_IsSubclass_Derived_From_Base); -COMPILE_ASSERT((!IsSubclass, TestDerivedClass>::value), WTF_Test_IsSubclass_Base_From_Derived); -COMPILE_ASSERT((IsSubclassOfTemplate::value), WTF_Test_IsSubclassOfTemplate_Base_From_Derived); -COMPILE_ASSERT((IsSameType, TestBaseClass>::Type, int>::value), WTF_Test_RemoveTemplate); -COMPILE_ASSERT((IsSameType::Type, int>::value), WTF_Test_RemoveTemplate_WithoutTemplate); -COMPILE_ASSERT((IsPointerConvertible >::Value), WTF_Test_IsPointerConvertible_Derived_To_Base); -COMPILE_ASSERT((!IsPointerConvertible, TestDerivedClass>::Value), WTF_Test_IsPointerConvertible_Base_To_Derived); - -COMPILE_ASSERT((IsSameType::Type>::value), WTF_test_RemoveConst_const_bool); -COMPILE_ASSERT((!IsSameType::Type>::value), WTF_test_RemoveConst_volatile_bool); - -COMPILE_ASSERT((IsSameType::Type>::value), WTF_test_RemoveVolatile_bool); -COMPILE_ASSERT((!IsSameType::Type>::value), WTF_test_RemoveVolatile_const_bool); -COMPILE_ASSERT((IsSameType::Type>::value), WTF_test_RemoveVolatile_volatile_bool); - -COMPILE_ASSERT((IsSameType::Type>::value), WTF_test_RemoveConstVolatile_bool); -COMPILE_ASSERT((IsSameType::Type>::value), WTF_test_RemoveConstVolatile_const_bool); -COMPILE_ASSERT((IsSameType::Type>::value), WTF_test_RemoveConstVolatile_volatile_bool); -COMPILE_ASSERT((IsSameType::Type>::value), WTF_test_RemoveConstVolatile_const_volatile_bool); - -COMPILE_ASSERT((IsSameType::Type>::value), WTF_Test_RemovePointer_int); -COMPILE_ASSERT((IsSameType::Type>::value), WTF_Test_RemovePointer_int_pointer); -COMPILE_ASSERT((!IsSameType::Type>::value), WTF_Test_RemovePointer_int_pointer_pointer); - -COMPILE_ASSERT((IsSameType::Type>::value), WTF_Test_RemoveReference_int); -COMPILE_ASSERT((IsSameType::Type>::value), WTF_Test_RemoveReference_int_reference); - +class TestBaseClass {}; + +class TestDerivedClass : public TestBaseClass {}; + +COMPILE_ASSERT((IsSubclass>::value), + WTF_Test_IsSubclass_Derived_From_Base); +COMPILE_ASSERT((!IsSubclass, TestDerivedClass>::value), + WTF_Test_IsSubclass_Base_From_Derived); +COMPILE_ASSERT((IsSubclassOfTemplate::value), + WTF_Test_IsSubclassOfTemplate_Base_From_Derived); +COMPILE_ASSERT( + (IsSameType, TestBaseClass>::Type, + int>::value), + WTF_Test_RemoveTemplate); +COMPILE_ASSERT( + (IsSameType::Type, int>::value), + WTF_Test_RemoveTemplate_WithoutTemplate); +COMPILE_ASSERT( + (IsPointerConvertible>::Value), + WTF_Test_IsPointerConvertible_Derived_To_Base); +COMPILE_ASSERT( + (!IsPointerConvertible, TestDerivedClass>::Value), + WTF_Test_IsPointerConvertible_Base_To_Derived); + +COMPILE_ASSERT((IsSameType::Type>::value), + WTF_test_RemoveConst_const_bool); +COMPILE_ASSERT((!IsSameType::Type>::value), + WTF_test_RemoveConst_volatile_bool); + +COMPILE_ASSERT((IsSameType::Type>::value), + WTF_test_RemoveVolatile_bool); +COMPILE_ASSERT((!IsSameType::Type>::value), + WTF_test_RemoveVolatile_const_bool); +COMPILE_ASSERT((IsSameType::Type>::value), + WTF_test_RemoveVolatile_volatile_bool); + +COMPILE_ASSERT((IsSameType::Type>::value), + WTF_test_RemoveConstVolatile_bool); +COMPILE_ASSERT((IsSameType::Type>::value), + WTF_test_RemoveConstVolatile_const_bool); +COMPILE_ASSERT( + (IsSameType::Type>::value), + WTF_test_RemoveConstVolatile_volatile_bool); +COMPILE_ASSERT( + (IsSameType::Type>::value), + WTF_test_RemoveConstVolatile_const_volatile_bool); + +COMPILE_ASSERT((IsSameType::Type>::value), + WTF_Test_RemovePointer_int); +COMPILE_ASSERT((IsSameType::Type>::value), + WTF_Test_RemovePointer_int_pointer); +COMPILE_ASSERT((!IsSameType::Type>::value), + WTF_Test_RemovePointer_int_pointer_pointer); + +COMPILE_ASSERT((IsSameType::Type>::value), + WTF_Test_RemoveReference_int); +COMPILE_ASSERT((IsSameType::Type>::value), + WTF_Test_RemoveReference_int_reference); typedef int IntArray[]; typedef int IntArraySized[4]; COMPILE_ASSERT((IsArray::value), WTF_Test_IsArray_int_array); -COMPILE_ASSERT((IsArray::value), WTF_Test_IsArray_int_sized_array); +COMPILE_ASSERT((IsArray::value), + WTF_Test_IsArray_int_sized_array); -COMPILE_ASSERT((IsSameType::Type>::value), WTF_Test_RemoveExtent_int_array); -COMPILE_ASSERT((IsSameType::Type>::value), WTF_Test_RemoveReference_int_sized_array); +COMPILE_ASSERT((IsSameType::Type>::value), + WTF_Test_RemoveExtent_int_array); +COMPILE_ASSERT((IsSameType::Type>::value), + WTF_Test_RemoveReference_int_sized_array); -} // namespace WTF +} // namespace WTF diff --git a/sky/engine/wtf/TypeTraits.h b/sky/engine/wtf/TypeTraits.h index cf58ffb9f3b17..ee082bc4bffd6 100644 --- a/sky/engine/wtf/TypeTraits.h +++ b/sky/engine/wtf/TypeTraits.h @@ -1,4 +1,4 @@ - /* +/* * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. * Copyright (C) 2009, 2010 Google Inc. All rights reserved. * @@ -27,267 +27,384 @@ namespace WTF { - // The following are provided in this file: - // - // IsInteger::value - // IsPod::value, see the definition for a note about its limitations - // IsConvertibleToInteger::value - // - // IsArray::value - // - // IsSameType::value - // - // RemovePointer::Type - // RemoveReference::Type - // RemoveConst::Type - // RemoveVolatile::Type - // RemoveConstVolatile::Type - // RemoveExtent::Type - // - // COMPILE_ASSERT's in TypeTraits.cpp illustrate their usage and what they do. - - template struct EnableIf; - template struct EnableIf { typedef T Type; }; - - template struct IsInteger { static const bool value = false; }; - template<> struct IsInteger { static const bool value = true; }; - template<> struct IsInteger { static const bool value = true; }; - template<> struct IsInteger { static const bool value = true; }; - template<> struct IsInteger { static const bool value = true; }; - template<> struct IsInteger { static const bool value = true; }; - template<> struct IsInteger { static const bool value = true; }; - template<> struct IsInteger { static const bool value = true; }; - template<> struct IsInteger { static const bool value = true; }; - template<> struct IsInteger { static const bool value = true; }; - template<> struct IsInteger { static const bool value = true; }; - template<> struct IsInteger { static const bool value = true; }; - template<> struct IsInteger { static const bool value = true; }; - template<> struct IsInteger { static const bool value = true; }; - - template struct IsFloatingPoint { static const bool value = false; }; - template<> struct IsFloatingPoint { static const bool value = true; }; - template<> struct IsFloatingPoint { static const bool value = true; }; - template<> struct IsFloatingPoint { static const bool value = true; }; - - template struct IsArithmetic { static const bool value = IsInteger::value || IsFloatingPoint::value; }; - - template struct IsWeak { static const bool value = false; }; - - enum WeakHandlingFlag { - NoWeakHandlingInCollections, - WeakHandlingInCollections +// The following are provided in this file: +// +// IsInteger::value +// IsPod::value, see the definition for a note about its limitations +// IsConvertibleToInteger::value +// +// IsArray::value +// +// IsSameType::value +// +// RemovePointer::Type +// RemoveReference::Type +// RemoveConst::Type +// RemoveVolatile::Type +// RemoveConstVolatile::Type +// RemoveExtent::Type +// +// COMPILE_ASSERT's in TypeTraits.cpp illustrate their usage and what they do. + +template +struct EnableIf; +template +struct EnableIf { + typedef T Type; +}; + +template +struct IsInteger { + static const bool value = false; +}; +template <> +struct IsInteger { + static const bool value = true; +}; +template <> +struct IsInteger { + static const bool value = true; +}; +template <> +struct IsInteger { + static const bool value = true; +}; +template <> +struct IsInteger { + static const bool value = true; +}; +template <> +struct IsInteger { + static const bool value = true; +}; +template <> +struct IsInteger { + static const bool value = true; +}; +template <> +struct IsInteger { + static const bool value = true; +}; +template <> +struct IsInteger { + static const bool value = true; +}; +template <> +struct IsInteger { + static const bool value = true; +}; +template <> +struct IsInteger { + static const bool value = true; +}; +template <> +struct IsInteger { + static const bool value = true; +}; +template <> +struct IsInteger { + static const bool value = true; +}; +template <> +struct IsInteger { + static const bool value = true; +}; + +template +struct IsFloatingPoint { + static const bool value = false; +}; +template <> +struct IsFloatingPoint { + static const bool value = true; +}; +template <> +struct IsFloatingPoint { + static const bool value = true; +}; +template <> +struct IsFloatingPoint { + static const bool value = true; +}; + +template +struct IsArithmetic { + static const bool value = IsInteger::value || IsFloatingPoint::value; +}; + +template +struct IsWeak { + static const bool value = false; +}; + +enum WeakHandlingFlag { + NoWeakHandlingInCollections, + WeakHandlingInCollections +}; + +// IsPod is misnamed as it doesn't cover all plain old data (pod) types. +// Specifically, it doesn't allow for enums or for structs. +template +struct IsPod { + static const bool value = IsArithmetic::value; +}; +template +struct IsPod { + static const bool value = true; +}; + +template +class IsConvertibleToInteger { + // Avoid "possible loss of data" warning when using Microsoft's C++ compiler + // by not converting int's to doubles. + template + class IsConvertibleToDouble; + template + class IsConvertibleToDouble { + public: + static const bool value = false; + }; + + template + class IsConvertibleToDouble { + typedef char YesType; + struct NoType { + char padding[8]; }; - // IsPod is misnamed as it doesn't cover all plain old data (pod) types. - // Specifically, it doesn't allow for enums or for structs. - template struct IsPod { static const bool value = IsArithmetic::value; }; - template struct IsPod { static const bool value = true; }; - - template class IsConvertibleToInteger { - // Avoid "possible loss of data" warning when using Microsoft's C++ compiler - // by not converting int's to doubles. - template class IsConvertibleToDouble; - template class IsConvertibleToDouble { - public: - static const bool value = false; - }; - - template class IsConvertibleToDouble { - typedef char YesType; - struct NoType { - char padding[8]; - }; - - static YesType floatCheck(long double); - static NoType floatCheck(...); - static T& t; - public: - static const bool value = sizeof(floatCheck(t)) == sizeof(YesType); - }; - - public: - static const bool value = IsInteger::value || IsConvertibleToDouble::value, T>::value; - }; - - template class IsPointerConvertible { - typedef char YesType; - struct NoType { - char padding[8]; - }; - - static YesType convertCheck(To* x); - static NoType convertCheck(...); - public: - enum { - Value = (sizeof(YesType) == sizeof(convertCheck(static_cast(0)))) - }; - }; - - template struct IsArray { - static const bool value = false; - }; - - template struct IsArray { - static const bool value = true; - }; - - template struct IsArray { - static const bool value = true; - }; - - - template struct IsSameType { - static const bool value = false; - }; - - template struct IsSameType { - static const bool value = true; - }; - - template class IsSubclass { - typedef char YesType; - struct NoType { - char padding[8]; - }; - - static YesType subclassCheck(U*); - static NoType subclassCheck(...); - static T* t; - public: - static const bool value = sizeof(subclassCheck(t)) == sizeof(YesType); - }; - - template class U> class IsSubclassOfTemplate { - typedef char YesType; - struct NoType { - char padding[8]; - }; - - template static YesType subclassCheck(U*); - static NoType subclassCheck(...); - static T* t; - public: - static const bool value = sizeof(subclassCheck(t)) == sizeof(YesType); - }; - - template class U> class IsSubclassOfTemplateTypenameSize { - typedef char YesType; - struct NoType { - char padding[8]; - }; - - template static YesType subclassCheck(U*); - static NoType subclassCheck(...); - static T* t; - public: - static const bool value = sizeof(subclassCheck(t)) == sizeof(YesType); - }; - - template class U> class IsSubclassOfTemplateTypenameSizeTypename { - typedef char YesType; - struct NoType { - char padding[8]; - }; - - template static YesType subclassCheck(U*); - static NoType subclassCheck(...); - static T* t; - public: - static const bool value = sizeof(subclassCheck(t)) == sizeof(YesType); - }; - - template class U> class IsSubclassOfTemplate3 { - typedef char YesType; - struct NoType { - char padding[8]; - }; - - template static YesType subclassCheck(U*); - static NoType subclassCheck(...); - static T* t; - public: - static const bool value = sizeof(subclassCheck(t)) == sizeof(YesType); - }; - - template class U> class IsSubclassOfTemplate5 { - typedef char YesType; - struct NoType { - char padding[8]; - }; - - template static YesType subclassCheck(U*); - static NoType subclassCheck(...); - static T* t; - public: - static const bool value = sizeof(subclassCheck(t)) == sizeof(YesType); - }; - - template class OuterTemplate> struct RemoveTemplate { - typedef T Type; - }; - - template class OuterTemplate> struct RemoveTemplate, OuterTemplate> { - typedef T Type; - }; - - template struct RemoveConst { - typedef T Type; - }; - - template struct RemoveConst { - typedef T Type; - }; - - template struct RemoveVolatile { - typedef T Type; - }; - - template struct RemoveVolatile { - typedef T Type; - }; - - template struct RemoveConstVolatile { - typedef typename RemoveVolatile::Type>::Type Type; - }; - - template struct RemovePointer { - typedef T Type; - }; - - template struct RemovePointer { - typedef T Type; - }; - - template struct RemoveReference { - typedef T Type; - }; - - template struct RemoveReference { - typedef T Type; - }; - - template struct RemoveExtent { - typedef T Type; - }; - - template struct RemoveExtent { - typedef T Type; - }; - - template struct RemoveExtent { - typedef T Type; - }; - - // Determines whether this type has a vtable. - template struct IsPolymorphic { - static const bool value = __is_polymorphic(T); - }; - -#define EnsurePtrConvertibleArgDecl(From, To) \ - typename WTF::EnableIf::Value, bool>::Type = true + static YesType floatCheck(long double); + static NoType floatCheck(...); + static T& t; + + public: + static const bool value = sizeof(floatCheck(t)) == sizeof(YesType); + }; + + public: + static const bool value = + IsInteger::value || + IsConvertibleToDouble::value, T>::value; +}; + +template +class IsPointerConvertible { + typedef char YesType; + struct NoType { + char padding[8]; + }; + + static YesType convertCheck(To* x); + static NoType convertCheck(...); + + public: + enum { + Value = (sizeof(YesType) == sizeof(convertCheck(static_cast(0)))) + }; +}; + +template +struct IsArray { + static const bool value = false; +}; + +template +struct IsArray { + static const bool value = true; +}; + +template +struct IsArray { + static const bool value = true; +}; + +template +struct IsSameType { + static const bool value = false; +}; + +template +struct IsSameType { + static const bool value = true; +}; + +template +class IsSubclass { + typedef char YesType; + struct NoType { + char padding[8]; + }; + + static YesType subclassCheck(U*); + static NoType subclassCheck(...); + static T* t; + + public: + static const bool value = sizeof(subclassCheck(t)) == sizeof(YesType); +}; + +template class U> +class IsSubclassOfTemplate { + typedef char YesType; + struct NoType { + char padding[8]; + }; + + template + static YesType subclassCheck(U*); + static NoType subclassCheck(...); + static T* t; + + public: + static const bool value = sizeof(subclassCheck(t)) == sizeof(YesType); +}; + +template class U> +class IsSubclassOfTemplateTypenameSize { + typedef char YesType; + struct NoType { + char padding[8]; + }; + + template + static YesType subclassCheck(U*); + static NoType subclassCheck(...); + static T* t; + + public: + static const bool value = sizeof(subclassCheck(t)) == sizeof(YesType); +}; + +template class U> +class IsSubclassOfTemplateTypenameSizeTypename { + typedef char YesType; + struct NoType { + char padding[8]; + }; + + template + static YesType subclassCheck(U*); + static NoType subclassCheck(...); + static T* t; + + public: + static const bool value = sizeof(subclassCheck(t)) == sizeof(YesType); +}; + +template class U> +class IsSubclassOfTemplate3 { + typedef char YesType; + struct NoType { + char padding[8]; + }; + + template + static YesType subclassCheck(U*); + static NoType subclassCheck(...); + static T* t; + + public: + static const bool value = sizeof(subclassCheck(t)) == sizeof(YesType); +}; + +template class U> +class IsSubclassOfTemplate5 { + typedef char YesType; + struct NoType { + char padding[8]; + }; + + template + static YesType subclassCheck(U*); + static NoType subclassCheck(...); + static T* t; + + public: + static const bool value = sizeof(subclassCheck(t)) == sizeof(YesType); +}; + +template class OuterTemplate> +struct RemoveTemplate { + typedef T Type; +}; + +template class OuterTemplate> +struct RemoveTemplate, OuterTemplate> { + typedef T Type; +}; + +template +struct RemoveConst { + typedef T Type; +}; + +template +struct RemoveConst { + typedef T Type; +}; + +template +struct RemoveVolatile { + typedef T Type; +}; + +template +struct RemoveVolatile { + typedef T Type; +}; + +template +struct RemoveConstVolatile { + typedef typename RemoveVolatile::Type>::Type Type; +}; + +template +struct RemovePointer { + typedef T Type; +}; + +template +struct RemovePointer { + typedef T Type; +}; + +template +struct RemoveReference { + typedef T Type; +}; + +template +struct RemoveReference { + typedef T Type; +}; + +template +struct RemoveExtent { + typedef T Type; +}; + +template +struct RemoveExtent { + typedef T Type; +}; + +template +struct RemoveExtent { + typedef T Type; +}; + +// Determines whether this type has a vtable. +template +struct IsPolymorphic { + static const bool value = __is_polymorphic(T); +}; + +#define EnsurePtrConvertibleArgDecl(From, To) \ + typename WTF::EnableIf::Value, \ + bool>::Type = true #define EnsurePtrConvertibleArgDefn(From, To) \ - typename WTF::EnableIf::Value, bool>::Type + typename WTF::EnableIf::Value, bool>::Type -} // namespace WTF +} // namespace WTF #endif // SKY_ENGINE_WTF_TYPETRAITS_H_ diff --git a/sky/engine/wtf/Vector.h b/sky/engine/wtf/Vector.h index 3c3651e82cf3b..16deb88572d55 100644 --- a/sky/engine/wtf/Vector.h +++ b/sky/engine/wtf/Vector.h @@ -44,1098 +44,1121 @@ static const size_t kInitialVectorSize = 1; static const size_t kInitialVectorSize = WTF_VECTOR_INITIAL_SIZE; #endif - template - class Deque; - - template - struct VectorDestructor; - - template - struct VectorDestructor - { - static void destruct(T*, T*) {} - }; - - template - struct VectorDestructor - { - static void destruct(T* begin, T* end) - { - for (T* cur = begin; cur != end; ++cur) - cur->~T(); - } - }; - - template - struct VectorInitializer; - - template - struct VectorInitializer - { - static void initialize(T* begin, T* end) - { - for (T* cur = begin; cur != end; ++cur) - new (NotNull, cur) T; - } - }; - - template - struct VectorInitializer - { - static void initialize(T* begin, T* end) - { - memset(begin, 0, reinterpret_cast(end) - reinterpret_cast(begin)); - } - }; - - template - struct VectorMover; - - template - struct VectorMover - { - static void move(const T* src, const T* srcEnd, T* dst) - { - while (src != srcEnd) { - new (NotNull, dst) T(*src); - src->~T(); - ++dst; - ++src; - } - } - static void moveOverlapping(const T* src, const T* srcEnd, T* dst) - { - if (src > dst) - move(src, srcEnd, dst); - else { - T* dstEnd = dst + (srcEnd - src); - while (src != srcEnd) { - --srcEnd; - --dstEnd; - new (NotNull, dstEnd) T(*srcEnd); - srcEnd->~T(); - } - } - } - static void swap(T* src, T* srcEnd, T* dst) - { - std::swap_ranges(src, srcEnd, dst); - } - }; - - template - struct VectorMover - { - static void move(const T* src, const T* srcEnd, T* dst) - { - memcpy(dst, src, reinterpret_cast(srcEnd) - reinterpret_cast(src)); - } - static void moveOverlapping(const T* src, const T* srcEnd, T* dst) - { - memmove(dst, src, reinterpret_cast(srcEnd) - reinterpret_cast(src)); - } - static void swap(T* src, T* srcEnd, T* dst) - { - std::swap_ranges(reinterpret_cast(src), reinterpret_cast(srcEnd), reinterpret_cast(dst)); - } - }; - - template - struct VectorCopier; - - template - struct VectorCopier - { - template - static void uninitializedCopy(const U* src, const U* srcEnd, T* dst) - { - while (src != srcEnd) { - new (NotNull, dst) T(*src); - ++dst; - ++src; - } - } - }; - - template - struct VectorCopier - { - static void uninitializedCopy(const T* src, const T* srcEnd, T* dst) - { - memcpy(dst, src, reinterpret_cast(srcEnd) - reinterpret_cast(src)); - } - template - static void uninitializedCopy(const U* src, const U* srcEnd, T* dst) - { - VectorCopier::uninitializedCopy(src, srcEnd, dst); - } - }; - - template - struct VectorFiller; - - template - struct VectorFiller - { - static void uninitializedFill(T* dst, T* dstEnd, const T& val) - { - while (dst != dstEnd) { - new (NotNull, dst) T(val); - ++dst; - } - } - }; - - template - struct VectorFiller - { - static void uninitializedFill(T* dst, T* dstEnd, const T& val) - { - COMPILE_ASSERT(sizeof(T) == sizeof(char), Size_of_type_should_be_equal_to_one); -#if COMPILER(GCC) && defined(_FORTIFY_SOURCE) - if (!__builtin_constant_p(dstEnd - dst) || (!(dstEnd - dst))) -#endif - memset(dst, val, dstEnd - dst); - } - }; - - template - struct VectorComparer; - - template - struct VectorComparer - { - static bool compare(const T* a, const T* b, size_t size) - { - if (LIKELY(a && b)) - return std::equal(a, a + size, b); - return !a && !b; - } - }; - - template - struct VectorComparer - { - static bool compare(const T* a, const T* b, size_t size) - { - return memcmp(a, b, sizeof(T) * size) == 0; - } - }; - - template - struct VectorTypeOperations - { - static void destruct(T* begin, T* end) - { - VectorDestructor::needsDestruction, T>::destruct(begin, end); - } - - static void initialize(T* begin, T* end) - { - VectorInitializer::canInitializeWithMemset, T>::initialize(begin, end); - } - - static void move(const T* src, const T* srcEnd, T* dst) - { - VectorMover::canMoveWithMemcpy, T>::move(src, srcEnd, dst); - } - - static void moveOverlapping(const T* src, const T* srcEnd, T* dst) - { - VectorMover::canMoveWithMemcpy, T>::moveOverlapping(src, srcEnd, dst); - } - - static void swap(T* src, T* srcEnd, T* dst) - { - VectorMover::canMoveWithMemcpy, T>::swap(src, srcEnd, dst); - } - - static void uninitializedCopy(const T* src, const T* srcEnd, T* dst) - { - VectorCopier::canCopyWithMemcpy, T>::uninitializedCopy(src, srcEnd, dst); - } - - static void uninitializedFill(T* dst, T* dstEnd, const T& val) - { - VectorFiller::canFillWithMemset, T>::uninitializedFill(dst, dstEnd, val); - } - - static bool compare(const T* a, const T* b, size_t size) - { - return VectorComparer::canCompareWithMemcmp, T>::compare(a, b, size); - } - }; - - template - class VectorBufferBase { - WTF_MAKE_NONCOPYABLE(VectorBufferBase); - public: - void allocateBuffer(size_t newCapacity) - { - typedef typename Allocator::template VectorBackingHelper >::Type VectorBacking; - ASSERT(newCapacity); - size_t sizeToAllocate = allocationSize(newCapacity); - m_buffer = Allocator::template backingMalloc(sizeToAllocate); - m_capacity = sizeToAllocate / sizeof(T); - } - - size_t allocationSize(size_t capacity) const - { - return Allocator::Quantizer::template quantizedSize(capacity); - } - - T* buffer() { return m_buffer; } - const T* buffer() const { return m_buffer; } - size_t capacity() const { return m_capacity; } - - protected: - VectorBufferBase() - : m_buffer(0) - , m_capacity(0) - { - } - - VectorBufferBase(T* buffer, size_t capacity) - : m_buffer(buffer) - , m_capacity(capacity) - { - } - - T* m_buffer; - unsigned m_capacity; - unsigned m_size; - }; - - template - class VectorBuffer; - - template - class VectorBuffer : protected VectorBufferBase { - private: - typedef VectorBufferBase Base; - public: - VectorBuffer() - { - } - - VectorBuffer(size_t capacity) - { - // Calling malloc(0) might take a lock and may actually do an - // allocation on some systems. - if (capacity) - allocateBuffer(capacity); - } - - void destruct() - { - deallocateBuffer(m_buffer); - m_buffer = 0; - } - - void deallocateBuffer(T* bufferToDeallocate) - { - Allocator::backingFree(bufferToDeallocate); - } - - void resetBufferPointer() - { - m_buffer = 0; - m_capacity = 0; - } - - void swapVectorBuffer(VectorBuffer& other) - { - std::swap(m_buffer, other.m_buffer); - std::swap(m_capacity, other.m_capacity); - } - - using Base::allocateBuffer; - using Base::allocationSize; - - using Base::buffer; - using Base::capacity; - - bool hasOutOfLineBuffer() const - { - // When inlineCapacity is 0 we have an out of line buffer if we have a buffer. - return buffer(); - } - - protected: - using Base::m_size; - - private: - using Base::m_buffer; - using Base::m_capacity; - }; - - template - class VectorBuffer : protected VectorBufferBase { - WTF_MAKE_NONCOPYABLE(VectorBuffer); - private: - typedef VectorBufferBase Base; - public: - VectorBuffer() - : Base(inlineBuffer(), inlineCapacity) - { - } - - VectorBuffer(size_t capacity) - : Base(inlineBuffer(), inlineCapacity) - { - if (capacity > inlineCapacity) - Base::allocateBuffer(capacity); - } - - void destruct() - { - deallocateBuffer(m_buffer); - m_buffer = 0; - } - - NEVER_INLINE void reallyDeallocateBuffer(T* bufferToDeallocate) - { - Allocator::backingFree(bufferToDeallocate); - } - - void deallocateBuffer(T* bufferToDeallocate) - { - if (UNLIKELY(bufferToDeallocate != inlineBuffer())) - reallyDeallocateBuffer(bufferToDeallocate); - } - - void resetBufferPointer() - { - m_buffer = inlineBuffer(); - m_capacity = inlineCapacity; - } - - void allocateBuffer(size_t newCapacity) - { - // FIXME: This should ASSERT(!m_buffer) to catch misuse/leaks. - if (newCapacity > inlineCapacity) - Base::allocateBuffer(newCapacity); - else - resetBufferPointer(); - } - - size_t allocationSize(size_t capacity) const - { - if (capacity <= inlineCapacity) - return m_inlineBufferSize; - return Base::allocationSize(capacity); - } - - void swapVectorBuffer(VectorBuffer& other) - { - typedef VectorTypeOperations TypeOperations; - - if (buffer() == inlineBuffer() && other.buffer() == other.inlineBuffer()) { - ASSERT(m_capacity == other.m_capacity); - if (m_size > other.m_size) { - TypeOperations::swap(inlineBuffer(), inlineBuffer() + other.m_size, other.inlineBuffer()); - TypeOperations::move(inlineBuffer() + other.m_size, inlineBuffer() + m_size, other.inlineBuffer() + other.m_size); - } else { - TypeOperations::swap(inlineBuffer(), inlineBuffer() + m_size, other.inlineBuffer()); - TypeOperations::move(other.inlineBuffer() + m_size, other.inlineBuffer() + other.m_size, inlineBuffer() + m_size); - } - } else if (buffer() == inlineBuffer()) { - m_buffer = other.m_buffer; - other.m_buffer = other.inlineBuffer(); - TypeOperations::move(inlineBuffer(), inlineBuffer() + m_size, other.inlineBuffer()); - std::swap(m_capacity, other.m_capacity); - } else if (other.buffer() == other.inlineBuffer()) { - other.m_buffer = m_buffer; - m_buffer = inlineBuffer(); - TypeOperations::move(other.inlineBuffer(), other.inlineBuffer() + other.m_size, inlineBuffer()); - std::swap(m_capacity, other.m_capacity); - } else { - std::swap(m_buffer, other.m_buffer); - std::swap(m_capacity, other.m_capacity); - } - } - - using Base::buffer; - using Base::capacity; - - bool hasOutOfLineBuffer() const - { - return buffer() && buffer() != inlineBuffer(); - } - - protected: - using Base::m_size; - - private: - using Base::m_buffer; - using Base::m_capacity; - - static const size_t m_inlineBufferSize = inlineCapacity * sizeof(T); - T* inlineBuffer() { return reinterpret_cast_ptr(m_inlineBuffer.buffer); } - const T* inlineBuffer() const { return reinterpret_cast_ptr(m_inlineBuffer.buffer); } - - AlignedBuffer m_inlineBuffer; - template - friend class Deque; - }; - - template - class Vector; - - // VectorDestructorBase defines the destructor of a vector. This base is used in order to - // completely avoid creating a destructor for a vector that does not need to be destructed. - // By doing so, the clang compiler will have correct information about whether or not a - // vector has a trivial destructor and we use that in a compiler plugin to ensure the - // correctness of non-finalized garbage-collected classes and the use of VectorTraits::needsDestruction. - - // All non-GC managed vectors need a destructor. This destructor will simply call finalize on the actual vector type. - template - class VectorDestructorBase { - public: - ~VectorDestructorBase() { static_cast(this)->finalize(); } - }; - - // Heap-allocated vectors with no inlineCapacity never need a destructor. - template - class VectorDestructorBase { }; - - // Heap-allocator vectors with inlineCapacity need a destructor if the inline elements do. - // The use of VectorTraits::needsDestruction is delayed until we know that - // inlineCapacity is non-zero to allow classes that recursively refer to themselves in vector - // members. If inlineCapacity is non-zero doing so would have undefined meaning, so in this - // case we can use HeapVectorWithInlineCapacityDestructorBase to define a destructor - // depending on the value of VectorTraits::needsDestruction. - template - class HeapVectorWithInlineCapacityDestructorBase; - - template - class HeapVectorWithInlineCapacityDestructorBase { - public: - ~HeapVectorWithInlineCapacityDestructorBase() { static_cast(this)->finalize(); } - }; - - template - class HeapVectorWithInlineCapacityDestructorBase { }; - - template - class VectorDestructorBase : public HeapVectorWithInlineCapacityDestructorBase::needsDestruction> { }; - - template - class Vector : private VectorBuffer, public VectorDestructorBase, T, (inlineCapacity > 0), Allocator::isGarbageCollected> { - WTF_USE_ALLOCATOR(Vector, Allocator); - private: - typedef VectorBuffer Base; - typedef VectorTypeOperations TypeOperations; - - public: - typedef T ValueType; - - typedef T* iterator; - typedef const T* const_iterator; - typedef std::reverse_iterator reverse_iterator; - typedef std::reverse_iterator const_reverse_iterator; - - Vector() - { - // Unused slots are initialized to zero so that the visitor and the - // finalizer can visit them safely. canInitializeWithMemset tells us - // that the class does not expect matching constructor and - // destructor calls as long as the memory is zeroed. - COMPILE_ASSERT(!Allocator::isGarbageCollected || !VectorTraits::needsDestruction || VectorTraits::canInitializeWithMemset, ClassHasProblemsWithFinalizersCalledOnClearedMemory); - COMPILE_ASSERT(!WTF::IsPolymorphic::value || !VectorTraits::canInitializeWithMemset, CantInitializeWithMemsetIfThereIsAVtable); - m_size = 0; - } - - explicit Vector(size_t size) - : Base(size) - { - // Unused slots are initialized to zero so that the visitor and the - // finalizer can visit them safely. canInitializeWithMemset tells us - // that the class does not expect matching constructor and - // destructor calls as long as the memory is zeroed. - COMPILE_ASSERT(!Allocator::isGarbageCollected || !VectorTraits::needsDestruction || VectorTraits::canInitializeWithMemset, ClassHasProblemsWithFinalizersCalledOnClearedMemory); - m_size = size; - TypeOperations::initialize(begin(), end()); - } - - // Off-GC-heap vectors: Destructor should be called. - // On-GC-heap vectors: Destructor should be called for inline buffers - // (if any) but destructor shouldn't be called for vector backing since - // it is managed by the traced GC heap. - void finalize() - { - if (!inlineCapacity) { - if (LIKELY(!Base::buffer())) - return; - } - if (LIKELY(m_size) && !(Allocator::isGarbageCollected && this->hasOutOfLineBuffer())) { - TypeOperations::destruct(begin(), end()); - m_size = 0; // Partial protection against use-after-free. - } - - Base::destruct(); - } - - Vector(const Vector&); - template - explicit Vector(const Vector&); - - Vector& operator=(const Vector&); - template - Vector& operator=(const Vector&); - - Vector(Vector&&); - Vector& operator=(Vector&&); - - size_t size() const { return m_size; } - size_t capacity() const { return Base::capacity(); } - bool isEmpty() const { return !size(); } - - T& at(size_t i) - { - RELEASE_ASSERT(i < size()); - return Base::buffer()[i]; - } - const T& at(size_t i) const - { - RELEASE_ASSERT(i < size()); - return Base::buffer()[i]; - } - - T& operator[](size_t i) { return at(i); } - const T& operator[](size_t i) const { return at(i); } - - T* data() { return Base::buffer(); } - const T* data() const { return Base::buffer(); } - - iterator begin() { return data(); } - iterator end() { return begin() + m_size; } - const_iterator begin() const { return data(); } - const_iterator end() const { return begin() + m_size; } - - reverse_iterator rbegin() { return reverse_iterator(end()); } - reverse_iterator rend() { return reverse_iterator(begin()); } - const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } - const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } - - T& first() { return at(0); } - const T& first() const { return at(0); } - T& last() { return at(size() - 1); } - const T& last() const { return at(size() - 1); } - - template bool contains(const U&) const; - template size_t find(const U&) const; - template size_t reverseFind(const U&) const; - - void shrink(size_t size); - void grow(size_t size); - void resize(size_t size); - void reserveCapacity(size_t newCapacity); - void reserveInitialCapacity(size_t initialCapacity); - void shrinkToFit() { shrinkCapacity(size()); } - void shrinkToReasonableCapacity() - { - if (size() * 2 < capacity()) - shrinkCapacity(size() + size() / 4 + 1); - } - - void clear() { shrinkCapacity(0); } - - template void append(const U*, size_t); - template void append(const U&); - template void uncheckedAppend(const U& val); - template void appendVector(const Vector&); - - template void insert(size_t position, const U*, size_t); - template void insert(size_t position, const U&); - template void insert(size_t position, const Vector&); - - template void prepend(const U*, size_t); - template void prepend(const U&); - template void prepend(const Vector&); - - void remove(size_t position); - void remove(size_t position, size_t length); - - void removeLast() - { - ASSERT(!isEmpty()); - shrink(size() - 1); - } - - Vector(size_t size, const T& val) - : Base(size) - { - m_size = size; - TypeOperations::uninitializedFill(begin(), end(), val); - } - - void fill(const T&, size_t); - void fill(const T& val) { fill(val, size()); } - - template void appendRange(Iterator start, Iterator end); - - void swap(Vector& other) - { - Base::swapVectorBuffer(other); - std::swap(m_size, other.m_size); - } - - void reverse(); - - private: - void expandCapacity(size_t newMinCapacity); - const T* expandCapacity(size_t newMinCapacity, const T*); - template U* expandCapacity(size_t newMinCapacity, U*); - void shrinkCapacity(size_t newCapacity); - template void appendSlowCase(const U&); - - using Base::m_size; - using Base::buffer; - using Base::capacity; - using Base::swapVectorBuffer; - using Base::allocateBuffer; - using Base::allocationSize; - }; - - template - Vector::Vector(const Vector& other) - : Base(other.capacity()) - { - m_size = other.size(); - TypeOperations::uninitializedCopy(other.begin(), other.end(), begin()); - } - - template - template - Vector::Vector(const Vector& other) - : Base(other.capacity()) - { - m_size = other.size(); - TypeOperations::uninitializedCopy(other.begin(), other.end(), begin()); - } - - template - Vector& Vector::operator=(const Vector& other) - { - if (UNLIKELY(&other == this)) - return *this; - - if (size() > other.size()) - shrink(other.size()); - else if (other.size() > capacity()) { - clear(); - reserveCapacity(other.size()); - ASSERT(begin()); - } - - std::copy(other.begin(), other.begin() + size(), begin()); - TypeOperations::uninitializedCopy(other.begin() + size(), other.end(), end()); - m_size = other.size(); - - return *this; - } - - inline bool typelessPointersAreEqual(const void* a, const void* b) { return a == b; } - - template - template - Vector& Vector::operator=(const Vector& other) - { - // If the inline capacities match, we should call the more specific - // template. If the inline capacities don't match, the two objects - // shouldn't be allocated the same address. - ASSERT(!typelessPointersAreEqual(&other, this)); - - if (size() > other.size()) - shrink(other.size()); - else if (other.size() > capacity()) { - clear(); - reserveCapacity(other.size()); - ASSERT(begin()); - } - - std::copy(other.begin(), other.begin() + size(), begin()); - TypeOperations::uninitializedCopy(other.begin() + size(), other.end(), end()); - m_size = other.size(); - - return *this; - } - - template - Vector::Vector(Vector&& other) - { - m_size = 0; - // It's a little weird to implement a move constructor using swap but this way we - // don't have to add a move constructor to VectorBuffer. - swap(other); - } - - template - Vector& Vector::operator=(Vector&& other) - { - swap(other); - return *this; - } - - template - template - bool Vector::contains(const U& value) const - { - return find(value) != kNotFound; - } - - template - template - size_t Vector::find(const U& value) const - { - const T* b = begin(); - const T* e = end(); - for (const T* iter = b; iter < e; ++iter) { - if (*iter == value) - return iter - b; - } - return kNotFound; - } - - template - template - size_t Vector::reverseFind(const U& value) const - { - const T* b = begin(); - const T* iter = end(); - while (iter > b) { - --iter; - if (*iter == value) - return iter - b; - } - return kNotFound; - } - - template - void Vector::fill(const T& val, size_t newSize) - { - if (size() > newSize) - shrink(newSize); - else if (newSize > capacity()) { - clear(); - reserveCapacity(newSize); - ASSERT(begin()); - } - - std::fill(begin(), end(), val); - TypeOperations::uninitializedFill(end(), begin() + newSize, val); - m_size = newSize; - } - - template - template - void Vector::appendRange(Iterator start, Iterator end) - { - for (Iterator it = start; it != end; ++it) - append(*it); - } - - template - void Vector::expandCapacity(size_t newMinCapacity) - { - size_t oldCapacity = capacity(); - size_t expandedCapacity = oldCapacity; - // We use a more aggressive expansion strategy for Vectors with inline storage. - // This is because they are more likely to be on the stack, so the risk of heap bloat is minimized. - // Furthermore, exceeding the inline capacity limit is not supposed to happen in the common case and may indicate a pathological condition or microbenchmark. - if (inlineCapacity) { - expandedCapacity *= 2; - // Check for integer overflow, which could happen in the 32-bit build. - RELEASE_ASSERT(expandedCapacity > oldCapacity); - } else { - // This cannot integer overflow. - // On 64-bit, the "expanded" integer is 32-bit, and any encroachment above 2^32 will fail allocation in allocateBuffer(). - // On 32-bit, there's not enough address space to hold the old and new buffers. - // In addition, our underlying allocator is supposed to always fail on > (2^31 - 1) allocations. - expandedCapacity += (expandedCapacity / 4) + 1; - } - reserveCapacity(std::max(newMinCapacity, std::max(static_cast(kInitialVectorSize), expandedCapacity))); - } - - template - const T* Vector::expandCapacity(size_t newMinCapacity, const T* ptr) - { - if (ptr < begin() || ptr >= end()) { - expandCapacity(newMinCapacity); - return ptr; - } - size_t index = ptr - begin(); - expandCapacity(newMinCapacity); - return begin() + index; - } - - template template - inline U* Vector::expandCapacity(size_t newMinCapacity, U* ptr) - { - expandCapacity(newMinCapacity); - return ptr; - } - - template - inline void Vector::resize(size_t size) - { - if (size <= m_size) - TypeOperations::destruct(begin() + size, end()); - else { - if (size > capacity()) - expandCapacity(size); - TypeOperations::initialize(end(), begin() + size); - } - - m_size = size; - } - - template - void Vector::shrink(size_t size) - { - ASSERT(size <= m_size); - TypeOperations::destruct(begin() + size, end()); - m_size = size; - } - - template - void Vector::grow(size_t size) - { - ASSERT(size >= m_size); - if (size > capacity()) - expandCapacity(size); - TypeOperations::initialize(end(), begin() + size); - m_size = size; - } - - template - void Vector::reserveCapacity(size_t newCapacity) - { - if (UNLIKELY(newCapacity <= capacity())) - return; - T* oldBuffer = begin(); - T* oldEnd = end(); - Base::allocateBuffer(newCapacity); - TypeOperations::move(oldBuffer, oldEnd, begin()); - Base::deallocateBuffer(oldBuffer); - } - - template - inline void Vector::reserveInitialCapacity(size_t initialCapacity) - { - ASSERT(!m_size); - ASSERT(capacity() == inlineCapacity); - if (initialCapacity > inlineCapacity) - Base::allocateBuffer(initialCapacity); +template +class Deque; + +template +struct VectorDestructor; + +template +struct VectorDestructor { + static void destruct(T*, T*) {} +}; + +template +struct VectorDestructor { + static void destruct(T* begin, T* end) { + for (T* cur = begin; cur != end; ++cur) + cur->~T(); + } +}; + +template +struct VectorInitializer; + +template +struct VectorInitializer { + static void initialize(T* begin, T* end) { + for (T* cur = begin; cur != end; ++cur) + new (NotNull, cur) T; + } +}; + +template +struct VectorInitializer { + static void initialize(T* begin, T* end) { + memset(begin, 0, + reinterpret_cast(end) - reinterpret_cast(begin)); + } +}; + +template +struct VectorMover; + +template +struct VectorMover { + static void move(const T* src, const T* srcEnd, T* dst) { + while (src != srcEnd) { + new (NotNull, dst) T(*src); + src->~T(); + ++dst; + ++src; } - - template - void Vector::shrinkCapacity(size_t newCapacity) - { - if (newCapacity >= capacity()) - return; - - if (newCapacity < size()) - shrink(newCapacity); - - T* oldBuffer = begin(); - if (newCapacity > 0) { - // Optimization: if we're downsizing inside the same allocator bucket, we can exit with no additional work. - if (Base::allocationSize(capacity()) == Base::allocationSize(newCapacity)) - return; - - T* oldEnd = end(); - Base::allocateBuffer(newCapacity); - if (begin() != oldBuffer) - TypeOperations::move(oldBuffer, oldEnd, begin()); - } else { - Base::resetBufferPointer(); - } - - Base::deallocateBuffer(oldBuffer); - } - - // Templatizing these is better than just letting the conversion happen implicitly, - // because for instance it allows a PassRefPtr to be appended to a RefPtr vector - // without refcount thrash. - - template template - void Vector::append(const U* data, size_t dataSize) - { - ASSERT(Allocator::isAllocationAllowed()); - size_t newSize = m_size + dataSize; - if (newSize > capacity()) { - data = expandCapacity(newSize, data); - ASSERT(begin()); - } - RELEASE_ASSERT(newSize >= m_size); - T* dest = end(); - VectorCopier::canCopyWithMemcpy, T>::uninitializedCopy(data, &data[dataSize], dest); - m_size = newSize; - } - - template template - ALWAYS_INLINE void Vector::append(const U& val) - { - ASSERT(Allocator::isAllocationAllowed()); - if (LIKELY(size() != capacity())) { - new (NotNull, end()) T(val); - ++m_size; - return; - } - - appendSlowCase(val); - } - - template template - NEVER_INLINE void Vector::appendSlowCase(const U& val) - { - ASSERT(size() == capacity()); - - const U* ptr = &val; - ptr = expandCapacity(size() + 1, ptr); - ASSERT(begin()); - - new (NotNull, end()) T(*ptr); - ++m_size; - } - - // This version of append saves a branch in the case where you know that the - // vector's capacity is large enough for the append to succeed. - - template template - ALWAYS_INLINE void Vector::uncheckedAppend(const U& val) - { - ASSERT(size() < capacity()); - const U* ptr = &val; - new (NotNull, end()) T(*ptr); - ++m_size; - } - - template template - inline void Vector::appendVector(const Vector& val) - { - append(val.begin(), val.size()); - } - - template template - void Vector::insert(size_t position, const U* data, size_t dataSize) - { - ASSERT(Allocator::isAllocationAllowed()); - RELEASE_ASSERT(position <= size()); - size_t newSize = m_size + dataSize; - if (newSize > capacity()) { - data = expandCapacity(newSize, data); - ASSERT(begin()); - } - RELEASE_ASSERT(newSize >= m_size); - T* spot = begin() + position; - TypeOperations::moveOverlapping(spot, end(), spot + dataSize); - VectorCopier::canCopyWithMemcpy, T>::uninitializedCopy(data, &data[dataSize], spot); - m_size = newSize; - } - - template template - inline void Vector::insert(size_t position, const U& val) - { - ASSERT(Allocator::isAllocationAllowed()); - RELEASE_ASSERT(position <= size()); - const U* data = &val; - if (size() == capacity()) { - data = expandCapacity(size() + 1, data); - ASSERT(begin()); - } - T* spot = begin() + position; - TypeOperations::moveOverlapping(spot, end(), spot + 1); - new (NotNull, spot) T(*data); - ++m_size; - } - - template template - inline void Vector::insert(size_t position, const Vector& val) - { - insert(position, val.begin(), val.size()); - } - - template template - void Vector::prepend(const U* data, size_t dataSize) - { - insert(0, data, dataSize); - } - - template template - inline void Vector::prepend(const U& val) - { - insert(0, val); + } + static void moveOverlapping(const T* src, const T* srcEnd, T* dst) { + if (src > dst) + move(src, srcEnd, dst); + else { + T* dstEnd = dst + (srcEnd - src); + while (src != srcEnd) { + --srcEnd; + --dstEnd; + new (NotNull, dstEnd) T(*srcEnd); + srcEnd->~T(); + } } - - template template - inline void Vector::prepend(const Vector& val) - { - insert(0, val.begin(), val.size()); + } + static void swap(T* src, T* srcEnd, T* dst) { + std::swap_ranges(src, srcEnd, dst); + } +}; + +template +struct VectorMover { + static void move(const T* src, const T* srcEnd, T* dst) { + memcpy(dst, src, + reinterpret_cast(srcEnd) - + reinterpret_cast(src)); + } + static void moveOverlapping(const T* src, const T* srcEnd, T* dst) { + memmove(dst, src, + reinterpret_cast(srcEnd) - + reinterpret_cast(src)); + } + static void swap(T* src, T* srcEnd, T* dst) { + std::swap_ranges(reinterpret_cast(src), + reinterpret_cast(srcEnd), + reinterpret_cast(dst)); + } +}; + +template +struct VectorCopier; + +template +struct VectorCopier { + template + static void uninitializedCopy(const U* src, const U* srcEnd, T* dst) { + while (src != srcEnd) { + new (NotNull, dst) T(*src); + ++dst; + ++src; } - - template - inline void Vector::remove(size_t position) - { - RELEASE_ASSERT(position < size()); - T* spot = begin() + position; - spot->~T(); - TypeOperations::moveOverlapping(spot + 1, end(), spot); - --m_size; + } +}; + +template +struct VectorCopier { + static void uninitializedCopy(const T* src, const T* srcEnd, T* dst) { + memcpy(dst, src, + reinterpret_cast(srcEnd) - + reinterpret_cast(src)); + } + template + static void uninitializedCopy(const U* src, const U* srcEnd, T* dst) { + VectorCopier::uninitializedCopy(src, srcEnd, dst); + } +}; + +template +struct VectorFiller; + +template +struct VectorFiller { + static void uninitializedFill(T* dst, T* dstEnd, const T& val) { + while (dst != dstEnd) { + new (NotNull, dst) T(val); + ++dst; } - - template - inline void Vector::remove(size_t position, size_t length) - { - ASSERT_WITH_SECURITY_IMPLICATION(position <= size()); - RELEASE_ASSERT(position + length <= size()); - T* beginSpot = begin() + position; - T* endSpot = beginSpot + length; - TypeOperations::destruct(beginSpot, endSpot); - TypeOperations::moveOverlapping(endSpot, end(), beginSpot); - m_size -= length; - } - - template - inline void Vector::reverse() - { - for (size_t i = 0; i < m_size / 2; ++i) - std::swap(at(i), at(m_size - 1 - i)); - } - - template - void deleteAllValues(const Vector& collection) - { - typedef typename Vector::const_iterator iterator; - iterator end = collection.end(); - for (iterator it = collection.begin(); it != end; ++it) - delete *it; - } - - template - inline void swap(Vector& a, Vector& b) - { - a.swap(b); + } +}; + +template +struct VectorFiller { + static void uninitializedFill(T* dst, T* dstEnd, const T& val) { + COMPILE_ASSERT(sizeof(T) == sizeof(char), + Size_of_type_should_be_equal_to_one); +#if COMPILER(GCC) && defined(_FORTIFY_SOURCE) + if (!__builtin_constant_p(dstEnd - dst) || (!(dstEnd - dst))) +#endif + memset(dst, val, dstEnd - dst); + } +}; + +template +struct VectorComparer; + +template +struct VectorComparer { + static bool compare(const T* a, const T* b, size_t size) { + if (LIKELY(a && b)) + return std::equal(a, a + size, b); + return !a && !b; + } +}; + +template +struct VectorComparer { + static bool compare(const T* a, const T* b, size_t size) { + return memcmp(a, b, sizeof(T) * size) == 0; + } +}; + +template +struct VectorTypeOperations { + static void destruct(T* begin, T* end) { + VectorDestructor::needsDestruction, T>::destruct(begin, + end); + } + + static void initialize(T* begin, T* end) { + VectorInitializer::canInitializeWithMemset, T>::initialize( + begin, end); + } + + static void move(const T* src, const T* srcEnd, T* dst) { + VectorMover::canMoveWithMemcpy, T>::move(src, srcEnd, dst); + } + + static void moveOverlapping(const T* src, const T* srcEnd, T* dst) { + VectorMover::canMoveWithMemcpy, T>::moveOverlapping( + src, srcEnd, dst); + } + + static void swap(T* src, T* srcEnd, T* dst) { + VectorMover::canMoveWithMemcpy, T>::swap(src, srcEnd, dst); + } + + static void uninitializedCopy(const T* src, const T* srcEnd, T* dst) { + VectorCopier::canCopyWithMemcpy, T>::uninitializedCopy( + src, srcEnd, dst); + } + + static void uninitializedFill(T* dst, T* dstEnd, const T& val) { + VectorFiller::canFillWithMemset, T>::uninitializedFill( + dst, dstEnd, val); + } + + static bool compare(const T* a, const T* b, size_t size) { + return VectorComparer::canCompareWithMemcmp, T>::compare( + a, b, size); + } +}; + +template +class VectorBufferBase { + WTF_MAKE_NONCOPYABLE(VectorBufferBase); + + public: + void allocateBuffer(size_t newCapacity) { + typedef typename Allocator::template VectorBackingHelper< + T, VectorTraits>::Type VectorBacking; + ASSERT(newCapacity); + size_t sizeToAllocate = allocationSize(newCapacity); + m_buffer = + Allocator::template backingMalloc(sizeToAllocate); + m_capacity = sizeToAllocate / sizeof(T); + } + + size_t allocationSize(size_t capacity) const { + return Allocator::Quantizer::template quantizedSize(capacity); + } + + T* buffer() { return m_buffer; } + const T* buffer() const { return m_buffer; } + size_t capacity() const { return m_capacity; } + + protected: + VectorBufferBase() : m_buffer(0), m_capacity(0) {} + + VectorBufferBase(T* buffer, size_t capacity) + : m_buffer(buffer), m_capacity(capacity) {} + + T* m_buffer; + unsigned m_capacity; + unsigned m_size; +}; + +template +class VectorBuffer; + +template +class VectorBuffer : protected VectorBufferBase { + private: + typedef VectorBufferBase Base; + + public: + VectorBuffer() {} + + VectorBuffer(size_t capacity) { + // Calling malloc(0) might take a lock and may actually do an + // allocation on some systems. + if (capacity) + allocateBuffer(capacity); + } + + void destruct() { + deallocateBuffer(m_buffer); + m_buffer = 0; + } + + void deallocateBuffer(T* bufferToDeallocate) { + Allocator::backingFree(bufferToDeallocate); + } + + void resetBufferPointer() { + m_buffer = 0; + m_capacity = 0; + } + + void swapVectorBuffer(VectorBuffer& other) { + std::swap(m_buffer, other.m_buffer); + std::swap(m_capacity, other.m_capacity); + } + + using Base::allocateBuffer; + using Base::allocationSize; + + using Base::buffer; + using Base::capacity; + + bool hasOutOfLineBuffer() const { + // When inlineCapacity is 0 we have an out of line buffer if we have a + // buffer. + return buffer(); + } + + protected: + using Base::m_size; + + private: + using Base::m_buffer; + using Base::m_capacity; +}; + +template +class VectorBuffer : protected VectorBufferBase { + WTF_MAKE_NONCOPYABLE(VectorBuffer); + + private: + typedef VectorBufferBase Base; + + public: + VectorBuffer() : Base(inlineBuffer(), inlineCapacity) {} + + VectorBuffer(size_t capacity) : Base(inlineBuffer(), inlineCapacity) { + if (capacity > inlineCapacity) + Base::allocateBuffer(capacity); + } + + void destruct() { + deallocateBuffer(m_buffer); + m_buffer = 0; + } + + NEVER_INLINE void reallyDeallocateBuffer(T* bufferToDeallocate) { + Allocator::backingFree(bufferToDeallocate); + } + + void deallocateBuffer(T* bufferToDeallocate) { + if (UNLIKELY(bufferToDeallocate != inlineBuffer())) + reallyDeallocateBuffer(bufferToDeallocate); + } + + void resetBufferPointer() { + m_buffer = inlineBuffer(); + m_capacity = inlineCapacity; + } + + void allocateBuffer(size_t newCapacity) { + // FIXME: This should ASSERT(!m_buffer) to catch misuse/leaks. + if (newCapacity > inlineCapacity) + Base::allocateBuffer(newCapacity); + else + resetBufferPointer(); + } + + size_t allocationSize(size_t capacity) const { + if (capacity <= inlineCapacity) + return m_inlineBufferSize; + return Base::allocationSize(capacity); + } + + void swapVectorBuffer(VectorBuffer& other) { + typedef VectorTypeOperations TypeOperations; + + if (buffer() == inlineBuffer() && other.buffer() == other.inlineBuffer()) { + ASSERT(m_capacity == other.m_capacity); + if (m_size > other.m_size) { + TypeOperations::swap(inlineBuffer(), inlineBuffer() + other.m_size, + other.inlineBuffer()); + TypeOperations::move(inlineBuffer() + other.m_size, + inlineBuffer() + m_size, + other.inlineBuffer() + other.m_size); + } else { + TypeOperations::swap(inlineBuffer(), inlineBuffer() + m_size, + other.inlineBuffer()); + TypeOperations::move(other.inlineBuffer() + m_size, + other.inlineBuffer() + other.m_size, + inlineBuffer() + m_size); + } + } else if (buffer() == inlineBuffer()) { + m_buffer = other.m_buffer; + other.m_buffer = other.inlineBuffer(); + TypeOperations::move(inlineBuffer(), inlineBuffer() + m_size, + other.inlineBuffer()); + std::swap(m_capacity, other.m_capacity); + } else if (other.buffer() == other.inlineBuffer()) { + other.m_buffer = m_buffer; + m_buffer = inlineBuffer(); + TypeOperations::move(other.inlineBuffer(), + other.inlineBuffer() + other.m_size, inlineBuffer()); + std::swap(m_capacity, other.m_capacity); + } else { + std::swap(m_buffer, other.m_buffer); + std::swap(m_capacity, other.m_capacity); } - - template - bool operator==(const Vector& a, const Vector& b) - { - if (a.size() != b.size()) - return false; - - return VectorTypeOperations::compare(a.data(), b.data(), a.size()); + } + + using Base::buffer; + using Base::capacity; + + bool hasOutOfLineBuffer() const { + return buffer() && buffer() != inlineBuffer(); + } + + protected: + using Base::m_size; + + private: + using Base::m_buffer; + using Base::m_capacity; + + static const size_t m_inlineBufferSize = inlineCapacity * sizeof(T); + T* inlineBuffer() { return reinterpret_cast_ptr(m_inlineBuffer.buffer); } + const T* inlineBuffer() const { + return reinterpret_cast_ptr(m_inlineBuffer.buffer); + } + + AlignedBuffer m_inlineBuffer; + template + friend class Deque; +}; + +template +class Vector; + +// VectorDestructorBase defines the destructor of a vector. This base is used in +// order to completely avoid creating a destructor for a vector that does not +// need to be destructed. By doing so, the clang compiler will have correct +// information about whether or not a vector has a trivial destructor and we use +// that in a compiler plugin to ensure the correctness of non-finalized +// garbage-collected classes and the use of VectorTraits::needsDestruction. + +// All non-GC managed vectors need a destructor. This destructor will simply +// call finalize on the actual vector type. +template +class VectorDestructorBase { + public: + ~VectorDestructorBase() { static_cast(this)->finalize(); } +}; + +// Heap-allocated vectors with no inlineCapacity never need a destructor. +template +class VectorDestructorBase {}; + +// Heap-allocator vectors with inlineCapacity need a destructor if the inline +// elements do. The use of VectorTraits::needsDestruction is delayed +// until we know that inlineCapacity is non-zero to allow classes that +// recursively refer to themselves in vector members. If inlineCapacity is +// non-zero doing so would have undefined meaning, so in this case we can use +// HeapVectorWithInlineCapacityDestructorBase to define a destructor depending +// on the value of VectorTraits::needsDestruction. +template +class HeapVectorWithInlineCapacityDestructorBase; + +template +class HeapVectorWithInlineCapacityDestructorBase { + public: + ~HeapVectorWithInlineCapacityDestructorBase() { + static_cast(this)->finalize(); + } +}; + +template +class HeapVectorWithInlineCapacityDestructorBase {}; + +template +class VectorDestructorBase + : public HeapVectorWithInlineCapacityDestructorBase< + Derived, + VectorTraits::needsDestruction> {}; + +template +class Vector : private VectorBuffer, + public VectorDestructorBase, + T, + (inlineCapacity > 0), + Allocator::isGarbageCollected> { + WTF_USE_ALLOCATOR(Vector, Allocator); + + private: + typedef VectorBuffer Base; + typedef VectorTypeOperations TypeOperations; + + public: + typedef T ValueType; + + typedef T* iterator; + typedef const T* const_iterator; + typedef std::reverse_iterator reverse_iterator; + typedef std::reverse_iterator const_reverse_iterator; + + Vector() { + // Unused slots are initialized to zero so that the visitor and the + // finalizer can visit them safely. canInitializeWithMemset tells us + // that the class does not expect matching constructor and + // destructor calls as long as the memory is zeroed. + COMPILE_ASSERT(!Allocator::isGarbageCollected || + !VectorTraits::needsDestruction || + VectorTraits::canInitializeWithMemset, + ClassHasProblemsWithFinalizersCalledOnClearedMemory); + COMPILE_ASSERT(!WTF::IsPolymorphic::value || + !VectorTraits::canInitializeWithMemset, + CantInitializeWithMemsetIfThereIsAVtable); + m_size = 0; + } + + explicit Vector(size_t size) : Base(size) { + // Unused slots are initialized to zero so that the visitor and the + // finalizer can visit them safely. canInitializeWithMemset tells us + // that the class does not expect matching constructor and + // destructor calls as long as the memory is zeroed. + COMPILE_ASSERT(!Allocator::isGarbageCollected || + !VectorTraits::needsDestruction || + VectorTraits::canInitializeWithMemset, + ClassHasProblemsWithFinalizersCalledOnClearedMemory); + m_size = size; + TypeOperations::initialize(begin(), end()); + } + + // Off-GC-heap vectors: Destructor should be called. + // On-GC-heap vectors: Destructor should be called for inline buffers + // (if any) but destructor shouldn't be called for vector backing since + // it is managed by the traced GC heap. + void finalize() { + if (!inlineCapacity) { + if (LIKELY(!Base::buffer())) + return; } - - template - inline bool operator!=(const Vector& a, const Vector& b) - { - return !(a == b); + if (LIKELY(m_size) && + !(Allocator::isGarbageCollected && this->hasOutOfLineBuffer())) { + TypeOperations::destruct(begin(), end()); + m_size = 0; // Partial protection against use-after-free. } -} // namespace WTF + Base::destruct(); + } + + Vector(const Vector&); + template + explicit Vector(const Vector&); + + Vector& operator=(const Vector&); + template + Vector& operator=(const Vector&); + + Vector(Vector&&); + Vector& operator=(Vector&&); + + size_t size() const { return m_size; } + size_t capacity() const { return Base::capacity(); } + bool isEmpty() const { return !size(); } + + T& at(size_t i) { + RELEASE_ASSERT(i < size()); + return Base::buffer()[i]; + } + const T& at(size_t i) const { + RELEASE_ASSERT(i < size()); + return Base::buffer()[i]; + } + + T& operator[](size_t i) { return at(i); } + const T& operator[](size_t i) const { return at(i); } + + T* data() { return Base::buffer(); } + const T* data() const { return Base::buffer(); } + + iterator begin() { return data(); } + iterator end() { return begin() + m_size; } + const_iterator begin() const { return data(); } + const_iterator end() const { return begin() + m_size; } + + reverse_iterator rbegin() { return reverse_iterator(end()); } + reverse_iterator rend() { return reverse_iterator(begin()); } + const_reverse_iterator rbegin() const { + return const_reverse_iterator(end()); + } + const_reverse_iterator rend() const { + return const_reverse_iterator(begin()); + } + + T& first() { return at(0); } + const T& first() const { return at(0); } + T& last() { return at(size() - 1); } + const T& last() const { return at(size() - 1); } + + template + bool contains(const U&) const; + template + size_t find(const U&) const; + template + size_t reverseFind(const U&) const; + + void shrink(size_t size); + void grow(size_t size); + void resize(size_t size); + void reserveCapacity(size_t newCapacity); + void reserveInitialCapacity(size_t initialCapacity); + void shrinkToFit() { shrinkCapacity(size()); } + void shrinkToReasonableCapacity() { + if (size() * 2 < capacity()) + shrinkCapacity(size() + size() / 4 + 1); + } + + void clear() { shrinkCapacity(0); } + + template + void append(const U*, size_t); + template + void append(const U&); + template + void uncheckedAppend(const U& val); + template + void appendVector(const Vector&); + + template + void insert(size_t position, const U*, size_t); + template + void insert(size_t position, const U&); + template + void insert(size_t position, const Vector&); + + template + void prepend(const U*, size_t); + template + void prepend(const U&); + template + void prepend(const Vector&); + + void remove(size_t position); + void remove(size_t position, size_t length); + + void removeLast() { + ASSERT(!isEmpty()); + shrink(size() - 1); + } + + Vector(size_t size, const T& val) : Base(size) { + m_size = size; + TypeOperations::uninitializedFill(begin(), end(), val); + } + + void fill(const T&, size_t); + void fill(const T& val) { fill(val, size()); } + + template + void appendRange(Iterator start, Iterator end); + + void swap(Vector& other) { + Base::swapVectorBuffer(other); + std::swap(m_size, other.m_size); + } + + void reverse(); + + private: + void expandCapacity(size_t newMinCapacity); + const T* expandCapacity(size_t newMinCapacity, const T*); + template + U* expandCapacity(size_t newMinCapacity, U*); + void shrinkCapacity(size_t newCapacity); + template + void appendSlowCase(const U&); + + using Base::allocateBuffer; + using Base::allocationSize; + using Base::buffer; + using Base::capacity; + using Base::m_size; + using Base::swapVectorBuffer; +}; + +template +Vector::Vector(const Vector& other) + : Base(other.capacity()) { + m_size = other.size(); + TypeOperations::uninitializedCopy(other.begin(), other.end(), begin()); +} + +template +template +Vector::Vector( + const Vector& other) + : Base(other.capacity()) { + m_size = other.size(); + TypeOperations::uninitializedCopy(other.begin(), other.end(), begin()); +} + +template +Vector& Vector:: +operator=(const Vector& other) { + if (UNLIKELY(&other == this)) + return *this; + + if (size() > other.size()) + shrink(other.size()); + else if (other.size() > capacity()) { + clear(); + reserveCapacity(other.size()); + ASSERT(begin()); + } + + std::copy(other.begin(), other.begin() + size(), begin()); + TypeOperations::uninitializedCopy(other.begin() + size(), other.end(), end()); + m_size = other.size(); + + return *this; +} + +inline bool typelessPointersAreEqual(const void* a, const void* b) { + return a == b; +} + +template +template +Vector& Vector:: +operator=(const Vector& other) { + // If the inline capacities match, we should call the more specific + // template. If the inline capacities don't match, the two objects + // shouldn't be allocated the same address. + ASSERT(!typelessPointersAreEqual(&other, this)); + + if (size() > other.size()) + shrink(other.size()); + else if (other.size() > capacity()) { + clear(); + reserveCapacity(other.size()); + ASSERT(begin()); + } + + std::copy(other.begin(), other.begin() + size(), begin()); + TypeOperations::uninitializedCopy(other.begin() + size(), other.end(), end()); + m_size = other.size(); + + return *this; +} + +template +Vector::Vector( + Vector&& other) { + m_size = 0; + // It's a little weird to implement a move constructor using swap but this way + // we don't have to add a move constructor to VectorBuffer. + swap(other); +} + +template +Vector& Vector:: +operator=(Vector&& other) { + swap(other); + return *this; +} + +template +template +bool Vector::contains(const U& value) const { + return find(value) != kNotFound; +} + +template +template +size_t Vector::find(const U& value) const { + const T* b = begin(); + const T* e = end(); + for (const T* iter = b; iter < e; ++iter) { + if (*iter == value) + return iter - b; + } + return kNotFound; +} + +template +template +size_t Vector::reverseFind(const U& value) const { + const T* b = begin(); + const T* iter = end(); + while (iter > b) { + --iter; + if (*iter == value) + return iter - b; + } + return kNotFound; +} + +template +void Vector::fill(const T& val, size_t newSize) { + if (size() > newSize) + shrink(newSize); + else if (newSize > capacity()) { + clear(); + reserveCapacity(newSize); + ASSERT(begin()); + } + + std::fill(begin(), end(), val); + TypeOperations::uninitializedFill(end(), begin() + newSize, val); + m_size = newSize; +} + +template +template +void Vector::appendRange(Iterator start, + Iterator end) { + for (Iterator it = start; it != end; ++it) + append(*it); +} + +template +void Vector::expandCapacity( + size_t newMinCapacity) { + size_t oldCapacity = capacity(); + size_t expandedCapacity = oldCapacity; + // We use a more aggressive expansion strategy for Vectors with inline + // storage. This is because they are more likely to be on the stack, so the + // risk of heap bloat is minimized. Furthermore, exceeding the inline capacity + // limit is not supposed to happen in the common case and may indicate a + // pathological condition or microbenchmark. + if (inlineCapacity) { + expandedCapacity *= 2; + // Check for integer overflow, which could happen in the 32-bit build. + RELEASE_ASSERT(expandedCapacity > oldCapacity); + } else { + // This cannot integer overflow. + // On 64-bit, the "expanded" integer is 32-bit, and any encroachment above + // 2^32 will fail allocation in allocateBuffer(). On 32-bit, there's not + // enough address space to hold the old and new buffers. In addition, our + // underlying allocator is supposed to always fail on > (2^31 - 1) + // allocations. + expandedCapacity += (expandedCapacity / 4) + 1; + } + reserveCapacity(std::max( + newMinCapacity, + std::max(static_cast(kInitialVectorSize), expandedCapacity))); +} + +template +const T* Vector::expandCapacity( + size_t newMinCapacity, + const T* ptr) { + if (ptr < begin() || ptr >= end()) { + expandCapacity(newMinCapacity); + return ptr; + } + size_t index = ptr - begin(); + expandCapacity(newMinCapacity); + return begin() + index; +} + +template +template +inline U* Vector::expandCapacity( + size_t newMinCapacity, + U* ptr) { + expandCapacity(newMinCapacity); + return ptr; +} + +template +inline void Vector::resize(size_t size) { + if (size <= m_size) + TypeOperations::destruct(begin() + size, end()); + else { + if (size > capacity()) + expandCapacity(size); + TypeOperations::initialize(end(), begin() + size); + } + + m_size = size; +} + +template +void Vector::shrink(size_t size) { + ASSERT(size <= m_size); + TypeOperations::destruct(begin() + size, end()); + m_size = size; +} + +template +void Vector::grow(size_t size) { + ASSERT(size >= m_size); + if (size > capacity()) + expandCapacity(size); + TypeOperations::initialize(end(), begin() + size); + m_size = size; +} + +template +void Vector::reserveCapacity(size_t newCapacity) { + if (UNLIKELY(newCapacity <= capacity())) + return; + T* oldBuffer = begin(); + T* oldEnd = end(); + Base::allocateBuffer(newCapacity); + TypeOperations::move(oldBuffer, oldEnd, begin()); + Base::deallocateBuffer(oldBuffer); +} + +template +inline void Vector::reserveInitialCapacity( + size_t initialCapacity) { + ASSERT(!m_size); + ASSERT(capacity() == inlineCapacity); + if (initialCapacity > inlineCapacity) + Base::allocateBuffer(initialCapacity); +} + +template +void Vector::shrinkCapacity(size_t newCapacity) { + if (newCapacity >= capacity()) + return; + + if (newCapacity < size()) + shrink(newCapacity); + + T* oldBuffer = begin(); + if (newCapacity > 0) { + // Optimization: if we're downsizing inside the same allocator bucket, we + // can exit with no additional work. + if (Base::allocationSize(capacity()) == Base::allocationSize(newCapacity)) + return; + + T* oldEnd = end(); + Base::allocateBuffer(newCapacity); + if (begin() != oldBuffer) + TypeOperations::move(oldBuffer, oldEnd, begin()); + } else { + Base::resetBufferPointer(); + } + + Base::deallocateBuffer(oldBuffer); +} + +// Templatizing these is better than just letting the conversion happen +// implicitly, because for instance it allows a PassRefPtr to be appended to a +// RefPtr vector without refcount thrash. + +template +template +void Vector::append(const U* data, + size_t dataSize) { + ASSERT(Allocator::isAllocationAllowed()); + size_t newSize = m_size + dataSize; + if (newSize > capacity()) { + data = expandCapacity(newSize, data); + ASSERT(begin()); + } + RELEASE_ASSERT(newSize >= m_size); + T* dest = end(); + VectorCopier::canCopyWithMemcpy, T>::uninitializedCopy( + data, &data[dataSize], dest); + m_size = newSize; +} + +template +template +ALWAYS_INLINE void Vector::append(const U& val) { + ASSERT(Allocator::isAllocationAllowed()); + if (LIKELY(size() != capacity())) { + new (NotNull, end()) T(val); + ++m_size; + return; + } + + appendSlowCase(val); +} + +template +template +NEVER_INLINE void Vector::appendSlowCase( + const U& val) { + ASSERT(size() == capacity()); + + const U* ptr = &val; + ptr = expandCapacity(size() + 1, ptr); + ASSERT(begin()); + + new (NotNull, end()) T(*ptr); + ++m_size; +} + +// This version of append saves a branch in the case where you know that the +// vector's capacity is large enough for the append to succeed. + +template +template +ALWAYS_INLINE void Vector::uncheckedAppend( + const U& val) { + ASSERT(size() < capacity()); + const U* ptr = &val; + new (NotNull, end()) T(*ptr); + ++m_size; +} + +template +template +inline void Vector::appendVector( + const Vector& val) { + append(val.begin(), val.size()); +} + +template +template +void Vector::insert(size_t position, + const U* data, + size_t dataSize) { + ASSERT(Allocator::isAllocationAllowed()); + RELEASE_ASSERT(position <= size()); + size_t newSize = m_size + dataSize; + if (newSize > capacity()) { + data = expandCapacity(newSize, data); + ASSERT(begin()); + } + RELEASE_ASSERT(newSize >= m_size); + T* spot = begin() + position; + TypeOperations::moveOverlapping(spot, end(), spot + dataSize); + VectorCopier::canCopyWithMemcpy, T>::uninitializedCopy( + data, &data[dataSize], spot); + m_size = newSize; +} + +template +template +inline void Vector::insert(size_t position, + const U& val) { + ASSERT(Allocator::isAllocationAllowed()); + RELEASE_ASSERT(position <= size()); + const U* data = &val; + if (size() == capacity()) { + data = expandCapacity(size() + 1, data); + ASSERT(begin()); + } + T* spot = begin() + position; + TypeOperations::moveOverlapping(spot, end(), spot + 1); + new (NotNull, spot) T(*data); + ++m_size; +} + +template +template +inline void Vector::insert( + size_t position, + const Vector& val) { + insert(position, val.begin(), val.size()); +} + +template +template +void Vector::prepend(const U* data, + size_t dataSize) { + insert(0, data, dataSize); +} + +template +template +inline void Vector::prepend(const U& val) { + insert(0, val); +} + +template +template +inline void Vector::prepend( + const Vector& val) { + insert(0, val.begin(), val.size()); +} + +template +inline void Vector::remove(size_t position) { + RELEASE_ASSERT(position < size()); + T* spot = begin() + position; + spot->~T(); + TypeOperations::moveOverlapping(spot + 1, end(), spot); + --m_size; +} + +template +inline void Vector::remove(size_t position, + size_t length) { + ASSERT_WITH_SECURITY_IMPLICATION(position <= size()); + RELEASE_ASSERT(position + length <= size()); + T* beginSpot = begin() + position; + T* endSpot = beginSpot + length; + TypeOperations::destruct(beginSpot, endSpot); + TypeOperations::moveOverlapping(endSpot, end(), beginSpot); + m_size -= length; +} + +template +inline void Vector::reverse() { + for (size_t i = 0; i < m_size / 2; ++i) + std::swap(at(i), at(m_size - 1 - i)); +} + +template +void deleteAllValues(const Vector& collection) { + typedef + typename Vector::const_iterator iterator; + iterator end = collection.end(); + for (iterator it = collection.begin(); it != end; ++it) + delete *it; +} + +template +inline void swap(Vector& a, + Vector& b) { + a.swap(b); +} + +template +bool operator==(const Vector& a, + const Vector& b) { + if (a.size() != b.size()) + return false; + + return VectorTypeOperations::compare(a.data(), b.data(), a.size()); +} + +template +inline bool operator!=(const Vector& a, + const Vector& b) { + return !(a == b); +} + +} // namespace WTF using WTF::Vector; diff --git a/sky/engine/wtf/VectorTest.cpp b/sky/engine/wtf/VectorTest.cpp index 550aed6ffede1..2b88ff2c51f2a 100644 --- a/sky/engine/wtf/VectorTest.cpp +++ b/sky/engine/wtf/VectorTest.cpp @@ -23,7 +23,6 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ - #include #include "flutter/sky/engine/wtf/HashSet.h" #include "flutter/sky/engine/wtf/OwnPtr.h" @@ -33,272 +32,258 @@ namespace { -TEST(VectorTest, Basic) -{ - Vector intVector; - EXPECT_TRUE(intVector.isEmpty()); - EXPECT_EQ(0ul, intVector.size()); - EXPECT_EQ(0ul, intVector.capacity()); +TEST(VectorTest, Basic) { + Vector intVector; + EXPECT_TRUE(intVector.isEmpty()); + EXPECT_EQ(0ul, intVector.size()); + EXPECT_EQ(0ul, intVector.capacity()); } -TEST(VectorTest, Reverse) -{ - Vector intVector; - intVector.append(10); - intVector.append(11); - intVector.append(12); - intVector.append(13); - intVector.reverse(); - - EXPECT_EQ(13, intVector[0]); - EXPECT_EQ(12, intVector[1]); - EXPECT_EQ(11, intVector[2]); - EXPECT_EQ(10, intVector[3]); - - intVector.append(9); - intVector.reverse(); - - EXPECT_EQ(9, intVector[0]); - EXPECT_EQ(10, intVector[1]); - EXPECT_EQ(11, intVector[2]); - EXPECT_EQ(12, intVector[3]); - EXPECT_EQ(13, intVector[4]); +TEST(VectorTest, Reverse) { + Vector intVector; + intVector.append(10); + intVector.append(11); + intVector.append(12); + intVector.append(13); + intVector.reverse(); + + EXPECT_EQ(13, intVector[0]); + EXPECT_EQ(12, intVector[1]); + EXPECT_EQ(11, intVector[2]); + EXPECT_EQ(10, intVector[3]); + + intVector.append(9); + intVector.reverse(); + + EXPECT_EQ(9, intVector[0]); + EXPECT_EQ(10, intVector[1]); + EXPECT_EQ(11, intVector[2]); + EXPECT_EQ(12, intVector[3]); + EXPECT_EQ(13, intVector[4]); } -TEST(VectorTest, Iterator) -{ - Vector intVector; - intVector.append(10); - intVector.append(11); - intVector.append(12); - intVector.append(13); - - Vector::iterator it = intVector.begin(); - Vector::iterator end = intVector.end(); - EXPECT_TRUE(end != it); - - EXPECT_EQ(10, *it); - ++it; - EXPECT_EQ(11, *it); - ++it; - EXPECT_EQ(12, *it); - ++it; - EXPECT_EQ(13, *it); - ++it; - - EXPECT_TRUE(end == it); +TEST(VectorTest, Iterator) { + Vector intVector; + intVector.append(10); + intVector.append(11); + intVector.append(12); + intVector.append(13); + + Vector::iterator it = intVector.begin(); + Vector::iterator end = intVector.end(); + EXPECT_TRUE(end != it); + + EXPECT_EQ(10, *it); + ++it; + EXPECT_EQ(11, *it); + ++it; + EXPECT_EQ(12, *it); + ++it; + EXPECT_EQ(13, *it); + ++it; + + EXPECT_TRUE(end == it); } -TEST(VectorTest, ReverseIterator) -{ - Vector intVector; - intVector.append(10); - intVector.append(11); - intVector.append(12); - intVector.append(13); - - Vector::reverse_iterator it = intVector.rbegin(); - Vector::reverse_iterator end = intVector.rend(); - EXPECT_TRUE(end != it); - - EXPECT_EQ(13, *it); - ++it; - EXPECT_EQ(12, *it); - ++it; - EXPECT_EQ(11, *it); - ++it; - EXPECT_EQ(10, *it); - ++it; - - EXPECT_TRUE(end == it); +TEST(VectorTest, ReverseIterator) { + Vector intVector; + intVector.append(10); + intVector.append(11); + intVector.append(12); + intVector.append(13); + + Vector::reverse_iterator it = intVector.rbegin(); + Vector::reverse_iterator end = intVector.rend(); + EXPECT_TRUE(end != it); + + EXPECT_EQ(13, *it); + ++it; + EXPECT_EQ(12, *it); + ++it; + EXPECT_EQ(11, *it); + ++it; + EXPECT_EQ(10, *it); + ++it; + + EXPECT_TRUE(end == it); } class DestructCounter { -public: - explicit DestructCounter(int i, int* destructNumber) - : m_i(i) - , m_destructNumber(destructNumber) - { } - - ~DestructCounter() { ++(*m_destructNumber); } - int get() const { return m_i; } - -private: - int m_i; - int* m_destructNumber; + public: + explicit DestructCounter(int i, int* destructNumber) + : m_i(i), m_destructNumber(destructNumber) {} + + ~DestructCounter() { ++(*m_destructNumber); } + int get() const { return m_i; } + + private: + int m_i; + int* m_destructNumber; }; -typedef WTF::Vector > OwnPtrVector; - -TEST(VectorTest, OwnPtr) -{ - int destructNumber = 0; - OwnPtrVector vector; - vector.append(adoptPtr(new DestructCounter(0, &destructNumber))); - vector.append(adoptPtr(new DestructCounter(1, &destructNumber))); - EXPECT_EQ(2u, vector.size()); - - OwnPtr& counter0 = vector.first(); - ASSERT_EQ(0, counter0->get()); - int counter1 = vector.last()->get(); - ASSERT_EQ(1, counter1); - ASSERT_EQ(0, destructNumber); - - size_t index = 0; - for (OwnPtrVector::iterator iter = vector.begin(); iter != vector.end(); ++iter) { - OwnPtr* refCounter = iter; - EXPECT_EQ(index, static_cast(refCounter->get()->get())); - EXPECT_EQ(index, static_cast((*refCounter)->get())); - index++; - } - EXPECT_EQ(0, destructNumber); - - for (index = 0; index < vector.size(); index++) { - OwnPtr& refCounter = vector[index]; - EXPECT_EQ(index, static_cast(refCounter->get())); - index++; - } - EXPECT_EQ(0, destructNumber); - - EXPECT_EQ(0, vector[0]->get()); - EXPECT_EQ(1, vector[1]->get()); - vector.remove(0); - EXPECT_EQ(1, vector[0]->get()); - EXPECT_EQ(1u, vector.size()); - EXPECT_EQ(1, destructNumber); - - OwnPtr ownCounter1 = vector[0].release(); - vector.remove(0); - ASSERT_EQ(counter1, ownCounter1->get()); - ASSERT_EQ(0u, vector.size()); - ASSERT_EQ(1, destructNumber); - - ownCounter1.clear(); - EXPECT_EQ(2, destructNumber); - - size_t count = 1025; - destructNumber = 0; - for (size_t i = 0; i < count; i++) - vector.prepend(adoptPtr(new DestructCounter(i, &destructNumber))); - - // Vector relocation must not destruct OwnPtr element. - EXPECT_EQ(0, destructNumber); - EXPECT_EQ(count, vector.size()); - - OwnPtrVector copyVector; - vector.swap(copyVector); - EXPECT_EQ(0, destructNumber); - EXPECT_EQ(count, copyVector.size()); - EXPECT_EQ(0u, vector.size()); - - copyVector.clear(); - EXPECT_EQ(count, static_cast(destructNumber)); +typedef WTF::Vector> OwnPtrVector; + +TEST(VectorTest, OwnPtr) { + int destructNumber = 0; + OwnPtrVector vector; + vector.append(adoptPtr(new DestructCounter(0, &destructNumber))); + vector.append(adoptPtr(new DestructCounter(1, &destructNumber))); + EXPECT_EQ(2u, vector.size()); + + OwnPtr& counter0 = vector.first(); + ASSERT_EQ(0, counter0->get()); + int counter1 = vector.last()->get(); + ASSERT_EQ(1, counter1); + ASSERT_EQ(0, destructNumber); + + size_t index = 0; + for (OwnPtrVector::iterator iter = vector.begin(); iter != vector.end(); + ++iter) { + OwnPtr* refCounter = iter; + EXPECT_EQ(index, static_cast(refCounter->get()->get())); + EXPECT_EQ(index, static_cast((*refCounter)->get())); + index++; + } + EXPECT_EQ(0, destructNumber); + + for (index = 0; index < vector.size(); index++) { + OwnPtr& refCounter = vector[index]; + EXPECT_EQ(index, static_cast(refCounter->get())); + index++; + } + EXPECT_EQ(0, destructNumber); + + EXPECT_EQ(0, vector[0]->get()); + EXPECT_EQ(1, vector[1]->get()); + vector.remove(0); + EXPECT_EQ(1, vector[0]->get()); + EXPECT_EQ(1u, vector.size()); + EXPECT_EQ(1, destructNumber); + + OwnPtr ownCounter1 = vector[0].release(); + vector.remove(0); + ASSERT_EQ(counter1, ownCounter1->get()); + ASSERT_EQ(0u, vector.size()); + ASSERT_EQ(1, destructNumber); + + ownCounter1.clear(); + EXPECT_EQ(2, destructNumber); + + size_t count = 1025; + destructNumber = 0; + for (size_t i = 0; i < count; i++) + vector.prepend(adoptPtr(new DestructCounter(i, &destructNumber))); + + // Vector relocation must not destruct OwnPtr element. + EXPECT_EQ(0, destructNumber); + EXPECT_EQ(count, vector.size()); + + OwnPtrVector copyVector; + vector.swap(copyVector); + EXPECT_EQ(0, destructNumber); + EXPECT_EQ(count, copyVector.size()); + EXPECT_EQ(0u, vector.size()); + + copyVector.clear(); + EXPECT_EQ(count, static_cast(destructNumber)); } // WrappedInt class will fail if it was memmoved or memcpyed. static HashSet constructedWrappedInts; class WrappedInt { -public: - WrappedInt(int i = 0) - : m_originalThisPtr(this) - , m_i(i) - { - constructedWrappedInts.add(this); - } - - WrappedInt(const WrappedInt& other) - : m_originalThisPtr(this) - , m_i(other.m_i) - { - constructedWrappedInts.add(this); - } - - WrappedInt& operator=(const WrappedInt& other) - { - m_i = other.m_i; - return *this; - } - - ~WrappedInt() - { - EXPECT_EQ(m_originalThisPtr, this); - EXPECT_TRUE(constructedWrappedInts.contains(this)); - constructedWrappedInts.remove(this); - } - - int get() const { return m_i; } - -private: - void* m_originalThisPtr; - int m_i; + public: + WrappedInt(int i = 0) : m_originalThisPtr(this), m_i(i) { + constructedWrappedInts.add(this); + } + + WrappedInt(const WrappedInt& other) + : m_originalThisPtr(this), m_i(other.m_i) { + constructedWrappedInts.add(this); + } + + WrappedInt& operator=(const WrappedInt& other) { + m_i = other.m_i; + return *this; + } + + ~WrappedInt() { + EXPECT_EQ(m_originalThisPtr, this); + EXPECT_TRUE(constructedWrappedInts.contains(this)); + constructedWrappedInts.remove(this); + } + + int get() const { return m_i; } + + private: + void* m_originalThisPtr; + int m_i; }; -TEST(VectorTest, SwapWithInlineCapacity) -{ - const size_t inlineCapacity = 2; - Vector vectorA; - vectorA.append(WrappedInt(1)); - Vector vectorB; - vectorB.append(WrappedInt(2)); - - EXPECT_EQ(vectorA.size(), vectorB.size()); - vectorA.swap(vectorB); - - EXPECT_EQ(1u, vectorA.size()); - EXPECT_EQ(2, vectorA.at(0).get()); - EXPECT_EQ(1u, vectorB.size()); - EXPECT_EQ(1, vectorB.at(0).get()); - - vectorA.append(WrappedInt(3)); - - EXPECT_GT(vectorA.size(), vectorB.size()); - vectorA.swap(vectorB); - - EXPECT_EQ(1u, vectorA.size()); - EXPECT_EQ(1, vectorA.at(0).get()); - EXPECT_EQ(2u, vectorB.size()); - EXPECT_EQ(2, vectorB.at(0).get()); - EXPECT_EQ(3, vectorB.at(1).get()); - - EXPECT_LT(vectorA.size(), vectorB.size()); - vectorA.swap(vectorB); - - EXPECT_EQ(2u, vectorA.size()); - EXPECT_EQ(2, vectorA.at(0).get()); - EXPECT_EQ(3, vectorA.at(1).get()); - EXPECT_EQ(1u, vectorB.size()); - EXPECT_EQ(1, vectorB.at(0).get()); - - vectorA.append(WrappedInt(4)); - EXPECT_GT(vectorA.size(), inlineCapacity); - vectorA.swap(vectorB); - - EXPECT_EQ(1u, vectorA.size()); - EXPECT_EQ(1, vectorA.at(0).get()); - EXPECT_EQ(3u, vectorB.size()); - EXPECT_EQ(2, vectorB.at(0).get()); - EXPECT_EQ(3, vectorB.at(1).get()); - EXPECT_EQ(4, vectorB.at(2).get()); - - vectorB.swap(vectorA); +TEST(VectorTest, SwapWithInlineCapacity) { + const size_t inlineCapacity = 2; + Vector vectorA; + vectorA.append(WrappedInt(1)); + Vector vectorB; + vectorB.append(WrappedInt(2)); + + EXPECT_EQ(vectorA.size(), vectorB.size()); + vectorA.swap(vectorB); + + EXPECT_EQ(1u, vectorA.size()); + EXPECT_EQ(2, vectorA.at(0).get()); + EXPECT_EQ(1u, vectorB.size()); + EXPECT_EQ(1, vectorB.at(0).get()); + + vectorA.append(WrappedInt(3)); + + EXPECT_GT(vectorA.size(), vectorB.size()); + vectorA.swap(vectorB); + + EXPECT_EQ(1u, vectorA.size()); + EXPECT_EQ(1, vectorA.at(0).get()); + EXPECT_EQ(2u, vectorB.size()); + EXPECT_EQ(2, vectorB.at(0).get()); + EXPECT_EQ(3, vectorB.at(1).get()); + + EXPECT_LT(vectorA.size(), vectorB.size()); + vectorA.swap(vectorB); + + EXPECT_EQ(2u, vectorA.size()); + EXPECT_EQ(2, vectorA.at(0).get()); + EXPECT_EQ(3, vectorA.at(1).get()); + EXPECT_EQ(1u, vectorB.size()); + EXPECT_EQ(1, vectorB.at(0).get()); + + vectorA.append(WrappedInt(4)); + EXPECT_GT(vectorA.size(), inlineCapacity); + vectorA.swap(vectorB); + + EXPECT_EQ(1u, vectorA.size()); + EXPECT_EQ(1, vectorA.at(0).get()); + EXPECT_EQ(3u, vectorB.size()); + EXPECT_EQ(2, vectorB.at(0).get()); + EXPECT_EQ(3, vectorB.at(1).get()); + EXPECT_EQ(4, vectorB.at(2).get()); + + vectorB.swap(vectorA); } -class Comparable { -}; -bool operator==(const Comparable& a, const Comparable& b) { return true; } - -template void compare() -{ - EXPECT_TRUE(Vector() == Vector()); - EXPECT_FALSE(Vector(1) == Vector(0)); - EXPECT_FALSE(Vector() == Vector(1)); - EXPECT_TRUE(Vector(1) == Vector(1)); +class Comparable {}; +bool operator==(const Comparable& a, const Comparable& b) { + return true; +} + +template +void compare() { + EXPECT_TRUE(Vector() == Vector()); + EXPECT_FALSE(Vector(1) == Vector(0)); + EXPECT_FALSE(Vector() == Vector(1)); + EXPECT_TRUE(Vector(1) == Vector(1)); } -TEST(VectorTest, Compare) -{ - compare(); - compare(); - compare(); +TEST(VectorTest, Compare) { + compare(); + compare(); + compare(); } -} // namespace +} // namespace diff --git a/sky/engine/wtf/VectorTraits.h b/sky/engine/wtf/VectorTraits.h index 9e1bc6a762462..f1d5fb88eabdf 100644 --- a/sky/engine/wtf/VectorTraits.h +++ b/sky/engine/wtf/VectorTraits.h @@ -30,86 +30,92 @@ using std::pair; namespace WTF { - class AtomicString; - - template - struct VectorTraitsBase - { - static const bool needsDestruction = !IsPod::value; - static const bool canInitializeWithMemset = IsPod::value; - static const bool canMoveWithMemcpy = IsPod::value; - static const bool canCopyWithMemcpy = IsPod::value; - static const bool canFillWithMemset = IsPod::value && (sizeof(T) == sizeof(char)); - static const bool canCompareWithMemcmp = IsPod::value; - static const WeakHandlingFlag weakHandlingFlag = NoWeakHandlingInCollections; // We don't support weak handling in vectors. - }; - - template - struct VectorTraits : VectorTraitsBase { }; - - // Classes marked with SimpleVectorTraits will use memmov, memcpy, memcmp - // instead of constructors, copy operators, etc for initialization, move - // and comparison. - template - struct SimpleClassVectorTraits : VectorTraitsBase - { - static const bool canInitializeWithMemset = true; - static const bool canMoveWithMemcpy = true; - static const bool canCompareWithMemcmp = true; - }; - - // We know OwnPtr and RefPtr are simple enough that initializing to 0 and - // moving with memcpy (and then not destructing the original) will totally - // work. - template - struct VectorTraits > : SimpleClassVectorTraits > { }; - - template - struct VectorTraits > : SimpleClassVectorTraits > { }; - - template - struct VectorTraits > - { - typedef VectorTraits FirstTraits; - typedef VectorTraits SecondTraits; - - static const bool needsDestruction = FirstTraits::needsDestruction || SecondTraits::needsDestruction; - static const bool canInitializeWithMemset = FirstTraits::canInitializeWithMemset && SecondTraits::canInitializeWithMemset; - static const bool canMoveWithMemcpy = FirstTraits::canMoveWithMemcpy && SecondTraits::canMoveWithMemcpy; - static const bool canCopyWithMemcpy = FirstTraits::canCopyWithMemcpy && SecondTraits::canCopyWithMemcpy; - static const bool canFillWithMemset = false; - static const bool canCompareWithMemcmp = FirstTraits::canCompareWithMemcmp && SecondTraits::canCompareWithMemcmp; - static const WeakHandlingFlag weakHandlingFlag = NoWeakHandlingInCollections; // We don't support weak handling in vectors. - }; - -} // namespace WTF - -#define WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(ClassName) \ -namespace WTF { \ - template<> \ - struct VectorTraits : SimpleClassVectorTraits { }; \ -} - -#define WTF_ALLOW_MOVE_AND_INIT_WITH_MEM_FUNCTIONS(ClassName) \ -namespace WTF { \ - template<> \ - struct VectorTraits : VectorTraitsBase \ - { \ - static const bool canInitializeWithMemset = true; \ - static const bool canMoveWithMemcpy = true; \ - }; \ -} - -#define WTF_ALLOW_INIT_WITH_MEM_FUNCTIONS(ClassName) \ -namespace WTF { \ - template<> \ - struct VectorTraits : VectorTraitsBase \ - { \ - static const bool canInitializeWithMemset = true; \ - }; \ -} +class AtomicString; + +template +struct VectorTraitsBase { + static const bool needsDestruction = !IsPod::value; + static const bool canInitializeWithMemset = IsPod::value; + static const bool canMoveWithMemcpy = IsPod::value; + static const bool canCopyWithMemcpy = IsPod::value; + static const bool canFillWithMemset = + IsPod::value && (sizeof(T) == sizeof(char)); + static const bool canCompareWithMemcmp = IsPod::value; + static const WeakHandlingFlag weakHandlingFlag = + NoWeakHandlingInCollections; // We don't support weak handling in + // vectors. +}; + +template +struct VectorTraits : VectorTraitsBase {}; + +// Classes marked with SimpleVectorTraits will use memmov, memcpy, memcmp +// instead of constructors, copy operators, etc for initialization, move +// and comparison. +template +struct SimpleClassVectorTraits : VectorTraitsBase { + static const bool canInitializeWithMemset = true; + static const bool canMoveWithMemcpy = true; + static const bool canCompareWithMemcmp = true; +}; + +// We know OwnPtr and RefPtr are simple enough that initializing to 0 and +// moving with memcpy (and then not destructing the original) will totally +// work. +template +struct VectorTraits> : SimpleClassVectorTraits> {}; + +template +struct VectorTraits> : SimpleClassVectorTraits> {}; + +template +struct VectorTraits> { + typedef VectorTraits FirstTraits; + typedef VectorTraits SecondTraits; + + static const bool needsDestruction = + FirstTraits::needsDestruction || SecondTraits::needsDestruction; + static const bool canInitializeWithMemset = + FirstTraits::canInitializeWithMemset && + SecondTraits::canInitializeWithMemset; + static const bool canMoveWithMemcpy = + FirstTraits::canMoveWithMemcpy && SecondTraits::canMoveWithMemcpy; + static const bool canCopyWithMemcpy = + FirstTraits::canCopyWithMemcpy && SecondTraits::canCopyWithMemcpy; + static const bool canFillWithMemset = false; + static const bool canCompareWithMemcmp = + FirstTraits::canCompareWithMemcmp && SecondTraits::canCompareWithMemcmp; + static const WeakHandlingFlag weakHandlingFlag = + NoWeakHandlingInCollections; // We don't support weak handling in + // vectors. +}; + +} // namespace WTF + +#define WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(ClassName) \ + namespace WTF { \ + template <> \ + struct VectorTraits : SimpleClassVectorTraits {}; \ + } + +#define WTF_ALLOW_MOVE_AND_INIT_WITH_MEM_FUNCTIONS(ClassName) \ + namespace WTF { \ + template <> \ + struct VectorTraits : VectorTraitsBase { \ + static const bool canInitializeWithMemset = true; \ + static const bool canMoveWithMemcpy = true; \ + }; \ + } + +#define WTF_ALLOW_INIT_WITH_MEM_FUNCTIONS(ClassName) \ + namespace WTF { \ + template <> \ + struct VectorTraits : VectorTraitsBase { \ + static const bool canInitializeWithMemset = true; \ + }; \ + } -using WTF::VectorTraits; using WTF::SimpleClassVectorTraits; +using WTF::VectorTraits; #endif // SKY_ENGINE_WTF_VECTORTRAITS_H_ diff --git a/sky/engine/wtf/WTF.cpp b/sky/engine/wtf/WTF.cpp index c59064ac4e91b..a61a70b1c4565 100644 --- a/sky/engine/wtf/WTF.cpp +++ b/sky/engine/wtf/WTF.cpp @@ -42,46 +42,41 @@ bool s_shutdown; bool Partitions::s_initialized; PartitionAllocatorGeneric Partitions::m_bufferAllocator; -void initialize() -{ - // WTF, and Blink in general, cannot handle being re-initialized, even if shutdown first. - // Make that explicit here. - ASSERT(!s_initialized); - ASSERT(!s_shutdown); - s_initialized = true; - Partitions::initialize(); - initializeThreading(); +void initialize() { + // WTF, and Blink in general, cannot handle being re-initialized, even if + // shutdown first. Make that explicit here. + ASSERT(!s_initialized); + ASSERT(!s_shutdown); + s_initialized = true; + Partitions::initialize(); + initializeThreading(); } -void shutdown() -{ - ASSERT(s_initialized); - ASSERT(!s_shutdown); - s_shutdown = true; - Partitions::shutdown(); +void shutdown() { + ASSERT(s_initialized); + ASSERT(!s_shutdown); + s_shutdown = true; + Partitions::shutdown(); } -bool isShutdown() -{ - return s_shutdown; +bool isShutdown() { + return s_shutdown; } -void Partitions::initialize() -{ - static int lock = 0; - // Guard against two threads hitting here in parallel. - spinLockLock(&lock); - if (!s_initialized) { - m_bufferAllocator.init(); - s_initialized = true; - } - spinLockUnlock(&lock); +void Partitions::initialize() { + static int lock = 0; + // Guard against two threads hitting here in parallel. + spinLockLock(&lock); + if (!s_initialized) { + m_bufferAllocator.init(); + s_initialized = true; + } + spinLockUnlock(&lock); } -void Partitions::shutdown() -{ - fastMallocShutdown(); - m_bufferAllocator.shutdown(); +void Partitions::shutdown() { + fastMallocShutdown(); + m_bufferAllocator.shutdown(); } -} // namespace WTF +} // namespace WTF diff --git a/sky/engine/wtf/WTF.h b/sky/engine/wtf/WTF.h index 7fc1f547d8d67..9cdb75e93233f 100644 --- a/sky/engine/wtf/WTF.h +++ b/sky/engine/wtf/WTF.h @@ -37,27 +37,27 @@ namespace WTF { -// This function must be called exactly once from the main thread before using anything else in WTF. +// This function must be called exactly once from the main thread before using +// anything else in WTF. WTF_EXPORT void initialize(); WTF_EXPORT void shutdown(); WTF_EXPORT bool isShutdown(); class WTF_EXPORT Partitions { -public: - static void initialize(); - static void shutdown(); - static ALWAYS_INLINE PartitionRootGeneric* getBufferPartition() - { - if (UNLIKELY(!s_initialized)) - initialize(); - return m_bufferAllocator.root(); - } - -private: - static bool s_initialized; - static PartitionAllocatorGeneric m_bufferAllocator; + public: + static void initialize(); + static void shutdown(); + static ALWAYS_INLINE PartitionRootGeneric* getBufferPartition() { + if (UNLIKELY(!s_initialized)) + initialize(); + return m_bufferAllocator.root(); + } + + private: + static bool s_initialized; + static PartitionAllocatorGeneric m_bufferAllocator; }; -} // namespace WTF +} // namespace WTF #endif // SKY_ENGINE_WTF_WTF_H_ diff --git a/sky/engine/wtf/WTFThreadData.cpp b/sky/engine/wtf/WTFThreadData.cpp index 776a43b119e3d..df0c1c81216c1 100644 --- a/sky/engine/wtf/WTFThreadData.cpp +++ b/sky/engine/wtf/WTFThreadData.cpp @@ -33,16 +33,13 @@ namespace WTF { ThreadSpecific* WTFThreadData::staticData; WTFThreadData::WTFThreadData() - : m_atomicStringTable(0) - , m_atomicStringTableDestructor(0) - , m_cachedConverterICU(adoptPtr(new ICUConverterWrapper)) -{ -} + : m_atomicStringTable(0), + m_atomicStringTableDestructor(0), + m_cachedConverterICU(adoptPtr(new ICUConverterWrapper)) {} -WTFThreadData::~WTFThreadData() -{ - if (m_atomicStringTableDestructor) - m_atomicStringTableDestructor(m_atomicStringTable); +WTFThreadData::~WTFThreadData() { + if (m_atomicStringTableDestructor) + m_atomicStringTableDestructor(m_atomicStringTable); } -} // namespace WTF +} // namespace WTF diff --git a/sky/engine/wtf/WTFThreadData.h b/sky/engine/wtf/WTFThreadData.h index 73eca9a04df24..dfd2f07095be9 100644 --- a/sky/engine/wtf/WTFThreadData.h +++ b/sky/engine/wtf/WTFThreadData.h @@ -43,42 +43,39 @@ struct ICUConverterWrapper; typedef void (*AtomicStringTableDestructor)(AtomicStringTable*); class WTF_EXPORT WTFThreadData { - WTF_MAKE_NONCOPYABLE(WTFThreadData); -public: - WTFThreadData(); - ~WTFThreadData(); + WTF_MAKE_NONCOPYABLE(WTFThreadData); - AtomicStringTable* atomicStringTable() - { - return m_atomicStringTable; - } + public: + WTFThreadData(); + ~WTFThreadData(); - ICUConverterWrapper& cachedConverterICU() { return *m_cachedConverterICU; } + AtomicStringTable* atomicStringTable() { return m_atomicStringTable; } -private: - AtomicStringTable* m_atomicStringTable; - AtomicStringTableDestructor m_atomicStringTableDestructor; - OwnPtr m_cachedConverterICU; + ICUConverterWrapper& cachedConverterICU() { return *m_cachedConverterICU; } - static ThreadSpecific* staticData; - friend WTFThreadData& wtfThreadData(); - friend class AtomicStringTable; + private: + AtomicStringTable* m_atomicStringTable; + AtomicStringTableDestructor m_atomicStringTableDestructor; + OwnPtr m_cachedConverterICU; + + static ThreadSpecific* staticData; + friend WTFThreadData& wtfThreadData(); + friend class AtomicStringTable; }; -inline WTFThreadData& wtfThreadData() -{ - // WRT WebCore: - // WTFThreadData is used on main thread before it could possibly be used - // on secondary ones, so there is no need for synchronization here. - // WRT JavaScriptCore: - // wtfThreadData() is initially called from initializeThreading(), ensuring - // this is initially called in a pthread_once locked context. - if (!WTFThreadData::staticData) - WTFThreadData::staticData = new ThreadSpecific; - return **WTFThreadData::staticData; +inline WTFThreadData& wtfThreadData() { + // WRT WebCore: + // WTFThreadData is used on main thread before it could possibly be used + // on secondary ones, so there is no need for synchronization here. + // WRT JavaScriptCore: + // wtfThreadData() is initially called from initializeThreading(), ensuring + // this is initially called in a pthread_once locked context. + if (!WTFThreadData::staticData) + WTFThreadData::staticData = new ThreadSpecific; + return **WTFThreadData::staticData; } -} // namespace WTF +} // namespace WTF using WTF::WTFThreadData; using WTF::wtfThreadData; diff --git a/sky/engine/wtf/asm/SaturatedArithmeticARM.h b/sky/engine/wtf/asm/SaturatedArithmeticARM.h index 913127e3d4e1a..b61227e3a03a3 100644 --- a/sky/engine/wtf/asm/SaturatedArithmeticARM.h +++ b/sky/engine/wtf/asm/SaturatedArithmeticARM.h @@ -9,97 +9,86 @@ #include #include "flutter/sky/engine/wtf/CPU.h" -ALWAYS_INLINE int32_t saturatedAddition(int32_t a, int32_t b) -{ - int32_t result; +ALWAYS_INLINE int32_t saturatedAddition(int32_t a, int32_t b) { + int32_t result; - asm("qadd %[output],%[first],%[second]" - : [output] "=r" (result) - : [first] "r" (a), - [second] "r" (b)); + asm("qadd %[output],%[first],%[second]" + : [output] "=r"(result) + : [first] "r"(a), [second] "r"(b)); - return result; + return result; } -ALWAYS_INLINE int32_t saturatedSubtraction(int32_t a, int32_t b) -{ - int32_t result; +ALWAYS_INLINE int32_t saturatedSubtraction(int32_t a, int32_t b) { + int32_t result; - asm("qsub %[output],%[first],%[second]" - : [output] "=r" (result) - : [first] "r" (a), - [second] "r" (b)); + asm("qsub %[output],%[first],%[second]" + : [output] "=r"(result) + : [first] "r"(a), [second] "r"(b)); - return result; + return result; } -inline int getMaxSaturatedSetResultForTesting(int FractionalShift) -{ - // For ARM Asm version the set function maxes out to the biggest - // possible integer part with the fractional part zero'd out. - // e.g. 0x7fffffc0. - return std::numeric_limits::max() & ~((1 << FractionalShift)-1); +inline int getMaxSaturatedSetResultForTesting(int FractionalShift) { + // For ARM Asm version the set function maxes out to the biggest + // possible integer part with the fractional part zero'd out. + // e.g. 0x7fffffc0. + return std::numeric_limits::max() & ~((1 << FractionalShift) - 1); } -inline int getMinSaturatedSetResultForTesting(int FractionalShift) -{ - return std::numeric_limits::min(); +inline int getMinSaturatedSetResultForTesting(int FractionalShift) { + return std::numeric_limits::min(); } -ALWAYS_INLINE int saturatedSet(int value, int FractionalShift) -{ - // Figure out how many bits are left for storing the integer part of - // the fixed point number, and saturate our input to that - const int saturate = 32 - FractionalShift; - - int result; - - // The following ARM code will Saturate the passed value to the number of - // bits used for the whole part of the fixed point representation, then - // shift it up into place. This will result in the low bits - // all being 0's. When the value saturates this gives a different result - // to from the C++ case; in the C++ code a saturated value has all the low - // bits set to 1 (for a +ve number at least). This cannot be done rapidly - // in ARM ... we live with the difference, for the sake of speed. - - asm("ssat %[output],%[saturate],%[value]\n\t" - "lsl %[output],%[shift]" - : [output] "=r" (result) - : [value] "r" (value), - [saturate] "n" (saturate), - [shift] "n" (FractionalShift)); - - return result; -} +ALWAYS_INLINE int saturatedSet(int value, int FractionalShift) { + // Figure out how many bits are left for storing the integer part of + // the fixed point number, and saturate our input to that + const int saturate = 32 - FractionalShift; + + int result; + + // The following ARM code will Saturate the passed value to the number of + // bits used for the whole part of the fixed point representation, then + // shift it up into place. This will result in the low bits + // all being 0's. When the value saturates this gives a different result + // to from the C++ case; in the C++ code a saturated value has all the low + // bits set to 1 (for a +ve number at least). This cannot be done rapidly + // in ARM ... we live with the difference, for the sake of speed. + asm("ssat %[output],%[saturate],%[value]\n\t" + "lsl %[output],%[shift]" + : [output] "=r"(result) + : [value] "r"(value), [saturate] "n"(saturate), + [shift] "n"(FractionalShift)); + + return result; +} -ALWAYS_INLINE int saturatedSet(unsigned value, int FractionalShift) -{ - // Here we are being passed an unsigned value to saturate, - // even though the result is returned as a signed integer. The ARM - // instruction for unsigned saturation therefore needs to be given one - // less bit (i.e. the sign bit) for the saturation to work correctly; hence - // the '31' below. - const int saturate = 31 - FractionalShift; - - // The following ARM code will Saturate the passed value to the number of - // bits used for the whole part of the fixed point representation, then - // shift it up into place. This will result in the low bits - // all being 0's. When the value saturates this gives a different result - // to from the C++ case; in the C++ code a saturated value has all the low - // bits set to 1. This cannot be done rapidly in ARM, so we live with the - // difference, for the sake of speed. - - int result; - - asm("usat %[output],%[saturate],%[value]\n\t" - "lsl %[output],%[shift]" - : [output] "=r" (result) - : [value] "r" (value), - [saturate] "n" (saturate), - [shift] "n" (FractionalShift)); - - return result; +ALWAYS_INLINE int saturatedSet(unsigned value, int FractionalShift) { + // Here we are being passed an unsigned value to saturate, + // even though the result is returned as a signed integer. The ARM + // instruction for unsigned saturation therefore needs to be given one + // less bit (i.e. the sign bit) for the saturation to work correctly; hence + // the '31' below. + const int saturate = 31 - FractionalShift; + + // The following ARM code will Saturate the passed value to the number of + // bits used for the whole part of the fixed point representation, then + // shift it up into place. This will result in the low bits + // all being 0's. When the value saturates this gives a different result + // to from the C++ case; in the C++ code a saturated value has all the low + // bits set to 1. This cannot be done rapidly in ARM, so we live with the + // difference, for the sake of speed. + + int result; + + asm("usat %[output],%[saturate],%[value]\n\t" + "lsl %[output],%[shift]" + : [output] "=r"(result) + : [value] "r"(value), [saturate] "n"(saturate), + [shift] "n"(FractionalShift)); + + return result; } #endif // SKY_ENGINE_WTF_ASM_SATURATEDARITHMETICARM_H_ diff --git a/sky/engine/wtf/dtoa.cpp b/sky/engine/wtf/dtoa.cpp index 81323e375b325..bdb9fc4d79674 100644 --- a/sky/engine/wtf/dtoa.cpp +++ b/sky/engine/wtf/dtoa.cpp @@ -3,7 +3,8 @@ * The author of this software is David M. Gay. * * Copyright (c) 1991, 2000, 2001 by Lucent Technologies. - * Copyright (C) 2002, 2005, 2006, 2007, 2008, 2010, 2012 Apple Inc. All rights reserved. + * Copyright (C) 2002, 2005, 2006, 2007, 2008, 2010, 2012 Apple Inc. + * All rights reserved. * * Permission to use, copy, modify, and distribute this software for any * purpose without fee is hereby granted, provided that this entire notice @@ -44,8 +45,8 @@ namespace WTF { Mutex* s_dtoaP5Mutex; typedef union { - double d; - uint32_t L[2]; + double d; + uint32_t L[2]; } U; #if CPU(BIG_ENDIAN) || CPU(MIDDLE_ENDIAN) @@ -57,22 +58,22 @@ typedef union { #endif #define dval(x) (x)->d -#define Exp_shift 20 +#define Exp_shift 20 #define Exp_shift1 20 -#define Exp_msk1 0x100000 -#define Exp_msk11 0x100000 -#define Exp_mask 0x7ff00000 +#define Exp_msk1 0x100000 +#define Exp_msk11 0x100000 +#define Exp_mask 0x7ff00000 #define P 53 #define Bias 1023 #define Emin (-1022) -#define Exp_1 0x3ff00000 +#define Exp_1 0x3ff00000 #define Exp_11 0x3ff00000 #define Ebits 11 -#define Frac_mask 0xfffff +#define Frac_mask 0xfffff #define Frac_mask1 0xfffff #define Ten_pmax 22 #define Bletch 0x10 -#define Bndry_mask 0xfffff +#define Bndry_mask 0xfffff #define Bndry_mask1 0xfffff #define LSB 1 #define Sign_bit 0x80000000 @@ -90,7 +91,8 @@ typedef union { #if CPU(X86_64) // FIXME: should we enable this on all 64-bit CPUs? -// 64-bit emulation provided by the compiler is likely to be slower than dtoa own code on 32-bit hardware. +// 64-bit emulation provided by the compiler is likely to be slower than dtoa +// own code on 32-bit hardware. #define USE_LONG_LONG #endif @@ -99,592 +101,569 @@ typedef union { * An alternative that might be better on some machines is * *p++ = high << 16 | low & 0xffff; */ -static ALWAYS_INLINE uint32_t* storeInc(uint32_t* p, uint16_t high, uint16_t low) -{ - uint16_t* p16 = reinterpret_cast(p); +static ALWAYS_INLINE uint32_t* storeInc(uint32_t* p, + uint16_t high, + uint16_t low) { + uint16_t* p16 = reinterpret_cast(p); #if CPU(BIG_ENDIAN) - p16[0] = high; - p16[1] = low; + p16[0] = high; + p16[1] = low; #else - p16[1] = high; - p16[0] = low; + p16[1] = high; + p16[0] = low; #endif - return p + 1; + return p + 1; } #endif struct BigInt { - BigInt() : sign(0) { } - int sign; + BigInt() : sign(0) {} + int sign; - void clear() - { - sign = 0; - m_words.clear(); - } + void clear() { + sign = 0; + m_words.clear(); + } - size_t size() const - { - return m_words.size(); - } + size_t size() const { return m_words.size(); } - void resize(size_t s) - { - m_words.resize(s); - } + void resize(size_t s) { m_words.resize(s); } - uint32_t* words() - { - return m_words.data(); - } + uint32_t* words() { return m_words.data(); } - const uint32_t* words() const - { - return m_words.data(); - } + const uint32_t* words() const { return m_words.data(); } - void append(uint32_t w) - { - m_words.append(w); - } + void append(uint32_t w) { m_words.append(w); } - Vector m_words; + Vector m_words; }; -static void multadd(BigInt& b, int m, int a) /* multiply by m and add a */ +static void multadd(BigInt& b, int m, int a) /* multiply by m and add a */ { #ifdef USE_LONG_LONG - unsigned long long carry; + unsigned long long carry; #else - uint32_t carry; + uint32_t carry; #endif - int wds = b.size(); - uint32_t* x = b.words(); - int i = 0; - carry = a; - do { + int wds = b.size(); + uint32_t* x = b.words(); + int i = 0; + carry = a; + do { #ifdef USE_LONG_LONG - unsigned long long y = *x * (unsigned long long)m + carry; - carry = y >> 32; - *x++ = (uint32_t)y & 0xffffffffUL; + unsigned long long y = *x * (unsigned long long)m + carry; + carry = y >> 32; + *x++ = (uint32_t)y & 0xffffffffUL; #else - uint32_t xi = *x; - uint32_t y = (xi & 0xffff) * m + carry; - uint32_t z = (xi >> 16) * m + (y >> 16); - carry = z >> 16; - *x++ = (z << 16) + (y & 0xffff); + uint32_t xi = *x; + uint32_t y = (xi & 0xffff) * m + carry; + uint32_t z = (xi >> 16) * m + (y >> 16); + carry = z >> 16; + *x++ = (z << 16) + (y & 0xffff); #endif - } while (++i < wds); + } while (++i < wds); - if (carry) - b.append((uint32_t)carry); + if (carry) + b.append((uint32_t)carry); } -static int hi0bits(uint32_t x) -{ - int k = 0; - - if (!(x & 0xffff0000)) { - k = 16; - x <<= 16; - } - if (!(x & 0xff000000)) { - k += 8; - x <<= 8; - } - if (!(x & 0xf0000000)) { - k += 4; - x <<= 4; - } - if (!(x & 0xc0000000)) { - k += 2; - x <<= 2; - } - if (!(x & 0x80000000)) { - k++; - if (!(x & 0x40000000)) - return 32; - } - return k; +static int hi0bits(uint32_t x) { + int k = 0; + + if (!(x & 0xffff0000)) { + k = 16; + x <<= 16; + } + if (!(x & 0xff000000)) { + k += 8; + x <<= 8; + } + if (!(x & 0xf0000000)) { + k += 4; + x <<= 4; + } + if (!(x & 0xc0000000)) { + k += 2; + x <<= 2; + } + if (!(x & 0x80000000)) { + k++; + if (!(x & 0x40000000)) + return 32; + } + return k; } -static int lo0bits(uint32_t* y) -{ - int k; - uint32_t x = *y; - - if (x & 7) { - if (x & 1) - return 0; - if (x & 2) { - *y = x >> 1; - return 1; - } - *y = x >> 2; - return 2; - } - k = 0; - if (!(x & 0xffff)) { - k = 16; - x >>= 16; - } - if (!(x & 0xff)) { - k += 8; - x >>= 8; - } - if (!(x & 0xf)) { - k += 4; - x >>= 4; - } - if (!(x & 0x3)) { - k += 2; - x >>= 2; - } - if (!(x & 1)) { - k++; - x >>= 1; - if (!x) - return 32; +static int lo0bits(uint32_t* y) { + int k; + uint32_t x = *y; + + if (x & 7) { + if (x & 1) + return 0; + if (x & 2) { + *y = x >> 1; + return 1; } - *y = x; - return k; + *y = x >> 2; + return 2; + } + k = 0; + if (!(x & 0xffff)) { + k = 16; + x >>= 16; + } + if (!(x & 0xff)) { + k += 8; + x >>= 8; + } + if (!(x & 0xf)) { + k += 4; + x >>= 4; + } + if (!(x & 0x3)) { + k += 2; + x >>= 2; + } + if (!(x & 1)) { + k++; + x >>= 1; + if (!x) + return 32; + } + *y = x; + return k; } -static void i2b(BigInt& b, int i) -{ - b.sign = 0; - b.resize(1); - b.words()[0] = i; +static void i2b(BigInt& b, int i) { + b.sign = 0; + b.resize(1); + b.words()[0] = i; } -static void mult(BigInt& aRef, const BigInt& bRef) -{ - const BigInt* a = &aRef; - const BigInt* b = &bRef; - BigInt c; - int wa, wb, wc; - const uint32_t* x = 0; - const uint32_t* xa; - const uint32_t* xb; - const uint32_t* xae; - const uint32_t* xbe; - uint32_t* xc; - uint32_t* xc0; - uint32_t y; +static void mult(BigInt& aRef, const BigInt& bRef) { + const BigInt* a = &aRef; + const BigInt* b = &bRef; + BigInt c; + int wa, wb, wc; + const uint32_t* x = 0; + const uint32_t* xa; + const uint32_t* xb; + const uint32_t* xae; + const uint32_t* xbe; + uint32_t* xc; + uint32_t* xc0; + uint32_t y; #ifdef USE_LONG_LONG - unsigned long long carry, z; + unsigned long long carry, z; #else - uint32_t carry, z; + uint32_t carry, z; #endif - if (a->size() < b->size()) { - const BigInt* tmp = a; - a = b; - b = tmp; - } - - wa = a->size(); - wb = b->size(); - wc = wa + wb; - c.resize(wc); - - for (xc = c.words(), xa = xc + wc; xc < xa; xc++) - *xc = 0; - xa = a->words(); - xae = xa + wa; - xb = b->words(); - xbe = xb + wb; - xc0 = c.words(); + if (a->size() < b->size()) { + const BigInt* tmp = a; + a = b; + b = tmp; + } + + wa = a->size(); + wb = b->size(); + wc = wa + wb; + c.resize(wc); + + for (xc = c.words(), xa = xc + wc; xc < xa; xc++) + *xc = 0; + xa = a->words(); + xae = xa + wa; + xb = b->words(); + xbe = xb + wb; + xc0 = c.words(); #ifdef USE_LONG_LONG - for (; xb < xbe; xc0++) { - if ((y = *xb++)) { - x = xa; - xc = xc0; - carry = 0; - do { - z = *x++ * (unsigned long long)y + *xc + carry; - carry = z >> 32; - *xc++ = (uint32_t)z & 0xffffffffUL; - } while (x < xae); - *xc = (uint32_t)carry; - } + for (; xb < xbe; xc0++) { + if ((y = *xb++)) { + x = xa; + xc = xc0; + carry = 0; + do { + z = *x++ * (unsigned long long)y + *xc + carry; + carry = z >> 32; + *xc++ = (uint32_t)z & 0xffffffffUL; + } while (x < xae); + *xc = (uint32_t)carry; } + } #else - for (; xb < xbe; xb++, xc0++) { - if ((y = *xb & 0xffff)) { - x = xa; - xc = xc0; - carry = 0; - do { - z = (*x & 0xffff) * y + (*xc & 0xffff) + carry; - carry = z >> 16; - uint32_t z2 = (*x++ >> 16) * y + (*xc >> 16) + carry; - carry = z2 >> 16; - xc = storeInc(xc, z2, z); - } while (x < xae); - *xc = carry; - } - if ((y = *xb >> 16)) { - x = xa; - xc = xc0; - carry = 0; - uint32_t z2 = *xc; - do { - z = (*x & 0xffff) * y + (*xc >> 16) + carry; - carry = z >> 16; - xc = storeInc(xc, z, z2); - z2 = (*x++ >> 16) * y + (*xc & 0xffff) + carry; - carry = z2 >> 16; - } while (x < xae); - *xc = z2; - } + for (; xb < xbe; xb++, xc0++) { + if ((y = *xb & 0xffff)) { + x = xa; + xc = xc0; + carry = 0; + do { + z = (*x & 0xffff) * y + (*xc & 0xffff) + carry; + carry = z >> 16; + uint32_t z2 = (*x++ >> 16) * y + (*xc >> 16) + carry; + carry = z2 >> 16; + xc = storeInc(xc, z2, z); + } while (x < xae); + *xc = carry; + } + if ((y = *xb >> 16)) { + x = xa; + xc = xc0; + carry = 0; + uint32_t z2 = *xc; + do { + z = (*x & 0xffff) * y + (*xc >> 16) + carry; + carry = z >> 16; + xc = storeInc(xc, z, z2); + z2 = (*x++ >> 16) * y + (*xc & 0xffff) + carry; + carry = z2 >> 16; + } while (x < xae); + *xc = z2; } + } #endif - for (xc0 = c.words(), xc = xc0 + wc; wc > 0 && !*--xc; --wc) { } - c.resize(wc); - aRef = c; + for (xc0 = c.words(), xc = xc0 + wc; wc > 0 && !*--xc; --wc) { + } + c.resize(wc); + aRef = c; } struct P5Node { - WTF_MAKE_NONCOPYABLE(P5Node); WTF_MAKE_FAST_ALLOCATED; -public: - P5Node() { } - BigInt val; - P5Node* next; + WTF_MAKE_NONCOPYABLE(P5Node); + WTF_MAKE_FAST_ALLOCATED; + + public: + P5Node() {} + BigInt val; + P5Node* next; }; static P5Node* p5s; static int p5sCount; -static ALWAYS_INLINE void pow5mult(BigInt& b, int k) -{ - static int p05[3] = { 5, 25, 125 }; +static ALWAYS_INLINE void pow5mult(BigInt& b, int k) { + static int p05[3] = {5, 25, 125}; - if (int i = k & 3) - multadd(b, p05[i - 1], 0); + if (int i = k & 3) + multadd(b, p05[i - 1], 0); - if (!(k >>= 2)) - return; - - s_dtoaP5Mutex->lock(); - P5Node* p5 = p5s; - - if (!p5) { - /* first time */ - p5 = new P5Node; - i2b(p5->val, 625); - p5->next = 0; - p5s = p5; - p5sCount = 1; - } - - int p5sCountLocal = p5sCount; - s_dtoaP5Mutex->unlock(); - int p5sUsed = 0; - - for (;;) { - if (k & 1) - mult(b, p5->val); - - if (!(k >>= 1)) - break; - - if (++p5sUsed == p5sCountLocal) { - s_dtoaP5Mutex->lock(); - if (p5sUsed == p5sCount) { - ASSERT(!p5->next); - p5->next = new P5Node; - p5->next->next = 0; - p5->next->val = p5->val; - mult(p5->next->val, p5->next->val); - ++p5sCount; - } + if (!(k >>= 2)) + return; - p5sCountLocal = p5sCount; - s_dtoaP5Mutex->unlock(); - } - p5 = p5->next; + s_dtoaP5Mutex->lock(); + P5Node* p5 = p5s; + + if (!p5) { + /* first time */ + p5 = new P5Node; + i2b(p5->val, 625); + p5->next = 0; + p5s = p5; + p5sCount = 1; + } + + int p5sCountLocal = p5sCount; + s_dtoaP5Mutex->unlock(); + int p5sUsed = 0; + + for (;;) { + if (k & 1) + mult(b, p5->val); + + if (!(k >>= 1)) + break; + + if (++p5sUsed == p5sCountLocal) { + s_dtoaP5Mutex->lock(); + if (p5sUsed == p5sCount) { + ASSERT(!p5->next); + p5->next = new P5Node; + p5->next->next = 0; + p5->next->val = p5->val; + mult(p5->next->val, p5->next->val); + ++p5sCount; + } + + p5sCountLocal = p5sCount; + s_dtoaP5Mutex->unlock(); } + p5 = p5->next; + } } -static ALWAYS_INLINE void lshift(BigInt& b, int k) -{ - int n = k >> 5; - - int origSize = b.size(); - int n1 = n + origSize + 1; - - if (k &= 0x1f) - b.resize(b.size() + n + 1); - else - b.resize(b.size() + n); - - const uint32_t* srcStart = b.words(); - uint32_t* dstStart = b.words(); - const uint32_t* src = srcStart + origSize - 1; - uint32_t* dst = dstStart + n1 - 1; - if (k) { - uint32_t hiSubword = 0; - int s = 32 - k; - for (; src >= srcStart; --src) { - *dst-- = hiSubword | *src >> s; - hiSubword = *src << k; - } - *dst = hiSubword; - ASSERT(dst == dstStart + n); - - b.resize(origSize + n + !!b.words()[n1 - 1]); - } - else { - do { - *--dst = *src--; - } while (src >= srcStart); +static ALWAYS_INLINE void lshift(BigInt& b, int k) { + int n = k >> 5; + + int origSize = b.size(); + int n1 = n + origSize + 1; + + if (k &= 0x1f) + b.resize(b.size() + n + 1); + else + b.resize(b.size() + n); + + const uint32_t* srcStart = b.words(); + uint32_t* dstStart = b.words(); + const uint32_t* src = srcStart + origSize - 1; + uint32_t* dst = dstStart + n1 - 1; + if (k) { + uint32_t hiSubword = 0; + int s = 32 - k; + for (; src >= srcStart; --src) { + *dst-- = hiSubword | *src >> s; + hiSubword = *src << k; } - for (dst = dstStart + n; dst != dstStart; ) - *--dst = 0; + *dst = hiSubword; + ASSERT(dst == dstStart + n); - ASSERT(b.size() <= 1 || b.words()[b.size() - 1]); + b.resize(origSize + n + !!b.words()[n1 - 1]); + } else { + do { + *--dst = *src--; + } while (src >= srcStart); + } + for (dst = dstStart + n; dst != dstStart;) + *--dst = 0; + + ASSERT(b.size() <= 1 || b.words()[b.size() - 1]); } -static int cmp(const BigInt& a, const BigInt& b) -{ - const uint32_t *xa, *xa0, *xb, *xb0; - int i, j; - - i = a.size(); - j = b.size(); - ASSERT(i <= 1 || a.words()[i - 1]); - ASSERT(j <= 1 || b.words()[j - 1]); - if (i -= j) - return i; - xa0 = a.words(); - xa = xa0 + j; - xb0 = b.words(); - xb = xb0 + j; - for (;;) { - if (*--xa != *--xb) - return *xa < *xb ? -1 : 1; - if (xa <= xa0) - break; - } - return 0; +static int cmp(const BigInt& a, const BigInt& b) { + const uint32_t *xa, *xa0, *xb, *xb0; + int i, j; + + i = a.size(); + j = b.size(); + ASSERT(i <= 1 || a.words()[i - 1]); + ASSERT(j <= 1 || b.words()[j - 1]); + if (i -= j) + return i; + xa0 = a.words(); + xa = xa0 + j; + xb0 = b.words(); + xb = xb0 + j; + for (;;) { + if (*--xa != *--xb) + return *xa < *xb ? -1 : 1; + if (xa <= xa0) + break; + } + return 0; } -static ALWAYS_INLINE void diff(BigInt& c, const BigInt& aRef, const BigInt& bRef) -{ - const BigInt* a = &aRef; - const BigInt* b = &bRef; - int i, wa, wb; - uint32_t* xc; - - i = cmp(*a, *b); - if (!i) { - c.sign = 0; - c.resize(1); - c.words()[0] = 0; - return; - } - if (i < 0) { - const BigInt* tmp = a; - a = b; - b = tmp; - i = 1; - } else - i = 0; - - wa = a->size(); - const uint32_t* xa = a->words(); - const uint32_t* xae = xa + wa; - wb = b->size(); - const uint32_t* xb = b->words(); - const uint32_t* xbe = xb + wb; - - c.resize(wa); - c.sign = i; - xc = c.words(); +static ALWAYS_INLINE void diff(BigInt& c, + const BigInt& aRef, + const BigInt& bRef) { + const BigInt* a = &aRef; + const BigInt* b = &bRef; + int i, wa, wb; + uint32_t* xc; + + i = cmp(*a, *b); + if (!i) { + c.sign = 0; + c.resize(1); + c.words()[0] = 0; + return; + } + if (i < 0) { + const BigInt* tmp = a; + a = b; + b = tmp; + i = 1; + } else + i = 0; + + wa = a->size(); + const uint32_t* xa = a->words(); + const uint32_t* xae = xa + wa; + wb = b->size(); + const uint32_t* xb = b->words(); + const uint32_t* xbe = xb + wb; + + c.resize(wa); + c.sign = i; + xc = c.words(); #ifdef USE_LONG_LONG - unsigned long long borrow = 0; - do { - unsigned long long y = (unsigned long long)*xa++ - *xb++ - borrow; - borrow = y >> 32 & (uint32_t)1; - *xc++ = (uint32_t)y & 0xffffffffUL; - } while (xb < xbe); - while (xa < xae) { - unsigned long long y = *xa++ - borrow; - borrow = y >> 32 & (uint32_t)1; - *xc++ = (uint32_t)y & 0xffffffffUL; - } + unsigned long long borrow = 0; + do { + unsigned long long y = (unsigned long long)*xa++ - *xb++ - borrow; + borrow = y >> 32 & (uint32_t)1; + *xc++ = (uint32_t)y & 0xffffffffUL; + } while (xb < xbe); + while (xa < xae) { + unsigned long long y = *xa++ - borrow; + borrow = y >> 32 & (uint32_t)1; + *xc++ = (uint32_t)y & 0xffffffffUL; + } #else - uint32_t borrow = 0; - do { - uint32_t y = (*xa & 0xffff) - (*xb & 0xffff) - borrow; - borrow = (y & 0x10000) >> 16; - uint32_t z = (*xa++ >> 16) - (*xb++ >> 16) - borrow; - borrow = (z & 0x10000) >> 16; - xc = storeInc(xc, z, y); - } while (xb < xbe); - while (xa < xae) { - uint32_t y = (*xa & 0xffff) - borrow; - borrow = (y & 0x10000) >> 16; - uint32_t z = (*xa++ >> 16) - borrow; - borrow = (z & 0x10000) >> 16; - xc = storeInc(xc, z, y); - } + uint32_t borrow = 0; + do { + uint32_t y = (*xa & 0xffff) - (*xb & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + uint32_t z = (*xa++ >> 16) - (*xb++ >> 16) - borrow; + borrow = (z & 0x10000) >> 16; + xc = storeInc(xc, z, y); + } while (xb < xbe); + while (xa < xae) { + uint32_t y = (*xa & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + uint32_t z = (*xa++ >> 16) - borrow; + borrow = (z & 0x10000) >> 16; + xc = storeInc(xc, z, y); + } #endif - while (!*--xc) - wa--; - c.resize(wa); + while (!*--xc) + wa--; + c.resize(wa); } -static ALWAYS_INLINE void d2b(BigInt& b, U* d, int* e, int* bits) -{ - int de, k; - uint32_t* x; - uint32_t y, z; - int i; +static ALWAYS_INLINE void d2b(BigInt& b, U* d, int* e, int* bits) { + int de, k; + uint32_t* x; + uint32_t y, z; + int i; #define d0 word0(d) #define d1 word1(d) - b.sign = 0; - b.resize(1); - x = b.words(); - - z = d0 & Frac_mask; - d0 &= 0x7fffffff; /* clear sign bit, which we ignore */ - if ((de = (int)(d0 >> Exp_shift))) - z |= Exp_msk1; - if ((y = d1)) { - if ((k = lo0bits(&y))) { - x[0] = y | (z << (32 - k)); - z >>= k; - } else - x[0] = y; - if (z) { - b.resize(2); - x[1] = z; - } - - i = b.size(); - } else { - k = lo0bits(&z); - x[0] = z; - i = 1; - b.resize(1); - k += 32; - } - if (de) { - *e = de - Bias - (P - 1) + k; - *bits = P - k; - } else { - *e = 0 - Bias - (P - 1) + 1 + k; - *bits = (32 * i) - hi0bits(x[i - 1]); + b.sign = 0; + b.resize(1); + x = b.words(); + + z = d0 & Frac_mask; + d0 &= 0x7fffffff; /* clear sign bit, which we ignore */ + if ((de = (int)(d0 >> Exp_shift))) + z |= Exp_msk1; + if ((y = d1)) { + if ((k = lo0bits(&y))) { + x[0] = y | (z << (32 - k)); + z >>= k; + } else + x[0] = y; + if (z) { + b.resize(2); + x[1] = z; } + + i = b.size(); + } else { + k = lo0bits(&z); + x[0] = z; + i = 1; + b.resize(1); + k += 32; + } + if (de) { + *e = de - Bias - (P - 1) + k; + *bits = P - k; + } else { + *e = 0 - Bias - (P - 1) + 1 + k; + *bits = (32 * i) - hi0bits(x[i - 1]); + } } #undef d0 #undef d1 -static const double tens[] = { - 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, - 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, - 1e20, 1e21, 1e22 -}; +static const double tens[] = {1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, + 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, + 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22}; -static const double bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 }; +static const double bigtens[] = {1e16, 1e32, 1e64, 1e128, 1e256}; #define Scale_Bit 0x10 #define n_bigtens 5 -static ALWAYS_INLINE int quorem(BigInt& b, BigInt& S) -{ - size_t n; - uint32_t* bx; - uint32_t* bxe; - uint32_t q; - uint32_t* sx; - uint32_t* sxe; +static ALWAYS_INLINE int quorem(BigInt& b, BigInt& S) { + size_t n; + uint32_t* bx; + uint32_t* bxe; + uint32_t q; + uint32_t* sx; + uint32_t* sxe; #ifdef USE_LONG_LONG - unsigned long long borrow, carry, y, ys; + unsigned long long borrow, carry, y, ys; #else - uint32_t borrow, carry, y, ys; - uint32_t si, z, zs; + uint32_t borrow, carry, y, ys; + uint32_t si, z, zs; #endif - ASSERT(b.size() <= 1 || b.words()[b.size() - 1]); - ASSERT(S.size() <= 1 || S.words()[S.size() - 1]); + ASSERT(b.size() <= 1 || b.words()[b.size() - 1]); + ASSERT(S.size() <= 1 || S.words()[S.size() - 1]); - n = S.size(); - ASSERT_WITH_MESSAGE(b.size() <= n, "oversize b in quorem"); - if (b.size() < n) - return 0; - sx = S.words(); - sxe = sx + --n; - bx = b.words(); - bxe = bx + n; - q = *bxe / (*sxe + 1); /* ensure q <= true quotient */ - ASSERT_WITH_MESSAGE(q <= 9, "oversized quotient in quorem"); - if (q) { - borrow = 0; - carry = 0; - do { + n = S.size(); + ASSERT_WITH_MESSAGE(b.size() <= n, "oversize b in quorem"); + if (b.size() < n) + return 0; + sx = S.words(); + sxe = sx + --n; + bx = b.words(); + bxe = bx + n; + q = *bxe / (*sxe + 1); /* ensure q <= true quotient */ + ASSERT_WITH_MESSAGE(q <= 9, "oversized quotient in quorem"); + if (q) { + borrow = 0; + carry = 0; + do { #ifdef USE_LONG_LONG - ys = *sx++ * (unsigned long long)q + carry; - carry = ys >> 32; - y = *bx - (ys & 0xffffffffUL) - borrow; - borrow = y >> 32 & (uint32_t)1; - *bx++ = (uint32_t)y & 0xffffffffUL; + ys = *sx++ * (unsigned long long)q + carry; + carry = ys >> 32; + y = *bx - (ys & 0xffffffffUL) - borrow; + borrow = y >> 32 & (uint32_t)1; + *bx++ = (uint32_t)y & 0xffffffffUL; #else - si = *sx++; - ys = (si & 0xffff) * q + carry; - zs = (si >> 16) * q + (ys >> 16); - carry = zs >> 16; - y = (*bx & 0xffff) - (ys & 0xffff) - borrow; - borrow = (y & 0x10000) >> 16; - z = (*bx >> 16) - (zs & 0xffff) - borrow; - borrow = (z & 0x10000) >> 16; - bx = storeInc(bx, z, y); + si = *sx++; + ys = (si & 0xffff) * q + carry; + zs = (si >> 16) * q + (ys >> 16); + carry = zs >> 16; + y = (*bx & 0xffff) - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*bx >> 16) - (zs & 0xffff) - borrow; + borrow = (z & 0x10000) >> 16; + bx = storeInc(bx, z, y); #endif - } while (sx <= sxe); - if (!*bxe) { - bx = b.words(); - while (--bxe > bx && !*bxe) - --n; - b.resize(n); - } + } while (sx <= sxe); + if (!*bxe) { + bx = b.words(); + while (--bxe > bx && !*bxe) + --n; + b.resize(n); } - if (cmp(b, S) >= 0) { - q++; - borrow = 0; - carry = 0; - bx = b.words(); - sx = S.words(); - do { + } + if (cmp(b, S) >= 0) { + q++; + borrow = 0; + carry = 0; + bx = b.words(); + sx = S.words(); + do { #ifdef USE_LONG_LONG - ys = *sx++ + carry; - carry = ys >> 32; - y = *bx - (ys & 0xffffffffUL) - borrow; - borrow = y >> 32 & (uint32_t)1; - *bx++ = (uint32_t)y & 0xffffffffUL; + ys = *sx++ + carry; + carry = ys >> 32; + y = *bx - (ys & 0xffffffffUL) - borrow; + borrow = y >> 32 & (uint32_t)1; + *bx++ = (uint32_t)y & 0xffffffffUL; #else - si = *sx++; - ys = (si & 0xffff) + carry; - zs = (si >> 16) + (ys >> 16); - carry = zs >> 16; - y = (*bx & 0xffff) - (ys & 0xffff) - borrow; - borrow = (y & 0x10000) >> 16; - z = (*bx >> 16) - (zs & 0xffff) - borrow; - borrow = (z & 0x10000) >> 16; - bx = storeInc(bx, z, y); + si = *sx++; + ys = (si & 0xffff) + carry; + zs = (si >> 16) + (ys >> 16); + carry = zs >> 16; + y = (*bx & 0xffff) - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*bx >> 16) - (zs & 0xffff) - borrow; + borrow = (z & 0x10000) >> 16; + bx = storeInc(bx, z, y); #endif - } while (sx <= sxe); - bx = b.words(); - bxe = bx + n; - if (!*bxe) { - while (--bxe > bx && !*bxe) - --n; - b.resize(n); - } + } while (sx <= sxe); + bx = b.words(); + bxe = bx + n; + if (!*bxe) { + while (--bxe > bx && !*bxe) + --n; + b.resize(n); } - return q; + } + return q; } /* dtoa for IEEE arithmetic (dmg): convert double to ASCII string. @@ -722,581 +701,616 @@ static ALWAYS_INLINE int quorem(BigInt& b, BigInt& S) * * Note: 'leftright' translates to 'generate shortest possible string'. */ -template -void dtoa(DtoaBuffer result, double dd, int ndigits, bool& signOut, int& exponentOut, unsigned& precisionOut) -{ - // Exactly one rounding mode must be specified. - ASSERT(roundingNone + roundingSignificantFigures + roundingDecimalPlaces == 1); - // roundingNone only allowed (only sensible?) with leftright set. - ASSERT(!roundingNone || leftright); - - ASSERT(std::isfinite(dd)); - - int bbits, b2, b5, be, dig, i, ieps, ilim = 0, ilim0, ilim1 = 0, - j, j1, k, k0, k_check, m2, m5, s2, s5, - spec_case; - int32_t L; - int denorm; - uint32_t x; - BigInt b, delta, mlo, mhi, S; - U d2, eps, u; - double ds; - char* s; - char* s0; - - u.d = dd; - - /* Infinity or NaN */ - ASSERT((word0(&u) & Exp_mask) != Exp_mask); - - // JavaScript toString conversion treats -0 as 0. - if (!dval(&u)) { - signOut = false; - exponentOut = 0; - precisionOut = 1; - result[0] = '0'; - result[1] = '\0'; - return; - } +template +void dtoa(DtoaBuffer result, + double dd, + int ndigits, + bool& signOut, + int& exponentOut, + unsigned& precisionOut) { + // Exactly one rounding mode must be specified. + ASSERT(roundingNone + roundingSignificantFigures + roundingDecimalPlaces == + 1); + // roundingNone only allowed (only sensible?) with leftright set. + ASSERT(!roundingNone || leftright); + + ASSERT(std::isfinite(dd)); + + int bbits, b2, b5, be, dig, i, ieps, ilim = 0, ilim0, ilim1 = 0, j, j1, k, k0, + k_check, m2, m5, s2, s5, spec_case; + int32_t L; + int denorm; + uint32_t x; + BigInt b, delta, mlo, mhi, S; + U d2, eps, u; + double ds; + char* s; + char* s0; + + u.d = dd; + + /* Infinity or NaN */ + ASSERT((word0(&u) & Exp_mask) != Exp_mask); + + // JavaScript toString conversion treats -0 as 0. + if (!dval(&u)) { + signOut = false; + exponentOut = 0; + precisionOut = 1; + result[0] = '0'; + result[1] = '\0'; + return; + } + + if (word0(&u) & Sign_bit) { + signOut = true; + word0(&u) &= ~Sign_bit; // clear sign bit + } else + signOut = false; + + d2b(b, &u, &be, &bbits); + if ((i = (int)(word0(&u) >> Exp_shift1 & (Exp_mask >> Exp_shift1)))) { + dval(&d2) = dval(&u); + word0(&d2) &= Frac_mask1; + word0(&d2) |= Exp_11; + + /* log(x) ~=~ log(1.5) + (x-1.5)/1.5 + * log10(x) = log(x) / log(10) + * ~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10)) + * log10(d) = (i-Bias)*log(2)/log(10) + log10(d2) + * + * This suggests computing an approximation k to log10(d) by + * + * k = (i - Bias)*0.301029995663981 + * + ( (d2-1.5)*0.289529654602168 + 0.176091259055681 ); + * + * We want k to be too large rather than too small. + * The error in the first-order Taylor series approximation + * is in our favor, so we just round up the constant enough + * to compensate for any error in the multiplication of + * (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077, + * and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14, + * adding 1e-13 to the constant term more than suffices. + * Hence we adjust the constant term to 0.1760912590558. + * (We could get a more accurate k by invoking log10, + * but this is probably not worthwhile.) + */ - if (word0(&u) & Sign_bit) { - signOut = true; - word0(&u) &= ~Sign_bit; // clear sign bit - } else - signOut = false; - - d2b(b, &u, &be, &bbits); - if ((i = (int)(word0(&u) >> Exp_shift1 & (Exp_mask >> Exp_shift1)))) { - dval(&d2) = dval(&u); - word0(&d2) &= Frac_mask1; - word0(&d2) |= Exp_11; - - /* log(x) ~=~ log(1.5) + (x-1.5)/1.5 - * log10(x) = log(x) / log(10) - * ~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10)) - * log10(d) = (i-Bias)*log(2)/log(10) + log10(d2) - * - * This suggests computing an approximation k to log10(d) by - * - * k = (i - Bias)*0.301029995663981 - * + ( (d2-1.5)*0.289529654602168 + 0.176091259055681 ); - * - * We want k to be too large rather than too small. - * The error in the first-order Taylor series approximation - * is in our favor, so we just round up the constant enough - * to compensate for any error in the multiplication of - * (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077, - * and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14, - * adding 1e-13 to the constant term more than suffices. - * Hence we adjust the constant term to 0.1760912590558. - * (We could get a more accurate k by invoking log10, - * but this is probably not worthwhile.) - */ - - i -= Bias; - denorm = 0; - } else { - /* d is denormalized */ - - i = bbits + be + (Bias + (P - 1) - 1); - x = (i > 32) ? (word0(&u) << (64 - i)) | (word1(&u) >> (i - 32)) - : word1(&u) << (32 - i); - dval(&d2) = x; - word0(&d2) -= 31 * Exp_msk1; /* adjust exponent */ - i -= (Bias + (P - 1) - 1) + 1; - denorm = 1; + i -= Bias; + denorm = 0; + } else { + /* d is denormalized */ + + i = bbits + be + (Bias + (P - 1) - 1); + x = (i > 32) ? (word0(&u) << (64 - i)) | (word1(&u) >> (i - 32)) + : word1(&u) << (32 - i); + dval(&d2) = x; + word0(&d2) -= 31 * Exp_msk1; /* adjust exponent */ + i -= (Bias + (P - 1) - 1) + 1; + denorm = 1; + } + ds = (dval(&d2) - 1.5) * 0.289529654602168 + 0.1760912590558 + + (i * 0.301029995663981); + k = (int)ds; + if (ds < 0. && ds != k) + k--; /* want k = floor(ds) */ + k_check = 1; + if (k >= 0 && k <= Ten_pmax) { + if (dval(&u) < tens[k]) + k--; + k_check = 0; + } + j = bbits - i - 1; + if (j >= 0) { + b2 = 0; + s2 = j; + } else { + b2 = -j; + s2 = 0; + } + if (k >= 0) { + b5 = 0; + s5 = k; + s2 += k; + } else { + b2 -= k; + b5 = -k; + s5 = 0; + } + + if (roundingNone) { + ilim = ilim1 = -1; + i = 18; + ndigits = 0; + } + if (roundingSignificantFigures) { + if (ndigits <= 0) + ndigits = 1; + ilim = ilim1 = i = ndigits; + } + if (roundingDecimalPlaces) { + i = ndigits + k + 1; + ilim = i; + ilim1 = i - 1; + if (i <= 0) + i = 1; + } + + s = s0 = result; + + if (ilim >= 0 && ilim <= Quick_max) { + /* Try to get by with floating-point arithmetic. */ + + i = 0; + dval(&d2) = dval(&u); + k0 = k; + ilim0 = ilim; + ieps = 2; /* conservative */ + if (k > 0) { + ds = tens[k & 0xf]; + j = k >> 4; + if (j & Bletch) { + /* prevent overflows */ + j &= Bletch - 1; + dval(&u) /= bigtens[n_bigtens - 1]; + ieps++; + } + for (; j; j >>= 1, i++) { + if (j & 1) { + ieps++; + ds *= bigtens[i]; + } + } + dval(&u) /= ds; + } else if ((j1 = -k)) { + dval(&u) *= tens[j1 & 0xf]; + for (j = j1 >> 4; j; j >>= 1, i++) { + if (j & 1) { + ieps++; + dval(&u) *= bigtens[i]; + } + } } - ds = (dval(&d2) - 1.5) * 0.289529654602168 + 0.1760912590558 + (i * 0.301029995663981); - k = (int)ds; - if (ds < 0. && ds != k) - k--; /* want k = floor(ds) */ - k_check = 1; - if (k >= 0 && k <= Ten_pmax) { - if (dval(&u) < tens[k]) - k--; - k_check = 0; + if (k_check && dval(&u) < 1. && ilim > 0) { + if (ilim1 <= 0) + goto fastFailed; + ilim = ilim1; + k--; + dval(&u) *= 10.; + ieps++; } - j = bbits - i - 1; - if (j >= 0) { - b2 = 0; - s2 = j; - } else { - b2 = -j; - s2 = 0; + dval(&eps) = (ieps * dval(&u)) + 7.; + word0(&eps) -= (P - 1) * Exp_msk1; + if (!ilim) { + S.clear(); + mhi.clear(); + dval(&u) -= 5.; + if (dval(&u) > dval(&eps)) + goto oneDigit; + if (dval(&u) < -dval(&eps)) + goto noDigits; + goto fastFailed; } - if (k >= 0) { - b5 = 0; - s5 = k; - s2 += k; + if (leftright) { + /* Use Steele & White method of only + * generating digits needed. + */ + dval(&eps) = (0.5 / tens[ilim - 1]) - dval(&eps); + for (i = 0;;) { + L = (long int)dval(&u); + dval(&u) -= L; + *s++ = '0' + (int)L; + if (dval(&u) < dval(&eps)) + goto ret; + if (1. - dval(&u) < dval(&eps)) + goto bumpUp; + if (++i >= ilim) + break; + dval(&eps) *= 10.; + dval(&u) *= 10.; + } } else { - b2 -= k; - b5 = -k; - s5 = 0; - } - - if (roundingNone) { - ilim = ilim1 = -1; - i = 18; - ndigits = 0; - } - if (roundingSignificantFigures) { - if (ndigits <= 0) - ndigits = 1; - ilim = ilim1 = i = ndigits; - } - if (roundingDecimalPlaces) { - i = ndigits + k + 1; - ilim = i; - ilim1 = i - 1; - if (i <= 0) - i = 1; - } - - s = s0 = result; - - if (ilim >= 0 && ilim <= Quick_max) { - /* Try to get by with floating-point arithmetic. */ - - i = 0; - dval(&d2) = dval(&u); - k0 = k; - ilim0 = ilim; - ieps = 2; /* conservative */ - if (k > 0) { - ds = tens[k & 0xf]; - j = k >> 4; - if (j & Bletch) { - /* prevent overflows */ - j &= Bletch - 1; - dval(&u) /= bigtens[n_bigtens - 1]; - ieps++; - } - for (; j; j >>= 1, i++) { - if (j & 1) { - ieps++; - ds *= bigtens[i]; - } - } - dval(&u) /= ds; - } else if ((j1 = -k)) { - dval(&u) *= tens[j1 & 0xf]; - for (j = j1 >> 4; j; j >>= 1, i++) { - if (j & 1) { - ieps++; - dval(&u) *= bigtens[i]; - } + /* Generate ilim digits, then fix them up. */ + dval(&eps) *= tens[ilim - 1]; + for (i = 1;; i++, dval(&u) *= 10.) { + L = (int32_t)(dval(&u)); + if (!(dval(&u) -= L)) + ilim = i; + *s++ = '0' + (int)L; + if (i == ilim) { + if (dval(&u) > 0.5 + dval(&eps)) + goto bumpUp; + if (dval(&u) < 0.5 - dval(&eps)) { + while (*--s == '0') { } + s++; + goto ret; + } + break; } - if (k_check && dval(&u) < 1. && ilim > 0) { - if (ilim1 <= 0) - goto fastFailed; - ilim = ilim1; - k--; - dval(&u) *= 10.; - ieps++; - } - dval(&eps) = (ieps * dval(&u)) + 7.; - word0(&eps) -= (P - 1) * Exp_msk1; - if (!ilim) { - S.clear(); - mhi.clear(); - dval(&u) -= 5.; - if (dval(&u) > dval(&eps)) - goto oneDigit; - if (dval(&u) < -dval(&eps)) - goto noDigits; - goto fastFailed; - } - if (leftright) { - /* Use Steele & White method of only - * generating digits needed. - */ - dval(&eps) = (0.5 / tens[ilim - 1]) - dval(&eps); - for (i = 0;;) { - L = (long int)dval(&u); - dval(&u) -= L; - *s++ = '0' + (int)L; - if (dval(&u) < dval(&eps)) - goto ret; - if (1. - dval(&u) < dval(&eps)) - goto bumpUp; - if (++i >= ilim) - break; - dval(&eps) *= 10.; - dval(&u) *= 10.; - } - } else { - /* Generate ilim digits, then fix them up. */ - dval(&eps) *= tens[ilim - 1]; - for (i = 1;; i++, dval(&u) *= 10.) { - L = (int32_t)(dval(&u)); - if (!(dval(&u) -= L)) - ilim = i; - *s++ = '0' + (int)L; - if (i == ilim) { - if (dval(&u) > 0.5 + dval(&eps)) - goto bumpUp; - if (dval(&u) < 0.5 - dval(&eps)) { - while (*--s == '0') { } - s++; - goto ret; - } - break; - } - } - } -fastFailed: - s = s0; - dval(&u) = dval(&d2); - k = k0; - ilim = ilim0; + } } - - /* Do we have a "small" integer? */ - - if (be >= 0 && k <= Int_max) { - /* Yes. */ - ds = tens[k]; - if (ndigits < 0 && ilim <= 0) { - S.clear(); - mhi.clear(); - if (ilim < 0 || dval(&u) <= 5 * ds) - goto noDigits; - goto oneDigit; - } - for (i = 1;; i++, dval(&u) *= 10.) { - L = (int32_t)(dval(&u) / ds); - dval(&u) -= L * ds; - *s++ = '0' + (int)L; - if (!dval(&u)) { - break; - } - if (i == ilim) { - dval(&u) += dval(&u); - if (dval(&u) > ds || (dval(&u) == ds && (L & 1))) { -bumpUp: - while (*--s == '9') - if (s == s0) { - k++; - *s = '0'; - break; - } - ++*s++; - } - break; + fastFailed: + s = s0; + dval(&u) = dval(&d2); + k = k0; + ilim = ilim0; + } + + /* Do we have a "small" integer? */ + + if (be >= 0 && k <= Int_max) { + /* Yes. */ + ds = tens[k]; + if (ndigits < 0 && ilim <= 0) { + S.clear(); + mhi.clear(); + if (ilim < 0 || dval(&u) <= 5 * ds) + goto noDigits; + goto oneDigit; + } + for (i = 1;; i++, dval(&u) *= 10.) { + L = (int32_t)(dval(&u) / ds); + dval(&u) -= L * ds; + *s++ = '0' + (int)L; + if (!dval(&u)) { + break; + } + if (i == ilim) { + dval(&u) += dval(&u); + if (dval(&u) > ds || (dval(&u) == ds && (L & 1))) { + bumpUp: + while (*--s == '9') + if (s == s0) { + k++; + *s = '0'; + break; } + ++*s++; } - goto ret; + break; + } } - - m2 = b2; - m5 = b5; - mhi.clear(); - mlo.clear(); + goto ret; + } + + m2 = b2; + m5 = b5; + mhi.clear(); + mlo.clear(); + if (leftright) { + i = denorm ? be + (Bias + (P - 1) - 1 + 1) : 1 + P - bbits; + b2 += i; + s2 += i; + i2b(mhi, 1); + } + if (m2 > 0 && s2 > 0) { + i = m2 < s2 ? m2 : s2; + b2 -= i; + m2 -= i; + s2 -= i; + } + if (b5 > 0) { if (leftright) { - i = denorm ? be + (Bias + (P - 1) - 1 + 1) : 1 + P - bbits; - b2 += i; - s2 += i; - i2b(mhi, 1); - } - if (m2 > 0 && s2 > 0) { - i = m2 < s2 ? m2 : s2; - b2 -= i; - m2 -= i; - s2 -= i; - } - if (b5 > 0) { - if (leftright) { - if (m5 > 0) { - pow5mult(mhi, m5); - mult(b, mhi); - } - if ((j = b5 - m5)) - pow5mult(b, j); - } else - pow5mult(b, b5); - } - i2b(S, 1); - if (s5 > 0) - pow5mult(S, s5); - - /* Check for special case that d is a normalized power of 2. */ - - spec_case = 0; - if ((roundingNone || leftright) && (!word1(&u) && !(word0(&u) & Bndry_mask) && word0(&u) & (Exp_mask & ~Exp_msk1))) { - /* The special case */ - b2 += Log2P; - s2 += Log2P; - spec_case = 1; + if (m5 > 0) { + pow5mult(mhi, m5); + mult(b, mhi); + } + if ((j = b5 - m5)) + pow5mult(b, j); + } else + pow5mult(b, b5); + } + i2b(S, 1); + if (s5 > 0) + pow5mult(S, s5); + + /* Check for special case that d is a normalized power of 2. */ + + spec_case = 0; + if ((roundingNone || leftright) && (!word1(&u) && !(word0(&u) & Bndry_mask) && + word0(&u) & (Exp_mask & ~Exp_msk1))) { + /* The special case */ + b2 += Log2P; + s2 += Log2P; + spec_case = 1; + } + + /* Arrange for convenient computation of quotients: + * shift left if necessary so divisor has 4 leading 0 bits. + * + * Perhaps we should just compute leading 28 bits of S once + * and for all and pass them and a shift to quorem, so it + * can do shifts and ors to compute the numerator for q. + */ + if ((i = ((s5 ? 32 - hi0bits(S.words()[S.size() - 1]) : 1) + s2) & 0x1f)) + i = 32 - i; + if (i > 4) { + i -= 4; + b2 += i; + m2 += i; + s2 += i; + } else if (i < 4) { + i += 28; + b2 += i; + m2 += i; + s2 += i; + } + if (b2 > 0) + lshift(b, b2); + if (s2 > 0) + lshift(S, s2); + if (k_check) { + if (cmp(b, S) < 0) { + k--; + multadd(b, 10, 0); /* we botched the k estimate */ + if (leftright) + multadd(mhi, 10, 0); + ilim = ilim1; } - - /* Arrange for convenient computation of quotients: - * shift left if necessary so divisor has 4 leading 0 bits. - * - * Perhaps we should just compute leading 28 bits of S once - * and for all and pass them and a shift to quorem, so it - * can do shifts and ors to compute the numerator for q. + } + if (ilim <= 0 && roundingDecimalPlaces) { + if (ilim < 0) + goto noDigits; + multadd(S, 5, 0); + // For IEEE-754 unbiased rounding this check should be <=, such that 0.5 + // would flush to zero. + if (cmp(b, S) < 0) + goto noDigits; + goto oneDigit; + } + if (leftright) { + if (m2 > 0) + lshift(mhi, m2); + + /* Compute mlo -- check for special case + * that d is a normalized power of 2. */ - if ((i = ((s5 ? 32 - hi0bits(S.words()[S.size() - 1]) : 1) + s2) & 0x1f)) - i = 32 - i; - if (i > 4) { - i -= 4; - b2 += i; - m2 += i; - s2 += i; - } else if (i < 4) { - i += 28; - b2 += i; - m2 += i; - s2 += i; - } - if (b2 > 0) - lshift(b, b2); - if (s2 > 0) - lshift(S, s2); - if (k_check) { - if (cmp(b, S) < 0) { - k--; - multadd(b, 10, 0); /* we botched the k estimate */ - if (leftright) - multadd(mhi, 10, 0); - ilim = ilim1; - } - } - if (ilim <= 0 && roundingDecimalPlaces) { - if (ilim < 0) - goto noDigits; - multadd(S, 5, 0); - // For IEEE-754 unbiased rounding this check should be <=, such that 0.5 would flush to zero. - if (cmp(b, S) < 0) - goto noDigits; - goto oneDigit; - } - if (leftright) { - if (m2 > 0) - lshift(mhi, m2); - - /* Compute mlo -- check for special case - * that d is a normalized power of 2. - */ - - mlo = mhi; - if (spec_case) - lshift(mhi, Log2P); - - for (i = 1;;i++) { - dig = quorem(b, S) + '0'; - /* Do we yet have the shortest decimal string - * that will round to d? - */ - j = cmp(b, mlo); - diff(delta, S, mhi); - j1 = delta.sign ? 1 : cmp(b, delta); + + mlo = mhi; + if (spec_case) + lshift(mhi, Log2P); + + for (i = 1;; i++) { + dig = quorem(b, S) + '0'; + /* Do we yet have the shortest decimal string + * that will round to d? + */ + j = cmp(b, mlo); + diff(delta, S, mhi); + j1 = delta.sign ? 1 : cmp(b, delta); #ifdef DTOA_ROUND_BIASED - if (j < 0 || !j) { + if (j < 0 || !j) { #else - // FIXME: ECMA-262 specifies that equidistant results round away from - // zero, which probably means we shouldn't be on the unbiased code path - // (the (word1(&u) & 1) clause is looking highly suspicious). I haven't - // yet understood this code well enough to make the call, but we should - // probably be enabling DTOA_ROUND_BIASED. I think the interesting corner - // case to understand is probably "Math.pow(0.5, 24).toString()". - // I believe this value is interesting because I think it is precisely - // representable in binary floating point, and its decimal representation - // has a single digit that Steele & White reduction can remove, with the - // value 5 (thus equidistant from the next numbers above and below). - // We produce the correct answer using either codepath, and I don't as - // yet understand why. :-) - if (!j1 && !(word1(&u) & 1)) { - if (dig == '9') - goto round9up; - if (j > 0) - dig++; - *s++ = dig; - goto ret; - } - if (j < 0 || (!j && !(word1(&u) & 1))) { + // FIXME: ECMA-262 specifies that equidistant results round away from + // zero, which probably means we shouldn't be on the unbiased code path + // (the (word1(&u) & 1) clause is looking highly suspicious). I haven't + // yet understood this code well enough to make the call, but we should + // probably be enabling DTOA_ROUND_BIASED. I think the interesting corner + // case to understand is probably "Math.pow(0.5, 24).toString()". + // I believe this value is interesting because I think it is precisely + // representable in binary floating point, and its decimal representation + // has a single digit that Steele & White reduction can remove, with the + // value 5 (thus equidistant from the next numbers above and below). + // We produce the correct answer using either codepath, and I don't as + // yet understand why. :-) + if (!j1 && !(word1(&u) & 1)) { + if (dig == '9') + goto round9up; + if (j > 0) + dig++; + *s++ = dig; + goto ret; + } + if (j < 0 || (!j && !(word1(&u) & 1))) { #endif - if ((b.words()[0] || b.size() > 1) && (j1 > 0)) { - lshift(b, 1); - j1 = cmp(b, S); - // For IEEE-754 round-to-even, this check should be (j1 > 0 || (!j1 && (dig & 1))), - // but ECMA-262 specifies that equidistant values (e.g. (.5).toFixed()) should - // be rounded away from zero. - if (j1 >= 0) { - if (dig == '9') - goto round9up; - dig++; - } - } - *s++ = dig; - goto ret; - } - if (j1 > 0) { - if (dig == '9') { /* possible if i == 1 */ -round9up: - *s++ = '9'; - goto roundoff; - } - *s++ = dig + 1; - goto ret; - } - *s++ = dig; - if (i == ilim) - break; - multadd(b, 10, 0); - multadd(mlo, 10, 0); - multadd(mhi, 10, 0); + if ((b.words()[0] || b.size() > 1) && (j1 > 0)) { + lshift(b, 1); + j1 = cmp(b, S); + // For IEEE-754 round-to-even, this check should be (j1 > 0 || (!j1 && + // (dig & 1))), but ECMA-262 specifies that equidistant values (e.g. + // (.5).toFixed()) should be rounded away from zero. + if (j1 >= 0) { + if (dig == '9') + goto round9up; + dig++; + } } - } else { - for (i = 1;; i++) { - *s++ = dig = quorem(b, S) + '0'; - if (!b.words()[0] && b.size() <= 1) - goto ret; - if (i >= ilim) - break; - multadd(b, 10, 0); + *s++ = dig; + goto ret; + } + if (j1 > 0) { + if (dig == '9') { /* possible if i == 1 */ + round9up: + *s++ = '9'; + goto roundoff; } + *s++ = dig + 1; + goto ret; + } + *s++ = dig; + if (i == ilim) + break; + multadd(b, 10, 0); + multadd(mlo, 10, 0); + multadd(mhi, 10, 0); } - - /* Round off last digit */ - - lshift(b, 1); - j = cmp(b, S); - // For IEEE-754 round-to-even, this check should be (j > 0 || (!j && (dig & 1))), - // but ECMA-262 specifies that equidistant values (e.g. (.5).toFixed()) should - // be rounded away from zero. - if (j >= 0) { -roundoff: - while (*--s == '9') - if (s == s0) { - k++; - *s++ = '1'; - goto ret; - } - ++*s++; - } else { - while (*--s == '0') { } - s++; + } else { + for (i = 1;; i++) { + *s++ = dig = quorem(b, S) + '0'; + if (!b.words()[0] && b.size() <= 1) + goto ret; + if (i >= ilim) + break; + multadd(b, 10, 0); } - goto ret; + } + + /* Round off last digit */ + + lshift(b, 1); + j = cmp(b, S); + // For IEEE-754 round-to-even, this check should be (j > 0 || (!j && (dig & + // 1))), but ECMA-262 specifies that equidistant values (e.g. (.5).toFixed()) + // should be rounded away from zero. + if (j >= 0) { + roundoff: + while (*--s == '9') + if (s == s0) { + k++; + *s++ = '1'; + goto ret; + } + ++*s++; + } else { + while (*--s == '0') { + } + s++; + } + goto ret; noDigits: - exponentOut = 0; - precisionOut = 1; - result[0] = '0'; - result[1] = '\0'; - return; + exponentOut = 0; + precisionOut = 1; + result[0] = '0'; + result[1] = '\0'; + return; oneDigit: - *s++ = '1'; - k++; - goto ret; + *s++ = '1'; + k++; + goto ret; ret: - ASSERT(s > result); - *s = 0; - exponentOut = k; - precisionOut = s - result; + ASSERT(s > result); + *s = 0; + exponentOut = k; + precisionOut = s - result; } -void dtoa(DtoaBuffer result, double dd, bool& sign, int& exponent, unsigned& precision) -{ - // flags are roundingNone, leftright. - dtoa(result, dd, 0, sign, exponent, precision); +void dtoa(DtoaBuffer result, + double dd, + bool& sign, + int& exponent, + unsigned& precision) { + // flags are roundingNone, leftright. + dtoa(result, dd, 0, sign, exponent, precision); } -void dtoaRoundSF(DtoaBuffer result, double dd, int ndigits, bool& sign, int& exponent, unsigned& precision) -{ - // flag is roundingSignificantFigures. - dtoa(result, dd, ndigits, sign, exponent, precision); +void dtoaRoundSF(DtoaBuffer result, + double dd, + int ndigits, + bool& sign, + int& exponent, + unsigned& precision) { + // flag is roundingSignificantFigures. + dtoa(result, dd, ndigits, sign, exponent, + precision); } -void dtoaRoundDP(DtoaBuffer result, double dd, int ndigits, bool& sign, int& exponent, unsigned& precision) -{ - // flag is roundingDecimalPlaces. - dtoa(result, dd, ndigits, sign, exponent, precision); +void dtoaRoundDP(DtoaBuffer result, + double dd, + int ndigits, + bool& sign, + int& exponent, + unsigned& precision) { + // flag is roundingDecimalPlaces. + dtoa(result, dd, ndigits, sign, exponent, + precision); } -const char* numberToString(double d, NumberToStringBuffer buffer) -{ - double_conversion::StringBuilder builder(buffer, NumberToStringBufferLength); - const double_conversion::DoubleToStringConverter& converter = double_conversion::DoubleToStringConverter::EcmaScriptConverter(); - converter.ToShortest(d, &builder); - return builder.Finalize(); +const char* numberToString(double d, NumberToStringBuffer buffer) { + double_conversion::StringBuilder builder(buffer, NumberToStringBufferLength); + const double_conversion::DoubleToStringConverter& converter = + double_conversion::DoubleToStringConverter::EcmaScriptConverter(); + converter.ToShortest(d, &builder); + return builder.Finalize(); } -static inline const char* formatStringTruncatingTrailingZerosIfNeeded(NumberToStringBuffer buffer, double_conversion::StringBuilder& builder) -{ - size_t length = builder.position(); - size_t decimalPointPosition = 0; - for (; decimalPointPosition < length; ++decimalPointPosition) { - if (buffer[decimalPointPosition] == '.') - break; - } - - // No decimal seperator found, early exit. - if (decimalPointPosition == length) - return builder.Finalize(); +static inline const char* formatStringTruncatingTrailingZerosIfNeeded( + NumberToStringBuffer buffer, + double_conversion::StringBuilder& builder) { + size_t length = builder.position(); + size_t decimalPointPosition = 0; + for (; decimalPointPosition < length; ++decimalPointPosition) { + if (buffer[decimalPointPosition] == '.') + break; + } + + // No decimal seperator found, early exit. + if (decimalPointPosition == length) + return builder.Finalize(); - size_t truncatedLength = length - 1; - for (; truncatedLength > decimalPointPosition; --truncatedLength) { - if (buffer[truncatedLength] != '0') - break; - } + size_t truncatedLength = length - 1; + for (; truncatedLength > decimalPointPosition; --truncatedLength) { + if (buffer[truncatedLength] != '0') + break; + } - // No trailing zeros found to strip. - if (truncatedLength == length - 1) - return builder.Finalize(); + // No trailing zeros found to strip. + if (truncatedLength == length - 1) + return builder.Finalize(); - // If we removed all trailing zeros, remove the decimal point as well. - if (truncatedLength == decimalPointPosition) { - ASSERT(truncatedLength > 0); - --truncatedLength; - } + // If we removed all trailing zeros, remove the decimal point as well. + if (truncatedLength == decimalPointPosition) { + ASSERT(truncatedLength > 0); + --truncatedLength; + } - // Truncate the StringBuilder, and return the final result. - builder.SetPosition(truncatedLength + 1); - return builder.Finalize(); + // Truncate the StringBuilder, and return the final result. + builder.SetPosition(truncatedLength + 1); + return builder.Finalize(); } -const char* numberToFixedPrecisionString(double d, unsigned significantFigures, NumberToStringBuffer buffer, bool truncateTrailingZeros) -{ - // Mimic String::format("%.[precision]g", ...), but use dtoas rounding facilities. - // "g": Signed value printed in f or e format, whichever is more compact for the given value and precision. - // The e format is used only when the exponent of the value is less than –4 or greater than or equal to the - // precision argument. Trailing zeros are truncated, and the decimal point appears only if one or more digits follow it. - // "precision": The precision specifies the maximum number of significant digits printed. - double_conversion::StringBuilder builder(buffer, NumberToStringBufferLength); - const double_conversion::DoubleToStringConverter& converter = double_conversion::DoubleToStringConverter::EcmaScriptConverter(); - converter.ToPrecision(d, significantFigures, &builder); - if (!truncateTrailingZeros) - return builder.Finalize(); - return formatStringTruncatingTrailingZerosIfNeeded(buffer, builder); +const char* numberToFixedPrecisionString(double d, + unsigned significantFigures, + NumberToStringBuffer buffer, + bool truncateTrailingZeros) { + // Mimic String::format("%.[precision]g", ...), but use dtoas rounding + // facilities. "g": Signed value printed in f or e format, whichever is more + // compact for the given value and precision. The e format is used only when + // the exponent of the value is less than –4 or greater than or equal to the + // precision argument. Trailing zeros are truncated, and the decimal point + // appears only if one or more digits follow it. "precision": The precision + // specifies the maximum number of significant digits printed. + double_conversion::StringBuilder builder(buffer, NumberToStringBufferLength); + const double_conversion::DoubleToStringConverter& converter = + double_conversion::DoubleToStringConverter::EcmaScriptConverter(); + converter.ToPrecision(d, significantFigures, &builder); + if (!truncateTrailingZeros) + return builder.Finalize(); + return formatStringTruncatingTrailingZerosIfNeeded(buffer, builder); } -const char* numberToFixedWidthString(double d, unsigned decimalPlaces, NumberToStringBuffer buffer) -{ - // Mimic String::format("%.[precision]f", ...), but use dtoas rounding facilities. - // "f": Signed value having the form [ – ]dddd.dddd, where dddd is one or more decimal digits. - // The number of digits before the decimal point depends on the magnitude of the number, and - // the number of digits after the decimal point depends on the requested precision. - // "precision": The precision value specifies the number of digits after the decimal point. - // If a decimal point appears, at least one digit appears before it. - // The value is rounded to the appropriate number of digits. - double_conversion::StringBuilder builder(buffer, NumberToStringBufferLength); - const double_conversion::DoubleToStringConverter& converter = double_conversion::DoubleToStringConverter::EcmaScriptConverter(); - converter.ToFixed(d, decimalPlaces, &builder); - return builder.Finalize(); +const char* numberToFixedWidthString(double d, + unsigned decimalPlaces, + NumberToStringBuffer buffer) { + // Mimic String::format("%.[precision]f", ...), but use dtoas rounding + // facilities. "f": Signed value having the form [ – ]dddd.dddd, where dddd is + // one or more decimal digits. The number of digits before the decimal point + // depends on the magnitude of the number, and the number of digits after the + // decimal point depends on the requested precision. "precision": The + // precision value specifies the number of digits after the decimal point. If + // a decimal point appears, at least one digit appears before it. The value is + // rounded to the appropriate number of digits. + double_conversion::StringBuilder builder(buffer, NumberToStringBufferLength); + const double_conversion::DoubleToStringConverter& converter = + double_conversion::DoubleToStringConverter::EcmaScriptConverter(); + converter.ToFixed(d, decimalPlaces, &builder); + return builder.Finalize(); } namespace Internal { -double parseDoubleFromLongString(const UChar* string, size_t length, size_t& parsedLength) -{ - Vector conversionBuffer(length); - for (size_t i = 0; i < length; ++i) - conversionBuffer[i] = isASCII(string[i]) ? string[i] : 0; - return parseDouble(conversionBuffer.data(), length, parsedLength); +double parseDoubleFromLongString(const UChar* string, + size_t length, + size_t& parsedLength) { + Vector conversionBuffer(length); + for (size_t i = 0; i < length; ++i) + conversionBuffer[i] = isASCII(string[i]) ? string[i] : 0; + return parseDouble(conversionBuffer.data(), length, parsedLength); } -} // namespace Internal +} // namespace Internal -} // namespace WTF +} // namespace WTF diff --git a/sky/engine/wtf/dtoa.h b/sky/engine/wtf/dtoa.h index fbf557f6611bf..e69caa1bc0d35 100644 --- a/sky/engine/wtf/dtoa.h +++ b/sky/engine/wtf/dtoa.h @@ -34,49 +34,79 @@ extern Mutex* s_dtoaP5Mutex; typedef char DtoaBuffer[80]; -WTF_EXPORT void dtoa(DtoaBuffer result, double dd, bool& sign, int& exponent, unsigned& precision); -WTF_EXPORT void dtoaRoundSF(DtoaBuffer result, double dd, int ndigits, bool& sign, int& exponent, unsigned& precision); -WTF_EXPORT void dtoaRoundDP(DtoaBuffer result, double dd, int ndigits, bool& sign, int& exponent, unsigned& precision); - -// Size = 80 for sizeof(DtoaBuffer) + some sign bits, decimal point, 'e', exponent digits. +WTF_EXPORT void dtoa(DtoaBuffer result, + double dd, + bool& sign, + int& exponent, + unsigned& precision); +WTF_EXPORT void dtoaRoundSF(DtoaBuffer result, + double dd, + int ndigits, + bool& sign, + int& exponent, + unsigned& precision); +WTF_EXPORT void dtoaRoundDP(DtoaBuffer result, + double dd, + int ndigits, + bool& sign, + int& exponent, + unsigned& precision); + +// Size = 80 for sizeof(DtoaBuffer) + some sign bits, decimal point, 'e', +// exponent digits. const unsigned NumberToStringBufferLength = 96; typedef char NumberToStringBuffer[NumberToStringBufferLength]; typedef LChar NumberToLStringBuffer[NumberToStringBufferLength]; WTF_EXPORT const char* numberToString(double, NumberToStringBuffer); -WTF_EXPORT const char* numberToFixedPrecisionString(double, unsigned significantFigures, NumberToStringBuffer, bool truncateTrailingZeros = false); -WTF_EXPORT const char* numberToFixedWidthString(double, unsigned decimalPlaces, NumberToStringBuffer); - -WTF_EXPORT double parseDouble(const LChar* string, size_t length, size_t& parsedLength); -WTF_EXPORT double parseDouble(const UChar* string, size_t length, size_t& parsedLength); +WTF_EXPORT const char* numberToFixedPrecisionString( + double, + unsigned significantFigures, + NumberToStringBuffer, + bool truncateTrailingZeros = false); +WTF_EXPORT const char* numberToFixedWidthString(double, + unsigned decimalPlaces, + NumberToStringBuffer); + +WTF_EXPORT double parseDouble(const LChar* string, + size_t length, + size_t& parsedLength); +WTF_EXPORT double parseDouble(const UChar* string, + size_t length, + size_t& parsedLength); namespace Internal { - double parseDoubleFromLongString(const UChar* string, size_t length, size_t& parsedLength); +double parseDoubleFromLongString(const UChar* string, + size_t length, + size_t& parsedLength); } -inline double parseDouble(const LChar* string, size_t length, size_t& parsedLength) -{ - return double_conversion::StringToDoubleConverter::StringToDouble(reinterpret_cast(string), length, &parsedLength); +inline double parseDouble(const LChar* string, + size_t length, + size_t& parsedLength) { + return double_conversion::StringToDoubleConverter::StringToDouble( + reinterpret_cast(string), length, &parsedLength); } -inline double parseDouble(const UChar* string, size_t length, size_t& parsedLength) -{ - const size_t conversionBufferSize = 64; - if (length > conversionBufferSize) - return Internal::parseDoubleFromLongString(string, length, parsedLength); - LChar conversionBuffer[conversionBufferSize]; - for (int i = 0; i < static_cast(length); ++i) - conversionBuffer[i] = isASCII(string[i]) ? string[i] : 0; - return parseDouble(conversionBuffer, length, parsedLength); +inline double parseDouble(const UChar* string, + size_t length, + size_t& parsedLength) { + const size_t conversionBufferSize = 64; + if (length > conversionBufferSize) + return Internal::parseDoubleFromLongString(string, length, parsedLength); + LChar conversionBuffer[conversionBufferSize]; + for (int i = 0; i < static_cast(length); ++i) + conversionBuffer[i] = isASCII(string[i]) ? string[i] : 0; + return parseDouble(conversionBuffer, length, parsedLength); } -} // namespace WTF +} // namespace WTF -using WTF::NumberToStringBuffer; using WTF::NumberToLStringBuffer; -using WTF::numberToString; +using WTF::NumberToStringBuffer; using WTF::numberToFixedPrecisionString; using WTF::numberToFixedWidthString; +using WTF::numberToString; using WTF::parseDouble; #endif // SKY_ENGINE_WTF_DTOA_H_ diff --git a/sky/engine/wtf/dtoa/bignum-dtoa.cc b/sky/engine/wtf/dtoa/bignum-dtoa.cc index 911125723a310..4c19220628d91 100644 --- a/sky/engine/wtf/dtoa/bignum-dtoa.cc +++ b/sky/engine/wtf/dtoa/bignum-dtoa.cc @@ -25,7 +25,6 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - #include #include "bignum-dtoa.h" @@ -37,622 +36,645 @@ namespace WTF { namespace double_conversion { - static int NormalizedExponent(uint64_t significand, int exponent) { - ASSERT(significand != 0); - while ((significand & Double::kHiddenBit) == 0) { - significand = significand << 1; - exponent = exponent - 1; - } - return exponent; - } - - - // Forward declarations: - // Returns an estimation of k such that 10^(k-1) <= v < 10^k. - static int EstimatePower(int exponent); - // Computes v / 10^estimated_power exactly, as a ratio of two bignums, numerator - // and denominator. - static void InitialScaledStartValues(double v, - int estimated_power, - bool need_boundary_deltas, - Bignum* numerator, - Bignum* denominator, - Bignum* delta_minus, - Bignum* delta_plus); - // Multiplies numerator/denominator so that its values lies in the range 1-10. - // Returns decimal_point s.t. - // v = numerator'/denominator' * 10^(decimal_point-1) - // where numerator' and denominator' are the values of numerator and - // denominator after the call to this function. - static void FixupMultiply10(int estimated_power, bool is_even, - int* decimal_point, - Bignum* numerator, Bignum* denominator, - Bignum* delta_minus, Bignum* delta_plus); - // Generates digits from the left to the right and stops when the generated - // digits yield the shortest decimal representation of v. - static void GenerateShortestDigits(Bignum* numerator, Bignum* denominator, - Bignum* delta_minus, Bignum* delta_plus, - bool is_even, - Vector buffer, int* length); - // Generates 'requested_digits' after the decimal point. - static void BignumToFixed(int requested_digits, int* decimal_point, - Bignum* numerator, Bignum* denominator, - Vector(buffer), int* length); - // Generates 'count' digits of numerator/denominator. - // Once 'count' digits have been produced rounds the result depending on the - // remainder (remainders of exactly .5 round upwards). Might update the - // decimal_point when rounding up (for example for 0.9999). - static void GenerateCountedDigits(int count, int* decimal_point, - Bignum* numerator, Bignum* denominator, - Vector(buffer), int* length); - - - void BignumDtoa(double v, BignumDtoaMode mode, int requested_digits, - Vector buffer, int* length, int* decimal_point) { - ASSERT(v > 0); - ASSERT(!Double(v).IsSpecial()); - uint64_t significand = Double(v).Significand(); - bool is_even = (significand & 1) == 0; - int exponent = Double(v).Exponent(); - int normalized_exponent = NormalizedExponent(significand, exponent); - // estimated_power might be too low by 1. - int estimated_power = EstimatePower(normalized_exponent); - - // Shortcut for Fixed. - // The requested digits correspond to the digits after the point. If the - // number is much too small, then there is no need in trying to get any - // digits. - if (mode == BIGNUM_DTOA_FIXED && -estimated_power - 1 > requested_digits) { - buffer[0] = '\0'; - *length = 0; - // Set decimal-point to -requested_digits. This is what Gay does. - // Note that it should not have any effect anyways since the string is - // empty. - *decimal_point = -requested_digits; - return; - } - - Bignum numerator; - Bignum denominator; - Bignum delta_minus; - Bignum delta_plus; - // Make sure the bignum can grow large enough. The smallest double equals - // 4e-324. In this case the denominator needs fewer than 324*4 binary digits. - // The maximum double is 1.7976931348623157e308 which needs fewer than - // 308*4 binary digits. - ASSERT(Bignum::kMaxSignificantBits >= 324*4); - bool need_boundary_deltas = (mode == BIGNUM_DTOA_SHORTEST); - InitialScaledStartValues(v, estimated_power, need_boundary_deltas, - &numerator, &denominator, - &delta_minus, &delta_plus); - // We now have v = (numerator / denominator) * 10^estimated_power. - FixupMultiply10(estimated_power, is_even, decimal_point, - &numerator, &denominator, - &delta_minus, &delta_plus); - // We now have v = (numerator / denominator) * 10^(decimal_point-1), and - // 1 <= (numerator + delta_plus) / denominator < 10 - switch (mode) { - case BIGNUM_DTOA_SHORTEST: - GenerateShortestDigits(&numerator, &denominator, - &delta_minus, &delta_plus, - is_even, buffer, length); - break; - case BIGNUM_DTOA_FIXED: - BignumToFixed(requested_digits, decimal_point, - &numerator, &denominator, - buffer, length); - break; - case BIGNUM_DTOA_PRECISION: - GenerateCountedDigits(requested_digits, decimal_point, - &numerator, &denominator, - buffer, length); - break; - default: - UNREACHABLE(); - } - buffer[*length] = '\0'; +static int NormalizedExponent(uint64_t significand, int exponent) { + ASSERT(significand != 0); + while ((significand & Double::kHiddenBit) == 0) { + significand = significand << 1; + exponent = exponent - 1; + } + return exponent; +} + +// Forward declarations: +// Returns an estimation of k such that 10^(k-1) <= v < 10^k. +static int EstimatePower(int exponent); +// Computes v / 10^estimated_power exactly, as a ratio of two bignums, numerator +// and denominator. +static void InitialScaledStartValues(double v, + int estimated_power, + bool need_boundary_deltas, + Bignum* numerator, + Bignum* denominator, + Bignum* delta_minus, + Bignum* delta_plus); +// Multiplies numerator/denominator so that its values lies in the range 1-10. +// Returns decimal_point s.t. +// v = numerator'/denominator' * 10^(decimal_point-1) +// where numerator' and denominator' are the values of numerator and +// denominator after the call to this function. +static void FixupMultiply10(int estimated_power, + bool is_even, + int* decimal_point, + Bignum* numerator, + Bignum* denominator, + Bignum* delta_minus, + Bignum* delta_plus); +// Generates digits from the left to the right and stops when the generated +// digits yield the shortest decimal representation of v. +static void GenerateShortestDigits(Bignum* numerator, + Bignum* denominator, + Bignum* delta_minus, + Bignum* delta_plus, + bool is_even, + Vector buffer, + int* length); +// Generates 'requested_digits' after the decimal point. +static void BignumToFixed(int requested_digits, + int* decimal_point, + Bignum* numerator, + Bignum* denominator, + Vector(buffer), + int* length); +// Generates 'count' digits of numerator/denominator. +// Once 'count' digits have been produced rounds the result depending on the +// remainder (remainders of exactly .5 round upwards). Might update the +// decimal_point when rounding up (for example for 0.9999). +static void GenerateCountedDigits(int count, + int* decimal_point, + Bignum* numerator, + Bignum* denominator, + Vector(buffer), + int* length); + +void BignumDtoa(double v, + BignumDtoaMode mode, + int requested_digits, + Vector buffer, + int* length, + int* decimal_point) { + ASSERT(v > 0); + ASSERT(!Double(v).IsSpecial()); + uint64_t significand = Double(v).Significand(); + bool is_even = (significand & 1) == 0; + int exponent = Double(v).Exponent(); + int normalized_exponent = NormalizedExponent(significand, exponent); + // estimated_power might be too low by 1. + int estimated_power = EstimatePower(normalized_exponent); + + // Shortcut for Fixed. + // The requested digits correspond to the digits after the point. If the + // number is much too small, then there is no need in trying to get any + // digits. + if (mode == BIGNUM_DTOA_FIXED && -estimated_power - 1 > requested_digits) { + buffer[0] = '\0'; + *length = 0; + // Set decimal-point to -requested_digits. This is what Gay does. + // Note that it should not have any effect anyways since the string is + // empty. + *decimal_point = -requested_digits; + return; + } + + Bignum numerator; + Bignum denominator; + Bignum delta_minus; + Bignum delta_plus; + // Make sure the bignum can grow large enough. The smallest double equals + // 4e-324. In this case the denominator needs fewer than 324*4 binary digits. + // The maximum double is 1.7976931348623157e308 which needs fewer than + // 308*4 binary digits. + ASSERT(Bignum::kMaxSignificantBits >= 324 * 4); + bool need_boundary_deltas = (mode == BIGNUM_DTOA_SHORTEST); + InitialScaledStartValues(v, estimated_power, need_boundary_deltas, &numerator, + &denominator, &delta_minus, &delta_plus); + // We now have v = (numerator / denominator) * 10^estimated_power. + FixupMultiply10(estimated_power, is_even, decimal_point, &numerator, + &denominator, &delta_minus, &delta_plus); + // We now have v = (numerator / denominator) * 10^(decimal_point-1), and + // 1 <= (numerator + delta_plus) / denominator < 10 + switch (mode) { + case BIGNUM_DTOA_SHORTEST: + GenerateShortestDigits(&numerator, &denominator, &delta_minus, + &delta_plus, is_even, buffer, length); + break; + case BIGNUM_DTOA_FIXED: + BignumToFixed(requested_digits, decimal_point, &numerator, &denominator, + buffer, length); + break; + case BIGNUM_DTOA_PRECISION: + GenerateCountedDigits(requested_digits, decimal_point, &numerator, + &denominator, buffer, length); + break; + default: + UNREACHABLE(); + } + buffer[*length] = '\0'; +} + +// The procedure starts generating digits from the left to the right and stops +// when the generated digits yield the shortest decimal representation of v. A +// decimal representation of v is a number lying closer to v than to any other +// double, so it converts to v when read. +// +// This is true if d, the decimal representation, is between m- and m+, the +// upper and lower boundaries. d must be strictly between them if !is_even. +// m- := (numerator - delta_minus) / denominator +// m+ := (numerator + delta_plus) / denominator +// +// Precondition: 0 <= (numerator+delta_plus) / denominator < 10. +// If 1 <= (numerator+delta_plus) / denominator < 10 then no leading 0 digit +// will be produced. This should be the standard precondition. +static void GenerateShortestDigits(Bignum* numerator, + Bignum* denominator, + Bignum* delta_minus, + Bignum* delta_plus, + bool is_even, + Vector buffer, + int* length) { + // Small optimization: if delta_minus and delta_plus are the same just reuse + // one of the two bignums. + if (Bignum::Equal(*delta_minus, *delta_plus)) { + delta_plus = delta_minus; + } + *length = 0; + while (true) { + uint16_t digit; + digit = numerator->DivideModuloIntBignum(*denominator); + ASSERT(digit <= 9); // digit is a uint16_t and therefore always positive. + // digit = numerator / denominator (integer division). + // numerator = numerator % denominator. + buffer[(*length)++] = digit + '0'; + + // Can we stop already? + // If the remainder of the division is less than the distance to the lower + // boundary we can stop. In this case we simply round down (discarding the + // remainder). + // Similarly we test if we can round up (using the upper boundary). + bool in_delta_room_minus; + bool in_delta_room_plus; + if (is_even) { + in_delta_room_minus = Bignum::LessEqual(*numerator, *delta_minus); + } else { + in_delta_room_minus = Bignum::Less(*numerator, *delta_minus); } - - - // The procedure starts generating digits from the left to the right and stops - // when the generated digits yield the shortest decimal representation of v. A - // decimal representation of v is a number lying closer to v than to any other - // double, so it converts to v when read. - // - // This is true if d, the decimal representation, is between m- and m+, the - // upper and lower boundaries. d must be strictly between them if !is_even. - // m- := (numerator - delta_minus) / denominator - // m+ := (numerator + delta_plus) / denominator - // - // Precondition: 0 <= (numerator+delta_plus) / denominator < 10. - // If 1 <= (numerator+delta_plus) / denominator < 10 then no leading 0 digit - // will be produced. This should be the standard precondition. - static void GenerateShortestDigits(Bignum* numerator, Bignum* denominator, - Bignum* delta_minus, Bignum* delta_plus, - bool is_even, - Vector buffer, int* length) { - // Small optimization: if delta_minus and delta_plus are the same just reuse - // one of the two bignums. - if (Bignum::Equal(*delta_minus, *delta_plus)) { - delta_plus = delta_minus; - } - *length = 0; - while (true) { - uint16_t digit; - digit = numerator->DivideModuloIntBignum(*denominator); - ASSERT(digit <= 9); // digit is a uint16_t and therefore always positive. - // digit = numerator / denominator (integer division). - // numerator = numerator % denominator. - buffer[(*length)++] = digit + '0'; - - // Can we stop already? - // If the remainder of the division is less than the distance to the lower - // boundary we can stop. In this case we simply round down (discarding the - // remainder). - // Similarly we test if we can round up (using the upper boundary). - bool in_delta_room_minus; - bool in_delta_room_plus; - if (is_even) { - in_delta_room_minus = Bignum::LessEqual(*numerator, *delta_minus); - } else { - in_delta_room_minus = Bignum::Less(*numerator, *delta_minus); - } - if (is_even) { - in_delta_room_plus = - Bignum::PlusCompare(*numerator, *delta_plus, *denominator) >= 0; - } else { - in_delta_room_plus = - Bignum::PlusCompare(*numerator, *delta_plus, *denominator) > 0; - } - if (!in_delta_room_minus && !in_delta_room_plus) { - // Prepare for next iteration. - numerator->Times10(); - delta_minus->Times10(); - // We optimized delta_plus to be equal to delta_minus (if they share the - // same value). So don't multiply delta_plus if they point to the same - // object. - if (delta_minus != delta_plus) { - delta_plus->Times10(); - } - } else if (in_delta_room_minus && in_delta_room_plus) { - // Let's see if 2*numerator < denominator. - // If yes, then the next digit would be < 5 and we can round down. - int compare = Bignum::PlusCompare(*numerator, *numerator, *denominator); - if (compare < 0) { - // Remaining digits are less than .5. -> Round down (== do nothing). - } else if (compare > 0) { - // Remaining digits are more than .5 of denominator. -> Round up. - // Note that the last digit could not be a '9' as otherwise the whole - // loop would have stopped earlier. - // We still have an assert here in case the preconditions were not - // satisfied. - ASSERT(buffer[(*length) - 1] != '9'); - buffer[(*length) - 1]++; - } else { - // Halfway case. - // TODO(floitsch): need a way to solve half-way cases. - // For now let's round towards even (since this is what Gay seems to - // do). - - if ((buffer[(*length) - 1] - '0') % 2 == 0) { - // Round down => Do nothing. - } else { - ASSERT(buffer[(*length) - 1] != '9'); - buffer[(*length) - 1]++; - } - } - return; - } else if (in_delta_room_minus) { - // Round down (== do nothing). - return; - } else { // in_delta_room_plus - // Round up. - // Note again that the last digit could not be '9' since this would have - // stopped the loop earlier. - // We still have an ASSERT here, in case the preconditions were not - // satisfied. - ASSERT(buffer[(*length) -1] != '9'); - buffer[(*length) - 1]++; - return; - } - } + if (is_even) { + in_delta_room_plus = + Bignum::PlusCompare(*numerator, *delta_plus, *denominator) >= 0; + } else { + in_delta_room_plus = + Bignum::PlusCompare(*numerator, *delta_plus, *denominator) > 0; } - - - // Let v = numerator / denominator < 10. - // Then we generate 'count' digits of d = x.xxxxx... (without the decimal point) - // from left to right. Once 'count' digits have been produced we decide wether - // to round up or down. Remainders of exactly .5 round upwards. Numbers such - // as 9.999999 propagate a carry all the way, and change the - // exponent (decimal_point), when rounding upwards. - static void GenerateCountedDigits(int count, int* decimal_point, - Bignum* numerator, Bignum* denominator, - Vector(buffer), int* length) { - ASSERT(count >= 0); - for (int i = 0; i < count - 1; ++i) { - uint16_t digit; - digit = numerator->DivideModuloIntBignum(*denominator); - ASSERT(digit <= 9); // digit is a uint16_t and therefore always positive. - // digit = numerator / denominator (integer division). - // numerator = numerator % denominator. - buffer[i] = digit + '0'; - // Prepare for next iteration. - numerator->Times10(); - } - // Generate the last digit. - uint16_t digit; - digit = numerator->DivideModuloIntBignum(*denominator); - if (Bignum::PlusCompare(*numerator, *numerator, *denominator) >= 0) { - digit++; - } - buffer[count - 1] = digit + '0'; - // Correct bad digits (in case we had a sequence of '9's). Propagate the - // carry until we hat a non-'9' or til we reach the first digit. - for (int i = count - 1; i > 0; --i) { - if (buffer[i] != '0' + 10) break; - buffer[i] = '0'; - buffer[i - 1]++; - } - if (buffer[0] == '0' + 10) { - // Propagate a carry past the top place. - buffer[0] = '1'; - (*decimal_point)++; - } - *length = count; - } - - - // Generates 'requested_digits' after the decimal point. It might omit - // trailing '0's. If the input number is too small then no digits at all are - // generated (ex.: 2 fixed digits for 0.00001). - // - // Input verifies: 1 <= (numerator + delta) / denominator < 10. - static void BignumToFixed(int requested_digits, int* decimal_point, - Bignum* numerator, Bignum* denominator, - Vector(buffer), int* length) { - // Note that we have to look at more than just the requested_digits, since - // a number could be rounded up. Example: v=0.5 with requested_digits=0. - // Even though the power of v equals 0 we can't just stop here. - if (-(*decimal_point) > requested_digits) { - // The number is definitively too small. - // Ex: 0.001 with requested_digits == 1. - // Set decimal-point to -requested_digits. This is what Gay does. - // Note that it should not have any effect anyways since the string is - // empty. - *decimal_point = -requested_digits; - *length = 0; - return; - } else if (-(*decimal_point) == requested_digits) { - // We only need to verify if the number rounds down or up. - // Ex: 0.04 and 0.06 with requested_digits == 1. - ASSERT(*decimal_point == -requested_digits); - // Initially the fraction lies in range (1, 10]. Multiply the denominator - // by 10 so that we can compare more easily. - denominator->Times10(); - if (Bignum::PlusCompare(*numerator, *numerator, *denominator) >= 0) { - // If the fraction is >= 0.5 then we have to include the rounded - // digit. - buffer[0] = '1'; - *length = 1; - (*decimal_point)++; - } else { - // Note that we caught most of similar cases earlier. - *length = 0; - } - return; + if (!in_delta_room_minus && !in_delta_room_plus) { + // Prepare for next iteration. + numerator->Times10(); + delta_minus->Times10(); + // We optimized delta_plus to be equal to delta_minus (if they share the + // same value). So don't multiply delta_plus if they point to the same + // object. + if (delta_minus != delta_plus) { + delta_plus->Times10(); + } + } else if (in_delta_room_minus && in_delta_room_plus) { + // Let's see if 2*numerator < denominator. + // If yes, then the next digit would be < 5 and we can round down. + int compare = Bignum::PlusCompare(*numerator, *numerator, *denominator); + if (compare < 0) { + // Remaining digits are less than .5. -> Round down (== do nothing). + } else if (compare > 0) { + // Remaining digits are more than .5 of denominator. -> Round up. + // Note that the last digit could not be a '9' as otherwise the whole + // loop would have stopped earlier. + // We still have an assert here in case the preconditions were not + // satisfied. + ASSERT(buffer[(*length) - 1] != '9'); + buffer[(*length) - 1]++; + } else { + // Halfway case. + // TODO(floitsch): need a way to solve half-way cases. + // For now let's round towards even (since this is what Gay seems to + // do). + + if ((buffer[(*length) - 1] - '0') % 2 == 0) { + // Round down => Do nothing. } else { - // The requested digits correspond to the digits after the point. - // The variable 'needed_digits' includes the digits before the point. - int needed_digits = (*decimal_point) + requested_digits; - GenerateCountedDigits(needed_digits, decimal_point, - numerator, denominator, - buffer, length); + ASSERT(buffer[(*length) - 1] != '9'); + buffer[(*length) - 1]++; } + } + return; + } else if (in_delta_room_minus) { + // Round down (== do nothing). + return; + } else { // in_delta_room_plus + // Round up. + // Note again that the last digit could not be '9' since this would have + // stopped the loop earlier. + // We still have an ASSERT here, in case the preconditions were not + // satisfied. + ASSERT(buffer[(*length) - 1] != '9'); + buffer[(*length) - 1]++; + return; } - - - // Returns an estimation of k such that 10^(k-1) <= v < 10^k where - // v = f * 2^exponent and 2^52 <= f < 2^53. - // v is hence a normalized double with the given exponent. The output is an - // approximation for the exponent of the decimal approimation .digits * 10^k. - // - // The result might undershoot by 1 in which case 10^k <= v < 10^k+1. - // Note: this property holds for v's upper boundary m+ too. - // 10^k <= m+ < 10^k+1. - // (see explanation below). - // - // Examples: - // EstimatePower(0) => 16 - // EstimatePower(-52) => 0 - // - // Note: e >= 0 => EstimatedPower(e) > 0. No similar claim can be made for e<0. - static int EstimatePower(int exponent) { - // This function estimates log10 of v where v = f*2^e (with e == exponent). - // Note that 10^floor(log10(v)) <= v, but v <= 10^ceil(log10(v)). - // Note that f is bounded by its container size. Let p = 53 (the double's - // significand size). Then 2^(p-1) <= f < 2^p. - // - // Given that log10(v) == log2(v)/log2(10) and e+(len(f)-1) is quite close - // to log2(v) the function is simplified to (e+(len(f)-1)/log2(10)). - // The computed number undershoots by less than 0.631 (when we compute log3 - // and not log10). - // - // Optimization: since we only need an approximated result this computation - // can be performed on 64 bit integers. On x86/x64 architecture the speedup is - // not really measurable, though. - // - // Since we want to avoid overshooting we decrement by 1e10 so that - // floating-point imprecisions don't affect us. - // - // Explanation for v's boundary m+: the computation takes advantage of - // the fact that 2^(p-1) <= f < 2^p. Boundaries still satisfy this requirement - // (even for denormals where the delta can be much more important). - - const double k1Log10 = 0.30102999566398114; // 1/lg(10) - - // For doubles len(f) == 53 (don't forget the hidden bit). - const int kSignificandSize = 53; - double estimate = ceil((exponent + kSignificandSize - 1) * k1Log10 - 1e-10); - return static_cast(estimate); - } - - - // See comments for InitialScaledStartValues. - static void InitialScaledStartValuesPositiveExponent( - double v, int estimated_power, bool need_boundary_deltas, - Bignum* numerator, Bignum* denominator, - Bignum* delta_minus, Bignum* delta_plus) { - // A positive exponent implies a positive power. - ASSERT(estimated_power >= 0); - // Since the estimated_power is positive we simply multiply the denominator - // by 10^estimated_power. - - // numerator = v. - numerator->AssignUInt64(Double(v).Significand()); - numerator->Shifxleft(Double(v).Exponent()); - // denominator = 10^estimated_power. - denominator->AssignPowerUInt16(10, estimated_power); - - if (need_boundary_deltas) { - // Introduce a common denominator so that the deltas to the boundaries are - // integers. - denominator->Shifxleft(1); - numerator->Shifxleft(1); - // Let v = f * 2^e, then m+ - v = 1/2 * 2^e; With the common - // denominator (of 2) delta_plus equals 2^e. - delta_plus->AssignUInt16(1); - delta_plus->Shifxleft(Double(v).Exponent()); - // Same for delta_minus (with adjustments below if f == 2^p-1). - delta_minus->AssignUInt16(1); - delta_minus->Shifxleft(Double(v).Exponent()); - - // If the significand (without the hidden bit) is 0, then the lower - // boundary is closer than just half a ulp (unit in the last place). - // There is only one exception: if the next lower number is a denormal then - // the distance is 1 ulp. This cannot be the case for exponent >= 0 (but we - // have to test it in the other function where exponent < 0). - uint64_t v_bits = Double(v).AsUint64(); - if ((v_bits & Double::kSignificandMask) == 0) { - // The lower boundary is closer at half the distance of "normal" numbers. - // Increase the common denominator and adapt all but the delta_minus. - denominator->Shifxleft(1); // *2 - numerator->Shifxleft(1); // *2 - delta_plus->Shifxleft(1); // *2 - } - } + } +} + +// Let v = numerator / denominator < 10. +// Then we generate 'count' digits of d = x.xxxxx... (without the decimal point) +// from left to right. Once 'count' digits have been produced we decide wether +// to round up or down. Remainders of exactly .5 round upwards. Numbers such +// as 9.999999 propagate a carry all the way, and change the +// exponent (decimal_point), when rounding upwards. +static void GenerateCountedDigits(int count, + int* decimal_point, + Bignum* numerator, + Bignum* denominator, + Vector(buffer), + int* length) { + ASSERT(count >= 0); + for (int i = 0; i < count - 1; ++i) { + uint16_t digit; + digit = numerator->DivideModuloIntBignum(*denominator); + ASSERT(digit <= 9); // digit is a uint16_t and therefore always positive. + // digit = numerator / denominator (integer division). + // numerator = numerator % denominator. + buffer[i] = digit + '0'; + // Prepare for next iteration. + numerator->Times10(); + } + // Generate the last digit. + uint16_t digit; + digit = numerator->DivideModuloIntBignum(*denominator); + if (Bignum::PlusCompare(*numerator, *numerator, *denominator) >= 0) { + digit++; + } + buffer[count - 1] = digit + '0'; + // Correct bad digits (in case we had a sequence of '9's). Propagate the + // carry until we hat a non-'9' or til we reach the first digit. + for (int i = count - 1; i > 0; --i) { + if (buffer[i] != '0' + 10) + break; + buffer[i] = '0'; + buffer[i - 1]++; + } + if (buffer[0] == '0' + 10) { + // Propagate a carry past the top place. + buffer[0] = '1'; + (*decimal_point)++; + } + *length = count; +} + +// Generates 'requested_digits' after the decimal point. It might omit +// trailing '0's. If the input number is too small then no digits at all are +// generated (ex.: 2 fixed digits for 0.00001). +// +// Input verifies: 1 <= (numerator + delta) / denominator < 10. +static void BignumToFixed(int requested_digits, + int* decimal_point, + Bignum* numerator, + Bignum* denominator, + Vector(buffer), + int* length) { + // Note that we have to look at more than just the requested_digits, since + // a number could be rounded up. Example: v=0.5 with requested_digits=0. + // Even though the power of v equals 0 we can't just stop here. + if (-(*decimal_point) > requested_digits) { + // The number is definitively too small. + // Ex: 0.001 with requested_digits == 1. + // Set decimal-point to -requested_digits. This is what Gay does. + // Note that it should not have any effect anyways since the string is + // empty. + *decimal_point = -requested_digits; + *length = 0; + return; + } else if (-(*decimal_point) == requested_digits) { + // We only need to verify if the number rounds down or up. + // Ex: 0.04 and 0.06 with requested_digits == 1. + ASSERT(*decimal_point == -requested_digits); + // Initially the fraction lies in range (1, 10]. Multiply the denominator + // by 10 so that we can compare more easily. + denominator->Times10(); + if (Bignum::PlusCompare(*numerator, *numerator, *denominator) >= 0) { + // If the fraction is >= 0.5 then we have to include the rounded + // digit. + buffer[0] = '1'; + *length = 1; + (*decimal_point)++; + } else { + // Note that we caught most of similar cases earlier. + *length = 0; } - - - // See comments for InitialScaledStartValues - static void InitialScaledStartValuesNegativeExponentPositivePower( - double v, int estimated_power, bool need_boundary_deltas, - Bignum* numerator, Bignum* denominator, - Bignum* delta_minus, Bignum* delta_plus) { - uint64_t significand = Double(v).Significand(); - int exponent = Double(v).Exponent(); - // v = f * 2^e with e < 0, and with estimated_power >= 0. - // This means that e is close to 0 (have a look at how estimated_power is - // computed). - - // numerator = significand - // since v = significand * 2^exponent this is equivalent to - // numerator = v * / 2^-exponent - numerator->AssignUInt64(significand); - // denominator = 10^estimated_power * 2^-exponent (with exponent < 0) - denominator->AssignPowerUInt16(10, estimated_power); - denominator->Shifxleft(-exponent); - - if (need_boundary_deltas) { - // Introduce a common denominator so that the deltas to the boundaries are - // integers. - denominator->Shifxleft(1); - numerator->Shifxleft(1); - // Let v = f * 2^e, then m+ - v = 1/2 * 2^e; With the common - // denominator (of 2) delta_plus equals 2^e. - // Given that the denominator already includes v's exponent the distance - // to the boundaries is simply 1. - delta_plus->AssignUInt16(1); - // Same for delta_minus (with adjustments below if f == 2^p-1). - delta_minus->AssignUInt16(1); - - // If the significand (without the hidden bit) is 0, then the lower - // boundary is closer than just one ulp (unit in the last place). - // There is only one exception: if the next lower number is a denormal - // then the distance is 1 ulp. Since the exponent is close to zero - // (otherwise estimated_power would have been negative) this cannot happen - // here either. - uint64_t v_bits = Double(v).AsUint64(); - if ((v_bits & Double::kSignificandMask) == 0) { - // The lower boundary is closer at half the distance of "normal" numbers. - // Increase the denominator and adapt all but the delta_minus. - denominator->Shifxleft(1); // *2 - numerator->Shifxleft(1); // *2 - delta_plus->Shifxleft(1); // *2 - } - } + return; + } else { + // The requested digits correspond to the digits after the point. + // The variable 'needed_digits' includes the digits before the point. + int needed_digits = (*decimal_point) + requested_digits; + GenerateCountedDigits(needed_digits, decimal_point, numerator, denominator, + buffer, length); + } +} + +// Returns an estimation of k such that 10^(k-1) <= v < 10^k where +// v = f * 2^exponent and 2^52 <= f < 2^53. +// v is hence a normalized double with the given exponent. The output is an +// approximation for the exponent of the decimal approimation .digits * 10^k. +// +// The result might undershoot by 1 in which case 10^k <= v < 10^k+1. +// Note: this property holds for v's upper boundary m+ too. +// 10^k <= m+ < 10^k+1. +// (see explanation below). +// +// Examples: +// EstimatePower(0) => 16 +// EstimatePower(-52) => 0 +// +// Note: e >= 0 => EstimatedPower(e) > 0. No similar claim can be made for e<0. +static int EstimatePower(int exponent) { + // This function estimates log10 of v where v = f*2^e (with e == exponent). + // Note that 10^floor(log10(v)) <= v, but v <= 10^ceil(log10(v)). + // Note that f is bounded by its container size. Let p = 53 (the double's + // significand size). Then 2^(p-1) <= f < 2^p. + // + // Given that log10(v) == log2(v)/log2(10) and e+(len(f)-1) is quite close + // to log2(v) the function is simplified to (e+(len(f)-1)/log2(10)). + // The computed number undershoots by less than 0.631 (when we compute log3 + // and not log10). + // + // Optimization: since we only need an approximated result this computation + // can be performed on 64 bit integers. On x86/x64 architecture the speedup is + // not really measurable, though. + // + // Since we want to avoid overshooting we decrement by 1e10 so that + // floating-point imprecisions don't affect us. + // + // Explanation for v's boundary m+: the computation takes advantage of + // the fact that 2^(p-1) <= f < 2^p. Boundaries still satisfy this requirement + // (even for denormals where the delta can be much more important). + + const double k1Log10 = 0.30102999566398114; // 1/lg(10) + + // For doubles len(f) == 53 (don't forget the hidden bit). + const int kSignificandSize = 53; + double estimate = ceil((exponent + kSignificandSize - 1) * k1Log10 - 1e-10); + return static_cast(estimate); +} + +// See comments for InitialScaledStartValues. +static void InitialScaledStartValuesPositiveExponent(double v, + int estimated_power, + bool need_boundary_deltas, + Bignum* numerator, + Bignum* denominator, + Bignum* delta_minus, + Bignum* delta_plus) { + // A positive exponent implies a positive power. + ASSERT(estimated_power >= 0); + // Since the estimated_power is positive we simply multiply the denominator + // by 10^estimated_power. + + // numerator = v. + numerator->AssignUInt64(Double(v).Significand()); + numerator->Shifxleft(Double(v).Exponent()); + // denominator = 10^estimated_power. + denominator->AssignPowerUInt16(10, estimated_power); + + if (need_boundary_deltas) { + // Introduce a common denominator so that the deltas to the boundaries are + // integers. + denominator->Shifxleft(1); + numerator->Shifxleft(1); + // Let v = f * 2^e, then m+ - v = 1/2 * 2^e; With the common + // denominator (of 2) delta_plus equals 2^e. + delta_plus->AssignUInt16(1); + delta_plus->Shifxleft(Double(v).Exponent()); + // Same for delta_minus (with adjustments below if f == 2^p-1). + delta_minus->AssignUInt16(1); + delta_minus->Shifxleft(Double(v).Exponent()); + + // If the significand (without the hidden bit) is 0, then the lower + // boundary is closer than just half a ulp (unit in the last place). + // There is only one exception: if the next lower number is a denormal then + // the distance is 1 ulp. This cannot be the case for exponent >= 0 (but we + // have to test it in the other function where exponent < 0). + uint64_t v_bits = Double(v).AsUint64(); + if ((v_bits & Double::kSignificandMask) == 0) { + // The lower boundary is closer at half the distance of "normal" numbers. + // Increase the common denominator and adapt all but the delta_minus. + denominator->Shifxleft(1); // *2 + numerator->Shifxleft(1); // *2 + delta_plus->Shifxleft(1); // *2 } - - - // See comments for InitialScaledStartValues - static void InitialScaledStartValuesNegativeExponentNegativePower( - double v, int estimated_power, bool need_boundary_deltas, - Bignum* numerator, Bignum* denominator, - Bignum* delta_minus, Bignum* delta_plus) { - const uint64_t kMinimalNormalizedExponent = - UINT64_2PART_C(0x00100000, 00000000); - uint64_t significand = Double(v).Significand(); - int exponent = Double(v).Exponent(); - // Instead of multiplying the denominator with 10^estimated_power we - // multiply all values (numerator and deltas) by 10^-estimated_power. - - // Use numerator as temporary container for power_ten. - Bignum* power_ten = numerator; - power_ten->AssignPowerUInt16(10, -estimated_power); - - if (need_boundary_deltas) { - // Since power_ten == numerator we must make a copy of 10^estimated_power - // before we complete the computation of the numerator. - // delta_plus = delta_minus = 10^estimated_power - delta_plus->AssignBignum(*power_ten); - delta_minus->AssignBignum(*power_ten); - } - - // numerator = significand * 2 * 10^-estimated_power - // since v = significand * 2^exponent this is equivalent to - // numerator = v * 10^-estimated_power * 2 * 2^-exponent. - // Remember: numerator has been abused as power_ten. So no need to assign it - // to itself. - ASSERT(numerator == power_ten); - numerator->MultiplyByUInt64(significand); - - // denominator = 2 * 2^-exponent with exponent < 0. - denominator->AssignUInt16(1); - denominator->Shifxleft(-exponent); - - if (need_boundary_deltas) { - // Introduce a common denominator so that the deltas to the boundaries are - // integers. - numerator->Shifxleft(1); - denominator->Shifxleft(1); - // With this shift the boundaries have their correct value, since - // delta_plus = 10^-estimated_power, and - // delta_minus = 10^-estimated_power. - // These assignments have been done earlier. - - // The special case where the lower boundary is twice as close. - // This time we have to look out for the exception too. - uint64_t v_bits = Double(v).AsUint64(); - if ((v_bits & Double::kSignificandMask) == 0 && - // The only exception where a significand == 0 has its boundaries at - // "normal" distances: - (v_bits & Double::kExponentMask) != kMinimalNormalizedExponent) { - numerator->Shifxleft(1); // *2 - denominator->Shifxleft(1); // *2 - delta_plus->Shifxleft(1); // *2 - } - } + } +} + +// See comments for InitialScaledStartValues +static void InitialScaledStartValuesNegativeExponentPositivePower( + double v, + int estimated_power, + bool need_boundary_deltas, + Bignum* numerator, + Bignum* denominator, + Bignum* delta_minus, + Bignum* delta_plus) { + uint64_t significand = Double(v).Significand(); + int exponent = Double(v).Exponent(); + // v = f * 2^e with e < 0, and with estimated_power >= 0. + // This means that e is close to 0 (have a look at how estimated_power is + // computed). + + // numerator = significand + // since v = significand * 2^exponent this is equivalent to + // numerator = v * / 2^-exponent + numerator->AssignUInt64(significand); + // denominator = 10^estimated_power * 2^-exponent (with exponent < 0) + denominator->AssignPowerUInt16(10, estimated_power); + denominator->Shifxleft(-exponent); + + if (need_boundary_deltas) { + // Introduce a common denominator so that the deltas to the boundaries are + // integers. + denominator->Shifxleft(1); + numerator->Shifxleft(1); + // Let v = f * 2^e, then m+ - v = 1/2 * 2^e; With the common + // denominator (of 2) delta_plus equals 2^e. + // Given that the denominator already includes v's exponent the distance + // to the boundaries is simply 1. + delta_plus->AssignUInt16(1); + // Same for delta_minus (with adjustments below if f == 2^p-1). + delta_minus->AssignUInt16(1); + + // If the significand (without the hidden bit) is 0, then the lower + // boundary is closer than just one ulp (unit in the last place). + // There is only one exception: if the next lower number is a denormal + // then the distance is 1 ulp. Since the exponent is close to zero + // (otherwise estimated_power would have been negative) this cannot happen + // here either. + uint64_t v_bits = Double(v).AsUint64(); + if ((v_bits & Double::kSignificandMask) == 0) { + // The lower boundary is closer at half the distance of "normal" numbers. + // Increase the denominator and adapt all but the delta_minus. + denominator->Shifxleft(1); // *2 + numerator->Shifxleft(1); // *2 + delta_plus->Shifxleft(1); // *2 } - - - // Let v = significand * 2^exponent. - // Computes v / 10^estimated_power exactly, as a ratio of two bignums, numerator - // and denominator. The functions GenerateShortestDigits and - // GenerateCountedDigits will then convert this ratio to its decimal - // representation d, with the required accuracy. - // Then d * 10^estimated_power is the representation of v. - // (Note: the fraction and the estimated_power might get adjusted before - // generating the decimal representation.) - // - // The initial start values consist of: - // - a scaled numerator: s.t. numerator/denominator == v / 10^estimated_power. - // - a scaled (common) denominator. - // optionally (used by GenerateShortestDigits to decide if it has the shortest - // decimal converting back to v): - // - v - m-: the distance to the lower boundary. - // - m+ - v: the distance to the upper boundary. - // - // v, m+, m-, and therefore v - m- and m+ - v all share the same denominator. - // - // Let ep == estimated_power, then the returned values will satisfy: - // v / 10^ep = numerator / denominator. - // v's boundarys m- and m+: - // m- / 10^ep == v / 10^ep - delta_minus / denominator - // m+ / 10^ep == v / 10^ep + delta_plus / denominator - // Or in other words: - // m- == v - delta_minus * 10^ep / denominator; - // m+ == v + delta_plus * 10^ep / denominator; - // - // Since 10^(k-1) <= v < 10^k (with k == estimated_power) - // or 10^k <= v < 10^(k+1) - // we then have 0.1 <= numerator/denominator < 1 - // or 1 <= numerator/denominator < 10 - // - // It is then easy to kickstart the digit-generation routine. - // - // The boundary-deltas are only filled if need_boundary_deltas is set. - static void InitialScaledStartValues(double v, - int estimated_power, - bool need_boundary_deltas, - Bignum* numerator, - Bignum* denominator, - Bignum* delta_minus, - Bignum* delta_plus) { - if (Double(v).Exponent() >= 0) { - InitialScaledStartValuesPositiveExponent( - v, estimated_power, need_boundary_deltas, - numerator, denominator, delta_minus, delta_plus); - } else if (estimated_power >= 0) { - InitialScaledStartValuesNegativeExponentPositivePower( - v, estimated_power, need_boundary_deltas, - numerator, denominator, delta_minus, delta_plus); - } else { - InitialScaledStartValuesNegativeExponentNegativePower( - v, estimated_power, need_boundary_deltas, - numerator, denominator, delta_minus, delta_plus); - } + } +} + +// See comments for InitialScaledStartValues +static void InitialScaledStartValuesNegativeExponentNegativePower( + double v, + int estimated_power, + bool need_boundary_deltas, + Bignum* numerator, + Bignum* denominator, + Bignum* delta_minus, + Bignum* delta_plus) { + const uint64_t kMinimalNormalizedExponent = + UINT64_2PART_C(0x00100000, 00000000); + uint64_t significand = Double(v).Significand(); + int exponent = Double(v).Exponent(); + // Instead of multiplying the denominator with 10^estimated_power we + // multiply all values (numerator and deltas) by 10^-estimated_power. + + // Use numerator as temporary container for power_ten. + Bignum* power_ten = numerator; + power_ten->AssignPowerUInt16(10, -estimated_power); + + if (need_boundary_deltas) { + // Since power_ten == numerator we must make a copy of 10^estimated_power + // before we complete the computation of the numerator. + // delta_plus = delta_minus = 10^estimated_power + delta_plus->AssignBignum(*power_ten); + delta_minus->AssignBignum(*power_ten); + } + + // numerator = significand * 2 * 10^-estimated_power + // since v = significand * 2^exponent this is equivalent to + // numerator = v * 10^-estimated_power * 2 * 2^-exponent. + // Remember: numerator has been abused as power_ten. So no need to assign it + // to itself. + ASSERT(numerator == power_ten); + numerator->MultiplyByUInt64(significand); + + // denominator = 2 * 2^-exponent with exponent < 0. + denominator->AssignUInt16(1); + denominator->Shifxleft(-exponent); + + if (need_boundary_deltas) { + // Introduce a common denominator so that the deltas to the boundaries are + // integers. + numerator->Shifxleft(1); + denominator->Shifxleft(1); + // With this shift the boundaries have their correct value, since + // delta_plus = 10^-estimated_power, and + // delta_minus = 10^-estimated_power. + // These assignments have been done earlier. + + // The special case where the lower boundary is twice as close. + // This time we have to look out for the exception too. + uint64_t v_bits = Double(v).AsUint64(); + if ((v_bits & Double::kSignificandMask) == 0 && + // The only exception where a significand == 0 has its boundaries at + // "normal" distances: + (v_bits & Double::kExponentMask) != kMinimalNormalizedExponent) { + numerator->Shifxleft(1); // *2 + denominator->Shifxleft(1); // *2 + delta_plus->Shifxleft(1); // *2 } - - - // This routine multiplies numerator/denominator so that its values lies in the - // range 1-10. That is after a call to this function we have: - // 1 <= (numerator + delta_plus) /denominator < 10. - // Let numerator the input before modification and numerator' the argument - // after modification, then the output-parameter decimal_point is such that - // numerator / denominator * 10^estimated_power == - // numerator' / denominator' * 10^(decimal_point - 1) - // In some cases estimated_power was too low, and this is already the case. We - // then simply adjust the power so that 10^(k-1) <= v < 10^k (with k == - // estimated_power) but do not touch the numerator or denominator. - // Otherwise the routine multiplies the numerator and the deltas by 10. - static void FixupMultiply10(int estimated_power, bool is_even, - int* decimal_point, - Bignum* numerator, Bignum* denominator, - Bignum* delta_minus, Bignum* delta_plus) { - bool in_range; - if (is_even) { - // For IEEE doubles half-way cases (in decimal system numbers ending with 5) - // are rounded to the closest floating-point number with even significand. - in_range = Bignum::PlusCompare(*numerator, *delta_plus, *denominator) >= 0; - } else { - in_range = Bignum::PlusCompare(*numerator, *delta_plus, *denominator) > 0; - } - if (in_range) { - // Since numerator + delta_plus >= denominator we already have - // 1 <= numerator/denominator < 10. Simply update the estimated_power. - *decimal_point = estimated_power + 1; - } else { - *decimal_point = estimated_power; - numerator->Times10(); - if (Bignum::Equal(*delta_minus, *delta_plus)) { - delta_minus->Times10(); - delta_plus->AssignBignum(*delta_minus); - } else { - delta_minus->Times10(); - delta_plus->Times10(); - } - } + } +} + +// Let v = significand * 2^exponent. +// Computes v / 10^estimated_power exactly, as a ratio of two bignums, numerator +// and denominator. The functions GenerateShortestDigits and +// GenerateCountedDigits will then convert this ratio to its decimal +// representation d, with the required accuracy. +// Then d * 10^estimated_power is the representation of v. +// (Note: the fraction and the estimated_power might get adjusted before +// generating the decimal representation.) +// +// The initial start values consist of: +// - a scaled numerator: s.t. numerator/denominator == v / 10^estimated_power. +// - a scaled (common) denominator. +// optionally (used by GenerateShortestDigits to decide if it has the shortest +// decimal converting back to v): +// - v - m-: the distance to the lower boundary. +// - m+ - v: the distance to the upper boundary. +// +// v, m+, m-, and therefore v - m- and m+ - v all share the same denominator. +// +// Let ep == estimated_power, then the returned values will satisfy: +// v / 10^ep = numerator / denominator. +// v's boundarys m- and m+: +// m- / 10^ep == v / 10^ep - delta_minus / denominator +// m+ / 10^ep == v / 10^ep + delta_plus / denominator +// Or in other words: +// m- == v - delta_minus * 10^ep / denominator; +// m+ == v + delta_plus * 10^ep / denominator; +// +// Since 10^(k-1) <= v < 10^k (with k == estimated_power) +// or 10^k <= v < 10^(k+1) +// we then have 0.1 <= numerator/denominator < 1 +// or 1 <= numerator/denominator < 10 +// +// It is then easy to kickstart the digit-generation routine. +// +// The boundary-deltas are only filled if need_boundary_deltas is set. +static void InitialScaledStartValues(double v, + int estimated_power, + bool need_boundary_deltas, + Bignum* numerator, + Bignum* denominator, + Bignum* delta_minus, + Bignum* delta_plus) { + if (Double(v).Exponent() >= 0) { + InitialScaledStartValuesPositiveExponent( + v, estimated_power, need_boundary_deltas, numerator, denominator, + delta_minus, delta_plus); + } else if (estimated_power >= 0) { + InitialScaledStartValuesNegativeExponentPositivePower( + v, estimated_power, need_boundary_deltas, numerator, denominator, + delta_minus, delta_plus); + } else { + InitialScaledStartValuesNegativeExponentNegativePower( + v, estimated_power, need_boundary_deltas, numerator, denominator, + delta_minus, delta_plus); + } +} + +// This routine multiplies numerator/denominator so that its values lies in the +// range 1-10. That is after a call to this function we have: +// 1 <= (numerator + delta_plus) /denominator < 10. +// Let numerator the input before modification and numerator' the argument +// after modification, then the output-parameter decimal_point is such that +// numerator / denominator * 10^estimated_power == +// numerator' / denominator' * 10^(decimal_point - 1) +// In some cases estimated_power was too low, and this is already the case. We +// then simply adjust the power so that 10^(k-1) <= v < 10^k (with k == +// estimated_power) but do not touch the numerator or denominator. +// Otherwise the routine multiplies the numerator and the deltas by 10. +static void FixupMultiply10(int estimated_power, + bool is_even, + int* decimal_point, + Bignum* numerator, + Bignum* denominator, + Bignum* delta_minus, + Bignum* delta_plus) { + bool in_range; + if (is_even) { + // For IEEE doubles half-way cases (in decimal system numbers ending with 5) + // are rounded to the closest floating-point number with even significand. + in_range = Bignum::PlusCompare(*numerator, *delta_plus, *denominator) >= 0; + } else { + in_range = Bignum::PlusCompare(*numerator, *delta_plus, *denominator) > 0; + } + if (in_range) { + // Since numerator + delta_plus >= denominator we already have + // 1 <= numerator/denominator < 10. Simply update the estimated_power. + *decimal_point = estimated_power + 1; + } else { + *decimal_point = estimated_power; + numerator->Times10(); + if (Bignum::Equal(*delta_minus, *delta_plus)) { + delta_minus->Times10(); + delta_plus->AssignBignum(*delta_minus); + } else { + delta_minus->Times10(); + delta_plus->Times10(); } + } +} } // namespace double_conversion -} // namespace WTF +} // namespace WTF diff --git a/sky/engine/wtf/dtoa/bignum-dtoa.h b/sky/engine/wtf/dtoa/bignum-dtoa.h index e28536d70eb42..81970fa506ff3 100644 --- a/sky/engine/wtf/dtoa/bignum-dtoa.h +++ b/sky/engine/wtf/dtoa/bignum-dtoa.h @@ -34,53 +34,57 @@ namespace WTF { namespace double_conversion { - enum BignumDtoaMode { - // Return the shortest correct representation. - // For example the output of 0.299999999999999988897 is (the less accurate but - // correct) 0.3. - BIGNUM_DTOA_SHORTEST, - // Return a fixed number of digits after the decimal point. - // For instance fixed(0.1, 4) becomes 0.1000 - // If the input number is big, the output will be big. - BIGNUM_DTOA_FIXED, - // Return a fixed number of digits, no matter what the exponent is. - BIGNUM_DTOA_PRECISION - }; +enum BignumDtoaMode { + // Return the shortest correct representation. + // For example the output of 0.299999999999999988897 is (the less accurate but + // correct) 0.3. + BIGNUM_DTOA_SHORTEST, + // Return a fixed number of digits after the decimal point. + // For instance fixed(0.1, 4) becomes 0.1000 + // If the input number is big, the output will be big. + BIGNUM_DTOA_FIXED, + // Return a fixed number of digits, no matter what the exponent is. + BIGNUM_DTOA_PRECISION +}; - // Converts the given double 'v' to ascii. - // The result should be interpreted as buffer * 10^(point-length). - // The buffer will be null-terminated. - // - // The input v must be > 0 and different from NaN, and Infinity. - // - // The output depends on the given mode: - // - SHORTEST: produce the least amount of digits for which the internal - // identity requirement is still satisfied. If the digits are printed - // (together with the correct exponent) then reading this number will give - // 'v' again. The buffer will choose the representation that is closest to - // 'v'. If there are two at the same distance, than the number is round up. - // In this mode the 'requested_digits' parameter is ignored. - // - FIXED: produces digits necessary to print a given number with - // 'requested_digits' digits after the decimal point. The produced digits - // might be too short in which case the caller has to fill the gaps with '0's. - // Example: toFixed(0.001, 5) is allowed to return buffer="1", point=-2. - // Halfway cases are rounded up. The call toFixed(0.15, 2) thus returns - // buffer="2", point=0. - // Note: the length of the returned buffer has no meaning wrt the significance - // of its digits. That is, just because it contains '0's does not mean that - // any other digit would not satisfy the internal identity requirement. - // - PRECISION: produces 'requested_digits' where the first digit is not '0'. - // Even though the length of produced digits usually equals - // 'requested_digits', the function is allowed to return fewer digits, in - // which case the caller has to fill the missing digits with '0's. - // Halfway cases are again rounded up. - // 'BignumDtoa' expects the given buffer to be big enough to hold all digits - // and a terminating null-character. - void BignumDtoa(double v, BignumDtoaMode mode, int requested_digits, - Vector buffer, int* length, int* point); +// Converts the given double 'v' to ascii. +// The result should be interpreted as buffer * 10^(point-length). +// The buffer will be null-terminated. +// +// The input v must be > 0 and different from NaN, and Infinity. +// +// The output depends on the given mode: +// - SHORTEST: produce the least amount of digits for which the internal +// identity requirement is still satisfied. If the digits are printed +// (together with the correct exponent) then reading this number will give +// 'v' again. The buffer will choose the representation that is closest to +// 'v'. If there are two at the same distance, than the number is round up. +// In this mode the 'requested_digits' parameter is ignored. +// - FIXED: produces digits necessary to print a given number with +// 'requested_digits' digits after the decimal point. The produced digits +// might be too short in which case the caller has to fill the gaps with '0's. +// Example: toFixed(0.001, 5) is allowed to return buffer="1", point=-2. +// Halfway cases are rounded up. The call toFixed(0.15, 2) thus returns +// buffer="2", point=0. +// Note: the length of the returned buffer has no meaning wrt the significance +// of its digits. That is, just because it contains '0's does not mean that +// any other digit would not satisfy the internal identity requirement. +// - PRECISION: produces 'requested_digits' where the first digit is not '0'. +// Even though the length of produced digits usually equals +// 'requested_digits', the function is allowed to return fewer digits, in +// which case the caller has to fill the missing digits with '0's. +// Halfway cases are again rounded up. +// 'BignumDtoa' expects the given buffer to be big enough to hold all digits +// and a terminating null-character. +void BignumDtoa(double v, + BignumDtoaMode mode, + int requested_digits, + Vector buffer, + int* length, + int* point); } // namespace double_conversion -} // namespace WTF +} // namespace WTF #endif // SKY_ENGINE_WTF_DTOA_BIGNUM_DTOA_H_ diff --git a/sky/engine/wtf/dtoa/bignum.cc b/sky/engine/wtf/dtoa/bignum.cc index 00ce351ce68ea..095109c36dd2d 100644 --- a/sky/engine/wtf/dtoa/bignum.cc +++ b/sky/engine/wtf/dtoa/bignum.cc @@ -25,7 +25,6 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - #include "flutter/sky/engine/wtf/dtoa/bignum.h" #include "flutter/sky/engine/wtf/dtoa/utils.h" @@ -33,737 +32,735 @@ namespace WTF { namespace double_conversion { - Bignum::Bignum() +Bignum::Bignum() : bigits_(bigits_buffer_, kBigitCapacity), used_digits_(0), exponent_(0) { - for (int i = 0; i < kBigitCapacity; ++i) { - bigits_[i] = 0; - } - } - - - template - static int BitSize(S value) { - return 8 * sizeof(value); - } - - // Guaranteed to lie in one Bigit. - void Bignum::AssignUInt16(uint16_t value) { - ASSERT(kBigitSize >= BitSize(value)); - Zero(); - if (value == 0) return; - - EnsureCapacity(1); - bigits_[0] = value; - used_digits_ = 1; - } - - - void Bignum::AssignUInt64(uint64_t value) { - const int kUInt64Size = 64; - - Zero(); - if (value == 0) return; - - int needed_bigits = kUInt64Size / kBigitSize + 1; - EnsureCapacity(needed_bigits); - for (int i = 0; i < needed_bigits; ++i) { - bigits_[i] = (uint32_t)value & kBigitMask; - value = value >> kBigitSize; - } - used_digits_ = needed_bigits; - Clamp(); - } - - - void Bignum::AssignBignum(const Bignum& other) { - exponent_ = other.exponent_; - for (int i = 0; i < other.used_digits_; ++i) { - bigits_[i] = other.bigits_[i]; - } - // Clear the excess digits (if there were any). - for (int i = other.used_digits_; i < used_digits_; ++i) { - bigits_[i] = 0; - } - used_digits_ = other.used_digits_; - } - - - static uint64_t ReadUInt64(Vector buffer, - int from, - int digits_to_read) { - uint64_t result = 0; - for (int i = from; i < from + digits_to_read; ++i) { - int digit = buffer[i] - '0'; - ASSERT(0 <= digit && digit <= 9); - result = result * 10 + digit; - } - return result; - } - - - void Bignum::AssignDecimalString(Vector value) { - // 2^64 = 18446744073709551616 > 10^19 - const int kMaxUint64DecimalDigits = 19; - Zero(); - int length = value.length(); - int pos = 0; - // Let's just say that each digit needs 4 bits. - while (length >= kMaxUint64DecimalDigits) { - uint64_t digits = ReadUInt64(value, pos, kMaxUint64DecimalDigits); - pos += kMaxUint64DecimalDigits; - length -= kMaxUint64DecimalDigits; - MultiplyByPowerOfTen(kMaxUint64DecimalDigits); - AddUInt64(digits); - } - uint64_t digits = ReadUInt64(value, pos, length); - MultiplyByPowerOfTen(length); - AddUInt64(digits); - Clamp(); - } - - - static int HexCharValue(char c) { - if ('0' <= c && c <= '9') return c - '0'; - if ('a' <= c && c <= 'f') return 10 + c - 'a'; - if ('A' <= c && c <= 'F') return 10 + c - 'A'; - UNREACHABLE(); - return 0; // To make compiler happy. - } - - - void Bignum::AssignHexString(Vector value) { - Zero(); - int length = value.length(); - - int needed_bigits = length * 4 / kBigitSize + 1; - EnsureCapacity(needed_bigits); - int string_index = length - 1; - for (int i = 0; i < needed_bigits - 1; ++i) { - // These bigits are guaranteed to be "full". - Chunk current_bigit = 0; - for (int j = 0; j < kBigitSize / 4; j++) { - current_bigit += HexCharValue(value[string_index--]) << (j * 4); - } - bigits_[i] = current_bigit; - } - used_digits_ = needed_bigits - 1; - - Chunk most_significant_bigit = 0; // Could be = 0; - for (int j = 0; j <= string_index; ++j) { - most_significant_bigit <<= 4; - most_significant_bigit += HexCharValue(value[j]); - } - if (most_significant_bigit != 0) { - bigits_[used_digits_] = most_significant_bigit; - used_digits_++; - } - Clamp(); - } - - - void Bignum::AddUInt64(uint64_t operand) { - if (operand == 0) return; - Bignum other; - other.AssignUInt64(operand); - AddBignum(other); - } - - - void Bignum::AddBignum(const Bignum& other) { - ASSERT(IsClamped()); - ASSERT(other.IsClamped()); - - // If this has a greater exponent than other append zero-bigits to this. - // After this call exponent_ <= other.exponent_. - Align(other); - - // There are two possibilities: - // aaaaaaaaaaa 0000 (where the 0s represent a's exponent) - // bbbbb 00000000 - // ---------------- - // ccccccccccc 0000 - // or - // aaaaaaaaaa 0000 - // bbbbbbbbb 0000000 - // ----------------- - // cccccccccccc 0000 - // In both cases we might need a carry bigit. - - EnsureCapacity(1 + Max(BigitLength(), other.BigitLength()) - exponent_); - Chunk carry = 0; - int bigit_pos = other.exponent_ - exponent_; - ASSERT(bigit_pos >= 0); - for (int i = 0; i < other.used_digits_; ++i) { - Chunk sum = bigits_[bigit_pos] + other.bigits_[i] + carry; - bigits_[bigit_pos] = sum & kBigitMask; - carry = sum >> kBigitSize; - bigit_pos++; - } - - while (carry != 0) { - Chunk sum = bigits_[bigit_pos] + carry; - bigits_[bigit_pos] = sum & kBigitMask; - carry = sum >> kBigitSize; - bigit_pos++; - } - used_digits_ = Max(bigit_pos, used_digits_); - ASSERT(IsClamped()); - } - - - void Bignum::SubtractBignum(const Bignum& other) { - ASSERT(IsClamped()); - ASSERT(other.IsClamped()); - // We require this to be bigger than other. - ASSERT(LessEqual(other, *this)); - - Align(other); - - int offset = other.exponent_ - exponent_; - Chunk borrow = 0; - int i; - for (i = 0; i < other.used_digits_; ++i) { - ASSERT((borrow == 0) || (borrow == 1)); - Chunk difference = bigits_[i + offset] - other.bigits_[i] - borrow; - bigits_[i + offset] = difference & kBigitMask; - borrow = difference >> (kChunkSize - 1); - } - while (borrow != 0) { - Chunk difference = bigits_[i + offset] - borrow; - bigits_[i + offset] = difference & kBigitMask; - borrow = difference >> (kChunkSize - 1); - ++i; - } - Clamp(); - } - - - void Bignum::Shifxleft(int shift_amount) { - if (used_digits_ == 0) return; - exponent_ += shift_amount / kBigitSize; - int local_shift = shift_amount % kBigitSize; - EnsureCapacity(used_digits_ + 1); - BigitsShifxleft(local_shift); - } - - - void Bignum::MultiplyByUInt32(uint32_t factor) { - if (factor == 1) return; - if (factor == 0) { - Zero(); - return; - } - if (used_digits_ == 0) return; - - // The product of a bigit with the factor is of size kBigitSize + 32. - // Assert that this number + 1 (for the carry) fits into double chunk. - ASSERT(kDoubleChunkSize >= kBigitSize + 32 + 1); - DoubleChunk carry = 0; - for (int i = 0; i < used_digits_; ++i) { - DoubleChunk product = static_cast(factor) * bigits_[i] + carry; - bigits_[i] = static_cast(product & kBigitMask); - carry = (product >> kBigitSize); - } - while (carry != 0) { - EnsureCapacity(used_digits_ + 1); - bigits_[used_digits_] = (uint32_t)carry & kBigitMask; - used_digits_++; - carry >>= kBigitSize; - } + for (int i = 0; i < kBigitCapacity; ++i) { + bigits_[i] = 0; + } +} + +template +static int BitSize(S value) { + return 8 * sizeof(value); +} + +// Guaranteed to lie in one Bigit. +void Bignum::AssignUInt16(uint16_t value) { + ASSERT(kBigitSize >= BitSize(value)); + Zero(); + if (value == 0) + return; + + EnsureCapacity(1); + bigits_[0] = value; + used_digits_ = 1; +} + +void Bignum::AssignUInt64(uint64_t value) { + const int kUInt64Size = 64; + + Zero(); + if (value == 0) + return; + + int needed_bigits = kUInt64Size / kBigitSize + 1; + EnsureCapacity(needed_bigits); + for (int i = 0; i < needed_bigits; ++i) { + bigits_[i] = (uint32_t)value & kBigitMask; + value = value >> kBigitSize; + } + used_digits_ = needed_bigits; + Clamp(); +} + +void Bignum::AssignBignum(const Bignum& other) { + exponent_ = other.exponent_; + for (int i = 0; i < other.used_digits_; ++i) { + bigits_[i] = other.bigits_[i]; + } + // Clear the excess digits (if there were any). + for (int i = other.used_digits_; i < used_digits_; ++i) { + bigits_[i] = 0; + } + used_digits_ = other.used_digits_; +} + +static uint64_t ReadUInt64(Vector buffer, + int from, + int digits_to_read) { + uint64_t result = 0; + for (int i = from; i < from + digits_to_read; ++i) { + int digit = buffer[i] - '0'; + ASSERT(0 <= digit && digit <= 9); + result = result * 10 + digit; + } + return result; +} + +void Bignum::AssignDecimalString(Vector value) { + // 2^64 = 18446744073709551616 > 10^19 + const int kMaxUint64DecimalDigits = 19; + Zero(); + int length = value.length(); + int pos = 0; + // Let's just say that each digit needs 4 bits. + while (length >= kMaxUint64DecimalDigits) { + uint64_t digits = ReadUInt64(value, pos, kMaxUint64DecimalDigits); + pos += kMaxUint64DecimalDigits; + length -= kMaxUint64DecimalDigits; + MultiplyByPowerOfTen(kMaxUint64DecimalDigits); + AddUInt64(digits); + } + uint64_t digits = ReadUInt64(value, pos, length); + MultiplyByPowerOfTen(length); + AddUInt64(digits); + Clamp(); +} + +static int HexCharValue(char c) { + if ('0' <= c && c <= '9') + return c - '0'; + if ('a' <= c && c <= 'f') + return 10 + c - 'a'; + if ('A' <= c && c <= 'F') + return 10 + c - 'A'; + UNREACHABLE(); + return 0; // To make compiler happy. +} + +void Bignum::AssignHexString(Vector value) { + Zero(); + int length = value.length(); + + int needed_bigits = length * 4 / kBigitSize + 1; + EnsureCapacity(needed_bigits); + int string_index = length - 1; + for (int i = 0; i < needed_bigits - 1; ++i) { + // These bigits are guaranteed to be "full". + Chunk current_bigit = 0; + for (int j = 0; j < kBigitSize / 4; j++) { + current_bigit += HexCharValue(value[string_index--]) << (j * 4); } - - - void Bignum::MultiplyByUInt64(uint64_t factor) { - if (factor == 1) return; - if (factor == 0) { - Zero(); - return; - } - ASSERT(kBigitSize < 32); - uint64_t carry = 0; - uint64_t low = factor & 0xFFFFFFFF; - uint64_t high = factor >> 32; - for (int i = 0; i < used_digits_; ++i) { - uint64_t product_low = low * bigits_[i]; - uint64_t product_high = high * bigits_[i]; - uint64_t tmp = (carry & kBigitMask) + product_low; - bigits_[i] = (uint32_t)tmp & kBigitMask; - carry = (carry >> kBigitSize) + (tmp >> kBigitSize) + + bigits_[i] = current_bigit; + } + used_digits_ = needed_bigits - 1; + + Chunk most_significant_bigit = 0; // Could be = 0; + for (int j = 0; j <= string_index; ++j) { + most_significant_bigit <<= 4; + most_significant_bigit += HexCharValue(value[j]); + } + if (most_significant_bigit != 0) { + bigits_[used_digits_] = most_significant_bigit; + used_digits_++; + } + Clamp(); +} + +void Bignum::AddUInt64(uint64_t operand) { + if (operand == 0) + return; + Bignum other; + other.AssignUInt64(operand); + AddBignum(other); +} + +void Bignum::AddBignum(const Bignum& other) { + ASSERT(IsClamped()); + ASSERT(other.IsClamped()); + + // If this has a greater exponent than other append zero-bigits to this. + // After this call exponent_ <= other.exponent_. + Align(other); + + // There are two possibilities: + // aaaaaaaaaaa 0000 (where the 0s represent a's exponent) + // bbbbb 00000000 + // ---------------- + // ccccccccccc 0000 + // or + // aaaaaaaaaa 0000 + // bbbbbbbbb 0000000 + // ----------------- + // cccccccccccc 0000 + // In both cases we might need a carry bigit. + + EnsureCapacity(1 + Max(BigitLength(), other.BigitLength()) - exponent_); + Chunk carry = 0; + int bigit_pos = other.exponent_ - exponent_; + ASSERT(bigit_pos >= 0); + for (int i = 0; i < other.used_digits_; ++i) { + Chunk sum = bigits_[bigit_pos] + other.bigits_[i] + carry; + bigits_[bigit_pos] = sum & kBigitMask; + carry = sum >> kBigitSize; + bigit_pos++; + } + + while (carry != 0) { + Chunk sum = bigits_[bigit_pos] + carry; + bigits_[bigit_pos] = sum & kBigitMask; + carry = sum >> kBigitSize; + bigit_pos++; + } + used_digits_ = Max(bigit_pos, used_digits_); + ASSERT(IsClamped()); +} + +void Bignum::SubtractBignum(const Bignum& other) { + ASSERT(IsClamped()); + ASSERT(other.IsClamped()); + // We require this to be bigger than other. + ASSERT(LessEqual(other, *this)); + + Align(other); + + int offset = other.exponent_ - exponent_; + Chunk borrow = 0; + int i; + for (i = 0; i < other.used_digits_; ++i) { + ASSERT((borrow == 0) || (borrow == 1)); + Chunk difference = bigits_[i + offset] - other.bigits_[i] - borrow; + bigits_[i + offset] = difference & kBigitMask; + borrow = difference >> (kChunkSize - 1); + } + while (borrow != 0) { + Chunk difference = bigits_[i + offset] - borrow; + bigits_[i + offset] = difference & kBigitMask; + borrow = difference >> (kChunkSize - 1); + ++i; + } + Clamp(); +} + +void Bignum::Shifxleft(int shift_amount) { + if (used_digits_ == 0) + return; + exponent_ += shift_amount / kBigitSize; + int local_shift = shift_amount % kBigitSize; + EnsureCapacity(used_digits_ + 1); + BigitsShifxleft(local_shift); +} + +void Bignum::MultiplyByUInt32(uint32_t factor) { + if (factor == 1) + return; + if (factor == 0) { + Zero(); + return; + } + if (used_digits_ == 0) + return; + + // The product of a bigit with the factor is of size kBigitSize + 32. + // Assert that this number + 1 (for the carry) fits into double chunk. + ASSERT(kDoubleChunkSize >= kBigitSize + 32 + 1); + DoubleChunk carry = 0; + for (int i = 0; i < used_digits_; ++i) { + DoubleChunk product = static_cast(factor) * bigits_[i] + carry; + bigits_[i] = static_cast(product & kBigitMask); + carry = (product >> kBigitSize); + } + while (carry != 0) { + EnsureCapacity(used_digits_ + 1); + bigits_[used_digits_] = (uint32_t)carry & kBigitMask; + used_digits_++; + carry >>= kBigitSize; + } +} + +void Bignum::MultiplyByUInt64(uint64_t factor) { + if (factor == 1) + return; + if (factor == 0) { + Zero(); + return; + } + ASSERT(kBigitSize < 32); + uint64_t carry = 0; + uint64_t low = factor & 0xFFFFFFFF; + uint64_t high = factor >> 32; + for (int i = 0; i < used_digits_; ++i) { + uint64_t product_low = low * bigits_[i]; + uint64_t product_high = high * bigits_[i]; + uint64_t tmp = (carry & kBigitMask) + product_low; + bigits_[i] = (uint32_t)tmp & kBigitMask; + carry = (carry >> kBigitSize) + (tmp >> kBigitSize) + (product_high << (32 - kBigitSize)); - } - while (carry != 0) { - EnsureCapacity(used_digits_ + 1); - bigits_[used_digits_] = (uint32_t)carry & kBigitMask; - used_digits_++; - carry >>= kBigitSize; - } + } + while (carry != 0) { + EnsureCapacity(used_digits_ + 1); + bigits_[used_digits_] = (uint32_t)carry & kBigitMask; + used_digits_++; + carry >>= kBigitSize; + } +} + +void Bignum::MultiplyByPowerOfTen(int exponent) { + const uint64_t kFive27 = UINT64_2PART_C(0x6765c793, fa10079d); + const uint16_t kFive1 = 5; + const uint16_t kFive2 = kFive1 * 5; + const uint16_t kFive3 = kFive2 * 5; + const uint16_t kFive4 = kFive3 * 5; + const uint16_t kFive5 = kFive4 * 5; + const uint16_t kFive6 = kFive5 * 5; + const uint32_t kFive7 = kFive6 * 5; + const uint32_t kFive8 = kFive7 * 5; + const uint32_t kFive9 = kFive8 * 5; + const uint32_t kFive10 = kFive9 * 5; + const uint32_t kFive11 = kFive10 * 5; + const uint32_t kFive12 = kFive11 * 5; + const uint32_t kFive13 = kFive12 * 5; + const uint32_t kFive1_to_12[] = {kFive1, kFive2, kFive3, kFive4, + kFive5, kFive6, kFive7, kFive8, + kFive9, kFive10, kFive11, kFive12}; + + ASSERT(exponent >= 0); + if (exponent == 0) + return; + if (used_digits_ == 0) + return; + + // We shift by exponent at the end just before returning. + int remaining_exponent = exponent; + while (remaining_exponent >= 27) { + MultiplyByUInt64(kFive27); + remaining_exponent -= 27; + } + while (remaining_exponent >= 13) { + MultiplyByUInt32(kFive13); + remaining_exponent -= 13; + } + if (remaining_exponent > 0) { + MultiplyByUInt32(kFive1_to_12[remaining_exponent - 1]); + } + Shifxleft(exponent); +} + +void Bignum::Square() { + ASSERT(IsClamped()); + int product_length = 2 * used_digits_; + EnsureCapacity(product_length); + + // Comba multiplication: compute each column separately. + // Example: r = a2a1a0 * b2b1b0. + // r = 1 * a0b0 + + // 10 * (a1b0 + a0b1) + + // 100 * (a2b0 + a1b1 + a0b2) + + // 1000 * (a2b1 + a1b2) + + // 10000 * a2b2 + // + // In the worst case we have to accumulate nb-digits products of digit*digit. + // + // Assert that the additional number of bits in a DoubleChunk are enough to + // sum up used_digits of Bigit*Bigit. + if ((1 << (2 * (kChunkSize - kBigitSize))) <= used_digits_) { + UNIMPLEMENTED(); + } + DoubleChunk accumulator = 0; + // First shift the digits so we don't overwrite them. + int copy_offset = used_digits_; + for (int i = 0; i < used_digits_; ++i) { + bigits_[copy_offset + i] = bigits_[i]; + } + // We have two loops to avoid some 'if's in the loop. + for (int i = 0; i < used_digits_; ++i) { + // Process temporary digit i with power i. + // The sum of the two indices must be equal to i. + int bigit_index1 = i; + int bigit_index2 = 0; + // Sum all of the sub-products. + while (bigit_index1 >= 0) { + Chunk chunk1 = bigits_[copy_offset + bigit_index1]; + Chunk chunk2 = bigits_[copy_offset + bigit_index2]; + accumulator += static_cast(chunk1) * chunk2; + bigit_index1--; + bigit_index2++; } - - - void Bignum::MultiplyByPowerOfTen(int exponent) { - const uint64_t kFive27 = UINT64_2PART_C(0x6765c793, fa10079d); - const uint16_t kFive1 = 5; - const uint16_t kFive2 = kFive1 * 5; - const uint16_t kFive3 = kFive2 * 5; - const uint16_t kFive4 = kFive3 * 5; - const uint16_t kFive5 = kFive4 * 5; - const uint16_t kFive6 = kFive5 * 5; - const uint32_t kFive7 = kFive6 * 5; - const uint32_t kFive8 = kFive7 * 5; - const uint32_t kFive9 = kFive8 * 5; - const uint32_t kFive10 = kFive9 * 5; - const uint32_t kFive11 = kFive10 * 5; - const uint32_t kFive12 = kFive11 * 5; - const uint32_t kFive13 = kFive12 * 5; - const uint32_t kFive1_to_12[] = - { kFive1, kFive2, kFive3, kFive4, kFive5, kFive6, - kFive7, kFive8, kFive9, kFive10, kFive11, kFive12 }; - - ASSERT(exponent >= 0); - if (exponent == 0) return; - if (used_digits_ == 0) return; - - // We shift by exponent at the end just before returning. - int remaining_exponent = exponent; - while (remaining_exponent >= 27) { - MultiplyByUInt64(kFive27); - remaining_exponent -= 27; - } - while (remaining_exponent >= 13) { - MultiplyByUInt32(kFive13); - remaining_exponent -= 13; - } - if (remaining_exponent > 0) { - MultiplyByUInt32(kFive1_to_12[remaining_exponent - 1]); - } - Shifxleft(exponent); + bigits_[i] = static_cast(accumulator) & kBigitMask; + accumulator >>= kBigitSize; + } + for (int i = used_digits_; i < product_length; ++i) { + int bigit_index1 = used_digits_ - 1; + int bigit_index2 = i - bigit_index1; + // Invariant: sum of both indices is again equal to i. + // Inner loop runs 0 times on last iteration, emptying accumulator. + while (bigit_index2 < used_digits_) { + Chunk chunk1 = bigits_[copy_offset + bigit_index1]; + Chunk chunk2 = bigits_[copy_offset + bigit_index2]; + accumulator += static_cast(chunk1) * chunk2; + bigit_index1--; + bigit_index2++; } - - - void Bignum::Square() { - ASSERT(IsClamped()); - int product_length = 2 * used_digits_; - EnsureCapacity(product_length); - - // Comba multiplication: compute each column separately. - // Example: r = a2a1a0 * b2b1b0. - // r = 1 * a0b0 + - // 10 * (a1b0 + a0b1) + - // 100 * (a2b0 + a1b1 + a0b2) + - // 1000 * (a2b1 + a1b2) + - // 10000 * a2b2 - // - // In the worst case we have to accumulate nb-digits products of digit*digit. - // - // Assert that the additional number of bits in a DoubleChunk are enough to - // sum up used_digits of Bigit*Bigit. - if ((1 << (2 * (kChunkSize - kBigitSize))) <= used_digits_) { - UNIMPLEMENTED(); - } - DoubleChunk accumulator = 0; - // First shift the digits so we don't overwrite them. - int copy_offset = used_digits_; - for (int i = 0; i < used_digits_; ++i) { - bigits_[copy_offset + i] = bigits_[i]; - } - // We have two loops to avoid some 'if's in the loop. - for (int i = 0; i < used_digits_; ++i) { - // Process temporary digit i with power i. - // The sum of the two indices must be equal to i. - int bigit_index1 = i; - int bigit_index2 = 0; - // Sum all of the sub-products. - while (bigit_index1 >= 0) { - Chunk chunk1 = bigits_[copy_offset + bigit_index1]; - Chunk chunk2 = bigits_[copy_offset + bigit_index2]; - accumulator += static_cast(chunk1) * chunk2; - bigit_index1--; - bigit_index2++; - } - bigits_[i] = static_cast(accumulator) & kBigitMask; - accumulator >>= kBigitSize; - } - for (int i = used_digits_; i < product_length; ++i) { - int bigit_index1 = used_digits_ - 1; - int bigit_index2 = i - bigit_index1; - // Invariant: sum of both indices is again equal to i. - // Inner loop runs 0 times on last iteration, emptying accumulator. - while (bigit_index2 < used_digits_) { - Chunk chunk1 = bigits_[copy_offset + bigit_index1]; - Chunk chunk2 = bigits_[copy_offset + bigit_index2]; - accumulator += static_cast(chunk1) * chunk2; - bigit_index1--; - bigit_index2++; - } - // The overwritten bigits_[i] will never be read in further loop iterations, - // because bigit_index1 and bigit_index2 are always greater - // than i - used_digits_. - bigits_[i] = static_cast(accumulator) & kBigitMask; - accumulator >>= kBigitSize; - } - // Since the result was guaranteed to lie inside the number the - // accumulator must be 0 now. - ASSERT(accumulator == 0); - - // Don't forget to update the used_digits and the exponent. - used_digits_ = product_length; - exponent_ *= 2; - Clamp(); + // The overwritten bigits_[i] will never be read in further loop iterations, + // because bigit_index1 and bigit_index2 are always greater + // than i - used_digits_. + bigits_[i] = static_cast(accumulator) & kBigitMask; + accumulator >>= kBigitSize; + } + // Since the result was guaranteed to lie inside the number the + // accumulator must be 0 now. + ASSERT(accumulator == 0); + + // Don't forget to update the used_digits and the exponent. + used_digits_ = product_length; + exponent_ *= 2; + Clamp(); +} + +void Bignum::AssignPowerUInt16(uint16_t base, int power_exponent) { + ASSERT(base != 0); + ASSERT(power_exponent >= 0); + if (power_exponent == 0) { + AssignUInt16(1); + return; + } + Zero(); + int shifts = 0; + // We expect base to be in range 2-32, and most often to be 10. + // It does not make much sense to implement different algorithms for counting + // the bits. + while ((base & 1) == 0) { + base >>= 1; + shifts++; + } + int bit_size = 0; + int tmp_base = base; + while (tmp_base != 0) { + tmp_base >>= 1; + bit_size++; + } + int final_size = bit_size * power_exponent; + // 1 extra bigit for the shifting, and one for rounded final_size. + EnsureCapacity(final_size / kBigitSize + 2); + + // Left to Right exponentiation. + int mask = 1; + while (power_exponent >= mask) + mask <<= 1; + + // The mask is now pointing to the bit above the most significant 1-bit of + // power_exponent. + // Get rid of first 1-bit; + mask >>= 2; + uint64_t this_value = base; + + bool delayed_multipliciation = false; + const uint64_t max_32bits = 0xFFFFFFFF; + while (mask != 0 && this_value <= max_32bits) { + this_value = this_value * this_value; + // Verify that there is enough space in this_value to perform the + // multiplication. The first bit_size bits must be 0. + if ((power_exponent & mask) != 0) { + uint64_t base_bits_mask = + ~((static_cast(1) << (64 - bit_size)) - 1); + bool high_bits_zero = (this_value & base_bits_mask) == 0; + if (high_bits_zero) { + this_value *= base; + } else { + delayed_multipliciation = true; + } } - - - void Bignum::AssignPowerUInt16(uint16_t base, int power_exponent) { - ASSERT(base != 0); - ASSERT(power_exponent >= 0); - if (power_exponent == 0) { - AssignUInt16(1); - return; - } - Zero(); - int shifts = 0; - // We expect base to be in range 2-32, and most often to be 10. - // It does not make much sense to implement different algorithms for counting - // the bits. - while ((base & 1) == 0) { - base >>= 1; - shifts++; - } - int bit_size = 0; - int tmp_base = base; - while (tmp_base != 0) { - tmp_base >>= 1; - bit_size++; - } - int final_size = bit_size * power_exponent; - // 1 extra bigit for the shifting, and one for rounded final_size. - EnsureCapacity(final_size / kBigitSize + 2); - - // Left to Right exponentiation. - int mask = 1; - while (power_exponent >= mask) mask <<= 1; - - // The mask is now pointing to the bit above the most significant 1-bit of - // power_exponent. - // Get rid of first 1-bit; - mask >>= 2; - uint64_t this_value = base; - - bool delayed_multipliciation = false; - const uint64_t max_32bits = 0xFFFFFFFF; - while (mask != 0 && this_value <= max_32bits) { - this_value = this_value * this_value; - // Verify that there is enough space in this_value to perform the - // multiplication. The first bit_size bits must be 0. - if ((power_exponent & mask) != 0) { - uint64_t base_bits_mask = - ~((static_cast(1) << (64 - bit_size)) - 1); - bool high_bits_zero = (this_value & base_bits_mask) == 0; - if (high_bits_zero) { - this_value *= base; - } else { - delayed_multipliciation = true; - } - } - mask >>= 1; - } - AssignUInt64(this_value); - if (delayed_multipliciation) { - MultiplyByUInt32(base); - } - - // Now do the same thing as a bignum. - while (mask != 0) { - Square(); - if ((power_exponent & mask) != 0) { - MultiplyByUInt32(base); - } - mask >>= 1; - } - - // And finally add the saved shifts. - Shifxleft(shifts * power_exponent); - } - - - // Precondition: this/other < 16bit. - uint16_t Bignum::DivideModuloIntBignum(const Bignum& other) { - ASSERT(IsClamped()); - ASSERT(other.IsClamped()); - ASSERT(other.used_digits_ > 0); - - // Easy case: if we have less digits than the divisor than the result is 0. - // Note: this handles the case where this == 0, too. - if (BigitLength() < other.BigitLength()) { - return 0; - } - - Align(other); - - uint16_t result = 0; - - // Start by removing multiples of 'other' until both numbers have the same - // number of digits. - while (BigitLength() > other.BigitLength()) { - // This naive approach is extremely inefficient if the this divided other - // might be big. This function is implemented for doubleToString where - // the result should be small (less than 10). - ASSERT(other.bigits_[other.used_digits_ - 1] >= ((1 << kBigitSize) / 16)); - // Remove the multiples of the first digit. - // Example this = 23 and other equals 9. -> Remove 2 multiples. - result += bigits_[used_digits_ - 1]; - SubtractTimes(other, bigits_[used_digits_ - 1]); - } - - ASSERT(BigitLength() == other.BigitLength()); - - // Both bignums are at the same length now. - // Since other has more than 0 digits we know that the access to - // bigits_[used_digits_ - 1] is safe. - Chunk this_bigit = bigits_[used_digits_ - 1]; - Chunk other_bigit = other.bigits_[other.used_digits_ - 1]; - - if (other.used_digits_ == 1) { - // Shortcut for easy (and common) case. - int quotient = this_bigit / other_bigit; - bigits_[used_digits_ - 1] = this_bigit - other_bigit * quotient; - result += quotient; - Clamp(); - return result; - } - - int division_estimate = this_bigit / (other_bigit + 1); - result += division_estimate; - SubtractTimes(other, division_estimate); - - if (other_bigit * (division_estimate + 1) > this_bigit) { - // No need to even try to subtract. Even if other's remaining digits were 0 - // another subtraction would be too much. - return result; - } - - while (LessEqual(other, *this)) { - SubtractBignum(other); - result++; - } - return result; + mask >>= 1; + } + AssignUInt64(this_value); + if (delayed_multipliciation) { + MultiplyByUInt32(base); + } + + // Now do the same thing as a bignum. + while (mask != 0) { + Square(); + if ((power_exponent & mask) != 0) { + MultiplyByUInt32(base); } - - - template - static int SizeInHexChars(S number) { - ASSERT(number > 0); - int result = 0; - while (number != 0) { - number >>= 4; - result++; - } - return result; - } - - - static char HexCharOfValue(int value) { - ASSERT(0 <= value && value <= 16); - if (value < 10) return value + '0'; - return value - 10 + 'A'; - } - - - bool Bignum::ToHexString(char* buffer, int buffer_size) const { - ASSERT(IsClamped()); - // Each bigit must be printable as separate hex-character. - ASSERT(kBigitSize % 4 == 0); - const int kHexCharsPerBigit = kBigitSize / 4; - - if (used_digits_ == 0) { - if (buffer_size < 2) return false; - buffer[0] = '0'; - buffer[1] = '\0'; - return true; - } - // We add 1 for the terminating '\0' character. - int needed_chars = (BigitLength() - 1) * kHexCharsPerBigit + - SizeInHexChars(bigits_[used_digits_ - 1]) + 1; - if (needed_chars > buffer_size) return false; - int string_index = needed_chars - 1; - buffer[string_index--] = '\0'; - for (int i = 0; i < exponent_; ++i) { - for (int j = 0; j < kHexCharsPerBigit; ++j) { - buffer[string_index--] = '0'; - } - } - for (int i = 0; i < used_digits_ - 1; ++i) { - Chunk current_bigit = bigits_[i]; - for (int j = 0; j < kHexCharsPerBigit; ++j) { - buffer[string_index--] = HexCharOfValue(current_bigit & 0xF); - current_bigit >>= 4; - } - } - // And finally the last bigit. - Chunk most_significant_bigit = bigits_[used_digits_ - 1]; - while (most_significant_bigit != 0) { - buffer[string_index--] = HexCharOfValue(most_significant_bigit & 0xF); - most_significant_bigit >>= 4; - } - return true; - } - - - Bignum::Chunk Bignum::BigitAt(int index) const { - if (index >= BigitLength()) return 0; - if (index < exponent_) return 0; - return bigits_[index - exponent_]; + mask >>= 1; + } + + // And finally add the saved shifts. + Shifxleft(shifts * power_exponent); +} + +// Precondition: this/other < 16bit. +uint16_t Bignum::DivideModuloIntBignum(const Bignum& other) { + ASSERT(IsClamped()); + ASSERT(other.IsClamped()); + ASSERT(other.used_digits_ > 0); + + // Easy case: if we have less digits than the divisor than the result is 0. + // Note: this handles the case where this == 0, too. + if (BigitLength() < other.BigitLength()) { + return 0; + } + + Align(other); + + uint16_t result = 0; + + // Start by removing multiples of 'other' until both numbers have the same + // number of digits. + while (BigitLength() > other.BigitLength()) { + // This naive approach is extremely inefficient if the this divided other + // might be big. This function is implemented for doubleToString where + // the result should be small (less than 10). + ASSERT(other.bigits_[other.used_digits_ - 1] >= ((1 << kBigitSize) / 16)); + // Remove the multiples of the first digit. + // Example this = 23 and other equals 9. -> Remove 2 multiples. + result += bigits_[used_digits_ - 1]; + SubtractTimes(other, bigits_[used_digits_ - 1]); + } + + ASSERT(BigitLength() == other.BigitLength()); + + // Both bignums are at the same length now. + // Since other has more than 0 digits we know that the access to + // bigits_[used_digits_ - 1] is safe. + Chunk this_bigit = bigits_[used_digits_ - 1]; + Chunk other_bigit = other.bigits_[other.used_digits_ - 1]; + + if (other.used_digits_ == 1) { + // Shortcut for easy (and common) case. + int quotient = this_bigit / other_bigit; + bigits_[used_digits_ - 1] = this_bigit - other_bigit * quotient; + result += quotient; + Clamp(); + return result; + } + + int division_estimate = this_bigit / (other_bigit + 1); + result += division_estimate; + SubtractTimes(other, division_estimate); + + if (other_bigit * (division_estimate + 1) > this_bigit) { + // No need to even try to subtract. Even if other's remaining digits were 0 + // another subtraction would be too much. + return result; + } + + while (LessEqual(other, *this)) { + SubtractBignum(other); + result++; + } + return result; +} + +template +static int SizeInHexChars(S number) { + ASSERT(number > 0); + int result = 0; + while (number != 0) { + number >>= 4; + result++; + } + return result; +} + +static char HexCharOfValue(int value) { + ASSERT(0 <= value && value <= 16); + if (value < 10) + return value + '0'; + return value - 10 + 'A'; +} + +bool Bignum::ToHexString(char* buffer, int buffer_size) const { + ASSERT(IsClamped()); + // Each bigit must be printable as separate hex-character. + ASSERT(kBigitSize % 4 == 0); + const int kHexCharsPerBigit = kBigitSize / 4; + + if (used_digits_ == 0) { + if (buffer_size < 2) + return false; + buffer[0] = '0'; + buffer[1] = '\0'; + return true; + } + // We add 1 for the terminating '\0' character. + int needed_chars = (BigitLength() - 1) * kHexCharsPerBigit + + SizeInHexChars(bigits_[used_digits_ - 1]) + 1; + if (needed_chars > buffer_size) + return false; + int string_index = needed_chars - 1; + buffer[string_index--] = '\0'; + for (int i = 0; i < exponent_; ++i) { + for (int j = 0; j < kHexCharsPerBigit; ++j) { + buffer[string_index--] = '0'; } - - - int Bignum::Compare(const Bignum& a, const Bignum& b) { - ASSERT(a.IsClamped()); - ASSERT(b.IsClamped()); - int bigit_length_a = a.BigitLength(); - int bigit_length_b = b.BigitLength(); - if (bigit_length_a < bigit_length_b) return -1; - if (bigit_length_a > bigit_length_b) return +1; - for (int i = bigit_length_a - 1; i >= Min(a.exponent_, b.exponent_); --i) { - Chunk bigit_a = a.BigitAt(i); - Chunk bigit_b = b.BigitAt(i); - if (bigit_a < bigit_b) return -1; - if (bigit_a > bigit_b) return +1; - // Otherwise they are equal up to this digit. Try the next digit. - } - return 0; + } + for (int i = 0; i < used_digits_ - 1; ++i) { + Chunk current_bigit = bigits_[i]; + for (int j = 0; j < kHexCharsPerBigit; ++j) { + buffer[string_index--] = HexCharOfValue(current_bigit & 0xF); + current_bigit >>= 4; } - - - int Bignum::PlusCompare(const Bignum& a, const Bignum& b, const Bignum& c) { - ASSERT(a.IsClamped()); - ASSERT(b.IsClamped()); - ASSERT(c.IsClamped()); - if (a.BigitLength() < b.BigitLength()) { - return PlusCompare(b, a, c); - } - if (a.BigitLength() + 1 < c.BigitLength()) return -1; - if (a.BigitLength() > c.BigitLength()) return +1; - // The exponent encodes 0-bigits. So if there are more 0-digits in 'a' than - // 'b' has digits, then the bigit-length of 'a'+'b' must be equal to the one - // of 'a'. - if (a.exponent_ >= b.BigitLength() && a.BigitLength() < c.BigitLength()) { - return -1; - } - - Chunk borrow = 0; - // Starting at min_exponent all digits are == 0. So no need to compare them. - int min_exponent = Min(Min(a.exponent_, b.exponent_), c.exponent_); - for (int i = c.BigitLength() - 1; i >= min_exponent; --i) { - Chunk chunk_a = a.BigitAt(i); - Chunk chunk_b = b.BigitAt(i); - Chunk chunk_c = c.BigitAt(i); - Chunk sum = chunk_a + chunk_b; - if (sum > chunk_c + borrow) { - return +1; - } else { - borrow = chunk_c + borrow - sum; - if (borrow > 1) return -1; - borrow <<= kBigitSize; - } - } - if (borrow == 0) return 0; + } + // And finally the last bigit. + Chunk most_significant_bigit = bigits_[used_digits_ - 1]; + while (most_significant_bigit != 0) { + buffer[string_index--] = HexCharOfValue(most_significant_bigit & 0xF); + most_significant_bigit >>= 4; + } + return true; +} + +Bignum::Chunk Bignum::BigitAt(int index) const { + if (index >= BigitLength()) + return 0; + if (index < exponent_) + return 0; + return bigits_[index - exponent_]; +} + +int Bignum::Compare(const Bignum& a, const Bignum& b) { + ASSERT(a.IsClamped()); + ASSERT(b.IsClamped()); + int bigit_length_a = a.BigitLength(); + int bigit_length_b = b.BigitLength(); + if (bigit_length_a < bigit_length_b) + return -1; + if (bigit_length_a > bigit_length_b) + return +1; + for (int i = bigit_length_a - 1; i >= Min(a.exponent_, b.exponent_); --i) { + Chunk bigit_a = a.BigitAt(i); + Chunk bigit_b = b.BigitAt(i); + if (bigit_a < bigit_b) + return -1; + if (bigit_a > bigit_b) + return +1; + // Otherwise they are equal up to this digit. Try the next digit. + } + return 0; +} + +int Bignum::PlusCompare(const Bignum& a, const Bignum& b, const Bignum& c) { + ASSERT(a.IsClamped()); + ASSERT(b.IsClamped()); + ASSERT(c.IsClamped()); + if (a.BigitLength() < b.BigitLength()) { + return PlusCompare(b, a, c); + } + if (a.BigitLength() + 1 < c.BigitLength()) + return -1; + if (a.BigitLength() > c.BigitLength()) + return +1; + // The exponent encodes 0-bigits. So if there are more 0-digits in 'a' than + // 'b' has digits, then the bigit-length of 'a'+'b' must be equal to the one + // of 'a'. + if (a.exponent_ >= b.BigitLength() && a.BigitLength() < c.BigitLength()) { + return -1; + } + + Chunk borrow = 0; + // Starting at min_exponent all digits are == 0. So no need to compare them. + int min_exponent = Min(Min(a.exponent_, b.exponent_), c.exponent_); + for (int i = c.BigitLength() - 1; i >= min_exponent; --i) { + Chunk chunk_a = a.BigitAt(i); + Chunk chunk_b = b.BigitAt(i); + Chunk chunk_c = c.BigitAt(i); + Chunk sum = chunk_a + chunk_b; + if (sum > chunk_c + borrow) { + return +1; + } else { + borrow = chunk_c + borrow - sum; + if (borrow > 1) return -1; + borrow <<= kBigitSize; } - - - void Bignum::Clamp() { - while (used_digits_ > 0 && bigits_[used_digits_ - 1] == 0) { - used_digits_--; - } - if (used_digits_ == 0) { - // Zero. - exponent_ = 0; - } + } + if (borrow == 0) + return 0; + return -1; +} + +void Bignum::Clamp() { + while (used_digits_ > 0 && bigits_[used_digits_ - 1] == 0) { + used_digits_--; + } + if (used_digits_ == 0) { + // Zero. + exponent_ = 0; + } +} + +bool Bignum::IsClamped() const { + return used_digits_ == 0 || bigits_[used_digits_ - 1] != 0; +} + +void Bignum::Zero() { + for (int i = 0; i < used_digits_; ++i) { + bigits_[i] = 0; + } + used_digits_ = 0; + exponent_ = 0; +} + +void Bignum::Align(const Bignum& other) { + if (exponent_ > other.exponent_) { + // If "X" represents a "hidden" digit (by the exponent) then we are in the + // following case (a == this, b == other): + // a: aaaaaaXXXX or a: aaaaaXXX + // b: bbbbbbX b: bbbbbbbbXX + // We replace some of the hidden digits (X) of a with 0 digits. + // a: aaaaaa000X or a: aaaaa0XX + int zero_digits = exponent_ - other.exponent_; + EnsureCapacity(used_digits_ + zero_digits); + for (int i = used_digits_ - 1; i >= 0; --i) { + bigits_[i + zero_digits] = bigits_[i]; } - - - bool Bignum::IsClamped() const { - return used_digits_ == 0 || bigits_[used_digits_ - 1] != 0; + for (int i = 0; i < zero_digits; ++i) { + bigits_[i] = 0; } - - - void Bignum::Zero() { - for (int i = 0; i < used_digits_; ++i) { - bigits_[i] = 0; - } - used_digits_ = 0; - exponent_ = 0; + used_digits_ += zero_digits; + exponent_ -= zero_digits; + ASSERT(used_digits_ >= 0); + ASSERT(exponent_ >= 0); + } +} + +void Bignum::BigitsShifxleft(int shift_amount) { + ASSERT(shift_amount < kBigitSize); + ASSERT(shift_amount >= 0); + Chunk carry = 0; + for (int i = 0; i < used_digits_; ++i) { + Chunk new_carry = bigits_[i] >> (kBigitSize - shift_amount); + bigits_[i] = ((bigits_[i] << shift_amount) + carry) & kBigitMask; + carry = new_carry; + } + if (carry != 0) { + bigits_[used_digits_] = carry; + used_digits_++; + } +} + +void Bignum::SubtractTimes(const Bignum& other, int factor) { + ASSERT(exponent_ <= other.exponent_); + if (factor < 3) { + for (int i = 0; i < factor; ++i) { + SubtractBignum(other); } - - - void Bignum::Align(const Bignum& other) { - if (exponent_ > other.exponent_) { - // If "X" represents a "hidden" digit (by the exponent) then we are in the - // following case (a == this, b == other): - // a: aaaaaaXXXX or a: aaaaaXXX - // b: bbbbbbX b: bbbbbbbbXX - // We replace some of the hidden digits (X) of a with 0 digits. - // a: aaaaaa000X or a: aaaaa0XX - int zero_digits = exponent_ - other.exponent_; - EnsureCapacity(used_digits_ + zero_digits); - for (int i = used_digits_ - 1; i >= 0; --i) { - bigits_[i + zero_digits] = bigits_[i]; - } - for (int i = 0; i < zero_digits; ++i) { - bigits_[i] = 0; - } - used_digits_ += zero_digits; - exponent_ -= zero_digits; - ASSERT(used_digits_ >= 0); - ASSERT(exponent_ >= 0); - } - } - - - void Bignum::BigitsShifxleft(int shift_amount) { - ASSERT(shift_amount < kBigitSize); - ASSERT(shift_amount >= 0); - Chunk carry = 0; - for (int i = 0; i < used_digits_; ++i) { - Chunk new_carry = bigits_[i] >> (kBigitSize - shift_amount); - bigits_[i] = ((bigits_[i] << shift_amount) + carry) & kBigitMask; - carry = new_carry; - } - if (carry != 0) { - bigits_[used_digits_] = carry; - used_digits_++; - } - } - - - void Bignum::SubtractTimes(const Bignum& other, int factor) { - ASSERT(exponent_ <= other.exponent_); - if (factor < 3) { - for (int i = 0; i < factor; ++i) { - SubtractBignum(other); - } - return; - } - Chunk borrow = 0; - int exponent_diff = other.exponent_ - exponent_; - for (int i = 0; i < other.used_digits_; ++i) { - DoubleChunk product = static_cast(factor) * other.bigits_[i]; - DoubleChunk remove = borrow + product; - Chunk difference = bigits_[i + exponent_diff] - ((uint32_t)remove & kBigitMask); - bigits_[i + exponent_diff] = difference & kBigitMask; - borrow = static_cast((difference >> (kChunkSize - 1)) + - (remove >> kBigitSize)); - } - for (int i = other.used_digits_ + exponent_diff; i < used_digits_; ++i) { - if (borrow == 0) return; - Chunk difference = bigits_[i] - borrow; - bigits_[i] = difference & kBigitMask; - borrow = difference >> (kChunkSize - 1); - ++i; - } - Clamp(); - } - + return; + } + Chunk borrow = 0; + int exponent_diff = other.exponent_ - exponent_; + for (int i = 0; i < other.used_digits_; ++i) { + DoubleChunk product = static_cast(factor) * other.bigits_[i]; + DoubleChunk remove = borrow + product; + Chunk difference = + bigits_[i + exponent_diff] - ((uint32_t)remove & kBigitMask); + bigits_[i + exponent_diff] = difference & kBigitMask; + borrow = static_cast((difference >> (kChunkSize - 1)) + + (remove >> kBigitSize)); + } + for (int i = other.used_digits_ + exponent_diff; i < used_digits_; ++i) { + if (borrow == 0) + return; + Chunk difference = bigits_[i] - borrow; + bigits_[i] = difference & kBigitMask; + borrow = difference >> (kChunkSize - 1); + ++i; + } + Clamp(); +} } // namespace double_conversion -} // namespace WTF +} // namespace WTF diff --git a/sky/engine/wtf/dtoa/bignum.h b/sky/engine/wtf/dtoa/bignum.h index c1a6d7bf9e6ee..f12728b342d55 100644 --- a/sky/engine/wtf/dtoa/bignum.h +++ b/sky/engine/wtf/dtoa/bignum.h @@ -34,112 +34,113 @@ namespace WTF { namespace double_conversion { - class Bignum { - public: - // 3584 = 128 * 28. We can represent 2^3584 > 10^1000 accurately. - // This bignum can encode much bigger numbers, since it contains an - // exponent. - static const int kMaxSignificantBits = 3584; - - Bignum(); - void AssignUInt16(uint16_t value); - void AssignUInt64(uint64_t value); - void AssignBignum(const Bignum& other); - - void AssignDecimalString(Vector value); - void AssignHexString(Vector value); - - void AssignPowerUInt16(uint16_t base, int exponent); - - void AddUInt16(uint16_t operand); - void AddUInt64(uint64_t operand); - void AddBignum(const Bignum& other); - // Precondition: this >= other. - void SubtractBignum(const Bignum& other); - - void Square(); - void Shifxleft(int shift_amount); - void MultiplyByUInt32(uint32_t factor); - void MultiplyByUInt64(uint64_t factor); - void MultiplyByPowerOfTen(int exponent); - void Times10() { return MultiplyByUInt32(10); } - // Pseudocode: - // int result = this / other; - // this = this % other; - // In the worst case this function is in O(this/other). - uint16_t DivideModuloIntBignum(const Bignum& other); - - bool ToHexString(char* buffer, int buffer_size) const; - - static int Compare(const Bignum& a, const Bignum& b); - static bool Equal(const Bignum& a, const Bignum& b) { - return Compare(a, b) == 0; - } - static bool LessEqual(const Bignum& a, const Bignum& b) { - return Compare(a, b) <= 0; - } - static bool Less(const Bignum& a, const Bignum& b) { - return Compare(a, b) < 0; - } - // Returns Compare(a + b, c); - static int PlusCompare(const Bignum& a, const Bignum& b, const Bignum& c); - // Returns a + b == c - static bool PlusEqual(const Bignum& a, const Bignum& b, const Bignum& c) { - return PlusCompare(a, b, c) == 0; - } - // Returns a + b <= c - static bool PlusLessEqual(const Bignum& a, const Bignum& b, const Bignum& c) { - return PlusCompare(a, b, c) <= 0; - } - // Returns a + b < c - static bool PlusLess(const Bignum& a, const Bignum& b, const Bignum& c) { - return PlusCompare(a, b, c) < 0; - } - private: - typedef uint32_t Chunk; - typedef uint64_t DoubleChunk; - - static const int kChunkSize = sizeof(Chunk) * 8; - static const int kDoubleChunkSize = sizeof(DoubleChunk) * 8; - // With bigit size of 28 we loose some bits, but a double still fits easily - // into two chunks, and more importantly we can use the Comba multiplication. - static const int kBigitSize = 28; - static const Chunk kBigitMask = (1 << kBigitSize) - 1; - // Every instance allocates kBigitLength chunks on the stack. Bignums cannot - // grow. There are no checks if the stack-allocated space is sufficient. - static const int kBigitCapacity = kMaxSignificantBits / kBigitSize; - - void EnsureCapacity(int size) { - if (size > kBigitCapacity) { - UNREACHABLE(); - } - } - void Align(const Bignum& other); - void Clamp(); - bool IsClamped() const; - void Zero(); - // Requires this to have enough capacity (no tests done). - // Updates used_digits_ if necessary. - // shift_amount must be < kBigitSize. - void BigitsShifxleft(int shift_amount); - // BigitLength includes the "hidden" digits encoded in the exponent. - int BigitLength() const { return used_digits_ + exponent_; } - Chunk BigitAt(int index) const; - void SubtractTimes(const Bignum& other, int factor); - - Chunk bigits_buffer_[kBigitCapacity]; - // A vector backed by bigits_buffer_. This way accesses to the array are - // checked for out-of-bounds errors. - Vector bigits_; - int used_digits_; - // The Bignum's value equals value(bigits_) * 2^(exponent_ * kBigitSize). - int exponent_; - - FXL_DISALLOW_COPY_AND_ASSIGN(Bignum); - }; +class Bignum { + public: + // 3584 = 128 * 28. We can represent 2^3584 > 10^1000 accurately. + // This bignum can encode much bigger numbers, since it contains an + // exponent. + static const int kMaxSignificantBits = 3584; + + Bignum(); + void AssignUInt16(uint16_t value); + void AssignUInt64(uint64_t value); + void AssignBignum(const Bignum& other); + + void AssignDecimalString(Vector value); + void AssignHexString(Vector value); + + void AssignPowerUInt16(uint16_t base, int exponent); + + void AddUInt16(uint16_t operand); + void AddUInt64(uint64_t operand); + void AddBignum(const Bignum& other); + // Precondition: this >= other. + void SubtractBignum(const Bignum& other); + + void Square(); + void Shifxleft(int shift_amount); + void MultiplyByUInt32(uint32_t factor); + void MultiplyByUInt64(uint64_t factor); + void MultiplyByPowerOfTen(int exponent); + void Times10() { return MultiplyByUInt32(10); } + // Pseudocode: + // int result = this / other; + // this = this % other; + // In the worst case this function is in O(this/other). + uint16_t DivideModuloIntBignum(const Bignum& other); + + bool ToHexString(char* buffer, int buffer_size) const; + + static int Compare(const Bignum& a, const Bignum& b); + static bool Equal(const Bignum& a, const Bignum& b) { + return Compare(a, b) == 0; + } + static bool LessEqual(const Bignum& a, const Bignum& b) { + return Compare(a, b) <= 0; + } + static bool Less(const Bignum& a, const Bignum& b) { + return Compare(a, b) < 0; + } + // Returns Compare(a + b, c); + static int PlusCompare(const Bignum& a, const Bignum& b, const Bignum& c); + // Returns a + b == c + static bool PlusEqual(const Bignum& a, const Bignum& b, const Bignum& c) { + return PlusCompare(a, b, c) == 0; + } + // Returns a + b <= c + static bool PlusLessEqual(const Bignum& a, const Bignum& b, const Bignum& c) { + return PlusCompare(a, b, c) <= 0; + } + // Returns a + b < c + static bool PlusLess(const Bignum& a, const Bignum& b, const Bignum& c) { + return PlusCompare(a, b, c) < 0; + } + + private: + typedef uint32_t Chunk; + typedef uint64_t DoubleChunk; + + static const int kChunkSize = sizeof(Chunk) * 8; + static const int kDoubleChunkSize = sizeof(DoubleChunk) * 8; + // With bigit size of 28 we loose some bits, but a double still fits easily + // into two chunks, and more importantly we can use the Comba multiplication. + static const int kBigitSize = 28; + static const Chunk kBigitMask = (1 << kBigitSize) - 1; + // Every instance allocates kBigitLength chunks on the stack. Bignums cannot + // grow. There are no checks if the stack-allocated space is sufficient. + static const int kBigitCapacity = kMaxSignificantBits / kBigitSize; + + void EnsureCapacity(int size) { + if (size > kBigitCapacity) { + UNREACHABLE(); + } + } + void Align(const Bignum& other); + void Clamp(); + bool IsClamped() const; + void Zero(); + // Requires this to have enough capacity (no tests done). + // Updates used_digits_ if necessary. + // shift_amount must be < kBigitSize. + void BigitsShifxleft(int shift_amount); + // BigitLength includes the "hidden" digits encoded in the exponent. + int BigitLength() const { return used_digits_ + exponent_; } + Chunk BigitAt(int index) const; + void SubtractTimes(const Bignum& other, int factor); + + Chunk bigits_buffer_[kBigitCapacity]; + // A vector backed by bigits_buffer_. This way accesses to the array are + // checked for out-of-bounds errors. + Vector bigits_; + int used_digits_; + // The Bignum's value equals value(bigits_) * 2^(exponent_ * kBigitSize). + int exponent_; + + FXL_DISALLOW_COPY_AND_ASSIGN(Bignum); +}; } // namespace double_conversion -} // namespace WTF +} // namespace WTF #endif // SKY_ENGINE_WTF_DTOA_BIGNUM_H_ diff --git a/sky/engine/wtf/dtoa/cached-powers.cc b/sky/engine/wtf/dtoa/cached-powers.cc index aeb10dc19e5e0..b3d490fdb5e95 100644 --- a/sky/engine/wtf/dtoa/cached-powers.cc +++ b/sky/engine/wtf/dtoa/cached-powers.cc @@ -25,10 +25,9 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include #include #include +#include #include "cached-powers.h" #include "flutter/sky/engine/wtf/dtoa/utils.h" @@ -37,159 +36,165 @@ namespace WTF { namespace double_conversion { - struct CachedPower { - uint64_t significand; - int16_t binary_exponent; - int16_t decimal_exponent; - }; +struct CachedPower { + uint64_t significand; + int16_t binary_exponent; + int16_t decimal_exponent; +}; - static const double kD_1_LOG2_10 = 0.30102999566398114; // 1 / lg(10) - static const CachedPower kCachedPowers[] = { - {UINT64_2PART_C(0xfa8fd5a0, 081c0288), -1220, -348}, - {UINT64_2PART_C(0xbaaee17f, a23ebf76), -1193, -340}, - {UINT64_2PART_C(0x8b16fb20, 3055ac76), -1166, -332}, - {UINT64_2PART_C(0xcf42894a, 5dce35ea), -1140, -324}, - {UINT64_2PART_C(0x9a6bb0aa, 55653b2d), -1113, -316}, - {UINT64_2PART_C(0xe61acf03, 3d1a45df), -1087, -308}, - {UINT64_2PART_C(0xab70fe17, c79ac6ca), -1060, -300}, - {UINT64_2PART_C(0xff77b1fc, bebcdc4f), -1034, -292}, - {UINT64_2PART_C(0xbe5691ef, 416bd60c), -1007, -284}, - {UINT64_2PART_C(0x8dd01fad, 907ffc3c), -980, -276}, - {UINT64_2PART_C(0xd3515c28, 31559a83), -954, -268}, - {UINT64_2PART_C(0x9d71ac8f, ada6c9b5), -927, -260}, - {UINT64_2PART_C(0xea9c2277, 23ee8bcb), -901, -252}, - {UINT64_2PART_C(0xaecc4991, 4078536d), -874, -244}, - {UINT64_2PART_C(0x823c1279, 5db6ce57), -847, -236}, - {UINT64_2PART_C(0xc2109436, 4dfb5637), -821, -228}, - {UINT64_2PART_C(0x9096ea6f, 3848984f), -794, -220}, - {UINT64_2PART_C(0xd77485cb, 25823ac7), -768, -212}, - {UINT64_2PART_C(0xa086cfcd, 97bf97f4), -741, -204}, - {UINT64_2PART_C(0xef340a98, 172aace5), -715, -196}, - {UINT64_2PART_C(0xb23867fb, 2a35b28e), -688, -188}, - {UINT64_2PART_C(0x84c8d4df, d2c63f3b), -661, -180}, - {UINT64_2PART_C(0xc5dd4427, 1ad3cdba), -635, -172}, - {UINT64_2PART_C(0x936b9fce, bb25c996), -608, -164}, - {UINT64_2PART_C(0xdbac6c24, 7d62a584), -582, -156}, - {UINT64_2PART_C(0xa3ab6658, 0d5fdaf6), -555, -148}, - {UINT64_2PART_C(0xf3e2f893, dec3f126), -529, -140}, - {UINT64_2PART_C(0xb5b5ada8, aaff80b8), -502, -132}, - {UINT64_2PART_C(0x87625f05, 6c7c4a8b), -475, -124}, - {UINT64_2PART_C(0xc9bcff60, 34c13053), -449, -116}, - {UINT64_2PART_C(0x964e858c, 91ba2655), -422, -108}, - {UINT64_2PART_C(0xdff97724, 70297ebd), -396, -100}, - {UINT64_2PART_C(0xa6dfbd9f, b8e5b88f), -369, -92}, - {UINT64_2PART_C(0xf8a95fcf, 88747d94), -343, -84}, - {UINT64_2PART_C(0xb9447093, 8fa89bcf), -316, -76}, - {UINT64_2PART_C(0x8a08f0f8, bf0f156b), -289, -68}, - {UINT64_2PART_C(0xcdb02555, 653131b6), -263, -60}, - {UINT64_2PART_C(0x993fe2c6, d07b7fac), -236, -52}, - {UINT64_2PART_C(0xe45c10c4, 2a2b3b06), -210, -44}, - {UINT64_2PART_C(0xaa242499, 697392d3), -183, -36}, - {UINT64_2PART_C(0xfd87b5f2, 8300ca0e), -157, -28}, - {UINT64_2PART_C(0xbce50864, 92111aeb), -130, -20}, - {UINT64_2PART_C(0x8cbccc09, 6f5088cc), -103, -12}, - {UINT64_2PART_C(0xd1b71758, e219652c), -77, -4}, - {UINT64_2PART_C(0x9c400000, 00000000), -50, 4}, - {UINT64_2PART_C(0xe8d4a510, 00000000), -24, 12}, - {UINT64_2PART_C(0xad78ebc5, ac620000), 3, 20}, - {UINT64_2PART_C(0x813f3978, f8940984), 30, 28}, - {UINT64_2PART_C(0xc097ce7b, c90715b3), 56, 36}, - {UINT64_2PART_C(0x8f7e32ce, 7bea5c70), 83, 44}, - {UINT64_2PART_C(0xd5d238a4, abe98068), 109, 52}, - {UINT64_2PART_C(0x9f4f2726, 179a2245), 136, 60}, - {UINT64_2PART_C(0xed63a231, d4c4fb27), 162, 68}, - {UINT64_2PART_C(0xb0de6538, 8cc8ada8), 189, 76}, - {UINT64_2PART_C(0x83c7088e, 1aab65db), 216, 84}, - {UINT64_2PART_C(0xc45d1df9, 42711d9a), 242, 92}, - {UINT64_2PART_C(0x924d692c, a61be758), 269, 100}, - {UINT64_2PART_C(0xda01ee64, 1a708dea), 295, 108}, - {UINT64_2PART_C(0xa26da399, 9aef774a), 322, 116}, - {UINT64_2PART_C(0xf209787b, b47d6b85), 348, 124}, - {UINT64_2PART_C(0xb454e4a1, 79dd1877), 375, 132}, - {UINT64_2PART_C(0x865b8692, 5b9bc5c2), 402, 140}, - {UINT64_2PART_C(0xc83553c5, c8965d3d), 428, 148}, - {UINT64_2PART_C(0x952ab45c, fa97a0b3), 455, 156}, - {UINT64_2PART_C(0xde469fbd, 99a05fe3), 481, 164}, - {UINT64_2PART_C(0xa59bc234, db398c25), 508, 172}, - {UINT64_2PART_C(0xf6c69a72, a3989f5c), 534, 180}, - {UINT64_2PART_C(0xb7dcbf53, 54e9bece), 561, 188}, - {UINT64_2PART_C(0x88fcf317, f22241e2), 588, 196}, - {UINT64_2PART_C(0xcc20ce9b, d35c78a5), 614, 204}, - {UINT64_2PART_C(0x98165af3, 7b2153df), 641, 212}, - {UINT64_2PART_C(0xe2a0b5dc, 971f303a), 667, 220}, - {UINT64_2PART_C(0xa8d9d153, 5ce3b396), 694, 228}, - {UINT64_2PART_C(0xfb9b7cd9, a4a7443c), 720, 236}, - {UINT64_2PART_C(0xbb764c4c, a7a44410), 747, 244}, - {UINT64_2PART_C(0x8bab8eef, b6409c1a), 774, 252}, - {UINT64_2PART_C(0xd01fef10, a657842c), 800, 260}, - {UINT64_2PART_C(0x9b10a4e5, e9913129), 827, 268}, - {UINT64_2PART_C(0xe7109bfb, a19c0c9d), 853, 276}, - {UINT64_2PART_C(0xac2820d9, 623bf429), 880, 284}, - {UINT64_2PART_C(0x80444b5e, 7aa7cf85), 907, 292}, - {UINT64_2PART_C(0xbf21e440, 03acdd2d), 933, 300}, - {UINT64_2PART_C(0x8e679c2f, 5e44ff8f), 960, 308}, - {UINT64_2PART_C(0xd433179d, 9c8cb841), 986, 316}, - {UINT64_2PART_C(0x9e19db92, b4e31ba9), 1013, 324}, - {UINT64_2PART_C(0xeb96bf6e, badf77d9), 1039, 332}, - {UINT64_2PART_C(0xaf87023b, 9bf0ee6b), 1066, 340}, - }; - static const int kCachedPowersOffset = 348; // -kCachedPowers[0].decimal_exponent +static const double kD_1_LOG2_10 = 0.30102999566398114; // 1 / lg(10) +static const CachedPower kCachedPowers[] = { + {UINT64_2PART_C(0xfa8fd5a0, 081c0288), -1220, -348}, + {UINT64_2PART_C(0xbaaee17f, a23ebf76), -1193, -340}, + {UINT64_2PART_C(0x8b16fb20, 3055ac76), -1166, -332}, + {UINT64_2PART_C(0xcf42894a, 5dce35ea), -1140, -324}, + {UINT64_2PART_C(0x9a6bb0aa, 55653b2d), -1113, -316}, + {UINT64_2PART_C(0xe61acf03, 3d1a45df), -1087, -308}, + {UINT64_2PART_C(0xab70fe17, c79ac6ca), -1060, -300}, + {UINT64_2PART_C(0xff77b1fc, bebcdc4f), -1034, -292}, + {UINT64_2PART_C(0xbe5691ef, 416bd60c), -1007, -284}, + {UINT64_2PART_C(0x8dd01fad, 907ffc3c), -980, -276}, + {UINT64_2PART_C(0xd3515c28, 31559a83), -954, -268}, + {UINT64_2PART_C(0x9d71ac8f, ada6c9b5), -927, -260}, + {UINT64_2PART_C(0xea9c2277, 23ee8bcb), -901, -252}, + {UINT64_2PART_C(0xaecc4991, 4078536d), -874, -244}, + {UINT64_2PART_C(0x823c1279, 5db6ce57), -847, -236}, + {UINT64_2PART_C(0xc2109436, 4dfb5637), -821, -228}, + {UINT64_2PART_C(0x9096ea6f, 3848984f), -794, -220}, + {UINT64_2PART_C(0xd77485cb, 25823ac7), -768, -212}, + {UINT64_2PART_C(0xa086cfcd, 97bf97f4), -741, -204}, + {UINT64_2PART_C(0xef340a98, 172aace5), -715, -196}, + {UINT64_2PART_C(0xb23867fb, 2a35b28e), -688, -188}, + {UINT64_2PART_C(0x84c8d4df, d2c63f3b), -661, -180}, + {UINT64_2PART_C(0xc5dd4427, 1ad3cdba), -635, -172}, + {UINT64_2PART_C(0x936b9fce, bb25c996), -608, -164}, + {UINT64_2PART_C(0xdbac6c24, 7d62a584), -582, -156}, + {UINT64_2PART_C(0xa3ab6658, 0d5fdaf6), -555, -148}, + {UINT64_2PART_C(0xf3e2f893, dec3f126), -529, -140}, + {UINT64_2PART_C(0xb5b5ada8, aaff80b8), -502, -132}, + {UINT64_2PART_C(0x87625f05, 6c7c4a8b), -475, -124}, + {UINT64_2PART_C(0xc9bcff60, 34c13053), -449, -116}, + {UINT64_2PART_C(0x964e858c, 91ba2655), -422, -108}, + {UINT64_2PART_C(0xdff97724, 70297ebd), -396, -100}, + {UINT64_2PART_C(0xa6dfbd9f, b8e5b88f), -369, -92}, + {UINT64_2PART_C(0xf8a95fcf, 88747d94), -343, -84}, + {UINT64_2PART_C(0xb9447093, 8fa89bcf), -316, -76}, + {UINT64_2PART_C(0x8a08f0f8, bf0f156b), -289, -68}, + {UINT64_2PART_C(0xcdb02555, 653131b6), -263, -60}, + {UINT64_2PART_C(0x993fe2c6, d07b7fac), -236, -52}, + {UINT64_2PART_C(0xe45c10c4, 2a2b3b06), -210, -44}, + {UINT64_2PART_C(0xaa242499, 697392d3), -183, -36}, + {UINT64_2PART_C(0xfd87b5f2, 8300ca0e), -157, -28}, + {UINT64_2PART_C(0xbce50864, 92111aeb), -130, -20}, + {UINT64_2PART_C(0x8cbccc09, 6f5088cc), -103, -12}, + {UINT64_2PART_C(0xd1b71758, e219652c), -77, -4}, + {UINT64_2PART_C(0x9c400000, 00000000), -50, 4}, + {UINT64_2PART_C(0xe8d4a510, 00000000), -24, 12}, + {UINT64_2PART_C(0xad78ebc5, ac620000), 3, 20}, + {UINT64_2PART_C(0x813f3978, f8940984), 30, 28}, + {UINT64_2PART_C(0xc097ce7b, c90715b3), 56, 36}, + {UINT64_2PART_C(0x8f7e32ce, 7bea5c70), 83, 44}, + {UINT64_2PART_C(0xd5d238a4, abe98068), 109, 52}, + {UINT64_2PART_C(0x9f4f2726, 179a2245), 136, 60}, + {UINT64_2PART_C(0xed63a231, d4c4fb27), 162, 68}, + {UINT64_2PART_C(0xb0de6538, 8cc8ada8), 189, 76}, + {UINT64_2PART_C(0x83c7088e, 1aab65db), 216, 84}, + {UINT64_2PART_C(0xc45d1df9, 42711d9a), 242, 92}, + {UINT64_2PART_C(0x924d692c, a61be758), 269, 100}, + {UINT64_2PART_C(0xda01ee64, 1a708dea), 295, 108}, + {UINT64_2PART_C(0xa26da399, 9aef774a), 322, 116}, + {UINT64_2PART_C(0xf209787b, b47d6b85), 348, 124}, + {UINT64_2PART_C(0xb454e4a1, 79dd1877), 375, 132}, + {UINT64_2PART_C(0x865b8692, 5b9bc5c2), 402, 140}, + {UINT64_2PART_C(0xc83553c5, c8965d3d), 428, 148}, + {UINT64_2PART_C(0x952ab45c, fa97a0b3), 455, 156}, + {UINT64_2PART_C(0xde469fbd, 99a05fe3), 481, 164}, + {UINT64_2PART_C(0xa59bc234, db398c25), 508, 172}, + {UINT64_2PART_C(0xf6c69a72, a3989f5c), 534, 180}, + {UINT64_2PART_C(0xb7dcbf53, 54e9bece), 561, 188}, + {UINT64_2PART_C(0x88fcf317, f22241e2), 588, 196}, + {UINT64_2PART_C(0xcc20ce9b, d35c78a5), 614, 204}, + {UINT64_2PART_C(0x98165af3, 7b2153df), 641, 212}, + {UINT64_2PART_C(0xe2a0b5dc, 971f303a), 667, 220}, + {UINT64_2PART_C(0xa8d9d153, 5ce3b396), 694, 228}, + {UINT64_2PART_C(0xfb9b7cd9, a4a7443c), 720, 236}, + {UINT64_2PART_C(0xbb764c4c, a7a44410), 747, 244}, + {UINT64_2PART_C(0x8bab8eef, b6409c1a), 774, 252}, + {UINT64_2PART_C(0xd01fef10, a657842c), 800, 260}, + {UINT64_2PART_C(0x9b10a4e5, e9913129), 827, 268}, + {UINT64_2PART_C(0xe7109bfb, a19c0c9d), 853, 276}, + {UINT64_2PART_C(0xac2820d9, 623bf429), 880, 284}, + {UINT64_2PART_C(0x80444b5e, 7aa7cf85), 907, 292}, + {UINT64_2PART_C(0xbf21e440, 03acdd2d), 933, 300}, + {UINT64_2PART_C(0x8e679c2f, 5e44ff8f), 960, 308}, + {UINT64_2PART_C(0xd433179d, 9c8cb841), 986, 316}, + {UINT64_2PART_C(0x9e19db92, b4e31ba9), 1013, 324}, + {UINT64_2PART_C(0xeb96bf6e, badf77d9), 1039, 332}, + {UINT64_2PART_C(0xaf87023b, 9bf0ee6b), 1066, 340}, +}; +static const int kCachedPowersOffset = + 348; // -kCachedPowers[0].decimal_exponent - const int PowersOfTenCache::kDecimalExponentDistance = 8; // kCachedPowers[1].decimal_exponent - kCachedPowers[0].decimal_exponent - const int PowersOfTenCache::kMinDecimalExponent = -348; // kCachedPowers[0].decimal_exponent - const int PowersOfTenCache::kMaxDecimalExponent = 340; // kCachedPowers[kCachedPowersLength - 1].decimal_exponent +const int PowersOfTenCache::kDecimalExponentDistance = + 8; // kCachedPowers[1].decimal_exponent - kCachedPowers[0].decimal_exponent +const int PowersOfTenCache::kMinDecimalExponent = + -348; // kCachedPowers[0].decimal_exponent +const int PowersOfTenCache::kMaxDecimalExponent = + 340; // kCachedPowers[kCachedPowersLength - 1].decimal_exponent #if ENABLE(ASSERT) - static const int kCachedPowersLength = ARRAY_SIZE(kCachedPowers); +static const int kCachedPowersLength = ARRAY_SIZE(kCachedPowers); - // Check that the static constants match the values in kCachedPowers. - static void validateStaticConstants() { - ASSERT(kCachedPowersOffset == -kCachedPowers[0].decimal_exponent); - ASSERT(PowersOfTenCache::kDecimalExponentDistance == (kCachedPowers[1].decimal_exponent - kCachedPowers[0].decimal_exponent)); - ASSERT(PowersOfTenCache::kMinDecimalExponent == kCachedPowers[0].decimal_exponent); - ASSERT(PowersOfTenCache::kMaxDecimalExponent == kCachedPowers[kCachedPowersLength - 1].decimal_exponent); - } +// Check that the static constants match the values in kCachedPowers. +static void validateStaticConstants() { + ASSERT(kCachedPowersOffset == -kCachedPowers[0].decimal_exponent); + ASSERT( + PowersOfTenCache::kDecimalExponentDistance == + (kCachedPowers[1].decimal_exponent - kCachedPowers[0].decimal_exponent)); + ASSERT(PowersOfTenCache::kMinDecimalExponent == + kCachedPowers[0].decimal_exponent); + ASSERT(PowersOfTenCache::kMaxDecimalExponent == + kCachedPowers[kCachedPowersLength - 1].decimal_exponent); +} #endif - void PowersOfTenCache::GetCachedPowerForBinaryExponentRange( - int min_exponent, - int max_exponent, - DiyFp* power, - int* decimal_exponent) { +void PowersOfTenCache::GetCachedPowerForBinaryExponentRange( + int min_exponent, + int max_exponent, + DiyFp* power, + int* decimal_exponent) { #if ENABLE(ASSERT) - validateStaticConstants(); + validateStaticConstants(); #endif - int kQ = DiyFp::kSignificandSize; - double k = ceil((min_exponent + kQ - 1) * kD_1_LOG2_10); - int foo = kCachedPowersOffset; - int index = - (foo + static_cast(k) - 1) / kDecimalExponentDistance + 1; - ASSERT(0 <= index && index < kCachedPowersLength); - CachedPower cached_power = kCachedPowers[index]; - ASSERT(min_exponent <= cached_power.binary_exponent); - ASSERT(cached_power.binary_exponent <= max_exponent); - *decimal_exponent = cached_power.decimal_exponent; - *power = DiyFp(cached_power.significand, cached_power.binary_exponent); - } - + int kQ = DiyFp::kSignificandSize; + double k = ceil((min_exponent + kQ - 1) * kD_1_LOG2_10); + int foo = kCachedPowersOffset; + int index = (foo + static_cast(k) - 1) / kDecimalExponentDistance + 1; + ASSERT(0 <= index && index < kCachedPowersLength); + CachedPower cached_power = kCachedPowers[index]; + ASSERT(min_exponent <= cached_power.binary_exponent); + ASSERT(cached_power.binary_exponent <= max_exponent); + *decimal_exponent = cached_power.decimal_exponent; + *power = DiyFp(cached_power.significand, cached_power.binary_exponent); +} - void PowersOfTenCache::GetCachedPowerForDecimalExponent(int requested_exponent, - DiyFp* power, - int* found_exponent) { - ASSERT(kMinDecimalExponent <= requested_exponent); - ASSERT(requested_exponent < kMaxDecimalExponent + kDecimalExponentDistance); +void PowersOfTenCache::GetCachedPowerForDecimalExponent(int requested_exponent, + DiyFp* power, + int* found_exponent) { + ASSERT(kMinDecimalExponent <= requested_exponent); + ASSERT(requested_exponent < kMaxDecimalExponent + kDecimalExponentDistance); #if ENABLE(ASSERT) - validateStaticConstants(); + validateStaticConstants(); #endif - int index = - (requested_exponent + kCachedPowersOffset) / kDecimalExponentDistance; - CachedPower cached_power = kCachedPowers[index]; - *power = DiyFp(cached_power.significand, cached_power.binary_exponent); - *found_exponent = cached_power.decimal_exponent; - ASSERT(*found_exponent <= requested_exponent); - ASSERT(requested_exponent < *found_exponent + kDecimalExponentDistance); - } + int index = + (requested_exponent + kCachedPowersOffset) / kDecimalExponentDistance; + CachedPower cached_power = kCachedPowers[index]; + *power = DiyFp(cached_power.significand, cached_power.binary_exponent); + *found_exponent = cached_power.decimal_exponent; + ASSERT(*found_exponent <= requested_exponent); + ASSERT(requested_exponent < *found_exponent + kDecimalExponentDistance); +} } // namespace double_conversion -} // namespace WTF +} // namespace WTF diff --git a/sky/engine/wtf/dtoa/cached-powers.h b/sky/engine/wtf/dtoa/cached-powers.h index 3bded35cbdcdf..663fdaeb21df3 100644 --- a/sky/engine/wtf/dtoa/cached-powers.h +++ b/sky/engine/wtf/dtoa/cached-powers.h @@ -34,34 +34,33 @@ namespace WTF { namespace double_conversion { - class PowersOfTenCache { - public: +class PowersOfTenCache { + public: + // Not all powers of ten are cached. The decimal exponent of two neighboring + // cached numbers will differ by kDecimalExponentDistance. + static const int kDecimalExponentDistance; - // Not all powers of ten are cached. The decimal exponent of two neighboring - // cached numbers will differ by kDecimalExponentDistance. - static const int kDecimalExponentDistance; + static const int kMinDecimalExponent; + static const int kMaxDecimalExponent; - static const int kMinDecimalExponent; - static const int kMaxDecimalExponent; + // Returns a cached power-of-ten with a binary exponent in the range + // [min_exponent; max_exponent] (boundaries included). + static void GetCachedPowerForBinaryExponentRange(int min_exponent, + int max_exponent, + DiyFp* power, + int* decimal_exponent); - // Returns a cached power-of-ten with a binary exponent in the range - // [min_exponent; max_exponent] (boundaries included). - static void GetCachedPowerForBinaryExponentRange(int min_exponent, - int max_exponent, - DiyFp* power, - int* decimal_exponent); - - // Returns a cached power of ten x ~= 10^k such that - // k <= decimal_exponent < k + kCachedPowersDecimalDistance. - // The given decimal_exponent must satisfy - // kMinDecimalExponent <= requested_exponent, and - // requested_exponent < kMaxDecimalExponent + kDecimalExponentDistance. - static void GetCachedPowerForDecimalExponent(int requested_exponent, - DiyFp* power, - int* found_exponent); - }; + // Returns a cached power of ten x ~= 10^k such that + // k <= decimal_exponent < k + kCachedPowersDecimalDistance. + // The given decimal_exponent must satisfy + // kMinDecimalExponent <= requested_exponent, and + // requested_exponent < kMaxDecimalExponent + kDecimalExponentDistance. + static void GetCachedPowerForDecimalExponent(int requested_exponent, + DiyFp* power, + int* found_exponent); +}; } // namespace double_conversion -} // namespace WTF +} // namespace WTF #endif // SKY_ENGINE_WTF_DTOA_CACHED_POWERS_H_ diff --git a/sky/engine/wtf/dtoa/diy-fp.cc b/sky/engine/wtf/dtoa/diy-fp.cc index f48526f03e382..ba9bd1081ab52 100644 --- a/sky/engine/wtf/dtoa/diy-fp.cc +++ b/sky/engine/wtf/dtoa/diy-fp.cc @@ -25,7 +25,6 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - #include "diy-fp.h" #include "flutter/sky/engine/wtf/dtoa/utils.h" @@ -33,29 +32,29 @@ namespace WTF { namespace double_conversion { - void DiyFp::Multiply(const DiyFp& other) { - // Simply "emulates" a 128 bit multiplication. - // However: the resulting number only contains 64 bits. The least - // significant 64 bits are only used for rounding the most significant 64 - // bits. - const uint64_t kM32 = 0xFFFFFFFFU; - uint64_t a = f_ >> 32; - uint64_t b = f_ & kM32; - uint64_t c = other.f_ >> 32; - uint64_t d = other.f_ & kM32; - uint64_t ac = a * c; - uint64_t bc = b * c; - uint64_t ad = a * d; - uint64_t bd = b * d; - uint64_t tmp = (bd >> 32) + (ad & kM32) + (bc & kM32); - // By adding 1U << 31 to tmp we round the final result. - // Halfway cases will be round up. - tmp += 1U << 31; - uint64_t result_f = ac + (ad >> 32) + (bc >> 32) + (tmp >> 32); - e_ += other.e_ + 64; - f_ = result_f; - } +void DiyFp::Multiply(const DiyFp& other) { + // Simply "emulates" a 128 bit multiplication. + // However: the resulting number only contains 64 bits. The least + // significant 64 bits are only used for rounding the most significant 64 + // bits. + const uint64_t kM32 = 0xFFFFFFFFU; + uint64_t a = f_ >> 32; + uint64_t b = f_ & kM32; + uint64_t c = other.f_ >> 32; + uint64_t d = other.f_ & kM32; + uint64_t ac = a * c; + uint64_t bc = b * c; + uint64_t ad = a * d; + uint64_t bd = b * d; + uint64_t tmp = (bd >> 32) + (ad & kM32) + (bc & kM32); + // By adding 1U << 31 to tmp we round the final result. + // Halfway cases will be round up. + tmp += 1U << 31; + uint64_t result_f = ac + (ad >> 32) + (bc >> 32) + (tmp >> 32); + e_ += other.e_ + 64; + f_ = result_f; +} } // namespace double_conversion -} // namespace WTF +} // namespace WTF diff --git a/sky/engine/wtf/dtoa/diy-fp.h b/sky/engine/wtf/dtoa/diy-fp.h index f670f8bb17b09..802c78e983725 100644 --- a/sky/engine/wtf/dtoa/diy-fp.h +++ b/sky/engine/wtf/dtoa/diy-fp.h @@ -34,89 +34,88 @@ namespace WTF { namespace double_conversion { - // This "Do It Yourself Floating Point" class implements a floating-point number - // with a uint64 significand and an int exponent. Normalized DiyFp numbers will - // have the most significant bit of the significand set. - // Multiplication and Subtraction do not normalize their results. - // DiyFp are not designed to contain special doubles (NaN and Infinity). - class DiyFp { - public: - static const int kSignificandSize = 64; - - DiyFp() : f_(0), e_(0) {} - DiyFp(uint64_t f, int e) : f_(f), e_(e) {} - - // this = this - other. - // The exponents of both numbers must be the same and the significand of this - // must be bigger than the significand of other. - // The result will not be normalized. - void Subtract(const DiyFp& other) { - ASSERT(e_ == other.e_); - ASSERT(f_ >= other.f_); - f_ -= other.f_; - } - - // Returns a - b. - // The exponents of both numbers must be the same and this must be bigger - // than other. The result will not be normalized. - static DiyFp Minus(const DiyFp& a, const DiyFp& b) { - DiyFp result = a; - result.Subtract(b); - return result; - } - - - // this = this * other. - void Multiply(const DiyFp& other); - - // returns a * b; - static DiyFp Times(const DiyFp& a, const DiyFp& b) { - DiyFp result = a; - result.Multiply(b); - return result; - } - - void Normalize() { - ASSERT(f_ != 0); - uint64_t f = f_; - int e = e_; - - // This method is mainly called for normalizing boundaries. In general - // boundaries need to be shifted by 10 bits. We thus optimize for this case. - const uint64_t k10MSBits = UINT64_2PART_C(0xFFC00000, 00000000); - while ((f & k10MSBits) == 0) { - f <<= 10; - e -= 10; - } - while ((f & kUint64MSB) == 0) { - f <<= 1; - e--; - } - f_ = f; - e_ = e; - } - - static DiyFp Normalize(const DiyFp& a) { - DiyFp result = a; - result.Normalize(); - return result; - } - - uint64_t f() const { return f_; } - int e() const { return e_; } - - void set_f(uint64_t new_value) { f_ = new_value; } - void set_e(int new_value) { e_ = new_value; } - - private: - static const uint64_t kUint64MSB = UINT64_2PART_C(0x80000000, 00000000); - - uint64_t f_; - int e_; - }; +// This "Do It Yourself Floating Point" class implements a floating-point number +// with a uint64 significand and an int exponent. Normalized DiyFp numbers will +// have the most significant bit of the significand set. +// Multiplication and Subtraction do not normalize their results. +// DiyFp are not designed to contain special doubles (NaN and Infinity). +class DiyFp { + public: + static const int kSignificandSize = 64; + + DiyFp() : f_(0), e_(0) {} + DiyFp(uint64_t f, int e) : f_(f), e_(e) {} + + // this = this - other. + // The exponents of both numbers must be the same and the significand of this + // must be bigger than the significand of other. + // The result will not be normalized. + void Subtract(const DiyFp& other) { + ASSERT(e_ == other.e_); + ASSERT(f_ >= other.f_); + f_ -= other.f_; + } + + // Returns a - b. + // The exponents of both numbers must be the same and this must be bigger + // than other. The result will not be normalized. + static DiyFp Minus(const DiyFp& a, const DiyFp& b) { + DiyFp result = a; + result.Subtract(b); + return result; + } + + // this = this * other. + void Multiply(const DiyFp& other); + + // returns a * b; + static DiyFp Times(const DiyFp& a, const DiyFp& b) { + DiyFp result = a; + result.Multiply(b); + return result; + } + + void Normalize() { + ASSERT(f_ != 0); + uint64_t f = f_; + int e = e_; + + // This method is mainly called for normalizing boundaries. In general + // boundaries need to be shifted by 10 bits. We thus optimize for this case. + const uint64_t k10MSBits = UINT64_2PART_C(0xFFC00000, 00000000); + while ((f & k10MSBits) == 0) { + f <<= 10; + e -= 10; + } + while ((f & kUint64MSB) == 0) { + f <<= 1; + e--; + } + f_ = f; + e_ = e; + } + + static DiyFp Normalize(const DiyFp& a) { + DiyFp result = a; + result.Normalize(); + return result; + } + + uint64_t f() const { return f_; } + int e() const { return e_; } + + void set_f(uint64_t new_value) { f_ = new_value; } + void set_e(int new_value) { e_ = new_value; } + + private: + static const uint64_t kUint64MSB = UINT64_2PART_C(0x80000000, 00000000); + + uint64_t f_; + int e_; +}; } // namespace double_conversion -} // namespace WTF +} // namespace WTF #endif // SKY_ENGINE_WTF_DTOA_DIY_FP_H_ diff --git a/sky/engine/wtf/dtoa/double-conversion.cc b/sky/engine/wtf/dtoa/double-conversion.cc index 0b3968efcd185..8095ca0cde3c6 100644 --- a/sky/engine/wtf/dtoa/double-conversion.cc +++ b/sky/engine/wtf/dtoa/double-conversion.cc @@ -25,16 +25,15 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - #include #include #include "double-conversion.h" #include "bignum-dtoa.h" -#include "flutter/sky/engine/wtf/dtoa/double.h" #include "fast-dtoa.h" #include "fixed-dtoa.h" +#include "flutter/sky/engine/wtf/dtoa/double.h" #include "flutter/sky/engine/wtf/dtoa/strtod.h" #include "flutter/sky/engine/wtf/dtoa/utils.h" @@ -42,564 +41,555 @@ namespace WTF { namespace double_conversion { - const DoubleToStringConverter& DoubleToStringConverter::EcmaScriptConverter() { - int flags = UNIQUE_ZERO | EMIT_POSITIVE_EXPONENT_SIGN; - static DoubleToStringConverter converter(flags, - "Infinity", - "NaN", - 'e', - -6, 21, - 6, 0); - return converter; +const DoubleToStringConverter& DoubleToStringConverter::EcmaScriptConverter() { + int flags = UNIQUE_ZERO | EMIT_POSITIVE_EXPONENT_SIGN; + static DoubleToStringConverter converter(flags, "Infinity", "NaN", 'e', -6, + 21, 6, 0); + return converter; +} + +bool DoubleToStringConverter::HandleSpecialValues( + double value, + StringBuilder* result_builder) const { + Double double_inspect(value); + if (double_inspect.IsInfinite()) { + if (infinity_symbol_ == NULL) + return false; + if (value < 0) { + result_builder->AddCharacter('-'); } - - - bool DoubleToStringConverter::HandleSpecialValues( - double value, - StringBuilder* result_builder) const { - Double double_inspect(value); - if (double_inspect.IsInfinite()) { - if (infinity_symbol_ == NULL) return false; - if (value < 0) { - result_builder->AddCharacter('-'); - } - result_builder->AddString(infinity_symbol_); - return true; - } - if (double_inspect.IsNan()) { - if (nan_symbol_ == NULL) return false; - result_builder->AddString(nan_symbol_); - return true; - } - return false; + result_builder->AddString(infinity_symbol_); + return true; + } + if (double_inspect.IsNan()) { + if (nan_symbol_ == NULL) + return false; + result_builder->AddString(nan_symbol_); + return true; + } + return false; +} + +void DoubleToStringConverter::CreateExponentialRepresentation( + const char* decimal_digits, + int length, + int exponent, + StringBuilder* result_builder) const { + ASSERT(length != 0); + result_builder->AddCharacter(decimal_digits[0]); + if (length != 1) { + result_builder->AddCharacter('.'); + result_builder->AddSubstring(&decimal_digits[1], length - 1); + } + result_builder->AddCharacter(exponent_character_); + if (exponent < 0) { + result_builder->AddCharacter('-'); + exponent = -exponent; + } else { + if ((flags_ & EMIT_POSITIVE_EXPONENT_SIGN) != 0) { + result_builder->AddCharacter('+'); } - - - void DoubleToStringConverter::CreateExponentialRepresentation( - const char* decimal_digits, - int length, - int exponent, - StringBuilder* result_builder) const { - ASSERT(length != 0); - result_builder->AddCharacter(decimal_digits[0]); - if (length != 1) { - result_builder->AddCharacter('.'); - result_builder->AddSubstring(&decimal_digits[1], length-1); - } - result_builder->AddCharacter(exponent_character_); - if (exponent < 0) { - result_builder->AddCharacter('-'); - exponent = -exponent; - } else { - if ((flags_ & EMIT_POSITIVE_EXPONENT_SIGN) != 0) { - result_builder->AddCharacter('+'); - } - } - if (exponent == 0) { - result_builder->AddCharacter('0'); - return; - } - ASSERT(exponent < 1e4); - const int kMaxExponentLength = 5; - char buffer[kMaxExponentLength + 1]; - int first_char_pos = kMaxExponentLength; - buffer[first_char_pos] = '\0'; - while (exponent > 0) { - buffer[--first_char_pos] = '0' + (exponent % 10); - exponent /= 10; - } - result_builder->AddSubstring(&buffer[first_char_pos], - kMaxExponentLength - first_char_pos); + } + if (exponent == 0) { + result_builder->AddCharacter('0'); + return; + } + ASSERT(exponent < 1e4); + const int kMaxExponentLength = 5; + char buffer[kMaxExponentLength + 1]; + int first_char_pos = kMaxExponentLength; + buffer[first_char_pos] = '\0'; + while (exponent > 0) { + buffer[--first_char_pos] = '0' + (exponent % 10); + exponent /= 10; + } + result_builder->AddSubstring(&buffer[first_char_pos], + kMaxExponentLength - first_char_pos); +} + +void DoubleToStringConverter::CreateDecimalRepresentation( + const char* decimal_digits, + int length, + int decimal_point, + int digits_after_point, + StringBuilder* result_builder) const { + // Create a representation that is padded with zeros if needed. + if (decimal_point <= 0) { + // "0.00000decimal_rep". + result_builder->AddCharacter('0'); + if (digits_after_point > 0) { + result_builder->AddCharacter('.'); + result_builder->AddPadding('0', -decimal_point); + ASSERT(length <= digits_after_point - (-decimal_point)); + result_builder->AddSubstring(decimal_digits, length); + int remaining_digits = digits_after_point - (-decimal_point) - length; + result_builder->AddPadding('0', remaining_digits); } - - - void DoubleToStringConverter::CreateDecimalRepresentation( - const char* decimal_digits, - int length, - int decimal_point, - int digits_after_point, - StringBuilder* result_builder) const { - // Create a representation that is padded with zeros if needed. - if (decimal_point <= 0) { - // "0.00000decimal_rep". - result_builder->AddCharacter('0'); - if (digits_after_point > 0) { - result_builder->AddCharacter('.'); - result_builder->AddPadding('0', -decimal_point); - ASSERT(length <= digits_after_point - (-decimal_point)); - result_builder->AddSubstring(decimal_digits, length); - int remaining_digits = digits_after_point - (-decimal_point) - length; - result_builder->AddPadding('0', remaining_digits); - } - } else if (decimal_point >= length) { - // "decimal_rep0000.00000" or "decimal_rep.0000" - result_builder->AddSubstring(decimal_digits, length); - result_builder->AddPadding('0', decimal_point - length); - if (digits_after_point > 0) { - result_builder->AddCharacter('.'); - result_builder->AddPadding('0', digits_after_point); - } - } else { - // "decima.l_rep000" - ASSERT(digits_after_point > 0); - result_builder->AddSubstring(decimal_digits, decimal_point); - result_builder->AddCharacter('.'); - ASSERT(length - decimal_point <= digits_after_point); - result_builder->AddSubstring(&decimal_digits[decimal_point], - length - decimal_point); - int remaining_digits = digits_after_point - (length - decimal_point); - result_builder->AddPadding('0', remaining_digits); - } - if (digits_after_point == 0) { - if ((flags_ & EMIT_TRAILING_DECIMAL_POINT) != 0) { - result_builder->AddCharacter('.'); - } - if ((flags_ & EMIT_TRAILING_ZERO_AFTER_POINT) != 0) { - result_builder->AddCharacter('0'); - } - } + } else if (decimal_point >= length) { + // "decimal_rep0000.00000" or "decimal_rep.0000" + result_builder->AddSubstring(decimal_digits, length); + result_builder->AddPadding('0', decimal_point - length); + if (digits_after_point > 0) { + result_builder->AddCharacter('.'); + result_builder->AddPadding('0', digits_after_point); } - - - bool DoubleToStringConverter::ToShortest(double value, - StringBuilder* result_builder) const { - if (Double(value).IsSpecial()) { - return HandleSpecialValues(value, result_builder); - } - - int decimal_point; - bool sign; - const int kDecimalRepCapacity = kBase10MaximalLength + 1; - char decimal_rep[kDecimalRepCapacity]; - int decimal_rep_length; - - DoubleToAscii(value, SHORTEST, 0, decimal_rep, kDecimalRepCapacity, - &sign, &decimal_rep_length, &decimal_point); - - bool unique_zero = (flags_ & UNIQUE_ZERO) != 0; - if (sign && (value != 0.0 || !unique_zero)) { - result_builder->AddCharacter('-'); - } - - int exponent = decimal_point - 1; - if ((decimal_in_shortest_low_ <= exponent) && - (exponent < decimal_in_shortest_high_)) { - CreateDecimalRepresentation(decimal_rep, decimal_rep_length, - decimal_point, - Max(0, decimal_rep_length - decimal_point), - result_builder); - } else { - CreateExponentialRepresentation(decimal_rep, decimal_rep_length, exponent, - result_builder); - } - return true; + } else { + // "decima.l_rep000" + ASSERT(digits_after_point > 0); + result_builder->AddSubstring(decimal_digits, decimal_point); + result_builder->AddCharacter('.'); + ASSERT(length - decimal_point <= digits_after_point); + result_builder->AddSubstring(&decimal_digits[decimal_point], + length - decimal_point); + int remaining_digits = digits_after_point - (length - decimal_point); + result_builder->AddPadding('0', remaining_digits); + } + if (digits_after_point == 0) { + if ((flags_ & EMIT_TRAILING_DECIMAL_POINT) != 0) { + result_builder->AddCharacter('.'); } - - - bool DoubleToStringConverter::ToFixed(double value, - int requested_digits, - StringBuilder* result_builder) const { - ASSERT(kMaxFixedDigitsBeforePoint == 60); - const double kFirstNonFixed = 1e60; - - if (Double(value).IsSpecial()) { - return HandleSpecialValues(value, result_builder); - } - - if (requested_digits > kMaxFixedDigitsAfterPoint) return false; - if (value >= kFirstNonFixed || value <= -kFirstNonFixed) return false; - - // Find a sufficiently precise decimal representation of n. - int decimal_point; - bool sign; - // Add space for the '\0' byte. - const int kDecimalRepCapacity = - kMaxFixedDigitsBeforePoint + kMaxFixedDigitsAfterPoint + 1; - char decimal_rep[kDecimalRepCapacity]; - int decimal_rep_length; - DoubleToAscii(value, FIXED, requested_digits, - decimal_rep, kDecimalRepCapacity, - &sign, &decimal_rep_length, &decimal_point); - - bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0); - if (sign && (value != 0.0 || !unique_zero)) { - result_builder->AddCharacter('-'); - } - - CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point, - requested_digits, result_builder); - return true; + if ((flags_ & EMIT_TRAILING_ZERO_AFTER_POINT) != 0) { + result_builder->AddCharacter('0'); } - - - bool DoubleToStringConverter::ToExponential( - double value, - int requested_digits, - StringBuilder* result_builder) const { - if (Double(value).IsSpecial()) { - return HandleSpecialValues(value, result_builder); - } - - if (requested_digits < -1) return false; - if (requested_digits > kMaxExponentialDigits) return false; - - int decimal_point; - bool sign; - // Add space for digit before the decimal point and the '\0' character. - const int kDecimalRepCapacity = kMaxExponentialDigits + 2; - ASSERT(kDecimalRepCapacity > kBase10MaximalLength); - char decimal_rep[kDecimalRepCapacity]; - int decimal_rep_length; - - if (requested_digits == -1) { - DoubleToAscii(value, SHORTEST, 0, - decimal_rep, kDecimalRepCapacity, - &sign, &decimal_rep_length, &decimal_point); - } else { - DoubleToAscii(value, PRECISION, requested_digits + 1, - decimal_rep, kDecimalRepCapacity, - &sign, &decimal_rep_length, &decimal_point); - ASSERT(decimal_rep_length <= requested_digits + 1); - - for (int i = decimal_rep_length; i < requested_digits + 1; ++i) { - decimal_rep[i] = '0'; - } - decimal_rep_length = requested_digits + 1; - } - - bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0); - if (sign && (value != 0.0 || !unique_zero)) { - result_builder->AddCharacter('-'); - } - - int exponent = decimal_point - 1; - CreateExponentialRepresentation(decimal_rep, - decimal_rep_length, - exponent, - result_builder); - return true; + } +} + +bool DoubleToStringConverter::ToShortest(double value, + StringBuilder* result_builder) const { + if (Double(value).IsSpecial()) { + return HandleSpecialValues(value, result_builder); + } + + int decimal_point; + bool sign; + const int kDecimalRepCapacity = kBase10MaximalLength + 1; + char decimal_rep[kDecimalRepCapacity]; + int decimal_rep_length; + + DoubleToAscii(value, SHORTEST, 0, decimal_rep, kDecimalRepCapacity, &sign, + &decimal_rep_length, &decimal_point); + + bool unique_zero = (flags_ & UNIQUE_ZERO) != 0; + if (sign && (value != 0.0 || !unique_zero)) { + result_builder->AddCharacter('-'); + } + + int exponent = decimal_point - 1; + if ((decimal_in_shortest_low_ <= exponent) && + (exponent < decimal_in_shortest_high_)) { + CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point, + Max(0, decimal_rep_length - decimal_point), + result_builder); + } else { + CreateExponentialRepresentation(decimal_rep, decimal_rep_length, exponent, + result_builder); + } + return true; +} + +bool DoubleToStringConverter::ToFixed(double value, + int requested_digits, + StringBuilder* result_builder) const { + ASSERT(kMaxFixedDigitsBeforePoint == 60); + const double kFirstNonFixed = 1e60; + + if (Double(value).IsSpecial()) { + return HandleSpecialValues(value, result_builder); + } + + if (requested_digits > kMaxFixedDigitsAfterPoint) + return false; + if (value >= kFirstNonFixed || value <= -kFirstNonFixed) + return false; + + // Find a sufficiently precise decimal representation of n. + int decimal_point; + bool sign; + // Add space for the '\0' byte. + const int kDecimalRepCapacity = + kMaxFixedDigitsBeforePoint + kMaxFixedDigitsAfterPoint + 1; + char decimal_rep[kDecimalRepCapacity]; + int decimal_rep_length; + DoubleToAscii(value, FIXED, requested_digits, decimal_rep, + kDecimalRepCapacity, &sign, &decimal_rep_length, + &decimal_point); + + bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0); + if (sign && (value != 0.0 || !unique_zero)) { + result_builder->AddCharacter('-'); + } + + CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point, + requested_digits, result_builder); + return true; +} + +bool DoubleToStringConverter::ToExponential( + double value, + int requested_digits, + StringBuilder* result_builder) const { + if (Double(value).IsSpecial()) { + return HandleSpecialValues(value, result_builder); + } + + if (requested_digits < -1) + return false; + if (requested_digits > kMaxExponentialDigits) + return false; + + int decimal_point; + bool sign; + // Add space for digit before the decimal point and the '\0' character. + const int kDecimalRepCapacity = kMaxExponentialDigits + 2; + ASSERT(kDecimalRepCapacity > kBase10MaximalLength); + char decimal_rep[kDecimalRepCapacity]; + int decimal_rep_length; + + if (requested_digits == -1) { + DoubleToAscii(value, SHORTEST, 0, decimal_rep, kDecimalRepCapacity, &sign, + &decimal_rep_length, &decimal_point); + } else { + DoubleToAscii(value, PRECISION, requested_digits + 1, decimal_rep, + kDecimalRepCapacity, &sign, &decimal_rep_length, + &decimal_point); + ASSERT(decimal_rep_length <= requested_digits + 1); + + for (int i = decimal_rep_length; i < requested_digits + 1; ++i) { + decimal_rep[i] = '0'; } - - - bool DoubleToStringConverter::ToPrecision(double value, - int precision, - StringBuilder* result_builder) const { - if (Double(value).IsSpecial()) { - return HandleSpecialValues(value, result_builder); - } - - if (precision < kMinPrecisionDigits || precision > kMaxPrecisionDigits) { - return false; - } - - // Find a sufficiently precise decimal representation of n. - int decimal_point; - bool sign; - // Add one for the terminating null character. - const int kDecimalRepCapacity = kMaxPrecisionDigits + 1; - char decimal_rep[kDecimalRepCapacity]; - int decimal_rep_length; - - DoubleToAscii(value, PRECISION, precision, - decimal_rep, kDecimalRepCapacity, - &sign, &decimal_rep_length, &decimal_point); - ASSERT(decimal_rep_length <= precision); - - bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0); - if (sign && (value != 0.0 || !unique_zero)) { - result_builder->AddCharacter('-'); - } - - // The exponent if we print the number as x.xxeyyy. That is with the - // decimal point after the first digit. - int exponent = decimal_point - 1; - - int extra_zero = ((flags_ & EMIT_TRAILING_ZERO_AFTER_POINT) != 0) ? 1 : 0; - if ((-decimal_point + 1 > max_leading_padding_zeroes_in_precision_mode_) || - (decimal_point - precision + extra_zero > - max_trailing_padding_zeroes_in_precision_mode_)) { - // Fill buffer to contain 'precision' digits. - // Usually the buffer is already at the correct length, but 'DoubleToAscii' - // is allowed to return less characters. - for (int i = decimal_rep_length; i < precision; ++i) { - decimal_rep[i] = '0'; - } - - CreateExponentialRepresentation(decimal_rep, - precision, - exponent, - result_builder); - } else { - CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point, - Max(0, precision - decimal_point), - result_builder); - } - return true; + decimal_rep_length = requested_digits + 1; + } + + bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0); + if (sign && (value != 0.0 || !unique_zero)) { + result_builder->AddCharacter('-'); + } + + int exponent = decimal_point - 1; + CreateExponentialRepresentation(decimal_rep, decimal_rep_length, exponent, + result_builder); + return true; +} + +bool DoubleToStringConverter::ToPrecision(double value, + int precision, + StringBuilder* result_builder) const { + if (Double(value).IsSpecial()) { + return HandleSpecialValues(value, result_builder); + } + + if (precision < kMinPrecisionDigits || precision > kMaxPrecisionDigits) { + return false; + } + + // Find a sufficiently precise decimal representation of n. + int decimal_point; + bool sign; + // Add one for the terminating null character. + const int kDecimalRepCapacity = kMaxPrecisionDigits + 1; + char decimal_rep[kDecimalRepCapacity]; + int decimal_rep_length; + + DoubleToAscii(value, PRECISION, precision, decimal_rep, kDecimalRepCapacity, + &sign, &decimal_rep_length, &decimal_point); + ASSERT(decimal_rep_length <= precision); + + bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0); + if (sign && (value != 0.0 || !unique_zero)) { + result_builder->AddCharacter('-'); + } + + // The exponent if we print the number as x.xxeyyy. That is with the + // decimal point after the first digit. + int exponent = decimal_point - 1; + + int extra_zero = ((flags_ & EMIT_TRAILING_ZERO_AFTER_POINT) != 0) ? 1 : 0; + if ((-decimal_point + 1 > max_leading_padding_zeroes_in_precision_mode_) || + (decimal_point - precision + extra_zero > + max_trailing_padding_zeroes_in_precision_mode_)) { + // Fill buffer to contain 'precision' digits. + // Usually the buffer is already at the correct length, but 'DoubleToAscii' + // is allowed to return less characters. + for (int i = decimal_rep_length; i < precision; ++i) { + decimal_rep[i] = '0'; } - - static BignumDtoaMode DtoaToBignumDtoaMode( - DoubleToStringConverter::DtoaMode dtoa_mode) { - switch (dtoa_mode) { - case DoubleToStringConverter::SHORTEST: return BIGNUM_DTOA_SHORTEST; - case DoubleToStringConverter::FIXED: return BIGNUM_DTOA_FIXED; - case DoubleToStringConverter::PRECISION: return BIGNUM_DTOA_PRECISION; - default: - UNREACHABLE(); - return BIGNUM_DTOA_SHORTEST; // To silence compiler. - } + CreateExponentialRepresentation(decimal_rep, precision, exponent, + result_builder); + } else { + CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point, + Max(0, precision - decimal_point), + result_builder); + } + return true; +} + +static BignumDtoaMode DtoaToBignumDtoaMode( + DoubleToStringConverter::DtoaMode dtoa_mode) { + switch (dtoa_mode) { + case DoubleToStringConverter::SHORTEST: + return BIGNUM_DTOA_SHORTEST; + case DoubleToStringConverter::FIXED: + return BIGNUM_DTOA_FIXED; + case DoubleToStringConverter::PRECISION: + return BIGNUM_DTOA_PRECISION; + default: + UNREACHABLE(); + return BIGNUM_DTOA_SHORTEST; // To silence compiler. + } +} + +void DoubleToStringConverter::DoubleToAscii(double v, + DtoaMode mode, + int requested_digits, + char* buffer, + int buffer_length, + bool* sign, + int* length, + int* point) { + Vector vector(buffer, buffer_length); + ASSERT(!Double(v).IsSpecial()); + ASSERT(mode == SHORTEST || requested_digits >= 0); + + if (Double(v).Sign() < 0) { + *sign = true; + v = -v; + } else { + *sign = false; + } + + if (mode == PRECISION && requested_digits == 0) { + vector[0] = '\0'; + *length = 0; + return; + } + + if (v == 0) { + vector[0] = '0'; + vector[1] = '\0'; + *length = 1; + *point = 1; + return; + } + + bool fast_worked; + switch (mode) { + case SHORTEST: + fast_worked = FastDtoa(v, FAST_DTOA_SHORTEST, 0, vector, length, point); + break; + case FIXED: + fast_worked = FastFixedDtoa(v, requested_digits, vector, length, point); + break; + case PRECISION: + fast_worked = FastDtoa(v, FAST_DTOA_PRECISION, requested_digits, vector, + length, point); + break; + default: + UNREACHABLE(); + fast_worked = false; + } + if (fast_worked) + return; + + // If the fast dtoa didn't succeed use the slower bignum version. + BignumDtoaMode bignum_mode = DtoaToBignumDtoaMode(mode); + BignumDtoa(v, bignum_mode, requested_digits, vector, length, point); + vector[*length] = '\0'; +} + +// Maximum number of significant digits in decimal representation. +// The longest possible double in decimal representation is +// (2^53 - 1) * 2 ^ -1074 that is (2 ^ 53 - 1) * 5 ^ 1074 / 10 ^ 1074 +// (768 digits). If we parse a number whose first digits are equal to a +// mean of 2 adjacent doubles (that could have up to 769 digits) the result +// must be rounded to the bigger one unless the tail consists of zeros, so +// we don't need to preserve all the digits. +const int kMaxSignificantDigits = 772; + +static double SignedZero(bool sign) { + return sign ? -0.0 : 0.0; +} + +double StringToDoubleConverter::StringToDouble( + const char* input, + size_t length, + size_t* processed_characters_count) { + const char* current = input; + const char* end = input + length; + + *processed_characters_count = 0; + + // To make sure that iterator dereferencing is valid the following + // convention is used: + // 1. Each '++current' statement is followed by check for equality to 'end'. + // 3. If 'current' becomes equal to 'end' the function returns or goes to + // 'parsing_done'. + // 4. 'current' is not dereferenced after the 'parsing_done' label. + // 5. Code before 'parsing_done' may rely on 'current != end'. + if (current == end) + return 0.0; + + // The longest form of simplified number is: "-.1eXXX\0". + const int kBufferSize = kMaxSignificantDigits + 10; + char buffer[kBufferSize]; // NOLINT: size is known at compile time. + int buffer_pos = 0; + + // Exponent will be adjusted if insignificant digits of the integer part + // or insignificant leading zeros of the fractional part are dropped. + int exponent = 0; + int significant_digits = 0; + int insignificant_digits = 0; + bool nonzero_digit_dropped = false; + bool sign = false; + + if (*current == '+' || *current == '-') { + sign = (*current == '-'); + ++current; + if (current == end) + return 0.0; + } + + bool leading_zero = false; + if (*current == '0') { + ++current; + if (current == end) { + *processed_characters_count = current - input; + return SignedZero(sign); } + leading_zero = true; - void DoubleToStringConverter::DoubleToAscii(double v, - DtoaMode mode, - int requested_digits, - char* buffer, - int buffer_length, - bool* sign, - int* length, - int* point) { - Vector vector(buffer, buffer_length); - ASSERT(!Double(v).IsSpecial()); - ASSERT(mode == SHORTEST || requested_digits >= 0); - - if (Double(v).Sign() < 0) { - *sign = true; - v = -v; - } else { - *sign = false; - } - - if (mode == PRECISION && requested_digits == 0) { - vector[0] = '\0'; - *length = 0; - return; - } - - if (v == 0) { - vector[0] = '0'; - vector[1] = '\0'; - *length = 1; - *point = 1; - return; - } - - bool fast_worked; - switch (mode) { - case SHORTEST: - fast_worked = FastDtoa(v, FAST_DTOA_SHORTEST, 0, vector, length, point); - break; - case FIXED: - fast_worked = FastFixedDtoa(v, requested_digits, vector, length, point); - break; - case PRECISION: - fast_worked = FastDtoa(v, FAST_DTOA_PRECISION, requested_digits, - vector, length, point); - break; - default: - UNREACHABLE(); - fast_worked = false; - } - if (fast_worked) return; - - // If the fast dtoa didn't succeed use the slower bignum version. - BignumDtoaMode bignum_mode = DtoaToBignumDtoaMode(mode); - BignumDtoa(v, bignum_mode, requested_digits, vector, length, point); - vector[*length] = '\0'; + // Ignore leading zeros in the integer part. + while (*current == '0') { + ++current; + if (current == end) { + *processed_characters_count = current - input; + return SignedZero(sign); + } } - - - // Maximum number of significant digits in decimal representation. - // The longest possible double in decimal representation is - // (2^53 - 1) * 2 ^ -1074 that is (2 ^ 53 - 1) * 5 ^ 1074 / 10 ^ 1074 - // (768 digits). If we parse a number whose first digits are equal to a - // mean of 2 adjacent doubles (that could have up to 769 digits) the result - // must be rounded to the bigger one unless the tail consists of zeros, so - // we don't need to preserve all the digits. - const int kMaxSignificantDigits = 772; - - - static double SignedZero(bool sign) { - return sign ? -0.0 : 0.0; + } + + // Copy significant digits of the integer part (if any) to the buffer. + while (*current >= '0' && *current <= '9') { + if (significant_digits < kMaxSignificantDigits) { + ASSERT(buffer_pos < kBufferSize); + buffer[buffer_pos++] = static_cast(*current); + significant_digits++; + } else { + insignificant_digits++; // Move the digit into the exponential part. + nonzero_digit_dropped = nonzero_digit_dropped || *current != '0'; + } + ++current; + if (current == end) + goto parsing_done; + } + + if (*current == '.') { + ++current; + if (current == end) { + if (significant_digits == 0 && !leading_zero) { + return 0.0; + } else { + goto parsing_done; + } } - - double StringToDoubleConverter::StringToDouble( - const char* input, - size_t length, - size_t* processed_characters_count) { - const char* current = input; - const char* end = input + length; - - *processed_characters_count = 0; - - // To make sure that iterator dereferencing is valid the following - // convention is used: - // 1. Each '++current' statement is followed by check for equality to 'end'. - // 3. If 'current' becomes equal to 'end' the function returns or goes to - // 'parsing_done'. - // 4. 'current' is not dereferenced after the 'parsing_done' label. - // 5. Code before 'parsing_done' may rely on 'current != end'. - if (current == end) return 0.0; - - // The longest form of simplified number is: "-.1eXXX\0". - const int kBufferSize = kMaxSignificantDigits + 10; - char buffer[kBufferSize]; // NOLINT: size is known at compile time. - int buffer_pos = 0; - - // Exponent will be adjusted if insignificant digits of the integer part - // or insignificant leading zeros of the fractional part are dropped. - int exponent = 0; - int significant_digits = 0; - int insignificant_digits = 0; - bool nonzero_digit_dropped = false; - bool sign = false; - - if (*current == '+' || *current == '-') { - sign = (*current == '-'); - ++current; - if (current == end) return 0.0; - } - - bool leading_zero = false; - if (*current == '0') { - ++current; - if (current == end) { - *processed_characters_count = current - input; - return SignedZero(sign); - } - - leading_zero = true; - - // Ignore leading zeros in the integer part. - while (*current == '0') { - ++current; - if (current == end) { - *processed_characters_count = current - input; - return SignedZero(sign); - } - } - } - - // Copy significant digits of the integer part (if any) to the buffer. - while (*current >= '0' && *current <= '9') { - if (significant_digits < kMaxSignificantDigits) { - ASSERT(buffer_pos < kBufferSize); - buffer[buffer_pos++] = static_cast(*current); - significant_digits++; - } else { - insignificant_digits++; // Move the digit into the exponential part. - nonzero_digit_dropped = nonzero_digit_dropped || *current != '0'; - } - ++current; - if (current == end) goto parsing_done; - } - - if (*current == '.') { - ++current; - if (current == end) { - if (significant_digits == 0 && !leading_zero) { - return 0.0; - } else { - goto parsing_done; - } - } - - if (significant_digits == 0) { - // Integer part consists of 0 or is absent. Significant digits start after - // leading zeros (if any). - while (*current == '0') { - ++current; - if (current == end) { - *processed_characters_count = current - input; - return SignedZero(sign); - } - exponent--; // Move this 0 into the exponent. - } - } - - // There is a fractional part. - while (*current >= '0' && *current <= '9') { - if (significant_digits < kMaxSignificantDigits) { - ASSERT(buffer_pos < kBufferSize); - buffer[buffer_pos++] = static_cast(*current); - significant_digits++; - exponent--; - } else { - // Ignore insignificant digits in the fractional part. - nonzero_digit_dropped = nonzero_digit_dropped || *current != '0'; - } - ++current; - if (current == end) goto parsing_done; - } - } - - if (!leading_zero && exponent == 0 && significant_digits == 0) { - // If leading_zeros is true then the string contains zeros. - // If exponent < 0 then string was [+-]\.0*... - // If significant_digits != 0 the string is not equal to 0. - // Otherwise there are no digits in the string. - return 0.0; - } - - // Parse exponential part. - if (*current == 'e' || *current == 'E') { - ++current; - if (current == end) { - --current; - goto parsing_done; - } - char sign = 0; - if (*current == '+' || *current == '-') { - sign = static_cast(*current); - ++current; - if (current == end) { - current -= 2; - goto parsing_done; - } - } - - if (*current < '0' || *current > '9') { - if (sign) - --current; - --current; - goto parsing_done; - } - - const int max_exponent = INT_MAX / 2; - ASSERT(-max_exponent / 2 <= exponent && exponent <= max_exponent / 2); - int num = 0; - do { - // Check overflow. - int digit = *current - '0'; - if (num >= max_exponent / 10 - && !(num == max_exponent / 10 && digit <= max_exponent % 10)) { - num = max_exponent; - } else { - num = num * 10 + digit; - } - ++current; - } while (current != end && *current >= '0' && *current <= '9'); - - exponent += (sign == '-' ? -num : num); - } - - parsing_done: - exponent += insignificant_digits; - - if (nonzero_digit_dropped) { - buffer[buffer_pos++] = '1'; - exponent--; + if (significant_digits == 0) { + // Integer part consists of 0 or is absent. Significant digits start after + // leading zeros (if any). + while (*current == '0') { + ++current; + if (current == end) { + *processed_characters_count = current - input; + return SignedZero(sign); } + exponent--; // Move this 0 into the exponent. + } + } + // There is a fractional part. + while (*current >= '0' && *current <= '9') { + if (significant_digits < kMaxSignificantDigits) { ASSERT(buffer_pos < kBufferSize); - buffer[buffer_pos] = '\0'; + buffer[buffer_pos++] = static_cast(*current); + significant_digits++; + exponent--; + } else { + // Ignore insignificant digits in the fractional part. + nonzero_digit_dropped = nonzero_digit_dropped || *current != '0'; + } + ++current; + if (current == end) + goto parsing_done; + } + } + + if (!leading_zero && exponent == 0 && significant_digits == 0) { + // If leading_zeros is true then the string contains zeros. + // If exponent < 0 then string was [+-]\.0*... + // If significant_digits != 0 the string is not equal to 0. + // Otherwise there are no digits in the string. + return 0.0; + } + + // Parse exponential part. + if (*current == 'e' || *current == 'E') { + ++current; + if (current == end) { + --current; + goto parsing_done; + } + char sign = 0; + if (*current == '+' || *current == '-') { + sign = static_cast(*current); + ++current; + if (current == end) { + current -= 2; + goto parsing_done; + } + } - double converted = Strtod(Vector(buffer, buffer_pos), exponent); - *processed_characters_count = current - input; - return sign? -converted: converted; + if (*current < '0' || *current > '9') { + if (sign) + --current; + --current; + goto parsing_done; } + const int max_exponent = INT_MAX / 2; + ASSERT(-max_exponent / 2 <= exponent && exponent <= max_exponent / 2); + int num = 0; + do { + // Check overflow. + int digit = *current - '0'; + if (num >= max_exponent / 10 && + !(num == max_exponent / 10 && digit <= max_exponent % 10)) { + num = max_exponent; + } else { + num = num * 10 + digit; + } + ++current; + } while (current != end && *current >= '0' && *current <= '9'); + + exponent += (sign == '-' ? -num : num); + } + +parsing_done: + exponent += insignificant_digits; + + if (nonzero_digit_dropped) { + buffer[buffer_pos++] = '1'; + exponent--; + } + + ASSERT(buffer_pos < kBufferSize); + buffer[buffer_pos] = '\0'; + + double converted = Strtod(Vector(buffer, buffer_pos), exponent); + *processed_characters_count = current - input; + return sign ? -converted : converted; +} + } // namespace double_conversion -} // namespace WTF +} // namespace WTF diff --git a/sky/engine/wtf/dtoa/double-conversion.h b/sky/engine/wtf/dtoa/double-conversion.h index de66bd8b130de..836ba4d52774c 100644 --- a/sky/engine/wtf/dtoa/double-conversion.h +++ b/sky/engine/wtf/dtoa/double-conversion.h @@ -34,344 +34,344 @@ namespace WTF { namespace double_conversion { - class DoubleToStringConverter { - public: - // When calling ToFixed with a double > 10^kMaxFixedDigitsBeforePoint - // or a requested_digits parameter > kMaxFixedDigitsAfterPoint then the - // function returns false. - static const int kMaxFixedDigitsBeforePoint = 60; - static const int kMaxFixedDigitsAfterPoint = 60; +class DoubleToStringConverter { + public: + // When calling ToFixed with a double > 10^kMaxFixedDigitsBeforePoint + // or a requested_digits parameter > kMaxFixedDigitsAfterPoint then the + // function returns false. + static const int kMaxFixedDigitsBeforePoint = 60; + static const int kMaxFixedDigitsAfterPoint = 60; - // When calling ToExponential with a requested_digits - // parameter > kMaxExponentialDigits then the function returns false. - static const int kMaxExponentialDigits = 120; + // When calling ToExponential with a requested_digits + // parameter > kMaxExponentialDigits then the function returns false. + static const int kMaxExponentialDigits = 120; - // When calling ToPrecision with a requested_digits - // parameter < kMinPrecisionDigits or requested_digits > kMaxPrecisionDigits - // then the function returns false. - static const int kMinPrecisionDigits = 1; - static const int kMaxPrecisionDigits = 120; + // When calling ToPrecision with a requested_digits + // parameter < kMinPrecisionDigits or requested_digits > kMaxPrecisionDigits + // then the function returns false. + static const int kMinPrecisionDigits = 1; + static const int kMaxPrecisionDigits = 120; - enum Flags { - NO_FLAGS = 0, - EMIT_POSITIVE_EXPONENT_SIGN = 1, - EMIT_TRAILING_DECIMAL_POINT = 2, - EMIT_TRAILING_ZERO_AFTER_POINT = 4, - UNIQUE_ZERO = 8 - }; + enum Flags { + NO_FLAGS = 0, + EMIT_POSITIVE_EXPONENT_SIGN = 1, + EMIT_TRAILING_DECIMAL_POINT = 2, + EMIT_TRAILING_ZERO_AFTER_POINT = 4, + UNIQUE_ZERO = 8 + }; - // Flags should be a bit-or combination of the possible Flags-enum. - // - NO_FLAGS: no special flags. - // - EMIT_POSITIVE_EXPONENT_SIGN: when the number is converted into exponent - // form, emits a '+' for positive exponents. Example: 1.2e+2. - // - EMIT_TRAILING_DECIMAL_POINT: when the input number is an integer and is - // converted into decimal format then a trailing decimal point is appended. - // Example: 2345.0 is converted to "2345.". - // - EMIT_TRAILING_ZERO_AFTER_POINT: in addition to a trailing decimal point - // emits a trailing '0'-character. This flag requires the - // EXMIT_TRAILING_DECIMAL_POINT flag. - // Example: 2345.0 is converted to "2345.0". - // - UNIQUE_ZERO: "-0.0" is converted to "0.0". - // - // Infinity symbol and nan_symbol provide the string representation for these - // special values. If the string is NULL and the special value is encountered - // then the conversion functions return false. - // - // The exponent_character is used in exponential representations. It is - // usually 'e' or 'E'. - // - // When converting to the shortest representation the converter will - // represent input numbers in decimal format if they are in the interval - // [10^decimal_in_shortest_low; 10^decimal_in_shortest_high[ - // (lower boundary included, greater boundary excluded). - // Example: with decimal_in_shortest_low = -6 and - // decimal_in_shortest_high = 21: - // ToShortest(0.000001) -> "0.000001" - // ToShortest(0.0000001) -> "1e-7" - // ToShortest(111111111111111111111.0) -> "111111111111111110000" - // ToShortest(100000000000000000000.0) -> "100000000000000000000" - // ToShortest(1111111111111111111111.0) -> "1.1111111111111111e+21" - // - // When converting to precision mode the converter may add - // max_leading_padding_zeroes before returning the number in exponential - // format. - // Example with max_leading_padding_zeroes_in_precision_mode = 6. - // ToPrecision(0.0000012345, 2) -> "0.0000012" - // ToPrecision(0.00000012345, 2) -> "1.2e-7" - // Similarily the converter may add up to - // max_trailing_padding_zeroes_in_precision_mode in precision mode to avoid - // returning an exponential representation. A zero added by the - // EMIT_TRAILING_ZERO_AFTER_POINT flag is counted for this limit. - // Examples for max_trailing_padding_zeroes_in_precision_mode = 1: - // ToPrecision(230.0, 2) -> "230" - // ToPrecision(230.0, 2) -> "230." with EMIT_TRAILING_DECIMAL_POINT. - // ToPrecision(230.0, 2) -> "2.3e2" with EMIT_TRAILING_ZERO_AFTER_POINT. - DoubleToStringConverter(int flags, - const char* infinity_symbol, - const char* nan_symbol, - char exponent_character, - int decimal_in_shortest_low, - int decimal_in_shortest_high, - int max_leading_padding_zeroes_in_precision_mode, - int max_trailing_padding_zeroes_in_precision_mode) - : flags_(flags), + // Flags should be a bit-or combination of the possible Flags-enum. + // - NO_FLAGS: no special flags. + // - EMIT_POSITIVE_EXPONENT_SIGN: when the number is converted into exponent + // form, emits a '+' for positive exponents. Example: 1.2e+2. + // - EMIT_TRAILING_DECIMAL_POINT: when the input number is an integer and is + // converted into decimal format then a trailing decimal point is appended. + // Example: 2345.0 is converted to "2345.". + // - EMIT_TRAILING_ZERO_AFTER_POINT: in addition to a trailing decimal point + // emits a trailing '0'-character. This flag requires the + // EXMIT_TRAILING_DECIMAL_POINT flag. + // Example: 2345.0 is converted to "2345.0". + // - UNIQUE_ZERO: "-0.0" is converted to "0.0". + // + // Infinity symbol and nan_symbol provide the string representation for these + // special values. If the string is NULL and the special value is encountered + // then the conversion functions return false. + // + // The exponent_character is used in exponential representations. It is + // usually 'e' or 'E'. + // + // When converting to the shortest representation the converter will + // represent input numbers in decimal format if they are in the interval + // [10^decimal_in_shortest_low; 10^decimal_in_shortest_high[ + // (lower boundary included, greater boundary excluded). + // Example: with decimal_in_shortest_low = -6 and + // decimal_in_shortest_high = 21: + // ToShortest(0.000001) -> "0.000001" + // ToShortest(0.0000001) -> "1e-7" + // ToShortest(111111111111111111111.0) -> "111111111111111110000" + // ToShortest(100000000000000000000.0) -> "100000000000000000000" + // ToShortest(1111111111111111111111.0) -> "1.1111111111111111e+21" + // + // When converting to precision mode the converter may add + // max_leading_padding_zeroes before returning the number in exponential + // format. + // Example with max_leading_padding_zeroes_in_precision_mode = 6. + // ToPrecision(0.0000012345, 2) -> "0.0000012" + // ToPrecision(0.00000012345, 2) -> "1.2e-7" + // Similarily the converter may add up to + // max_trailing_padding_zeroes_in_precision_mode in precision mode to avoid + // returning an exponential representation. A zero added by the + // EMIT_TRAILING_ZERO_AFTER_POINT flag is counted for this limit. + // Examples for max_trailing_padding_zeroes_in_precision_mode = 1: + // ToPrecision(230.0, 2) -> "230" + // ToPrecision(230.0, 2) -> "230." with EMIT_TRAILING_DECIMAL_POINT. + // ToPrecision(230.0, 2) -> "2.3e2" with EMIT_TRAILING_ZERO_AFTER_POINT. + DoubleToStringConverter(int flags, + const char* infinity_symbol, + const char* nan_symbol, + char exponent_character, + int decimal_in_shortest_low, + int decimal_in_shortest_high, + int max_leading_padding_zeroes_in_precision_mode, + int max_trailing_padding_zeroes_in_precision_mode) + : flags_(flags), infinity_symbol_(infinity_symbol), nan_symbol_(nan_symbol), exponent_character_(exponent_character), decimal_in_shortest_low_(decimal_in_shortest_low), decimal_in_shortest_high_(decimal_in_shortest_high), max_leading_padding_zeroes_in_precision_mode_( - max_leading_padding_zeroes_in_precision_mode), + max_leading_padding_zeroes_in_precision_mode), max_trailing_padding_zeroes_in_precision_mode_( - max_trailing_padding_zeroes_in_precision_mode) { - // When 'trailing zero after the point' is set, then 'trailing point' - // must be set too. - ASSERT(((flags & EMIT_TRAILING_DECIMAL_POINT) != 0) || - !((flags & EMIT_TRAILING_ZERO_AFTER_POINT) != 0)); - } + max_trailing_padding_zeroes_in_precision_mode) { + // When 'trailing zero after the point' is set, then 'trailing point' + // must be set too. + ASSERT(((flags & EMIT_TRAILING_DECIMAL_POINT) != 0) || + !((flags & EMIT_TRAILING_ZERO_AFTER_POINT) != 0)); + } - // Returns a converter following the EcmaScript specification. - static const DoubleToStringConverter& EcmaScriptConverter(); + // Returns a converter following the EcmaScript specification. + static const DoubleToStringConverter& EcmaScriptConverter(); - // Computes the shortest string of digits that correctly represent the input - // number. Depending on decimal_in_shortest_low and decimal_in_shortest_high - // (see constructor) it then either returns a decimal representation, or an - // exponential representation. - // Example with decimal_in_shortest_low = -6, - // decimal_in_shortest_high = 21, - // EMIT_POSITIVE_EXPONENT_SIGN activated, and - // EMIT_TRAILING_DECIMAL_POINT deactived: - // ToShortest(0.000001) -> "0.000001" - // ToShortest(0.0000001) -> "1e-7" - // ToShortest(111111111111111111111.0) -> "111111111111111110000" - // ToShortest(100000000000000000000.0) -> "100000000000000000000" - // ToShortest(1111111111111111111111.0) -> "1.1111111111111111e+21" - // - // Note: the conversion may round the output if the returned string - // is accurate enough to uniquely identify the input-number. - // For example the most precise representation of the double 9e59 equals - // "899999999999999918767229449717619953810131273674690656206848", but - // the converter will return the shorter (but still correct) "9e59". - // - // Returns true if the conversion succeeds. The conversion always succeeds - // except when the input value is special and no infinity_symbol or - // nan_symbol has been given to the constructor. - bool ToShortest(double value, StringBuilder* result_builder) const; + // Computes the shortest string of digits that correctly represent the input + // number. Depending on decimal_in_shortest_low and decimal_in_shortest_high + // (see constructor) it then either returns a decimal representation, or an + // exponential representation. + // Example with decimal_in_shortest_low = -6, + // decimal_in_shortest_high = 21, + // EMIT_POSITIVE_EXPONENT_SIGN activated, and + // EMIT_TRAILING_DECIMAL_POINT deactived: + // ToShortest(0.000001) -> "0.000001" + // ToShortest(0.0000001) -> "1e-7" + // ToShortest(111111111111111111111.0) -> "111111111111111110000" + // ToShortest(100000000000000000000.0) -> "100000000000000000000" + // ToShortest(1111111111111111111111.0) -> "1.1111111111111111e+21" + // + // Note: the conversion may round the output if the returned string + // is accurate enough to uniquely identify the input-number. + // For example the most precise representation of the double 9e59 equals + // "899999999999999918767229449717619953810131273674690656206848", but + // the converter will return the shorter (but still correct) "9e59". + // + // Returns true if the conversion succeeds. The conversion always succeeds + // except when the input value is special and no infinity_symbol or + // nan_symbol has been given to the constructor. + bool ToShortest(double value, StringBuilder* result_builder) const; + // Computes a decimal representation with a fixed number of digits after the + // decimal point. The last emitted digit is rounded. + // + // Examples: + // ToFixed(3.12, 1) -> "3.1" + // ToFixed(3.1415, 3) -> "3.142" + // ToFixed(1234.56789, 4) -> "1234.5679" + // ToFixed(1.23, 5) -> "1.23000" + // ToFixed(0.1, 4) -> "0.1000" + // ToFixed(1e30, 2) -> "1000000000000000019884624838656.00" + // ToFixed(0.1, 30) -> "0.100000000000000005551115123126" + // ToFixed(0.1, 17) -> "0.10000000000000001" + // + // If requested_digits equals 0, then the tail of the result depends on + // the EMIT_TRAILING_DECIMAL_POINT and EMIT_TRAILING_ZERO_AFTER_POINT. + // Examples, for requested_digits == 0, + // let EMIT_TRAILING_DECIMAL_POINT and EMIT_TRAILING_ZERO_AFTER_POINT be + // - false and false: then 123.45 -> 123 + // 0.678 -> 1 + // - true and false: then 123.45 -> 123. + // 0.678 -> 1. + // - true and true: then 123.45 -> 123.0 + // 0.678 -> 1.0 + // + // Returns true if the conversion succeeds. The conversion always succeeds + // except for the following cases: + // - the input value is special and no infinity_symbol or nan_symbol has + // been provided to the constructor, + // - 'value' > 10^kMaxFixedDigitsBeforePoint, or + // - 'requested_digits' > kMaxFixedDigitsAfterPoint. + // The last two conditions imply that the result will never contain more than + // 1 + kMaxFixedDigitsBeforePoint + 1 + kMaxFixedDigitsAfterPoint characters + // (one additional character for the sign, and one for the decimal point). + bool ToFixed(double value, + int requested_digits, + StringBuilder* result_builder) const; - // Computes a decimal representation with a fixed number of digits after the - // decimal point. The last emitted digit is rounded. - // - // Examples: - // ToFixed(3.12, 1) -> "3.1" - // ToFixed(3.1415, 3) -> "3.142" - // ToFixed(1234.56789, 4) -> "1234.5679" - // ToFixed(1.23, 5) -> "1.23000" - // ToFixed(0.1, 4) -> "0.1000" - // ToFixed(1e30, 2) -> "1000000000000000019884624838656.00" - // ToFixed(0.1, 30) -> "0.100000000000000005551115123126" - // ToFixed(0.1, 17) -> "0.10000000000000001" - // - // If requested_digits equals 0, then the tail of the result depends on - // the EMIT_TRAILING_DECIMAL_POINT and EMIT_TRAILING_ZERO_AFTER_POINT. - // Examples, for requested_digits == 0, - // let EMIT_TRAILING_DECIMAL_POINT and EMIT_TRAILING_ZERO_AFTER_POINT be - // - false and false: then 123.45 -> 123 - // 0.678 -> 1 - // - true and false: then 123.45 -> 123. - // 0.678 -> 1. - // - true and true: then 123.45 -> 123.0 - // 0.678 -> 1.0 - // - // Returns true if the conversion succeeds. The conversion always succeeds - // except for the following cases: - // - the input value is special and no infinity_symbol or nan_symbol has - // been provided to the constructor, - // - 'value' > 10^kMaxFixedDigitsBeforePoint, or - // - 'requested_digits' > kMaxFixedDigitsAfterPoint. - // The last two conditions imply that the result will never contain more than - // 1 + kMaxFixedDigitsBeforePoint + 1 + kMaxFixedDigitsAfterPoint characters - // (one additional character for the sign, and one for the decimal point). - bool ToFixed(double value, + // Computes a representation in exponential format with requested_digits + // after the decimal point. The last emitted digit is rounded. + // If requested_digits equals -1, then the shortest exponential representation + // is computed. + // + // Examples with EMIT_POSITIVE_EXPONENT_SIGN deactivated, and + // exponent_character set to 'e'. + // ToExponential(3.12, 1) -> "3.1e0" + // ToExponential(5.0, 3) -> "5.000e0" + // ToExponential(0.001, 2) -> "1.00e-3" + // ToExponential(3.1415, -1) -> "3.1415e0" + // ToExponential(3.1415, 4) -> "3.1415e0" + // ToExponential(3.1415, 3) -> "3.142e0" + // ToExponential(123456789000000, 3) -> "1.235e14" + // ToExponential(1000000000000000019884624838656.0, -1) -> "1e30" + // ToExponential(1000000000000000019884624838656.0, 32) -> + // "1.00000000000000001988462483865600e30" + // ToExponential(1234, 0) -> "1e3" + // + // Returns true if the conversion succeeds. The conversion always succeeds + // except for the following cases: + // - the input value is special and no infinity_symbol or nan_symbol has + // been provided to the constructor, + // - 'requested_digits' > kMaxExponentialDigits. + // The last condition implies that the result will never contain more than + // kMaxExponentialDigits + 8 characters (the sign, the digit before the + // decimal point, the decimal point, the exponent character, the + // exponent's sign, and at most 3 exponent digits). + bool ToExponential(double value, int requested_digits, StringBuilder* result_builder) const; - // Computes a representation in exponential format with requested_digits - // after the decimal point. The last emitted digit is rounded. - // If requested_digits equals -1, then the shortest exponential representation - // is computed. - // - // Examples with EMIT_POSITIVE_EXPONENT_SIGN deactivated, and - // exponent_character set to 'e'. - // ToExponential(3.12, 1) -> "3.1e0" - // ToExponential(5.0, 3) -> "5.000e0" - // ToExponential(0.001, 2) -> "1.00e-3" - // ToExponential(3.1415, -1) -> "3.1415e0" - // ToExponential(3.1415, 4) -> "3.1415e0" - // ToExponential(3.1415, 3) -> "3.142e0" - // ToExponential(123456789000000, 3) -> "1.235e14" - // ToExponential(1000000000000000019884624838656.0, -1) -> "1e30" - // ToExponential(1000000000000000019884624838656.0, 32) -> - // "1.00000000000000001988462483865600e30" - // ToExponential(1234, 0) -> "1e3" - // - // Returns true if the conversion succeeds. The conversion always succeeds - // except for the following cases: - // - the input value is special and no infinity_symbol or nan_symbol has - // been provided to the constructor, - // - 'requested_digits' > kMaxExponentialDigits. - // The last condition implies that the result will never contain more than - // kMaxExponentialDigits + 8 characters (the sign, the digit before the - // decimal point, the decimal point, the exponent character, the - // exponent's sign, and at most 3 exponent digits). - bool ToExponential(double value, - int requested_digits, - StringBuilder* result_builder) const; + // Computes 'precision' leading digits of the given 'value' and returns them + // either in exponential or decimal format, depending on + // max_{leading|trailing}_padding_zeroes_in_precision_mode (given to the + // constructor). + // The last computed digit is rounded. + // + // Example with max_leading_padding_zeroes_in_precision_mode = 6. + // ToPrecision(0.0000012345, 2) -> "0.0000012" + // ToPrecision(0.00000012345, 2) -> "1.2e-7" + // Similarily the converter may add up to + // max_trailing_padding_zeroes_in_precision_mode in precision mode to avoid + // returning an exponential representation. A zero added by the + // EMIT_TRAILING_ZERO_AFTER_POINT flag is counted for this limit. + // Examples for max_trailing_padding_zeroes_in_precision_mode = 1: + // ToPrecision(230.0, 2) -> "230" + // ToPrecision(230.0, 2) -> "230." with EMIT_TRAILING_DECIMAL_POINT. + // ToPrecision(230.0, 2) -> "2.3e2" with EMIT_TRAILING_ZERO_AFTER_POINT. + // Examples for max_trailing_padding_zeroes_in_precision_mode = 3, and no + // EMIT_TRAILING_ZERO_AFTER_POINT: + // ToPrecision(123450.0, 6) -> "123450" + // ToPrecision(123450.0, 5) -> "123450" + // ToPrecision(123450.0, 4) -> "123500" + // ToPrecision(123450.0, 3) -> "123000" + // ToPrecision(123450.0, 2) -> "1.2e5" + // + // Returns true if the conversion succeeds. The conversion always succeeds + // except for the following cases: + // - the input value is special and no infinity_symbol or nan_symbol has + // been provided to the constructor, + // - precision < kMinPericisionDigits + // - precision > kMaxPrecisionDigits + // The last condition implies that the result will never contain more than + // kMaxPrecisionDigits + 7 characters (the sign, the decimal point, the + // exponent character, the exponent's sign, and at most 3 exponent digits). + bool ToPrecision(double value, + int precision, + StringBuilder* result_builder) const; - // Computes 'precision' leading digits of the given 'value' and returns them - // either in exponential or decimal format, depending on - // max_{leading|trailing}_padding_zeroes_in_precision_mode (given to the - // constructor). - // The last computed digit is rounded. - // - // Example with max_leading_padding_zeroes_in_precision_mode = 6. - // ToPrecision(0.0000012345, 2) -> "0.0000012" - // ToPrecision(0.00000012345, 2) -> "1.2e-7" - // Similarily the converter may add up to - // max_trailing_padding_zeroes_in_precision_mode in precision mode to avoid - // returning an exponential representation. A zero added by the - // EMIT_TRAILING_ZERO_AFTER_POINT flag is counted for this limit. - // Examples for max_trailing_padding_zeroes_in_precision_mode = 1: - // ToPrecision(230.0, 2) -> "230" - // ToPrecision(230.0, 2) -> "230." with EMIT_TRAILING_DECIMAL_POINT. - // ToPrecision(230.0, 2) -> "2.3e2" with EMIT_TRAILING_ZERO_AFTER_POINT. - // Examples for max_trailing_padding_zeroes_in_precision_mode = 3, and no - // EMIT_TRAILING_ZERO_AFTER_POINT: - // ToPrecision(123450.0, 6) -> "123450" - // ToPrecision(123450.0, 5) -> "123450" - // ToPrecision(123450.0, 4) -> "123500" - // ToPrecision(123450.0, 3) -> "123000" - // ToPrecision(123450.0, 2) -> "1.2e5" - // - // Returns true if the conversion succeeds. The conversion always succeeds - // except for the following cases: - // - the input value is special and no infinity_symbol or nan_symbol has - // been provided to the constructor, - // - precision < kMinPericisionDigits - // - precision > kMaxPrecisionDigits - // The last condition implies that the result will never contain more than - // kMaxPrecisionDigits + 7 characters (the sign, the decimal point, the - // exponent character, the exponent's sign, and at most 3 exponent digits). - bool ToPrecision(double value, - int precision, - StringBuilder* result_builder) const; + enum DtoaMode { + // Produce the shortest correct representation. + // For example the output of 0.299999999999999988897 is (the less accurate + // but correct) 0.3. + SHORTEST, + // Produce a fixed number of digits after the decimal point. + // For instance fixed(0.1, 4) becomes 0.1000 + // If the input number is big, the output will be big. + FIXED, + // Fixed number of digits (independent of the decimal point). + PRECISION + }; - enum DtoaMode { - // Produce the shortest correct representation. - // For example the output of 0.299999999999999988897 is (the less accurate - // but correct) 0.3. - SHORTEST, - // Produce a fixed number of digits after the decimal point. - // For instance fixed(0.1, 4) becomes 0.1000 - // If the input number is big, the output will be big. - FIXED, - // Fixed number of digits (independent of the decimal point). - PRECISION - }; + // The maximal number of digits that are needed to emit a double in base 10. + // A higher precision can be achieved by using more digits, but the shortest + // accurate representation of any double will never use more digits than + // kBase10MaximalLength. + // Note that DoubleToAscii null-terminates its input. So the given buffer + // should be at least kBase10MaximalLength + 1 characters long. + static const int kBase10MaximalLength = 17; - // The maximal number of digits that are needed to emit a double in base 10. - // A higher precision can be achieved by using more digits, but the shortest - // accurate representation of any double will never use more digits than - // kBase10MaximalLength. - // Note that DoubleToAscii null-terminates its input. So the given buffer - // should be at least kBase10MaximalLength + 1 characters long. - static const int kBase10MaximalLength = 17; + // Converts the given double 'v' to ascii. + // The result should be interpreted as buffer * 10^(point-length). + // + // The output depends on the given mode: + // - SHORTEST: produce the least amount of digits for which the internal + // identity requirement is still satisfied. If the digits are printed + // (together with the correct exponent) then reading this number will give + // 'v' again. The buffer will choose the representation that is closest to + // 'v'. If there are two at the same distance, than the one farther away + // from 0 is chosen (halfway cases - ending with 5 - are rounded up). + // In this mode the 'requested_digits' parameter is ignored. + // - FIXED: produces digits necessary to print a given number with + // 'requested_digits' digits after the decimal point. The produced digits + // might be too short in which case the caller has to fill the remainder + // with '0's. + // Example: toFixed(0.001, 5) is allowed to return buffer="1", point=-2. + // Halfway cases are rounded towards +/-Infinity (away from 0). The call + // toFixed(0.15, 2) thus returns buffer="2", point=0. + // The returned buffer may contain digits that would be truncated from the + // shortest representation of the input. + // - PRECISION: produces 'requested_digits' where the first digit is not '0'. + // Even though the length of produced digits usually equals + // 'requested_digits', the function is allowed to return fewer digits, in + // which case the caller has to fill the missing digits with '0's. + // Halfway cases are again rounded away from 0. + // DoubleToAscii expects the given buffer to be big enough to hold all + // digits and a terminating null-character. In SHORTEST-mode it expects a + // buffer of at least kBase10MaximalLength + 1. In all other modes the + // requested_digits parameter (+ 1 for the null-character) limits the size of + // the output. The given length is only used in debug mode to ensure the + // buffer is big enough. + static void DoubleToAscii(double v, + DtoaMode mode, + int requested_digits, + char* buffer, + int buffer_length, + bool* sign, + int* length, + int* point); - // Converts the given double 'v' to ascii. - // The result should be interpreted as buffer * 10^(point-length). - // - // The output depends on the given mode: - // - SHORTEST: produce the least amount of digits for which the internal - // identity requirement is still satisfied. If the digits are printed - // (together with the correct exponent) then reading this number will give - // 'v' again. The buffer will choose the representation that is closest to - // 'v'. If there are two at the same distance, than the one farther away - // from 0 is chosen (halfway cases - ending with 5 - are rounded up). - // In this mode the 'requested_digits' parameter is ignored. - // - FIXED: produces digits necessary to print a given number with - // 'requested_digits' digits after the decimal point. The produced digits - // might be too short in which case the caller has to fill the remainder - // with '0's. - // Example: toFixed(0.001, 5) is allowed to return buffer="1", point=-2. - // Halfway cases are rounded towards +/-Infinity (away from 0). The call - // toFixed(0.15, 2) thus returns buffer="2", point=0. - // The returned buffer may contain digits that would be truncated from the - // shortest representation of the input. - // - PRECISION: produces 'requested_digits' where the first digit is not '0'. - // Even though the length of produced digits usually equals - // 'requested_digits', the function is allowed to return fewer digits, in - // which case the caller has to fill the missing digits with '0's. - // Halfway cases are again rounded away from 0. - // DoubleToAscii expects the given buffer to be big enough to hold all - // digits and a terminating null-character. In SHORTEST-mode it expects a - // buffer of at least kBase10MaximalLength + 1. In all other modes the - // requested_digits parameter (+ 1 for the null-character) limits the size of - // the output. The given length is only used in debug mode to ensure the - // buffer is big enough. - static void DoubleToAscii(double v, - DtoaMode mode, - int requested_digits, - char* buffer, - int buffer_length, - bool* sign, - int* length, - int* point); + private: + // If the value is a special value (NaN or Infinity) constructs the + // corresponding string using the configured infinity/nan-symbol. + // If either of them is NULL or the value is not special then the + // function returns false. + bool HandleSpecialValues(double value, StringBuilder* result_builder) const; + // Constructs an exponential representation (i.e. 1.234e56). + // The given exponent assumes a decimal point after the first decimal digit. + void CreateExponentialRepresentation(const char* decimal_digits, + int length, + int exponent, + StringBuilder* result_builder) const; + // Creates a decimal representation (i.e 1234.5678). + void CreateDecimalRepresentation(const char* decimal_digits, + int length, + int decimal_point, + int digits_after_point, + StringBuilder* result_builder) const; - private: - // If the value is a special value (NaN or Infinity) constructs the - // corresponding string using the configured infinity/nan-symbol. - // If either of them is NULL or the value is not special then the - // function returns false. - bool HandleSpecialValues(double value, StringBuilder* result_builder) const; - // Constructs an exponential representation (i.e. 1.234e56). - // The given exponent assumes a decimal point after the first decimal digit. - void CreateExponentialRepresentation(const char* decimal_digits, - int length, - int exponent, - StringBuilder* result_builder) const; - // Creates a decimal representation (i.e 1234.5678). - void CreateDecimalRepresentation(const char* decimal_digits, - int length, - int decimal_point, - int digits_after_point, - StringBuilder* result_builder) const; + const int flags_; + const char* const infinity_symbol_; + const char* const nan_symbol_; + const char exponent_character_; + const int decimal_in_shortest_low_; + const int decimal_in_shortest_high_; + const int max_leading_padding_zeroes_in_precision_mode_; + const int max_trailing_padding_zeroes_in_precision_mode_; - const int flags_; - const char* const infinity_symbol_; - const char* const nan_symbol_; - const char exponent_character_; - const int decimal_in_shortest_low_; - const int decimal_in_shortest_high_; - const int max_leading_padding_zeroes_in_precision_mode_; - const int max_trailing_padding_zeroes_in_precision_mode_; + FXL_DISALLOW_IMPLICIT_CONSTRUCTORS(DoubleToStringConverter); +}; - FXL_DISALLOW_IMPLICIT_CONSTRUCTORS(DoubleToStringConverter); - }; +class StringToDoubleConverter { + public: + // Performs the conversion. + // The output parameter 'processed_characters_count' is set to the number + // of characters that have been processed to read the number. + static double StringToDouble(const char* buffer, + size_t length, + size_t* processed_characters_count); - - class StringToDoubleConverter { - public: - // Performs the conversion. - // The output parameter 'processed_characters_count' is set to the number - // of characters that have been processed to read the number. - static double StringToDouble(const char* buffer, size_t length, size_t* processed_characters_count); - - private: - FXL_DISALLOW_IMPLICIT_CONSTRUCTORS(StringToDoubleConverter); - }; + private: + FXL_DISALLOW_IMPLICIT_CONSTRUCTORS(StringToDoubleConverter); +}; } // namespace double_conversion -} // namespace WTF +} // namespace WTF #endif // SKY_ENGINE_WTF_DTOA_DOUBLE_CONVERSION_H_ diff --git a/sky/engine/wtf/dtoa/double.h b/sky/engine/wtf/dtoa/double.h index 92ac041218717..88c5810d47ec9 100644 --- a/sky/engine/wtf/dtoa/double.h +++ b/sky/engine/wtf/dtoa/double.h @@ -34,216 +34,216 @@ namespace WTF { namespace double_conversion { - // We assume that doubles and uint64_t have the same endianness. - static uint64_t double_to_uint64(double d) { return BitCast(d); } - static double uint64_to_double(uint64_t d64) { return BitCast(d64); } - - // Helper functions for doubles. - class Double { - public: - static const uint64_t kSignMask = UINT64_2PART_C(0x80000000, 00000000); - static const uint64_t kExponentMask = UINT64_2PART_C(0x7FF00000, 00000000); - static const uint64_t kSignificandMask = UINT64_2PART_C(0x000FFFFF, FFFFFFFF); - static const uint64_t kHiddenBit = UINT64_2PART_C(0x00100000, 00000000); - static const int kPhysicalSignificandSize = 52; // Excludes the hidden bit. - static const int kSignificandSize = 53; - - Double() : d64_(0) {} - explicit Double(double d) : d64_(double_to_uint64(d)) {} - explicit Double(uint64_t d64) : d64_(d64) {} - explicit Double(DiyFp diy_fp) - : d64_(DiyFpToUint64(diy_fp)) {} - - // The value encoded by this Double must be greater or equal to +0.0. - // It must not be special (infinity, or NaN). - DiyFp AsDiyFp() const { - ASSERT(Sign() > 0); - ASSERT(!IsSpecial()); - return DiyFp(Significand(), Exponent()); - } - - // The value encoded by this Double must be strictly greater than 0. - DiyFp AsNormalizedDiyFp() const { - ASSERT(value() > 0.0); - uint64_t f = Significand(); - int e = Exponent(); - - // The current double could be a denormal. - while ((f & kHiddenBit) == 0) { - f <<= 1; - e--; - } - // Do the final shifts in one go. - f <<= DiyFp::kSignificandSize - kSignificandSize; - e -= DiyFp::kSignificandSize - kSignificandSize; - return DiyFp(f, e); - } - - // Returns the double's bit as uint64. - uint64_t AsUint64() const { - return d64_; - } - - // Returns the next greater double. Returns +infinity on input +infinity. - double NextDouble() const { - if (d64_ == kInfinity) return Double(kInfinity).value(); - if (Sign() < 0 && Significand() == 0) { - // -0.0 - return 0.0; - } - if (Sign() < 0) { - return Double(d64_ - 1).value(); - } else { - return Double(d64_ + 1).value(); - } - } - - int Exponent() const { - if (IsDenormal()) return kDenormalExponent; - - uint64_t d64 = AsUint64(); - int biased_e = - static_cast((d64 & kExponentMask) >> kPhysicalSignificandSize); - return biased_e - kExponentBias; - } - - uint64_t Significand() const { - uint64_t d64 = AsUint64(); - uint64_t significand = d64 & kSignificandMask; - if (!IsDenormal()) { - return significand + kHiddenBit; - } else { - return significand; - } - } - - // Returns true if the double is a denormal. - bool IsDenormal() const { - uint64_t d64 = AsUint64(); - return (d64 & kExponentMask) == 0; - } - - // We consider denormals not to be special. - // Hence only Infinity and NaN are special. - bool IsSpecial() const { - uint64_t d64 = AsUint64(); - return (d64 & kExponentMask) == kExponentMask; - } - - bool IsNan() const { - uint64_t d64 = AsUint64(); - return ((d64 & kExponentMask) == kExponentMask) && - ((d64 & kSignificandMask) != 0); - } - - bool IsInfinite() const { - uint64_t d64 = AsUint64(); - return ((d64 & kExponentMask) == kExponentMask) && - ((d64 & kSignificandMask) == 0); - } - - int Sign() const { - uint64_t d64 = AsUint64(); - return (d64 & kSignMask) == 0? 1: -1; - } - - // Precondition: the value encoded by this Double must be greater or equal - // than +0.0. - DiyFp UpperBoundary() const { - ASSERT(Sign() > 0); - return DiyFp(Significand() * 2 + 1, Exponent() - 1); - } - - // Computes the two boundaries of this. - // The bigger boundary (m_plus) is normalized. The lower boundary has the same - // exponent as m_plus. - // Precondition: the value encoded by this Double must be greater than 0. - void NormalizedBoundaries(DiyFp* out_m_minus, DiyFp* out_m_plus) const { - ASSERT(value() > 0.0); - DiyFp v = this->AsDiyFp(); - bool significand_is_zero = (v.f() == kHiddenBit); - DiyFp m_plus = DiyFp::Normalize(DiyFp((v.f() << 1) + 1, v.e() - 1)); - DiyFp m_minus; - if (significand_is_zero && v.e() != kDenormalExponent) { - // The boundary is closer. Think of v = 1000e10 and v- = 9999e9. - // Then the boundary (== (v - v-)/2) is not just at a distance of 1e9 but - // at a distance of 1e8. - // The only exception is for the smallest normal: the largest denormal is - // at the same distance as its successor. - // Note: denormals have the same exponent as the smallest normals. - m_minus = DiyFp((v.f() << 2) - 1, v.e() - 2); - } else { - m_minus = DiyFp((v.f() << 1) - 1, v.e() - 1); - } - m_minus.set_f(m_minus.f() << (m_minus.e() - m_plus.e())); - m_minus.set_e(m_plus.e()); - *out_m_plus = m_plus; - *out_m_minus = m_minus; - } - - double value() const { return uint64_to_double(d64_); } - - // Returns the significand size for a given order of magnitude. - // If v = f*2^e with 2^p-1 <= f <= 2^p then p+e is v's order of magnitude. - // This function returns the number of significant binary digits v will have - // once it's encoded into a double. In almost all cases this is equal to - // kSignificandSize. The only exceptions are denormals. They start with - // leading zeroes and their effective significand-size is hence smaller. - static int SignificandSizeForOrderOfMagnitude(int order) { - if (order >= (kDenormalExponent + kSignificandSize)) { - return kSignificandSize; - } - if (order <= kDenormalExponent) return 0; - return order - kDenormalExponent; - } - - static double Infinity() { - return Double(kInfinity).value(); - } - - static double NaN() { - return Double(kNaN).value(); - } - - private: - static const int kExponentBias = 0x3FF + kPhysicalSignificandSize; - static const int kDenormalExponent = -kExponentBias + 1; - static const int kMaxExponent = 0x7FF - kExponentBias; - static const uint64_t kInfinity = UINT64_2PART_C(0x7FF00000, 00000000); - static const uint64_t kNaN = UINT64_2PART_C(0x7FF80000, 00000000); - - const uint64_t d64_; - - static uint64_t DiyFpToUint64(DiyFp diy_fp) { - uint64_t significand = diy_fp.f(); - int exponent = diy_fp.e(); - while (significand > kHiddenBit + kSignificandMask) { - significand >>= 1; - exponent++; - } - if (exponent >= kMaxExponent) { - return kInfinity; - } - if (exponent < kDenormalExponent) { - return 0; - } - while (exponent > kDenormalExponent && (significand & kHiddenBit) == 0) { - significand <<= 1; - exponent--; - } - uint64_t biased_exponent; - if (exponent == kDenormalExponent && (significand & kHiddenBit) == 0) { - biased_exponent = 0; - } else { - biased_exponent = static_cast(exponent + kExponentBias); - } - return (significand & kSignificandMask) | - (biased_exponent << kPhysicalSignificandSize); - } - }; +// We assume that doubles and uint64_t have the same endianness. +static uint64_t double_to_uint64(double d) { + return BitCast(d); +} +static double uint64_to_double(uint64_t d64) { + return BitCast(d64); +} + +// Helper functions for doubles. +class Double { + public: + static const uint64_t kSignMask = UINT64_2PART_C(0x80000000, 00000000); + static const uint64_t kExponentMask = UINT64_2PART_C(0x7FF00000, 00000000); + static const uint64_t kSignificandMask = UINT64_2PART_C(0x000FFFFF, FFFFFFFF); + static const uint64_t kHiddenBit = UINT64_2PART_C(0x00100000, 00000000); + static const int kPhysicalSignificandSize = 52; // Excludes the hidden bit. + static const int kSignificandSize = 53; + + Double() : d64_(0) {} + explicit Double(double d) : d64_(double_to_uint64(d)) {} + explicit Double(uint64_t d64) : d64_(d64) {} + explicit Double(DiyFp diy_fp) : d64_(DiyFpToUint64(diy_fp)) {} + + // The value encoded by this Double must be greater or equal to +0.0. + // It must not be special (infinity, or NaN). + DiyFp AsDiyFp() const { + ASSERT(Sign() > 0); + ASSERT(!IsSpecial()); + return DiyFp(Significand(), Exponent()); + } + + // The value encoded by this Double must be strictly greater than 0. + DiyFp AsNormalizedDiyFp() const { + ASSERT(value() > 0.0); + uint64_t f = Significand(); + int e = Exponent(); + + // The current double could be a denormal. + while ((f & kHiddenBit) == 0) { + f <<= 1; + e--; + } + // Do the final shifts in one go. + f <<= DiyFp::kSignificandSize - kSignificandSize; + e -= DiyFp::kSignificandSize - kSignificandSize; + return DiyFp(f, e); + } + + // Returns the double's bit as uint64. + uint64_t AsUint64() const { return d64_; } + + // Returns the next greater double. Returns +infinity on input +infinity. + double NextDouble() const { + if (d64_ == kInfinity) + return Double(kInfinity).value(); + if (Sign() < 0 && Significand() == 0) { + // -0.0 + return 0.0; + } + if (Sign() < 0) { + return Double(d64_ - 1).value(); + } else { + return Double(d64_ + 1).value(); + } + } + + int Exponent() const { + if (IsDenormal()) + return kDenormalExponent; + + uint64_t d64 = AsUint64(); + int biased_e = + static_cast((d64 & kExponentMask) >> kPhysicalSignificandSize); + return biased_e - kExponentBias; + } + + uint64_t Significand() const { + uint64_t d64 = AsUint64(); + uint64_t significand = d64 & kSignificandMask; + if (!IsDenormal()) { + return significand + kHiddenBit; + } else { + return significand; + } + } + + // Returns true if the double is a denormal. + bool IsDenormal() const { + uint64_t d64 = AsUint64(); + return (d64 & kExponentMask) == 0; + } + + // We consider denormals not to be special. + // Hence only Infinity and NaN are special. + bool IsSpecial() const { + uint64_t d64 = AsUint64(); + return (d64 & kExponentMask) == kExponentMask; + } + + bool IsNan() const { + uint64_t d64 = AsUint64(); + return ((d64 & kExponentMask) == kExponentMask) && + ((d64 & kSignificandMask) != 0); + } + + bool IsInfinite() const { + uint64_t d64 = AsUint64(); + return ((d64 & kExponentMask) == kExponentMask) && + ((d64 & kSignificandMask) == 0); + } + + int Sign() const { + uint64_t d64 = AsUint64(); + return (d64 & kSignMask) == 0 ? 1 : -1; + } + + // Precondition: the value encoded by this Double must be greater or equal + // than +0.0. + DiyFp UpperBoundary() const { + ASSERT(Sign() > 0); + return DiyFp(Significand() * 2 + 1, Exponent() - 1); + } + + // Computes the two boundaries of this. + // The bigger boundary (m_plus) is normalized. The lower boundary has the same + // exponent as m_plus. + // Precondition: the value encoded by this Double must be greater than 0. + void NormalizedBoundaries(DiyFp* out_m_minus, DiyFp* out_m_plus) const { + ASSERT(value() > 0.0); + DiyFp v = this->AsDiyFp(); + bool significand_is_zero = (v.f() == kHiddenBit); + DiyFp m_plus = DiyFp::Normalize(DiyFp((v.f() << 1) + 1, v.e() - 1)); + DiyFp m_minus; + if (significand_is_zero && v.e() != kDenormalExponent) { + // The boundary is closer. Think of v = 1000e10 and v- = 9999e9. + // Then the boundary (== (v - v-)/2) is not just at a distance of 1e9 but + // at a distance of 1e8. + // The only exception is for the smallest normal: the largest denormal is + // at the same distance as its successor. + // Note: denormals have the same exponent as the smallest normals. + m_minus = DiyFp((v.f() << 2) - 1, v.e() - 2); + } else { + m_minus = DiyFp((v.f() << 1) - 1, v.e() - 1); + } + m_minus.set_f(m_minus.f() << (m_minus.e() - m_plus.e())); + m_minus.set_e(m_plus.e()); + *out_m_plus = m_plus; + *out_m_minus = m_minus; + } + + double value() const { return uint64_to_double(d64_); } + + // Returns the significand size for a given order of magnitude. + // If v = f*2^e with 2^p-1 <= f <= 2^p then p+e is v's order of magnitude. + // This function returns the number of significant binary digits v will have + // once it's encoded into a double. In almost all cases this is equal to + // kSignificandSize. The only exceptions are denormals. They start with + // leading zeroes and their effective significand-size is hence smaller. + static int SignificandSizeForOrderOfMagnitude(int order) { + if (order >= (kDenormalExponent + kSignificandSize)) { + return kSignificandSize; + } + if (order <= kDenormalExponent) + return 0; + return order - kDenormalExponent; + } + + static double Infinity() { return Double(kInfinity).value(); } + + static double NaN() { return Double(kNaN).value(); } + + private: + static const int kExponentBias = 0x3FF + kPhysicalSignificandSize; + static const int kDenormalExponent = -kExponentBias + 1; + static const int kMaxExponent = 0x7FF - kExponentBias; + static const uint64_t kInfinity = UINT64_2PART_C(0x7FF00000, 00000000); + static const uint64_t kNaN = UINT64_2PART_C(0x7FF80000, 00000000); + + const uint64_t d64_; + + static uint64_t DiyFpToUint64(DiyFp diy_fp) { + uint64_t significand = diy_fp.f(); + int exponent = diy_fp.e(); + while (significand > kHiddenBit + kSignificandMask) { + significand >>= 1; + exponent++; + } + if (exponent >= kMaxExponent) { + return kInfinity; + } + if (exponent < kDenormalExponent) { + return 0; + } + while (exponent > kDenormalExponent && (significand & kHiddenBit) == 0) { + significand <<= 1; + exponent--; + } + uint64_t biased_exponent; + if (exponent == kDenormalExponent && (significand & kHiddenBit) == 0) { + biased_exponent = 0; + } else { + biased_exponent = static_cast(exponent + kExponentBias); + } + return (significand & kSignificandMask) | + (biased_exponent << kPhysicalSignificandSize); + } +}; } // namespace double_conversion -} // namespace WTF +} // namespace WTF #endif // SKY_ENGINE_WTF_DTOA_DOUBLE_H_ diff --git a/sky/engine/wtf/dtoa/fast-dtoa.cc b/sky/engine/wtf/dtoa/fast-dtoa.cc index c30c23b2aa143..a80636857252d 100644 --- a/sky/engine/wtf/dtoa/fast-dtoa.cc +++ b/sky/engine/wtf/dtoa/fast-dtoa.cc @@ -25,7 +25,6 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - #include "fast-dtoa.h" #include "cached-powers.h" @@ -36,705 +35,698 @@ namespace WTF { namespace double_conversion { - // The minimal and maximal target exponent define the range of w's binary - // exponent, where 'w' is the result of multiplying the input by a cached power - // of ten. - // - // A different range might be chosen on a different platform, to optimize digit - // generation, but a smaller range requires more powers of ten to be cached. - static const int kMinimalTargetExponent = -60; - static const int kMaximalTargetExponent = -32; - - - // Adjusts the last digit of the generated number, and screens out generated - // solutions that may be inaccurate. A solution may be inaccurate if it is - // outside the safe interval, or if we cannot prove that it is closer to the - // input than a neighboring representation of the same length. - // - // Input: * buffer containing the digits of too_high / 10^kappa - // * the buffer's length - // * distance_too_high_w == (too_high - w).f() * unit - // * unsafe_interval == (too_high - too_low).f() * unit - // * rest = (too_high - buffer * 10^kappa).f() * unit - // * ten_kappa = 10^kappa * unit - // * unit = the common multiplier - // Output: returns true if the buffer is guaranteed to contain the closest - // representable number to the input. - // Modifies the generated digits in the buffer to approach (round towards) w. - static bool RoundWeed(Vector buffer, - int length, - uint64_t distance_too_high_w, - uint64_t unsafe_interval, - uint64_t rest, - uint64_t ten_kappa, - uint64_t unit) { - uint64_t small_distance = distance_too_high_w - unit; - uint64_t big_distance = distance_too_high_w + unit; - // Let w_low = too_high - big_distance, and - // w_high = too_high - small_distance. - // Note: w_low < w < w_high - // - // The real w (* unit) must lie somewhere inside the interval - // ]w_low; w_high[ (often written as "(w_low; w_high)") - - // Basically the buffer currently contains a number in the unsafe interval - // ]too_low; too_high[ with too_low < w < too_high - // - // too_high - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // ^v 1 unit ^ ^ ^ ^ - // boundary_high --------------------- . . . . - // ^v 1 unit . . . . - // - - - - - - - - - - - - - - - - - - - + - - + - - - - - - . . - // . . ^ . . - // . big_distance . . . - // . . . . rest - // small_distance . . . . - // v . . . . - // w_high - - - - - - - - - - - - - - - - - - . . . . - // ^v 1 unit . . . . - // w ---------------------------------------- . . . . - // ^v 1 unit v . . . - // w_low - - - - - - - - - - - - - - - - - - - - - . . . - // . . v - // buffer --------------------------------------------------+-------+-------- - // . . - // safe_interval . - // v . - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - . - // ^v 1 unit . - // boundary_low ------------------------- unsafe_interval - // ^v 1 unit v - // too_low - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - // - // Note that the value of buffer could lie anywhere inside the range too_low - // to too_high. - // - // boundary_low, boundary_high and w are approximations of the real boundaries - // and v (the input number). They are guaranteed to be precise up to one unit. - // In fact the error is guaranteed to be strictly less than one unit. - // - // Anything that lies outside the unsafe interval is guaranteed not to round - // to v when read again. - // Anything that lies inside the safe interval is guaranteed to round to v - // when read again. - // If the number inside the buffer lies inside the unsafe interval but not - // inside the safe interval then we simply do not know and bail out (returning - // false). - // - // Similarly we have to take into account the imprecision of 'w' when finding - // the closest representation of 'w'. If we have two potential - // representations, and one is closer to both w_low and w_high, then we know - // it is closer to the actual value v. - // - // By generating the digits of too_high we got the largest (closest to - // too_high) buffer that is still in the unsafe interval. In the case where - // w_high < buffer < too_high we try to decrement the buffer. - // This way the buffer approaches (rounds towards) w. - // There are 3 conditions that stop the decrementation process: - // 1) the buffer is already below w_high - // 2) decrementing the buffer would make it leave the unsafe interval - // 3) decrementing the buffer would yield a number below w_high and farther - // away than the current number. In other words: - // (buffer{-1} < w_high) && w_high - buffer{-1} > buffer - w_high - // Instead of using the buffer directly we use its distance to too_high. - // Conceptually rest ~= too_high - buffer - // We need to do the following tests in this order to avoid over- and - // underflows. - ASSERT(rest <= unsafe_interval); - while (rest < small_distance && // Negated condition 1 - unsafe_interval - rest >= ten_kappa && // Negated condition 2 - (rest + ten_kappa < small_distance || // buffer{-1} > w_high - small_distance - rest >= rest + ten_kappa - small_distance)) { - buffer[length - 1]--; - rest += ten_kappa; - } - - // We have approached w+ as much as possible. We now test if approaching w- - // would require changing the buffer. If yes, then we have two possible - // representations close to w, but we cannot decide which one is closer. - if (rest < big_distance && - unsafe_interval - rest >= ten_kappa && - (rest + ten_kappa < big_distance || - big_distance - rest > rest + ten_kappa - big_distance)) { - return false; - } - - // Weeding test. - // The safe interval is [too_low + 2 ulp; too_high - 2 ulp] - // Since too_low = too_high - unsafe_interval this is equivalent to - // [too_high - unsafe_interval + 4 ulp; too_high - 2 ulp] - // Conceptually we have: rest ~= too_high - buffer - return (2 * unit <= rest) && (rest <= unsafe_interval - 4 * unit); - } - - - // Rounds the buffer upwards if the result is closer to v by possibly adding - // 1 to the buffer. If the precision of the calculation is not sufficient to - // round correctly, return false. - // The rounding might shift the whole buffer in which case the kappa is - // adjusted. For example "99", kappa = 3 might become "10", kappa = 4. - // - // If 2*rest > ten_kappa then the buffer needs to be round up. - // rest can have an error of +/- 1 unit. This function accounts for the - // imprecision and returns false, if the rounding direction cannot be - // unambiguously determined. - // - // Precondition: rest < ten_kappa. - static bool RoundWeedCounted(Vector buffer, - int length, - uint64_t rest, - uint64_t ten_kappa, - uint64_t unit, - int* kappa) { - ASSERT(rest < ten_kappa); - // The following tests are done in a specific order to avoid overflows. They - // will work correctly with any uint64 values of rest < ten_kappa and unit. - // - // If the unit is too big, then we don't know which way to round. For example - // a unit of 50 means that the real number lies within rest +/- 50. If - // 10^kappa == 40 then there is no way to tell which way to round. - if (unit >= ten_kappa) return false; - // Even if unit is just half the size of 10^kappa we are already completely - // lost. (And after the previous test we know that the expression will not - // over/underflow.) - if (ten_kappa - unit <= unit) return false; - // If 2 * (rest + unit) <= 10^kappa we can safely round down. - if ((ten_kappa - rest > rest) && (ten_kappa - 2 * rest >= 2 * unit)) { - return true; - } - // If 2 * (rest - unit) >= 10^kappa, then we can safely round up. - if ((rest > unit) && (ten_kappa - (rest - unit) <= (rest - unit))) { - // Increment the last digit recursively until we find a non '9' digit. - buffer[length - 1]++; - for (int i = length - 1; i > 0; --i) { - if (buffer[i] != '0' + 10) break; - buffer[i] = '0'; - buffer[i - 1]++; - } - // If the first digit is now '0'+ 10 we had a buffer with all '9's. With the - // exception of the first digit all digits are now '0'. Simply switch the - // first digit to '1' and adjust the kappa. Example: "99" becomes "10" and - // the power (the kappa) is increased. - if (buffer[0] == '0' + 10) { - buffer[0] = '1'; - (*kappa) += 1; - } - return true; - } - return false; - } - - - static const uint32_t kTen4 = 10000; - static const uint32_t kTen5 = 100000; - static const uint32_t kTen6 = 1000000; - static const uint32_t kTen7 = 10000000; - static const uint32_t kTen8 = 100000000; - static const uint32_t kTen9 = 1000000000; - - // Returns the biggest power of ten that is less than or equal to the given - // number. We furthermore receive the maximum number of bits 'number' has. - // If number_bits == 0 then 0^-1 is returned - // The number of bits must be <= 32. - // Precondition: number < (1 << (number_bits + 1)). - static void BiggestPowerTen(uint32_t number, - int number_bits, - uint32_t* power, - int* exponent) { - ASSERT(number < (uint32_t)(1 << (number_bits + 1))); - - switch (number_bits) { - case 32: - case 31: - case 30: - if (kTen9 <= number) { - *power = kTen9; - *exponent = 9; - break; - } // else fallthrough - case 29: - case 28: - case 27: - if (kTen8 <= number) { - *power = kTen8; - *exponent = 8; - break; - } // else fallthrough - case 26: - case 25: - case 24: - if (kTen7 <= number) { - *power = kTen7; - *exponent = 7; - break; - } // else fallthrough - case 23: - case 22: - case 21: - case 20: - if (kTen6 <= number) { - *power = kTen6; - *exponent = 6; - break; - } // else fallthrough - case 19: - case 18: - case 17: - if (kTen5 <= number) { - *power = kTen5; - *exponent = 5; - break; - } // else fallthrough - case 16: - case 15: - case 14: - if (kTen4 <= number) { - *power = kTen4; - *exponent = 4; - break; - } // else fallthrough - case 13: - case 12: - case 11: - case 10: - if (1000 <= number) { - *power = 1000; - *exponent = 3; - break; - } // else fallthrough - case 9: - case 8: - case 7: - if (100 <= number) { - *power = 100; - *exponent = 2; - break; - } // else fallthrough - case 6: - case 5: - case 4: - if (10 <= number) { - *power = 10; - *exponent = 1; - break; - } // else fallthrough - case 3: - case 2: - case 1: - if (1 <= number) { - *power = 1; - *exponent = 0; - break; - } // else fallthrough - case 0: - *power = 0; - *exponent = -1; - break; - default: - // Following assignments are here to silence compiler warnings. - *power = 0; - *exponent = 0; - UNREACHABLE(); - } - } - - - // Generates the digits of input number w. - // w is a floating-point number (DiyFp), consisting of a significand and an - // exponent. Its exponent is bounded by kMinimalTargetExponent and - // kMaximalTargetExponent. - // Hence -60 <= w.e() <= -32. - // - // Returns false if it fails, in which case the generated digits in the buffer - // should not be used. - // Preconditions: - // * low, w and high are correct up to 1 ulp (unit in the last place). That - // is, their error must be less than a unit of their last digits. - // * low.e() == w.e() == high.e() - // * low < w < high, and taking into account their error: low~ <= high~ - // * kMinimalTargetExponent <= w.e() <= kMaximalTargetExponent - // Postconditions: returns false if procedure fails. - // otherwise: - // * buffer is not null-terminated, but len contains the number of digits. - // * buffer contains the shortest possible decimal digit-sequence - // such that LOW < buffer * 10^kappa < HIGH, where LOW and HIGH are the - // correct values of low and high (without their error). - // * if more than one decimal representation gives the minimal number of - // decimal digits then the one closest to W (where W is the correct value - // of w) is chosen. - // Remark: this procedure takes into account the imprecision of its input - // numbers. If the precision is not enough to guarantee all the postconditions - // then false is returned. This usually happens rarely (~0.5%). - // - // Say, for the sake of example, that - // w.e() == -48, and w.f() == 0x1234567890abcdef - // w's value can be computed by w.f() * 2^w.e() - // We can obtain w's integral digits by simply shifting w.f() by -w.e(). - // -> w's integral part is 0x1234 - // w's fractional part is therefore 0x567890abcdef. - // Printing w's integral part is easy (simply print 0x1234 in decimal). - // In order to print its fraction we repeatedly multiply the fraction by 10 and - // get each digit. Example the first digit after the point would be computed by - // (0x567890abcdef * 10) >> 48. -> 3 - // The whole thing becomes slightly more complicated because we want to stop - // once we have enough digits. That is, once the digits inside the buffer - // represent 'w' we can stop. Everything inside the interval low - high - // represents w. However we have to pay attention to low, high and w's - // imprecision. - static bool DigitGen(DiyFp low, - DiyFp w, - DiyFp high, - Vector buffer, - int* length, - int* kappa) { - ASSERT(low.e() == w.e() && w.e() == high.e()); - ASSERT(low.f() + 1 <= high.f() - 1); - ASSERT(kMinimalTargetExponent <= w.e() && w.e() <= kMaximalTargetExponent); - // low, w and high are imprecise, but by less than one ulp (unit in the last - // place). - // If we remove (resp. add) 1 ulp from low (resp. high) we are certain that - // the new numbers are outside of the interval we want the final - // representation to lie in. - // Inversely adding (resp. removing) 1 ulp from low (resp. high) would yield - // numbers that are certain to lie in the interval. We will use this fact - // later on. - // We will now start by generating the digits within the uncertain - // interval. Later we will weed out representations that lie outside the safe - // interval and thus _might_ lie outside the correct interval. - uint64_t unit = 1; - DiyFp too_low = DiyFp(low.f() - unit, low.e()); - DiyFp too_high = DiyFp(high.f() + unit, high.e()); - // too_low and too_high are guaranteed to lie outside the interval we want the - // generated number in. - DiyFp unsafe_interval = DiyFp::Minus(too_high, too_low); - // We now cut the input number into two parts: the integral digits and the - // fractionals. We will not write any decimal separator though, but adapt - // kappa instead. - // Reminder: we are currently computing the digits (stored inside the buffer) - // such that: too_low < buffer * 10^kappa < too_high - // We use too_high for the digit_generation and stop as soon as possible. - // If we stop early we effectively round down. - DiyFp one = DiyFp(static_cast(1) << -w.e(), w.e()); - // Division by one is a shift. - uint32_t integrals = static_cast(too_high.f() >> -one.e()); - // Modulo by one is an and. - uint64_t fractionals = too_high.f() & (one.f() - 1); - uint32_t divisor; - int divisor_exponent; - BiggestPowerTen(integrals, DiyFp::kSignificandSize - (-one.e()), - &divisor, &divisor_exponent); - *kappa = divisor_exponent + 1; - *length = 0; - // Loop invariant: buffer = too_high / 10^kappa (integer division) - // The invariant holds for the first iteration: kappa has been initialized - // with the divisor exponent + 1. And the divisor is the biggest power of ten - // that is smaller than integrals. - while (*kappa > 0) { - int digit = integrals / divisor; - buffer[*length] = '0' + digit; - (*length)++; - integrals %= divisor; - (*kappa)--; - // Note that kappa now equals the exponent of the divisor and that the - // invariant thus holds again. - uint64_t rest = - (static_cast(integrals) << -one.e()) + fractionals; - // Invariant: too_high = buffer * 10^kappa + DiyFp(rest, one.e()) - // Reminder: unsafe_interval.e() == one.e() - if (rest < unsafe_interval.f()) { - // Rounding down (by not emitting the remaining digits) yields a number - // that lies within the unsafe interval. - return RoundWeed(buffer, *length, DiyFp::Minus(too_high, w).f(), - unsafe_interval.f(), rest, - static_cast(divisor) << -one.e(), unit); - } - divisor /= 10; - } - - // The integrals have been generated. We are at the point of the decimal - // separator. In the following loop we simply multiply the remaining digits by - // 10 and divide by one. We just need to pay attention to multiply associated - // data (like the interval or 'unit'), too. - // Note that the multiplication by 10 does not overflow, because w.e >= -60 - // and thus one.e >= -60. - ASSERT(one.e() >= -60); - ASSERT(fractionals < one.f()); - ASSERT(UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF) / 10 >= one.f()); - while (true) { - fractionals *= 10; - unit *= 10; - unsafe_interval.set_f(unsafe_interval.f() * 10); - // Integer division by one. - int digit = static_cast(fractionals >> -one.e()); - buffer[*length] = '0' + digit; - (*length)++; - fractionals &= one.f() - 1; // Modulo by one. - (*kappa)--; - if (fractionals < unsafe_interval.f()) { - return RoundWeed(buffer, *length, DiyFp::Minus(too_high, w).f() * unit, - unsafe_interval.f(), fractionals, one.f(), unit); - } - } - } - - - - // Generates (at most) requested_digits digits of input number w. - // w is a floating-point number (DiyFp), consisting of a significand and an - // exponent. Its exponent is bounded by kMinimalTargetExponent and - // kMaximalTargetExponent. - // Hence -60 <= w.e() <= -32. - // - // Returns false if it fails, in which case the generated digits in the buffer - // should not be used. - // Preconditions: - // * w is correct up to 1 ulp (unit in the last place). That - // is, its error must be strictly less than a unit of its last digit. - // * kMinimalTargetExponent <= w.e() <= kMaximalTargetExponent - // - // Postconditions: returns false if procedure fails. - // otherwise: - // * buffer is not null-terminated, but length contains the number of - // digits. - // * the representation in buffer is the most precise representation of - // requested_digits digits. - // * buffer contains at most requested_digits digits of w. If there are less - // than requested_digits digits then some trailing '0's have been removed. - // * kappa is such that - // w = buffer * 10^kappa + eps with |eps| < 10^kappa / 2. - // - // Remark: This procedure takes into account the imprecision of its input - // numbers. If the precision is not enough to guarantee all the postconditions - // then false is returned. This usually happens rarely, but the failure-rate - // increases with higher requested_digits. - static bool DigitGenCounted(DiyFp w, - int requested_digits, - Vector buffer, - int* length, - int* kappa) { - ASSERT(kMinimalTargetExponent <= w.e() && w.e() <= kMaximalTargetExponent); - ASSERT(kMinimalTargetExponent >= -60); - ASSERT(kMaximalTargetExponent <= -32); - // w is assumed to have an error less than 1 unit. Whenever w is scaled we - // also scale its error. - uint64_t w_error = 1; - // We cut the input number into two parts: the integral digits and the - // fractional digits. We don't emit any decimal separator, but adapt kappa - // instead. Example: instead of writing "1.2" we put "12" into the buffer and - // increase kappa by 1. - DiyFp one = DiyFp(static_cast(1) << -w.e(), w.e()); - // Division by one is a shift. - uint32_t integrals = static_cast(w.f() >> -one.e()); - // Modulo by one is an and. - uint64_t fractionals = w.f() & (one.f() - 1); - uint32_t divisor; - int divisor_exponent; - BiggestPowerTen(integrals, DiyFp::kSignificandSize - (-one.e()), - &divisor, &divisor_exponent); - *kappa = divisor_exponent + 1; - *length = 0; - - // Loop invariant: buffer = w / 10^kappa (integer division) - // The invariant holds for the first iteration: kappa has been initialized - // with the divisor exponent + 1. And the divisor is the biggest power of ten - // that is smaller than 'integrals'. - while (*kappa > 0) { - int digit = integrals / divisor; - buffer[*length] = '0' + digit; - (*length)++; - requested_digits--; - integrals %= divisor; - (*kappa)--; - // Note that kappa now equals the exponent of the divisor and that the - // invariant thus holds again. - if (requested_digits == 0) break; - divisor /= 10; - } - - if (requested_digits == 0) { - uint64_t rest = - (static_cast(integrals) << -one.e()) + fractionals; - return RoundWeedCounted(buffer, *length, rest, - static_cast(divisor) << -one.e(), w_error, - kappa); - } - - // The integrals have been generated. We are at the point of the decimal - // separator. In the following loop we simply multiply the remaining digits by - // 10 and divide by one. We just need to pay attention to multiply associated - // data (the 'unit'), too. - // Note that the multiplication by 10 does not overflow, because w.e >= -60 - // and thus one.e >= -60. - ASSERT(one.e() >= -60); - ASSERT(fractionals < one.f()); - ASSERT(UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF) / 10 >= one.f()); - while (requested_digits > 0 && fractionals > w_error) { - fractionals *= 10; - w_error *= 10; - // Integer division by one. - int digit = static_cast(fractionals >> -one.e()); - buffer[*length] = '0' + digit; - (*length)++; - requested_digits--; - fractionals &= one.f() - 1; // Modulo by one. - (*kappa)--; - } - if (requested_digits != 0) return false; - return RoundWeedCounted(buffer, *length, fractionals, one.f(), w_error, - kappa); +// The minimal and maximal target exponent define the range of w's binary +// exponent, where 'w' is the result of multiplying the input by a cached power +// of ten. +// +// A different range might be chosen on a different platform, to optimize digit +// generation, but a smaller range requires more powers of ten to be cached. +static const int kMinimalTargetExponent = -60; +static const int kMaximalTargetExponent = -32; + +// Adjusts the last digit of the generated number, and screens out generated +// solutions that may be inaccurate. A solution may be inaccurate if it is +// outside the safe interval, or if we cannot prove that it is closer to the +// input than a neighboring representation of the same length. +// +// Input: * buffer containing the digits of too_high / 10^kappa +// * the buffer's length +// * distance_too_high_w == (too_high - w).f() * unit +// * unsafe_interval == (too_high - too_low).f() * unit +// * rest = (too_high - buffer * 10^kappa).f() * unit +// * ten_kappa = 10^kappa * unit +// * unit = the common multiplier +// Output: returns true if the buffer is guaranteed to contain the closest +// representable number to the input. +// Modifies the generated digits in the buffer to approach (round towards) w. +static bool RoundWeed(Vector buffer, + int length, + uint64_t distance_too_high_w, + uint64_t unsafe_interval, + uint64_t rest, + uint64_t ten_kappa, + uint64_t unit) { + uint64_t small_distance = distance_too_high_w - unit; + uint64_t big_distance = distance_too_high_w + unit; + // Let w_low = too_high - big_distance, and + // w_high = too_high - small_distance. + // Note: w_low < w < w_high + // + // The real w (* unit) must lie somewhere inside the interval + // ]w_low; w_high[ (often written as "(w_low; w_high)") + + // Basically the buffer currently contains a number in the unsafe interval + // ]too_low; too_high[ with too_low < w < too_high + // + // too_high - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // ^v 1 unit ^ ^ ^ ^ + // boundary_high --------------------- . . . . + // ^v 1 unit . . . . + // - - - - - - - - - - - - - - - - - - - + - - + - - - - - - . . + // . . ^ . . + // . big_distance . . . + // . . . . rest + // small_distance . . . . + // v . . . . + // w_high - - - - - - - - - - - - - - - - - - . . . . + // ^v 1 unit . . . . + // w ---------------------------------------- . . . . + // ^v 1 unit v . . . + // w_low - - - - - - - - - - - - - - - - - - - - - . . . + // . . v + // buffer --------------------------------------------------+-------+-------- + // . . + // safe_interval . + // v . + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - . + // ^v 1 unit . + // boundary_low ------------------------- unsafe_interval + // ^v 1 unit v + // too_low - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // + // + // Note that the value of buffer could lie anywhere inside the range too_low + // to too_high. + // + // boundary_low, boundary_high and w are approximations of the real boundaries + // and v (the input number). They are guaranteed to be precise up to one unit. + // In fact the error is guaranteed to be strictly less than one unit. + // + // Anything that lies outside the unsafe interval is guaranteed not to round + // to v when read again. + // Anything that lies inside the safe interval is guaranteed to round to v + // when read again. + // If the number inside the buffer lies inside the unsafe interval but not + // inside the safe interval then we simply do not know and bail out (returning + // false). + // + // Similarly we have to take into account the imprecision of 'w' when finding + // the closest representation of 'w'. If we have two potential + // representations, and one is closer to both w_low and w_high, then we know + // it is closer to the actual value v. + // + // By generating the digits of too_high we got the largest (closest to + // too_high) buffer that is still in the unsafe interval. In the case where + // w_high < buffer < too_high we try to decrement the buffer. + // This way the buffer approaches (rounds towards) w. + // There are 3 conditions that stop the decrementation process: + // 1) the buffer is already below w_high + // 2) decrementing the buffer would make it leave the unsafe interval + // 3) decrementing the buffer would yield a number below w_high and farther + // away than the current number. In other words: + // (buffer{-1} < w_high) && w_high - buffer{-1} > buffer - w_high + // Instead of using the buffer directly we use its distance to too_high. + // Conceptually rest ~= too_high - buffer + // We need to do the following tests in this order to avoid over- and + // underflows. + ASSERT(rest <= unsafe_interval); + while (rest < small_distance && // Negated condition 1 + unsafe_interval - rest >= ten_kappa && // Negated condition 2 + (rest + ten_kappa < small_distance || // buffer{-1} > w_high + small_distance - rest >= rest + ten_kappa - small_distance)) { + buffer[length - 1]--; + rest += ten_kappa; + } + + // We have approached w+ as much as possible. We now test if approaching w- + // would require changing the buffer. If yes, then we have two possible + // representations close to w, but we cannot decide which one is closer. + if (rest < big_distance && unsafe_interval - rest >= ten_kappa && + (rest + ten_kappa < big_distance || + big_distance - rest > rest + ten_kappa - big_distance)) { + return false; + } + + // Weeding test. + // The safe interval is [too_low + 2 ulp; too_high - 2 ulp] + // Since too_low = too_high - unsafe_interval this is equivalent to + // [too_high - unsafe_interval + 4 ulp; too_high - 2 ulp] + // Conceptually we have: rest ~= too_high - buffer + return (2 * unit <= rest) && (rest <= unsafe_interval - 4 * unit); +} + +// Rounds the buffer upwards if the result is closer to v by possibly adding +// 1 to the buffer. If the precision of the calculation is not sufficient to +// round correctly, return false. +// The rounding might shift the whole buffer in which case the kappa is +// adjusted. For example "99", kappa = 3 might become "10", kappa = 4. +// +// If 2*rest > ten_kappa then the buffer needs to be round up. +// rest can have an error of +/- 1 unit. This function accounts for the +// imprecision and returns false, if the rounding direction cannot be +// unambiguously determined. +// +// Precondition: rest < ten_kappa. +static bool RoundWeedCounted(Vector buffer, + int length, + uint64_t rest, + uint64_t ten_kappa, + uint64_t unit, + int* kappa) { + ASSERT(rest < ten_kappa); + // The following tests are done in a specific order to avoid overflows. They + // will work correctly with any uint64 values of rest < ten_kappa and unit. + // + // If the unit is too big, then we don't know which way to round. For example + // a unit of 50 means that the real number lies within rest +/- 50. If + // 10^kappa == 40 then there is no way to tell which way to round. + if (unit >= ten_kappa) + return false; + // Even if unit is just half the size of 10^kappa we are already completely + // lost. (And after the previous test we know that the expression will not + // over/underflow.) + if (ten_kappa - unit <= unit) + return false; + // If 2 * (rest + unit) <= 10^kappa we can safely round down. + if ((ten_kappa - rest > rest) && (ten_kappa - 2 * rest >= 2 * unit)) { + return true; + } + // If 2 * (rest - unit) >= 10^kappa, then we can safely round up. + if ((rest > unit) && (ten_kappa - (rest - unit) <= (rest - unit))) { + // Increment the last digit recursively until we find a non '9' digit. + buffer[length - 1]++; + for (int i = length - 1; i > 0; --i) { + if (buffer[i] != '0' + 10) + break; + buffer[i] = '0'; + buffer[i - 1]++; } - - - // Provides a decimal representation of v. - // Returns true if it succeeds, otherwise the result cannot be trusted. - // There will be *length digits inside the buffer (not null-terminated). - // If the function returns true then - // v == (double) (buffer * 10^decimal_exponent). - // The digits in the buffer are the shortest representation possible: no - // 0.09999999999999999 instead of 0.1. The shorter representation will even be - // chosen even if the longer one would be closer to v. - // The last digit will be closest to the actual v. That is, even if several - // digits might correctly yield 'v' when read again, the closest will be - // computed. - static bool Grisu3(double v, - Vector buffer, - int* length, - int* decimal_exponent) { - DiyFp w = Double(v).AsNormalizedDiyFp(); - // boundary_minus and boundary_plus are the boundaries between v and its - // closest floating-point neighbors. Any number strictly between - // boundary_minus and boundary_plus will round to v when convert to a double. - // Grisu3 will never output representations that lie exactly on a boundary. - DiyFp boundary_minus, boundary_plus; - Double(v).NormalizedBoundaries(&boundary_minus, &boundary_plus); - ASSERT(boundary_plus.e() == w.e()); - DiyFp ten_mk; // Cached power of ten: 10^-k - int mk; // -k - int ten_mk_minimal_binary_exponent = - kMinimalTargetExponent - (w.e() + DiyFp::kSignificandSize); - int ten_mk_maximal_binary_exponent = - kMaximalTargetExponent - (w.e() + DiyFp::kSignificandSize); - PowersOfTenCache::GetCachedPowerForBinaryExponentRange( - ten_mk_minimal_binary_exponent, - ten_mk_maximal_binary_exponent, - &ten_mk, &mk); - ASSERT((kMinimalTargetExponent <= w.e() + ten_mk.e() + - DiyFp::kSignificandSize) && - (kMaximalTargetExponent >= w.e() + ten_mk.e() + - DiyFp::kSignificandSize)); - // Note that ten_mk is only an approximation of 10^-k. A DiyFp only contains a - // 64 bit significand and ten_mk is thus only precise up to 64 bits. - - // The DiyFp::Times procedure rounds its result, and ten_mk is approximated - // too. The variable scaled_w (as well as scaled_boundary_minus/plus) are now - // off by a small amount. - // In fact: scaled_w - w*10^k < 1ulp (unit in the last place) of scaled_w. - // In other words: let f = scaled_w.f() and e = scaled_w.e(), then - // (f-1) * 2^e < w*10^k < (f+1) * 2^e - DiyFp scaled_w = DiyFp::Times(w, ten_mk); - ASSERT(scaled_w.e() == - boundary_plus.e() + ten_mk.e() + DiyFp::kSignificandSize); - // In theory it would be possible to avoid some recomputations by computing - // the difference between w and boundary_minus/plus (a power of 2) and to - // compute scaled_boundary_minus/plus by subtracting/adding from - // scaled_w. However the code becomes much less readable and the speed - // enhancements are not terriffic. - DiyFp scaled_boundary_minus = DiyFp::Times(boundary_minus, ten_mk); - DiyFp scaled_boundary_plus = DiyFp::Times(boundary_plus, ten_mk); - - // DigitGen will generate the digits of scaled_w. Therefore we have - // v == (double) (scaled_w * 10^-mk). - // Set decimal_exponent == -mk and pass it to DigitGen. If scaled_w is not an - // integer than it will be updated. For instance if scaled_w == 1.23 then - // the buffer will be filled with "123" und the decimal_exponent will be - // decreased by 2. - int kappa; - bool result = DigitGen(scaled_boundary_minus, scaled_w, scaled_boundary_plus, - buffer, length, &kappa); - *decimal_exponent = -mk + kappa; - return result; + // If the first digit is now '0'+ 10 we had a buffer with all '9's. With the + // exception of the first digit all digits are now '0'. Simply switch the + // first digit to '1' and adjust the kappa. Example: "99" becomes "10" and + // the power (the kappa) is increased. + if (buffer[0] == '0' + 10) { + buffer[0] = '1'; + (*kappa) += 1; } - - - // The "counted" version of grisu3 (see above) only generates requested_digits - // number of digits. This version does not generate the shortest representation, - // and with enough requested digits 0.1 will at some point print as 0.9999999... - // Grisu3 is too imprecise for real halfway cases (1.5 will not work) and - // therefore the rounding strategy for halfway cases is irrelevant. - static bool Grisu3Counted(double v, - int requested_digits, - Vector buffer, - int* length, - int* decimal_exponent) { - DiyFp w = Double(v).AsNormalizedDiyFp(); - DiyFp ten_mk; // Cached power of ten: 10^-k - int mk; // -k - int ten_mk_minimal_binary_exponent = - kMinimalTargetExponent - (w.e() + DiyFp::kSignificandSize); - int ten_mk_maximal_binary_exponent = - kMaximalTargetExponent - (w.e() + DiyFp::kSignificandSize); - PowersOfTenCache::GetCachedPowerForBinaryExponentRange( - ten_mk_minimal_binary_exponent, - ten_mk_maximal_binary_exponent, - &ten_mk, &mk); - ASSERT((kMinimalTargetExponent <= w.e() + ten_mk.e() + - DiyFp::kSignificandSize) && - (kMaximalTargetExponent >= w.e() + ten_mk.e() + - DiyFp::kSignificandSize)); - // Note that ten_mk is only an approximation of 10^-k. A DiyFp only contains a - // 64 bit significand and ten_mk is thus only precise up to 64 bits. - - // The DiyFp::Times procedure rounds its result, and ten_mk is approximated - // too. The variable scaled_w (as well as scaled_boundary_minus/plus) are now - // off by a small amount. - // In fact: scaled_w - w*10^k < 1ulp (unit in the last place) of scaled_w. - // In other words: let f = scaled_w.f() and e = scaled_w.e(), then - // (f-1) * 2^e < w*10^k < (f+1) * 2^e - DiyFp scaled_w = DiyFp::Times(w, ten_mk); - - // We now have (double) (scaled_w * 10^-mk). - // DigitGen will generate the first requested_digits digits of scaled_w and - // return together with a kappa such that scaled_w ~= buffer * 10^kappa. (It - // will not always be exactly the same since DigitGenCounted only produces a - // limited number of digits.) - int kappa; - bool result = DigitGenCounted(scaled_w, requested_digits, - buffer, length, &kappa); - *decimal_exponent = -mk + kappa; - return result; + return true; + } + return false; +} + +static const uint32_t kTen4 = 10000; +static const uint32_t kTen5 = 100000; +static const uint32_t kTen6 = 1000000; +static const uint32_t kTen7 = 10000000; +static const uint32_t kTen8 = 100000000; +static const uint32_t kTen9 = 1000000000; + +// Returns the biggest power of ten that is less than or equal to the given +// number. We furthermore receive the maximum number of bits 'number' has. +// If number_bits == 0 then 0^-1 is returned +// The number of bits must be <= 32. +// Precondition: number < (1 << (number_bits + 1)). +static void BiggestPowerTen(uint32_t number, + int number_bits, + uint32_t* power, + int* exponent) { + ASSERT(number < (uint32_t)(1 << (number_bits + 1))); + + switch (number_bits) { + case 32: + case 31: + case 30: + if (kTen9 <= number) { + *power = kTen9; + *exponent = 9; + break; + } // else fallthrough + case 29: + case 28: + case 27: + if (kTen8 <= number) { + *power = kTen8; + *exponent = 8; + break; + } // else fallthrough + case 26: + case 25: + case 24: + if (kTen7 <= number) { + *power = kTen7; + *exponent = 7; + break; + } // else fallthrough + case 23: + case 22: + case 21: + case 20: + if (kTen6 <= number) { + *power = kTen6; + *exponent = 6; + break; + } // else fallthrough + case 19: + case 18: + case 17: + if (kTen5 <= number) { + *power = kTen5; + *exponent = 5; + break; + } // else fallthrough + case 16: + case 15: + case 14: + if (kTen4 <= number) { + *power = kTen4; + *exponent = 4; + break; + } // else fallthrough + case 13: + case 12: + case 11: + case 10: + if (1000 <= number) { + *power = 1000; + *exponent = 3; + break; + } // else fallthrough + case 9: + case 8: + case 7: + if (100 <= number) { + *power = 100; + *exponent = 2; + break; + } // else fallthrough + case 6: + case 5: + case 4: + if (10 <= number) { + *power = 10; + *exponent = 1; + break; + } // else fallthrough + case 3: + case 2: + case 1: + if (1 <= number) { + *power = 1; + *exponent = 0; + break; + } // else fallthrough + case 0: + *power = 0; + *exponent = -1; + break; + default: + // Following assignments are here to silence compiler warnings. + *power = 0; + *exponent = 0; + UNREACHABLE(); + } +} + +// Generates the digits of input number w. +// w is a floating-point number (DiyFp), consisting of a significand and an +// exponent. Its exponent is bounded by kMinimalTargetExponent and +// kMaximalTargetExponent. +// Hence -60 <= w.e() <= -32. +// +// Returns false if it fails, in which case the generated digits in the buffer +// should not be used. +// Preconditions: +// * low, w and high are correct up to 1 ulp (unit in the last place). That +// is, their error must be less than a unit of their last digits. +// * low.e() == w.e() == high.e() +// * low < w < high, and taking into account their error: low~ <= high~ +// * kMinimalTargetExponent <= w.e() <= kMaximalTargetExponent +// Postconditions: returns false if procedure fails. +// otherwise: +// * buffer is not null-terminated, but len contains the number of digits. +// * buffer contains the shortest possible decimal digit-sequence +// such that LOW < buffer * 10^kappa < HIGH, where LOW and HIGH are the +// correct values of low and high (without their error). +// * if more than one decimal representation gives the minimal number of +// decimal digits then the one closest to W (where W is the correct value +// of w) is chosen. +// Remark: this procedure takes into account the imprecision of its input +// numbers. If the precision is not enough to guarantee all the postconditions +// then false is returned. This usually happens rarely (~0.5%). +// +// Say, for the sake of example, that +// w.e() == -48, and w.f() == 0x1234567890abcdef +// w's value can be computed by w.f() * 2^w.e() +// We can obtain w's integral digits by simply shifting w.f() by -w.e(). +// -> w's integral part is 0x1234 +// w's fractional part is therefore 0x567890abcdef. +// Printing w's integral part is easy (simply print 0x1234 in decimal). +// In order to print its fraction we repeatedly multiply the fraction by 10 and +// get each digit. Example the first digit after the point would be computed by +// (0x567890abcdef * 10) >> 48. -> 3 +// The whole thing becomes slightly more complicated because we want to stop +// once we have enough digits. That is, once the digits inside the buffer +// represent 'w' we can stop. Everything inside the interval low - high +// represents w. However we have to pay attention to low, high and w's +// imprecision. +static bool DigitGen(DiyFp low, + DiyFp w, + DiyFp high, + Vector buffer, + int* length, + int* kappa) { + ASSERT(low.e() == w.e() && w.e() == high.e()); + ASSERT(low.f() + 1 <= high.f() - 1); + ASSERT(kMinimalTargetExponent <= w.e() && w.e() <= kMaximalTargetExponent); + // low, w and high are imprecise, but by less than one ulp (unit in the last + // place). + // If we remove (resp. add) 1 ulp from low (resp. high) we are certain that + // the new numbers are outside of the interval we want the final + // representation to lie in. + // Inversely adding (resp. removing) 1 ulp from low (resp. high) would yield + // numbers that are certain to lie in the interval. We will use this fact + // later on. + // We will now start by generating the digits within the uncertain + // interval. Later we will weed out representations that lie outside the safe + // interval and thus _might_ lie outside the correct interval. + uint64_t unit = 1; + DiyFp too_low = DiyFp(low.f() - unit, low.e()); + DiyFp too_high = DiyFp(high.f() + unit, high.e()); + // too_low and too_high are guaranteed to lie outside the interval we want the + // generated number in. + DiyFp unsafe_interval = DiyFp::Minus(too_high, too_low); + // We now cut the input number into two parts: the integral digits and the + // fractionals. We will not write any decimal separator though, but adapt + // kappa instead. + // Reminder: we are currently computing the digits (stored inside the buffer) + // such that: too_low < buffer * 10^kappa < too_high + // We use too_high for the digit_generation and stop as soon as possible. + // If we stop early we effectively round down. + DiyFp one = DiyFp(static_cast(1) << -w.e(), w.e()); + // Division by one is a shift. + uint32_t integrals = static_cast(too_high.f() >> -one.e()); + // Modulo by one is an and. + uint64_t fractionals = too_high.f() & (one.f() - 1); + uint32_t divisor; + int divisor_exponent; + BiggestPowerTen(integrals, DiyFp::kSignificandSize - (-one.e()), &divisor, + &divisor_exponent); + *kappa = divisor_exponent + 1; + *length = 0; + // Loop invariant: buffer = too_high / 10^kappa (integer division) + // The invariant holds for the first iteration: kappa has been initialized + // with the divisor exponent + 1. And the divisor is the biggest power of ten + // that is smaller than integrals. + while (*kappa > 0) { + int digit = integrals / divisor; + buffer[*length] = '0' + digit; + (*length)++; + integrals %= divisor; + (*kappa)--; + // Note that kappa now equals the exponent of the divisor and that the + // invariant thus holds again. + uint64_t rest = + (static_cast(integrals) << -one.e()) + fractionals; + // Invariant: too_high = buffer * 10^kappa + DiyFp(rest, one.e()) + // Reminder: unsafe_interval.e() == one.e() + if (rest < unsafe_interval.f()) { + // Rounding down (by not emitting the remaining digits) yields a number + // that lies within the unsafe interval. + return RoundWeed(buffer, *length, DiyFp::Minus(too_high, w).f(), + unsafe_interval.f(), rest, + static_cast(divisor) << -one.e(), unit); } - - - bool FastDtoa(double v, - FastDtoaMode mode, - int requested_digits, - Vector buffer, - int* length, - int* decimal_point) { - ASSERT(v > 0); - ASSERT(!Double(v).IsSpecial()); - - bool result = false; - int decimal_exponent = 0; - switch (mode) { - case FAST_DTOA_SHORTEST: - result = Grisu3(v, buffer, length, &decimal_exponent); - break; - case FAST_DTOA_PRECISION: - result = Grisu3Counted(v, requested_digits, - buffer, length, &decimal_exponent); - break; - default: - UNREACHABLE(); - } - if (result) { - *decimal_point = *length + decimal_exponent; - buffer[*length] = '\0'; - } - return result; + divisor /= 10; + } + + // The integrals have been generated. We are at the point of the decimal + // separator. In the following loop we simply multiply the remaining digits by + // 10 and divide by one. We just need to pay attention to multiply associated + // data (like the interval or 'unit'), too. + // Note that the multiplication by 10 does not overflow, because w.e >= -60 + // and thus one.e >= -60. + ASSERT(one.e() >= -60); + ASSERT(fractionals < one.f()); + ASSERT(UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF) / 10 >= one.f()); + while (true) { + fractionals *= 10; + unit *= 10; + unsafe_interval.set_f(unsafe_interval.f() * 10); + // Integer division by one. + int digit = static_cast(fractionals >> -one.e()); + buffer[*length] = '0' + digit; + (*length)++; + fractionals &= one.f() - 1; // Modulo by one. + (*kappa)--; + if (fractionals < unsafe_interval.f()) { + return RoundWeed(buffer, *length, DiyFp::Minus(too_high, w).f() * unit, + unsafe_interval.f(), fractionals, one.f(), unit); } + } +} + +// Generates (at most) requested_digits digits of input number w. +// w is a floating-point number (DiyFp), consisting of a significand and an +// exponent. Its exponent is bounded by kMinimalTargetExponent and +// kMaximalTargetExponent. +// Hence -60 <= w.e() <= -32. +// +// Returns false if it fails, in which case the generated digits in the buffer +// should not be used. +// Preconditions: +// * w is correct up to 1 ulp (unit in the last place). That +// is, its error must be strictly less than a unit of its last digit. +// * kMinimalTargetExponent <= w.e() <= kMaximalTargetExponent +// +// Postconditions: returns false if procedure fails. +// otherwise: +// * buffer is not null-terminated, but length contains the number of +// digits. +// * the representation in buffer is the most precise representation of +// requested_digits digits. +// * buffer contains at most requested_digits digits of w. If there are less +// than requested_digits digits then some trailing '0's have been removed. +// * kappa is such that +// w = buffer * 10^kappa + eps with |eps| < 10^kappa / 2. +// +// Remark: This procedure takes into account the imprecision of its input +// numbers. If the precision is not enough to guarantee all the postconditions +// then false is returned. This usually happens rarely, but the failure-rate +// increases with higher requested_digits. +static bool DigitGenCounted(DiyFp w, + int requested_digits, + Vector buffer, + int* length, + int* kappa) { + ASSERT(kMinimalTargetExponent <= w.e() && w.e() <= kMaximalTargetExponent); + ASSERT(kMinimalTargetExponent >= -60); + ASSERT(kMaximalTargetExponent <= -32); + // w is assumed to have an error less than 1 unit. Whenever w is scaled we + // also scale its error. + uint64_t w_error = 1; + // We cut the input number into two parts: the integral digits and the + // fractional digits. We don't emit any decimal separator, but adapt kappa + // instead. Example: instead of writing "1.2" we put "12" into the buffer and + // increase kappa by 1. + DiyFp one = DiyFp(static_cast(1) << -w.e(), w.e()); + // Division by one is a shift. + uint32_t integrals = static_cast(w.f() >> -one.e()); + // Modulo by one is an and. + uint64_t fractionals = w.f() & (one.f() - 1); + uint32_t divisor; + int divisor_exponent; + BiggestPowerTen(integrals, DiyFp::kSignificandSize - (-one.e()), &divisor, + &divisor_exponent); + *kappa = divisor_exponent + 1; + *length = 0; + + // Loop invariant: buffer = w / 10^kappa (integer division) + // The invariant holds for the first iteration: kappa has been initialized + // with the divisor exponent + 1. And the divisor is the biggest power of ten + // that is smaller than 'integrals'. + while (*kappa > 0) { + int digit = integrals / divisor; + buffer[*length] = '0' + digit; + (*length)++; + requested_digits--; + integrals %= divisor; + (*kappa)--; + // Note that kappa now equals the exponent of the divisor and that the + // invariant thus holds again. + if (requested_digits == 0) + break; + divisor /= 10; + } + + if (requested_digits == 0) { + uint64_t rest = + (static_cast(integrals) << -one.e()) + fractionals; + return RoundWeedCounted(buffer, *length, rest, + static_cast(divisor) << -one.e(), w_error, + kappa); + } + + // The integrals have been generated. We are at the point of the decimal + // separator. In the following loop we simply multiply the remaining digits by + // 10 and divide by one. We just need to pay attention to multiply associated + // data (the 'unit'), too. + // Note that the multiplication by 10 does not overflow, because w.e >= -60 + // and thus one.e >= -60. + ASSERT(one.e() >= -60); + ASSERT(fractionals < one.f()); + ASSERT(UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF) / 10 >= one.f()); + while (requested_digits > 0 && fractionals > w_error) { + fractionals *= 10; + w_error *= 10; + // Integer division by one. + int digit = static_cast(fractionals >> -one.e()); + buffer[*length] = '0' + digit; + (*length)++; + requested_digits--; + fractionals &= one.f() - 1; // Modulo by one. + (*kappa)--; + } + if (requested_digits != 0) + return false; + return RoundWeedCounted(buffer, *length, fractionals, one.f(), w_error, + kappa); +} + +// Provides a decimal representation of v. +// Returns true if it succeeds, otherwise the result cannot be trusted. +// There will be *length digits inside the buffer (not null-terminated). +// If the function returns true then +// v == (double) (buffer * 10^decimal_exponent). +// The digits in the buffer are the shortest representation possible: no +// 0.09999999999999999 instead of 0.1. The shorter representation will even be +// chosen even if the longer one would be closer to v. +// The last digit will be closest to the actual v. That is, even if several +// digits might correctly yield 'v' when read again, the closest will be +// computed. +static bool Grisu3(double v, + Vector buffer, + int* length, + int* decimal_exponent) { + DiyFp w = Double(v).AsNormalizedDiyFp(); + // boundary_minus and boundary_plus are the boundaries between v and its + // closest floating-point neighbors. Any number strictly between + // boundary_minus and boundary_plus will round to v when convert to a double. + // Grisu3 will never output representations that lie exactly on a boundary. + DiyFp boundary_minus, boundary_plus; + Double(v).NormalizedBoundaries(&boundary_minus, &boundary_plus); + ASSERT(boundary_plus.e() == w.e()); + DiyFp ten_mk; // Cached power of ten: 10^-k + int mk; // -k + int ten_mk_minimal_binary_exponent = + kMinimalTargetExponent - (w.e() + DiyFp::kSignificandSize); + int ten_mk_maximal_binary_exponent = + kMaximalTargetExponent - (w.e() + DiyFp::kSignificandSize); + PowersOfTenCache::GetCachedPowerForBinaryExponentRange( + ten_mk_minimal_binary_exponent, ten_mk_maximal_binary_exponent, &ten_mk, + &mk); + ASSERT( + (kMinimalTargetExponent <= + w.e() + ten_mk.e() + DiyFp::kSignificandSize) && + (kMaximalTargetExponent >= w.e() + ten_mk.e() + DiyFp::kSignificandSize)); + // Note that ten_mk is only an approximation of 10^-k. A DiyFp only contains a + // 64 bit significand and ten_mk is thus only precise up to 64 bits. + + // The DiyFp::Times procedure rounds its result, and ten_mk is approximated + // too. The variable scaled_w (as well as scaled_boundary_minus/plus) are now + // off by a small amount. + // In fact: scaled_w - w*10^k < 1ulp (unit in the last place) of scaled_w. + // In other words: let f = scaled_w.f() and e = scaled_w.e(), then + // (f-1) * 2^e < w*10^k < (f+1) * 2^e + DiyFp scaled_w = DiyFp::Times(w, ten_mk); + ASSERT(scaled_w.e() == + boundary_plus.e() + ten_mk.e() + DiyFp::kSignificandSize); + // In theory it would be possible to avoid some recomputations by computing + // the difference between w and boundary_minus/plus (a power of 2) and to + // compute scaled_boundary_minus/plus by subtracting/adding from + // scaled_w. However the code becomes much less readable and the speed + // enhancements are not terriffic. + DiyFp scaled_boundary_minus = DiyFp::Times(boundary_minus, ten_mk); + DiyFp scaled_boundary_plus = DiyFp::Times(boundary_plus, ten_mk); + + // DigitGen will generate the digits of scaled_w. Therefore we have + // v == (double) (scaled_w * 10^-mk). + // Set decimal_exponent == -mk and pass it to DigitGen. If scaled_w is not an + // integer than it will be updated. For instance if scaled_w == 1.23 then + // the buffer will be filled with "123" und the decimal_exponent will be + // decreased by 2. + int kappa; + bool result = DigitGen(scaled_boundary_minus, scaled_w, scaled_boundary_plus, + buffer, length, &kappa); + *decimal_exponent = -mk + kappa; + return result; +} + +// The "counted" version of grisu3 (see above) only generates requested_digits +// number of digits. This version does not generate the shortest representation, +// and with enough requested digits 0.1 will at some point print as 0.9999999... +// Grisu3 is too imprecise for real halfway cases (1.5 will not work) and +// therefore the rounding strategy for halfway cases is irrelevant. +static bool Grisu3Counted(double v, + int requested_digits, + Vector buffer, + int* length, + int* decimal_exponent) { + DiyFp w = Double(v).AsNormalizedDiyFp(); + DiyFp ten_mk; // Cached power of ten: 10^-k + int mk; // -k + int ten_mk_minimal_binary_exponent = + kMinimalTargetExponent - (w.e() + DiyFp::kSignificandSize); + int ten_mk_maximal_binary_exponent = + kMaximalTargetExponent - (w.e() + DiyFp::kSignificandSize); + PowersOfTenCache::GetCachedPowerForBinaryExponentRange( + ten_mk_minimal_binary_exponent, ten_mk_maximal_binary_exponent, &ten_mk, + &mk); + ASSERT( + (kMinimalTargetExponent <= + w.e() + ten_mk.e() + DiyFp::kSignificandSize) && + (kMaximalTargetExponent >= w.e() + ten_mk.e() + DiyFp::kSignificandSize)); + // Note that ten_mk is only an approximation of 10^-k. A DiyFp only contains a + // 64 bit significand and ten_mk is thus only precise up to 64 bits. + + // The DiyFp::Times procedure rounds its result, and ten_mk is approximated + // too. The variable scaled_w (as well as scaled_boundary_minus/plus) are now + // off by a small amount. + // In fact: scaled_w - w*10^k < 1ulp (unit in the last place) of scaled_w. + // In other words: let f = scaled_w.f() and e = scaled_w.e(), then + // (f-1) * 2^e < w*10^k < (f+1) * 2^e + DiyFp scaled_w = DiyFp::Times(w, ten_mk); + + // We now have (double) (scaled_w * 10^-mk). + // DigitGen will generate the first requested_digits digits of scaled_w and + // return together with a kappa such that scaled_w ~= buffer * 10^kappa. (It + // will not always be exactly the same since DigitGenCounted only produces a + // limited number of digits.) + int kappa; + bool result = + DigitGenCounted(scaled_w, requested_digits, buffer, length, &kappa); + *decimal_exponent = -mk + kappa; + return result; +} + +bool FastDtoa(double v, + FastDtoaMode mode, + int requested_digits, + Vector buffer, + int* length, + int* decimal_point) { + ASSERT(v > 0); + ASSERT(!Double(v).IsSpecial()); + + bool result = false; + int decimal_exponent = 0; + switch (mode) { + case FAST_DTOA_SHORTEST: + result = Grisu3(v, buffer, length, &decimal_exponent); + break; + case FAST_DTOA_PRECISION: + result = + Grisu3Counted(v, requested_digits, buffer, length, &decimal_exponent); + break; + default: + UNREACHABLE(); + } + if (result) { + *decimal_point = *length + decimal_exponent; + buffer[*length] = '\0'; + } + return result; +} } // namespace double_conversion -} // namespace WTF +} // namespace WTF diff --git a/sky/engine/wtf/dtoa/fast-dtoa.h b/sky/engine/wtf/dtoa/fast-dtoa.h index 88ef158785ba6..25c70126f812a 100644 --- a/sky/engine/wtf/dtoa/fast-dtoa.h +++ b/sky/engine/wtf/dtoa/fast-dtoa.h @@ -34,55 +34,55 @@ namespace WTF { namespace double_conversion { - enum FastDtoaMode { - // Computes the shortest representation of the given input. The returned - // result will be the most accurate number of this length. Longer - // representations might be more accurate. - FAST_DTOA_SHORTEST, - // Computes a representation where the precision (number of digits) is - // given as input. The precision is independent of the decimal point. - FAST_DTOA_PRECISION - }; +enum FastDtoaMode { + // Computes the shortest representation of the given input. The returned + // result will be the most accurate number of this length. Longer + // representations might be more accurate. + FAST_DTOA_SHORTEST, + // Computes a representation where the precision (number of digits) is + // given as input. The precision is independent of the decimal point. + FAST_DTOA_PRECISION +}; - // FastDtoa will produce at most kFastDtoaMaximalLength digits. This does not - // include the terminating '\0' character. - static const int kFastDtoaMaximalLength = 17; +// FastDtoa will produce at most kFastDtoaMaximalLength digits. This does not +// include the terminating '\0' character. +static const int kFastDtoaMaximalLength = 17; - // Provides a decimal representation of v. - // The result should be interpreted as buffer * 10^(point - length). - // - // Precondition: - // * v must be a strictly positive finite double. - // - // Returns true if it succeeds, otherwise the result can not be trusted. - // There will be *length digits inside the buffer followed by a null terminator. - // If the function returns true and mode equals - // - FAST_DTOA_SHORTEST, then - // the parameter requested_digits is ignored. - // The result satisfies - // v == (double) (buffer * 10^(point - length)). - // The digits in the buffer are the shortest representation possible. E.g. - // if 0.099999999999 and 0.1 represent the same double then "1" is returned - // with point = 0. - // The last digit will be closest to the actual v. That is, even if several - // digits might correctly yield 'v' when read again, the buffer will contain - // the one closest to v. - // - FAST_DTOA_PRECISION, then - // the buffer contains requested_digits digits. - // the difference v - (buffer * 10^(point-length)) is closest to zero for - // all possible representations of requested_digits digits. - // If there are two values that are equally close, then FastDtoa returns - // false. - // For both modes the buffer must be large enough to hold the result. - bool FastDtoa(double d, - FastDtoaMode mode, - int requested_digits, - Vector buffer, - int* length, - int* decimal_point); +// Provides a decimal representation of v. +// The result should be interpreted as buffer * 10^(point - length). +// +// Precondition: +// * v must be a strictly positive finite double. +// +// Returns true if it succeeds, otherwise the result can not be trusted. +// There will be *length digits inside the buffer followed by a null terminator. +// If the function returns true and mode equals +// - FAST_DTOA_SHORTEST, then +// the parameter requested_digits is ignored. +// The result satisfies +// v == (double) (buffer * 10^(point - length)). +// The digits in the buffer are the shortest representation possible. E.g. +// if 0.099999999999 and 0.1 represent the same double then "1" is returned +// with point = 0. +// The last digit will be closest to the actual v. That is, even if several +// digits might correctly yield 'v' when read again, the buffer will contain +// the one closest to v. +// - FAST_DTOA_PRECISION, then +// the buffer contains requested_digits digits. +// the difference v - (buffer * 10^(point-length)) is closest to zero for +// all possible representations of requested_digits digits. +// If there are two values that are equally close, then FastDtoa returns +// false. +// For both modes the buffer must be large enough to hold the result. +bool FastDtoa(double d, + FastDtoaMode mode, + int requested_digits, + Vector buffer, + int* length, + int* decimal_point); } // namespace double_conversion -} // namespace WTF +} // namespace WTF #endif // SKY_ENGINE_WTF_DTOA_FAST_DTOA_H_ diff --git a/sky/engine/wtf/dtoa/fixed-dtoa.cc b/sky/engine/wtf/dtoa/fixed-dtoa.cc index 8e61cb4f0eb92..2671701a0da8c 100644 --- a/sky/engine/wtf/dtoa/fixed-dtoa.cc +++ b/sky/engine/wtf/dtoa/fixed-dtoa.cc @@ -25,383 +25,382 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - #include -#include "flutter/sky/engine/wtf/dtoa/double.h" #include "fixed-dtoa.h" +#include "flutter/sky/engine/wtf/dtoa/double.h" namespace WTF { namespace double_conversion { - // Represents a 128bit type. This class should be replaced by a native type on - // platforms that support 128bit integers. - class UInt128 { - public: - UInt128() : high_bits_(0), low_bits_(0) { } - UInt128(uint64_t high, uint64_t low) : high_bits_(high), low_bits_(low) { } - - void Multiply(uint32_t multiplicand) { - uint64_t accumulator; - - accumulator = (low_bits_ & kMask32) * multiplicand; - uint32_t part = static_cast(accumulator & kMask32); - accumulator >>= 32; - accumulator = accumulator + (low_bits_ >> 32) * multiplicand; - low_bits_ = (accumulator << 32) + part; - accumulator >>= 32; - accumulator = accumulator + (high_bits_ & kMask32) * multiplicand; - part = static_cast(accumulator & kMask32); - accumulator >>= 32; - accumulator = accumulator + (high_bits_ >> 32) * multiplicand; - high_bits_ = (accumulator << 32) + part; - ASSERT((accumulator >> 32) == 0); - } - - void Shift(int shift_amount) { - ASSERT(-64 <= shift_amount && shift_amount <= 64); - if (shift_amount == 0) { - return; - } else if (shift_amount == -64) { - high_bits_ = low_bits_; - low_bits_ = 0; - } else if (shift_amount == 64) { - low_bits_ = high_bits_; - high_bits_ = 0; - } else if (shift_amount <= 0) { - high_bits_ <<= -shift_amount; - high_bits_ += low_bits_ >> (64 + shift_amount); - low_bits_ <<= -shift_amount; - } else { - low_bits_ >>= shift_amount; - low_bits_ += high_bits_ << (64 - shift_amount); - high_bits_ >>= shift_amount; - } - } - - // Modifies *this to *this MOD (2^power). - // Returns *this DIV (2^power). - int DivModPowerOf2(int power) { - if (power >= 64) { - int result = static_cast(high_bits_ >> (power - 64)); - high_bits_ -= static_cast(result) << (power - 64); - return result; - } else { - uint64_t part_low = low_bits_ >> power; - uint64_t part_high = high_bits_ << (64 - power); - int result = static_cast(part_low + part_high); - high_bits_ = 0; - low_bits_ -= part_low << power; - return result; - } - } - - bool IsZero() const { - return high_bits_ == 0 && low_bits_ == 0; - } - - int BitAt(int position) { - if (position >= 64) { - return static_cast(high_bits_ >> (position - 64)) & 1; - } else { - return static_cast(low_bits_ >> position) & 1; - } - } - - private: - static const uint64_t kMask32 = 0xFFFFFFFF; - // Value == (high_bits_ << 64) + low_bits_ - uint64_t high_bits_; - uint64_t low_bits_; - }; - - - static const int kDoubleSignificandSize = 53; // Includes the hidden bit. - - - static void FillDigits32FixedLength(uint32_t number, int requested_length, - Vector buffer, int* length) { - for (int i = requested_length - 1; i >= 0; --i) { - buffer[(*length) + i] = '0' + number % 10; - number /= 10; - } - *length += requested_length; +// Represents a 128bit type. This class should be replaced by a native type on +// platforms that support 128bit integers. +class UInt128 { + public: + UInt128() : high_bits_(0), low_bits_(0) {} + UInt128(uint64_t high, uint64_t low) : high_bits_(high), low_bits_(low) {} + + void Multiply(uint32_t multiplicand) { + uint64_t accumulator; + + accumulator = (low_bits_ & kMask32) * multiplicand; + uint32_t part = static_cast(accumulator & kMask32); + accumulator >>= 32; + accumulator = accumulator + (low_bits_ >> 32) * multiplicand; + low_bits_ = (accumulator << 32) + part; + accumulator >>= 32; + accumulator = accumulator + (high_bits_ & kMask32) * multiplicand; + part = static_cast(accumulator & kMask32); + accumulator >>= 32; + accumulator = accumulator + (high_bits_ >> 32) * multiplicand; + high_bits_ = (accumulator << 32) + part; + ASSERT((accumulator >> 32) == 0); + } + + void Shift(int shift_amount) { + ASSERT(-64 <= shift_amount && shift_amount <= 64); + if (shift_amount == 0) { + return; + } else if (shift_amount == -64) { + high_bits_ = low_bits_; + low_bits_ = 0; + } else if (shift_amount == 64) { + low_bits_ = high_bits_; + high_bits_ = 0; + } else if (shift_amount <= 0) { + high_bits_ <<= -shift_amount; + high_bits_ += low_bits_ >> (64 + shift_amount); + low_bits_ <<= -shift_amount; + } else { + low_bits_ >>= shift_amount; + low_bits_ += high_bits_ << (64 - shift_amount); + high_bits_ >>= shift_amount; } - - - static void FillDigits32(uint32_t number, Vector buffer, int* length) { - int number_length = 0; - // We fill the digits in reverse order and exchange them afterwards. - while (number != 0) { - int digit = number % 10; - number /= 10; - buffer[(*length) + number_length] = '0' + digit; - number_length++; - } - // Exchange the digits. - int i = *length; - int j = *length + number_length - 1; - while (i < j) { - char tmp = buffer[i]; - buffer[i] = buffer[j]; - buffer[j] = tmp; - i++; - j--; - } - *length += number_length; + } + + // Modifies *this to *this MOD (2^power). + // Returns *this DIV (2^power). + int DivModPowerOf2(int power) { + if (power >= 64) { + int result = static_cast(high_bits_ >> (power - 64)); + high_bits_ -= static_cast(result) << (power - 64); + return result; + } else { + uint64_t part_low = low_bits_ >> power; + uint64_t part_high = high_bits_ << (64 - power); + int result = static_cast(part_low + part_high); + high_bits_ = 0; + low_bits_ -= part_low << power; + return result; } + } + bool IsZero() const { return high_bits_ == 0 && low_bits_ == 0; } - static void FillDigits64FixedLength(uint64_t number, int, - Vector buffer, int* length) { - const uint32_t kTen7 = 10000000; - // For efficiency cut the number into 3 uint32_t parts, and print those. - uint32_t part2 = static_cast(number % kTen7); - number /= kTen7; - uint32_t part1 = static_cast(number % kTen7); - uint32_t part0 = static_cast(number / kTen7); - - FillDigits32FixedLength(part0, 3, buffer, length); - FillDigits32FixedLength(part1, 7, buffer, length); - FillDigits32FixedLength(part2, 7, buffer, length); + int BitAt(int position) { + if (position >= 64) { + return static_cast(high_bits_ >> (position - 64)) & 1; + } else { + return static_cast(low_bits_ >> position) & 1; } - - - static void FillDigits64(uint64_t number, Vector buffer, int* length) { - const uint32_t kTen7 = 10000000; - // For efficiency cut the number into 3 uint32_t parts, and print those. - uint32_t part2 = static_cast(number % kTen7); - number /= kTen7; - uint32_t part1 = static_cast(number % kTen7); - uint32_t part0 = static_cast(number / kTen7); - - if (part0 != 0) { - FillDigits32(part0, buffer, length); - FillDigits32FixedLength(part1, 7, buffer, length); - FillDigits32FixedLength(part2, 7, buffer, length); - } else if (part1 != 0) { - FillDigits32(part1, buffer, length); - FillDigits32FixedLength(part2, 7, buffer, length); - } else { - FillDigits32(part2, buffer, length); - } + } + + private: + static const uint64_t kMask32 = 0xFFFFFFFF; + // Value == (high_bits_ << 64) + low_bits_ + uint64_t high_bits_; + uint64_t low_bits_; +}; + +static const int kDoubleSignificandSize = 53; // Includes the hidden bit. + +static void FillDigits32FixedLength(uint32_t number, + int requested_length, + Vector buffer, + int* length) { + for (int i = requested_length - 1; i >= 0; --i) { + buffer[(*length) + i] = '0' + number % 10; + number /= 10; + } + *length += requested_length; +} + +static void FillDigits32(uint32_t number, Vector buffer, int* length) { + int number_length = 0; + // We fill the digits in reverse order and exchange them afterwards. + while (number != 0) { + int digit = number % 10; + number /= 10; + buffer[(*length) + number_length] = '0' + digit; + number_length++; + } + // Exchange the digits. + int i = *length; + int j = *length + number_length - 1; + while (i < j) { + char tmp = buffer[i]; + buffer[i] = buffer[j]; + buffer[j] = tmp; + i++; + j--; + } + *length += number_length; +} + +static void FillDigits64FixedLength(uint64_t number, + int, + Vector buffer, + int* length) { + const uint32_t kTen7 = 10000000; + // For efficiency cut the number into 3 uint32_t parts, and print those. + uint32_t part2 = static_cast(number % kTen7); + number /= kTen7; + uint32_t part1 = static_cast(number % kTen7); + uint32_t part0 = static_cast(number / kTen7); + + FillDigits32FixedLength(part0, 3, buffer, length); + FillDigits32FixedLength(part1, 7, buffer, length); + FillDigits32FixedLength(part2, 7, buffer, length); +} + +static void FillDigits64(uint64_t number, Vector buffer, int* length) { + const uint32_t kTen7 = 10000000; + // For efficiency cut the number into 3 uint32_t parts, and print those. + uint32_t part2 = static_cast(number % kTen7); + number /= kTen7; + uint32_t part1 = static_cast(number % kTen7); + uint32_t part0 = static_cast(number / kTen7); + + if (part0 != 0) { + FillDigits32(part0, buffer, length); + FillDigits32FixedLength(part1, 7, buffer, length); + FillDigits32FixedLength(part2, 7, buffer, length); + } else if (part1 != 0) { + FillDigits32(part1, buffer, length); + FillDigits32FixedLength(part2, 7, buffer, length); + } else { + FillDigits32(part2, buffer, length); + } +} + +static void RoundUp(Vector buffer, int* length, int* decimal_point) { + // An empty buffer represents 0. + if (*length == 0) { + buffer[0] = '1'; + *decimal_point = 1; + *length = 1; + return; + } + // Round the last digit until we either have a digit that was not '9' or until + // we reached the first digit. + buffer[(*length) - 1]++; + for (int i = (*length) - 1; i > 0; --i) { + if (buffer[i] != '0' + 10) { + return; } - - - static void RoundUp(Vector buffer, int* length, int* decimal_point) { - // An empty buffer represents 0. - if (*length == 0) { - buffer[0] = '1'; - *decimal_point = 1; - *length = 1; - return; - } - // Round the last digit until we either have a digit that was not '9' or until - // we reached the first digit. - buffer[(*length) - 1]++; - for (int i = (*length) - 1; i > 0; --i) { - if (buffer[i] != '0' + 10) { - return; - } - buffer[i] = '0'; - buffer[i - 1]++; - } - // If the first digit is now '0' + 10, we would need to set it to '0' and add - // a '1' in front. However we reach the first digit only if all following - // digits had been '9' before rounding up. Now all trailing digits are '0' and - // we simply switch the first digit to '1' and update the decimal-point - // (indicating that the point is now one digit to the right). - if (buffer[0] == '0' + 10) { - buffer[0] = '1'; - (*decimal_point)++; - } + buffer[i] = '0'; + buffer[i - 1]++; + } + // If the first digit is now '0' + 10, we would need to set it to '0' and add + // a '1' in front. However we reach the first digit only if all following + // digits had been '9' before rounding up. Now all trailing digits are '0' and + // we simply switch the first digit to '1' and update the decimal-point + // (indicating that the point is now one digit to the right). + if (buffer[0] == '0' + 10) { + buffer[0] = '1'; + (*decimal_point)++; + } +} + +// The given fractionals number represents a fixed-point number with binary +// point at bit (-exponent). +// Preconditions: +// -128 <= exponent <= 0. +// 0 <= fractionals * 2^exponent < 1 +// The buffer holds the result. +// The function will round its result. During the rounding-process digits not +// generated by this function might be updated, and the decimal-point variable +// might be updated. If this function generates the digits 99 and the buffer +// already contained "199" (thus yielding a buffer of "19999") then a +// rounding-up will change the contents of the buffer to "20000". +static void FillFractionals(uint64_t fractionals, + int exponent, + int fractional_count, + Vector buffer, + int* length, + int* decimal_point) { + ASSERT(-128 <= exponent && exponent <= 0); + // 'fractionals' is a fixed-point number, with binary point at bit + // (-exponent). Inside the function the non-converted remainder of fractionals + // is a fixed-point number, with binary point at bit 'point'. + if (-exponent <= 64) { + // One 64 bit number is sufficient. + ASSERT(fractionals >> 56 == 0); + int point = -exponent; + for (int i = 0; i < fractional_count; ++i) { + if (fractionals == 0) + break; + // Instead of multiplying by 10 we multiply by 5 and adjust the point + // location. This way the fractionals variable will not overflow. + // Invariant at the beginning of the loop: fractionals < 2^point. + // Initially we have: point <= 64 and fractionals < 2^56 + // After each iteration the point is decremented by one. + // Note that 5^3 = 125 < 128 = 2^7. + // Therefore three iterations of this loop will not overflow fractionals + // (even without the subtraction at the end of the loop body). At this + // time point will satisfy point <= 61 and therefore fractionals < 2^point + // and any further multiplication of fractionals by 5 will not overflow. + fractionals *= 5; + point--; + int digit = static_cast(fractionals >> point); + buffer[*length] = '0' + digit; + (*length)++; + fractionals -= static_cast(digit) << point; } - - - // The given fractionals number represents a fixed-point number with binary - // point at bit (-exponent). - // Preconditions: - // -128 <= exponent <= 0. - // 0 <= fractionals * 2^exponent < 1 - // The buffer holds the result. - // The function will round its result. During the rounding-process digits not - // generated by this function might be updated, and the decimal-point variable - // might be updated. If this function generates the digits 99 and the buffer - // already contained "199" (thus yielding a buffer of "19999") then a - // rounding-up will change the contents of the buffer to "20000". - static void FillFractionals(uint64_t fractionals, int exponent, - int fractional_count, Vector buffer, - int* length, int* decimal_point) { - ASSERT(-128 <= exponent && exponent <= 0); - // 'fractionals' is a fixed-point number, with binary point at bit - // (-exponent). Inside the function the non-converted remainder of fractionals - // is a fixed-point number, with binary point at bit 'point'. - if (-exponent <= 64) { - // One 64 bit number is sufficient. - ASSERT(fractionals >> 56 == 0); - int point = -exponent; - for (int i = 0; i < fractional_count; ++i) { - if (fractionals == 0) break; - // Instead of multiplying by 10 we multiply by 5 and adjust the point - // location. This way the fractionals variable will not overflow. - // Invariant at the beginning of the loop: fractionals < 2^point. - // Initially we have: point <= 64 and fractionals < 2^56 - // After each iteration the point is decremented by one. - // Note that 5^3 = 125 < 128 = 2^7. - // Therefore three iterations of this loop will not overflow fractionals - // (even without the subtraction at the end of the loop body). At this - // time point will satisfy point <= 61 and therefore fractionals < 2^point - // and any further multiplication of fractionals by 5 will not overflow. - fractionals *= 5; - point--; - int digit = static_cast(fractionals >> point); - buffer[*length] = '0' + digit; - (*length)++; - fractionals -= static_cast(digit) << point; - } - // If the first bit after the point is set we have to round up. - if (((fractionals >> (point - 1)) & 1) == 1) { - RoundUp(buffer, length, decimal_point); - } - } else { // We need 128 bits. - ASSERT(64 < -exponent && -exponent <= 128); - UInt128 fractionals128 = UInt128(fractionals, 0); - fractionals128.Shift(-exponent - 64); - int point = 128; - for (int i = 0; i < fractional_count; ++i) { - if (fractionals128.IsZero()) break; - // As before: instead of multiplying by 10 we multiply by 5 and adjust the - // point location. - // This multiplication will not overflow for the same reasons as before. - fractionals128.Multiply(5); - point--; - int digit = fractionals128.DivModPowerOf2(point); - buffer[*length] = '0' + digit; - (*length)++; - } - if (fractionals128.BitAt(point - 1) == 1) { - RoundUp(buffer, length, decimal_point); - } - } + // If the first bit after the point is set we have to round up. + if (((fractionals >> (point - 1)) & 1) == 1) { + RoundUp(buffer, length, decimal_point); } - - - // Removes leading and trailing zeros. - // If leading zeros are removed then the decimal point position is adjusted. - static void TrimZeros(Vector buffer, int* length, int* decimal_point) { - while (*length > 0 && buffer[(*length) - 1] == '0') { - (*length)--; - } - int first_non_zero = 0; - while (first_non_zero < *length && buffer[first_non_zero] == '0') { - first_non_zero++; - } - if (first_non_zero != 0) { - for (int i = first_non_zero; i < *length; ++i) { - buffer[i - first_non_zero] = buffer[i]; - } - *length -= first_non_zero; - *decimal_point -= first_non_zero; - } + } else { // We need 128 bits. + ASSERT(64 < -exponent && -exponent <= 128); + UInt128 fractionals128 = UInt128(fractionals, 0); + fractionals128.Shift(-exponent - 64); + int point = 128; + for (int i = 0; i < fractional_count; ++i) { + if (fractionals128.IsZero()) + break; + // As before: instead of multiplying by 10 we multiply by 5 and adjust the + // point location. + // This multiplication will not overflow for the same reasons as before. + fractionals128.Multiply(5); + point--; + int digit = fractionals128.DivModPowerOf2(point); + buffer[*length] = '0' + digit; + (*length)++; } - - - bool FastFixedDtoa(double v, - int fractional_count, - Vector buffer, - int* length, - int* decimal_point) { - const uint32_t kMaxUInt32 = 0xFFFFFFFF; - uint64_t significand = Double(v).Significand(); - int exponent = Double(v).Exponent(); - // v = significand * 2^exponent (with significand a 53bit integer). - // If the exponent is larger than 20 (i.e. we may have a 73bit number) then we - // don't know how to compute the representation. 2^73 ~= 9.5*10^21. - // If necessary this limit could probably be increased, but we don't need - // more. - if (exponent > 20) return false; - if (fractional_count > 20) return false; - *length = 0; - // At most kDoubleSignificandSize bits of the significand are non-zero. - // Given a 64 bit integer we have 11 0s followed by 53 potentially non-zero - // bits: 0..11*..0xxx..53*..xx - if (exponent + kDoubleSignificandSize > 64) { - // The exponent must be > 11. - // - // We know that v = significand * 2^exponent. - // And the exponent > 11. - // We simplify the task by dividing v by 10^17. - // The quotient delivers the first digits, and the remainder fits into a 64 - // bit number. - // Dividing by 10^17 is equivalent to dividing by 5^17*2^17. - const uint64_t kFive17 = UINT64_2PART_C(0xB1, A2BC2EC5); // 5^17 - uint64_t divisor = kFive17; - int divisor_power = 17; - uint64_t dividend = significand; - uint32_t quotient; - uint64_t remainder; - // Let v = f * 2^e with f == significand and e == exponent. - // Then need q (quotient) and r (remainder) as follows: - // v = q * 10^17 + r - // f * 2^e = q * 10^17 + r - // f * 2^e = q * 5^17 * 2^17 + r - // If e > 17 then - // f * 2^(e-17) = q * 5^17 + r/2^17 - // else - // f = q * 5^17 * 2^(17-e) + r/2^e - if (exponent > divisor_power) { - // We only allow exponents of up to 20 and therefore (17 - e) <= 3 - dividend <<= exponent - divisor_power; - quotient = static_cast(dividend / divisor); - remainder = (dividend % divisor) << divisor_power; - } else { - divisor <<= divisor_power - exponent; - quotient = static_cast(dividend / divisor); - remainder = (dividend % divisor) << exponent; - } - FillDigits32(quotient, buffer, length); - FillDigits64FixedLength(remainder, divisor_power, buffer, length); - *decimal_point = *length; - } else if (exponent >= 0) { - // 0 <= exponent <= 11 - significand <<= exponent; - FillDigits64(significand, buffer, length); - *decimal_point = *length; - } else if (exponent > -kDoubleSignificandSize) { - // We have to cut the number. - uint64_t integrals = significand >> -exponent; - uint64_t fractionals = significand - (integrals << -exponent); - if (integrals > kMaxUInt32) { - FillDigits64(integrals, buffer, length); - } else { - FillDigits32(static_cast(integrals), buffer, length); - } - *decimal_point = *length; - FillFractionals(fractionals, exponent, fractional_count, - buffer, length, decimal_point); - } else if (exponent < -128) { - // This configuration (with at most 20 digits) means that all digits must be - // 0. - ASSERT(fractional_count <= 20); - buffer[0] = '\0'; - *length = 0; - *decimal_point = -fractional_count; - } else { - *decimal_point = 0; - FillFractionals(significand, exponent, fractional_count, - buffer, length, decimal_point); - } - TrimZeros(buffer, length, decimal_point); - buffer[*length] = '\0'; - if ((*length) == 0) { - // The string is empty and the decimal_point thus has no importance. Mimick - // Gay's dtoa and and set it to -fractional_count. - *decimal_point = -fractional_count; - } - return true; + if (fractionals128.BitAt(point - 1) == 1) { + RoundUp(buffer, length, decimal_point); + } + } +} + +// Removes leading and trailing zeros. +// If leading zeros are removed then the decimal point position is adjusted. +static void TrimZeros(Vector buffer, int* length, int* decimal_point) { + while (*length > 0 && buffer[(*length) - 1] == '0') { + (*length)--; + } + int first_non_zero = 0; + while (first_non_zero < *length && buffer[first_non_zero] == '0') { + first_non_zero++; + } + if (first_non_zero != 0) { + for (int i = first_non_zero; i < *length; ++i) { + buffer[i - first_non_zero] = buffer[i]; + } + *length -= first_non_zero; + *decimal_point -= first_non_zero; + } +} + +bool FastFixedDtoa(double v, + int fractional_count, + Vector buffer, + int* length, + int* decimal_point) { + const uint32_t kMaxUInt32 = 0xFFFFFFFF; + uint64_t significand = Double(v).Significand(); + int exponent = Double(v).Exponent(); + // v = significand * 2^exponent (with significand a 53bit integer). + // If the exponent is larger than 20 (i.e. we may have a 73bit number) then we + // don't know how to compute the representation. 2^73 ~= 9.5*10^21. + // If necessary this limit could probably be increased, but we don't need + // more. + if (exponent > 20) + return false; + if (fractional_count > 20) + return false; + *length = 0; + // At most kDoubleSignificandSize bits of the significand are non-zero. + // Given a 64 bit integer we have 11 0s followed by 53 potentially non-zero + // bits: 0..11*..0xxx..53*..xx + if (exponent + kDoubleSignificandSize > 64) { + // The exponent must be > 11. + // + // We know that v = significand * 2^exponent. + // And the exponent > 11. + // We simplify the task by dividing v by 10^17. + // The quotient delivers the first digits, and the remainder fits into a 64 + // bit number. + // Dividing by 10^17 is equivalent to dividing by 5^17*2^17. + const uint64_t kFive17 = UINT64_2PART_C(0xB1, A2BC2EC5); // 5^17 + uint64_t divisor = kFive17; + int divisor_power = 17; + uint64_t dividend = significand; + uint32_t quotient; + uint64_t remainder; + // Let v = f * 2^e with f == significand and e == exponent. + // Then need q (quotient) and r (remainder) as follows: + // v = q * 10^17 + r + // f * 2^e = q * 10^17 + r + // f * 2^e = q * 5^17 * 2^17 + r + // If e > 17 then + // f * 2^(e-17) = q * 5^17 + r/2^17 + // else + // f = q * 5^17 * 2^(17-e) + r/2^e + if (exponent > divisor_power) { + // We only allow exponents of up to 20 and therefore (17 - e) <= 3 + dividend <<= exponent - divisor_power; + quotient = static_cast(dividend / divisor); + remainder = (dividend % divisor) << divisor_power; + } else { + divisor <<= divisor_power - exponent; + quotient = static_cast(dividend / divisor); + remainder = (dividend % divisor) << exponent; + } + FillDigits32(quotient, buffer, length); + FillDigits64FixedLength(remainder, divisor_power, buffer, length); + *decimal_point = *length; + } else if (exponent >= 0) { + // 0 <= exponent <= 11 + significand <<= exponent; + FillDigits64(significand, buffer, length); + *decimal_point = *length; + } else if (exponent > -kDoubleSignificandSize) { + // We have to cut the number. + uint64_t integrals = significand >> -exponent; + uint64_t fractionals = significand - (integrals << -exponent); + if (integrals > kMaxUInt32) { + FillDigits64(integrals, buffer, length); + } else { + FillDigits32(static_cast(integrals), buffer, length); } + *decimal_point = *length; + FillFractionals(fractionals, exponent, fractional_count, buffer, length, + decimal_point); + } else if (exponent < -128) { + // This configuration (with at most 20 digits) means that all digits must be + // 0. + ASSERT(fractional_count <= 20); + buffer[0] = '\0'; + *length = 0; + *decimal_point = -fractional_count; + } else { + *decimal_point = 0; + FillFractionals(significand, exponent, fractional_count, buffer, length, + decimal_point); + } + TrimZeros(buffer, length, decimal_point); + buffer[*length] = '\0'; + if ((*length) == 0) { + // The string is empty and the decimal_point thus has no importance. Mimick + // Gay's dtoa and and set it to -fractional_count. + *decimal_point = -fractional_count; + } + return true; +} } // namespace double_conversion -} // namespace WTF +} // namespace WTF diff --git a/sky/engine/wtf/dtoa/fixed-dtoa.h b/sky/engine/wtf/dtoa/fixed-dtoa.h index e668ef29c15d8..57528c81962ee 100644 --- a/sky/engine/wtf/dtoa/fixed-dtoa.h +++ b/sky/engine/wtf/dtoa/fixed-dtoa.h @@ -34,27 +34,30 @@ namespace WTF { namespace double_conversion { - // Produces digits necessary to print a given number with - // 'fractional_count' digits after the decimal point. - // The buffer must be big enough to hold the result plus one terminating null - // character. - // - // The produced digits might be too short in which case the caller has to fill - // the gaps with '0's. - // Example: FastFixedDtoa(0.001, 5, ...) is allowed to return buffer = "1", and - // decimal_point = -2. - // Halfway cases are rounded towards +/-Infinity (away from 0). The call - // FastFixedDtoa(0.15, 2, ...) thus returns buffer = "2", decimal_point = 0. - // The returned buffer may contain digits that would be truncated from the - // shortest representation of the input. - // - // This method only works for some parameters. If it can't handle the input it - // returns false. The output is null-terminated when the function succeeds. - bool FastFixedDtoa(double v, int fractional_count, - Vector buffer, int* length, int* decimal_point); +// Produces digits necessary to print a given number with +// 'fractional_count' digits after the decimal point. +// The buffer must be big enough to hold the result plus one terminating null +// character. +// +// The produced digits might be too short in which case the caller has to fill +// the gaps with '0's. +// Example: FastFixedDtoa(0.001, 5, ...) is allowed to return buffer = "1", and +// decimal_point = -2. +// Halfway cases are rounded towards +/-Infinity (away from 0). The call +// FastFixedDtoa(0.15, 2, ...) thus returns buffer = "2", decimal_point = 0. +// The returned buffer may contain digits that would be truncated from the +// shortest representation of the input. +// +// This method only works for some parameters. If it can't handle the input it +// returns false. The output is null-terminated when the function succeeds. +bool FastFixedDtoa(double v, + int fractional_count, + Vector buffer, + int* length, + int* decimal_point); } // namespace double_conversion -} // namespace WTF +} // namespace WTF #endif // SKY_ENGINE_WTF_DTOA_FIXED_DTOA_H_ diff --git a/sky/engine/wtf/dtoa/strtod.cc b/sky/engine/wtf/dtoa/strtod.cc index 8895d3119f76b..56dc41f8d0c6d 100644 --- a/sky/engine/wtf/dtoa/strtod.cc +++ b/sky/engine/wtf/dtoa/strtod.cc @@ -25,422 +25,405 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include #include +#include -#include "flutter/sky/engine/wtf/dtoa/strtod.h" -#include "flutter/sky/engine/wtf/dtoa/bignum.h" #include "cached-powers.h" +#include "flutter/sky/engine/wtf/dtoa/bignum.h" #include "flutter/sky/engine/wtf/dtoa/double.h" +#include "flutter/sky/engine/wtf/dtoa/strtod.h" namespace WTF { namespace double_conversion { - // 2^53 = 9007199254740992. - // Any integer with at most 15 decimal digits will hence fit into a double - // (which has a 53bit significand) without loss of precision. - static const int kMaxExactDoubleIntegerDecimalDigits = 15; - // 2^64 = 18446744073709551616 > 10^19 - static const int kMaxUint64DecimalDigits = 19; - - // Max double: 1.7976931348623157 x 10^308 - // Min non-zero double: 4.9406564584124654 x 10^-324 - // Any x >= 10^309 is interpreted as +infinity. - // Any x <= 10^-324 is interpreted as 0. - // Note that 2.5e-324 (despite being smaller than the min double) will be read - // as non-zero (equal to the min non-zero double). - static const int kMaxDecimalPower = 309; - static const int kMinDecimalPower = -324; - - // 2^64 = 18446744073709551616 - static const uint64_t kMaxUint64 = UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF); - - - static const double exact_powers_of_ten[] = { - 1.0, // 10^0 - 10.0, - 100.0, - 1000.0, - 10000.0, - 100000.0, - 1000000.0, - 10000000.0, - 100000000.0, - 1000000000.0, - 10000000000.0, // 10^10 - 100000000000.0, - 1000000000000.0, - 10000000000000.0, - 100000000000000.0, - 1000000000000000.0, - 10000000000000000.0, - 100000000000000000.0, - 1000000000000000000.0, - 10000000000000000000.0, - 100000000000000000000.0, // 10^20 - 1000000000000000000000.0, - // 10^22 = 0x21e19e0c9bab2400000 = 0x878678326eac9 * 2^22 - 10000000000000000000000.0 - }; - static const int kExactPowersOfTenSize = ARRAY_SIZE(exact_powers_of_ten); - - // Maximum number of significant digits in the decimal representation. - // In fact the value is 772 (see conversions.cc), but to give us some margin - // we round up to 780. - static const int kMaxSignificantDecimalDigits = 780; - - static Vector TrimLeadingZeros(Vector buffer) { - for (int i = 0; i < buffer.length(); i++) { - if (buffer[i] != '0') { - return buffer.SubVector(i, buffer.length()); - } - } - return Vector(buffer.start(), 0); +// 2^53 = 9007199254740992. +// Any integer with at most 15 decimal digits will hence fit into a double +// (which has a 53bit significand) without loss of precision. +static const int kMaxExactDoubleIntegerDecimalDigits = 15; +// 2^64 = 18446744073709551616 > 10^19 +static const int kMaxUint64DecimalDigits = 19; + +// Max double: 1.7976931348623157 x 10^308 +// Min non-zero double: 4.9406564584124654 x 10^-324 +// Any x >= 10^309 is interpreted as +infinity. +// Any x <= 10^-324 is interpreted as 0. +// Note that 2.5e-324 (despite being smaller than the min double) will be read +// as non-zero (equal to the min non-zero double). +static const int kMaxDecimalPower = 309; +static const int kMinDecimalPower = -324; + +// 2^64 = 18446744073709551616 +static const uint64_t kMaxUint64 = UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF); + +static const double exact_powers_of_ten[] = { + 1.0, // 10^0 + 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, 10000000.0, 100000000.0, + 1000000000.0, + 10000000000.0, // 10^10 + 100000000000.0, 1000000000000.0, 10000000000000.0, 100000000000000.0, + 1000000000000000.0, 10000000000000000.0, 100000000000000000.0, + 1000000000000000000.0, 10000000000000000000.0, + 100000000000000000000.0, // 10^20 + 1000000000000000000000.0, + // 10^22 = 0x21e19e0c9bab2400000 = 0x878678326eac9 * 2^22 + 10000000000000000000000.0}; +static const int kExactPowersOfTenSize = ARRAY_SIZE(exact_powers_of_ten); + +// Maximum number of significant digits in the decimal representation. +// In fact the value is 772 (see conversions.cc), but to give us some margin +// we round up to 780. +static const int kMaxSignificantDecimalDigits = 780; + +static Vector TrimLeadingZeros(Vector buffer) { + for (int i = 0; i < buffer.length(); i++) { + if (buffer[i] != '0') { + return buffer.SubVector(i, buffer.length()); } - - - static Vector TrimTrailingZeros(Vector buffer) { - for (int i = buffer.length() - 1; i >= 0; --i) { - if (buffer[i] != '0') { - return buffer.SubVector(0, i + 1); - } - } - return Vector(buffer.start(), 0); + } + return Vector(buffer.start(), 0); +} + +static Vector TrimTrailingZeros(Vector buffer) { + for (int i = buffer.length() - 1; i >= 0; --i) { + if (buffer[i] != '0') { + return buffer.SubVector(0, i + 1); } - - - static void TrimToMaxSignificantDigits(Vector buffer, - int exponent, - char* significant_buffer, - int* significant_exponent) { - for (int i = 0; i < kMaxSignificantDecimalDigits - 1; ++i) { - significant_buffer[i] = buffer[i]; - } - // The input buffer has been trimmed. Therefore the last digit must be - // different from '0'. - ASSERT(buffer[buffer.length() - 1] != '0'); - // Set the last digit to be non-zero. This is sufficient to guarantee - // correct rounding. - significant_buffer[kMaxSignificantDecimalDigits - 1] = '1'; - *significant_exponent = - exponent + (buffer.length() - kMaxSignificantDecimalDigits); - } - - // Reads digits from the buffer and converts them to a uint64. - // Reads in as many digits as fit into a uint64. - // When the string starts with "1844674407370955161" no further digit is read. - // Since 2^64 = 18446744073709551616 it would still be possible read another - // digit if it was less or equal than 6, but this would complicate the code. - static uint64_t ReadUint64(Vector buffer, - int* number_of_read_digits) { - uint64_t result = 0; - int i = 0; - while (i < buffer.length() && result <= (kMaxUint64 / 10 - 1)) { - int digit = buffer[i++] - '0'; - ASSERT(0 <= digit && digit <= 9); - result = 10 * result + digit; - } - *number_of_read_digits = i; - return result; - } - - - // Reads a DiyFp from the buffer. - // The returned DiyFp is not necessarily normalized. - // If remaining_decimals is zero then the returned DiyFp is accurate. - // Otherwise it has been rounded and has error of at most 1/2 ulp. - static void ReadDiyFp(Vector buffer, - DiyFp* result, - int* remaining_decimals) { - int read_digits; - uint64_t significand = ReadUint64(buffer, &read_digits); - if (buffer.length() == read_digits) { - *result = DiyFp(significand, 0); - *remaining_decimals = 0; - } else { - // Round the significand. - if (buffer[read_digits] >= '5') { - significand++; - } - // Compute the binary exponent. - int exponent = 0; - *result = DiyFp(significand, exponent); - *remaining_decimals = buffer.length() - read_digits; - } + } + return Vector(buffer.start(), 0); +} + +static void TrimToMaxSignificantDigits(Vector buffer, + int exponent, + char* significant_buffer, + int* significant_exponent) { + for (int i = 0; i < kMaxSignificantDecimalDigits - 1; ++i) { + significant_buffer[i] = buffer[i]; + } + // The input buffer has been trimmed. Therefore the last digit must be + // different from '0'. + ASSERT(buffer[buffer.length() - 1] != '0'); + // Set the last digit to be non-zero. This is sufficient to guarantee + // correct rounding. + significant_buffer[kMaxSignificantDecimalDigits - 1] = '1'; + *significant_exponent = + exponent + (buffer.length() - kMaxSignificantDecimalDigits); +} + +// Reads digits from the buffer and converts them to a uint64. +// Reads in as many digits as fit into a uint64. +// When the string starts with "1844674407370955161" no further digit is read. +// Since 2^64 = 18446744073709551616 it would still be possible read another +// digit if it was less or equal than 6, but this would complicate the code. +static uint64_t ReadUint64(Vector buffer, + int* number_of_read_digits) { + uint64_t result = 0; + int i = 0; + while (i < buffer.length() && result <= (kMaxUint64 / 10 - 1)) { + int digit = buffer[i++] - '0'; + ASSERT(0 <= digit && digit <= 9); + result = 10 * result + digit; + } + *number_of_read_digits = i; + return result; +} + +// Reads a DiyFp from the buffer. +// The returned DiyFp is not necessarily normalized. +// If remaining_decimals is zero then the returned DiyFp is accurate. +// Otherwise it has been rounded and has error of at most 1/2 ulp. +static void ReadDiyFp(Vector buffer, + DiyFp* result, + int* remaining_decimals) { + int read_digits; + uint64_t significand = ReadUint64(buffer, &read_digits); + if (buffer.length() == read_digits) { + *result = DiyFp(significand, 0); + *remaining_decimals = 0; + } else { + // Round the significand. + if (buffer[read_digits] >= '5') { + significand++; } - - - static bool DoubleStrtod(Vector trimmed, - int exponent, - double* result) { + // Compute the binary exponent. + int exponent = 0; + *result = DiyFp(significand, exponent); + *remaining_decimals = buffer.length() - read_digits; + } +} + +static bool DoubleStrtod(Vector trimmed, + int exponent, + double* result) { #if !defined(DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS) - // On x86 the floating-point stack can be 64 or 80 bits wide. If it is - // 80 bits wide (as is the case on Linux) then double-rounding occurs and the - // result is not accurate. - // We know that Windows32 uses 64 bits and is therefore accurate. - // Note that the ARM simulator is compiled for 32bits. It therefore exhibits - // the same problem. - return false; + // On x86 the floating-point stack can be 64 or 80 bits wide. If it is + // 80 bits wide (as is the case on Linux) then double-rounding occurs and the + // result is not accurate. + // We know that Windows32 uses 64 bits and is therefore accurate. + // Note that the ARM simulator is compiled for 32bits. It therefore exhibits + // the same problem. + return false; #endif - if (trimmed.length() <= kMaxExactDoubleIntegerDecimalDigits) { - int read_digits; - // The trimmed input fits into a double. - // If the 10^exponent (resp. 10^-exponent) fits into a double too then we - // can compute the result-double simply by multiplying (resp. dividing) the - // two numbers. - // This is possible because IEEE guarantees that floating-point operations - // return the best possible approximation. - if (exponent < 0 && -exponent < kExactPowersOfTenSize) { - // 10^-exponent fits into a double. - *result = static_cast(ReadUint64(trimmed, &read_digits)); - ASSERT(read_digits == trimmed.length()); - *result /= exact_powers_of_ten[-exponent]; - return true; - } - if (0 <= exponent && exponent < kExactPowersOfTenSize) { - // 10^exponent fits into a double. - *result = static_cast(ReadUint64(trimmed, &read_digits)); - ASSERT(read_digits == trimmed.length()); - *result *= exact_powers_of_ten[exponent]; - return true; - } - int remaining_digits = - kMaxExactDoubleIntegerDecimalDigits - trimmed.length(); - if ((0 <= exponent) && - (exponent - remaining_digits < kExactPowersOfTenSize)) { - // The trimmed string was short and we can multiply it with - // 10^remaining_digits. As a result the remaining exponent now fits - // into a double too. - *result = static_cast(ReadUint64(trimmed, &read_digits)); - ASSERT(read_digits == trimmed.length()); - *result *= exact_powers_of_ten[remaining_digits]; - *result *= exact_powers_of_ten[exponent - remaining_digits]; - return true; - } - } - return false; + if (trimmed.length() <= kMaxExactDoubleIntegerDecimalDigits) { + int read_digits; + // The trimmed input fits into a double. + // If the 10^exponent (resp. 10^-exponent) fits into a double too then we + // can compute the result-double simply by multiplying (resp. dividing) the + // two numbers. + // This is possible because IEEE guarantees that floating-point operations + // return the best possible approximation. + if (exponent < 0 && -exponent < kExactPowersOfTenSize) { + // 10^-exponent fits into a double. + *result = static_cast(ReadUint64(trimmed, &read_digits)); + ASSERT(read_digits == trimmed.length()); + *result /= exact_powers_of_ten[-exponent]; + return true; } - - - // Returns 10^exponent as an exact DiyFp. - // The given exponent must be in the range [1; kDecimalExponentDistance[. - static DiyFp AdjustmentPowerOfTen(int exponent) { - ASSERT(0 < exponent); - ASSERT(exponent < PowersOfTenCache::kDecimalExponentDistance); - // Simply hardcode the remaining powers for the given decimal exponent - // distance. - ASSERT(PowersOfTenCache::kDecimalExponentDistance == 8); - switch (exponent) { - case 1: return DiyFp(UINT64_2PART_C(0xa0000000, 00000000), -60); - case 2: return DiyFp(UINT64_2PART_C(0xc8000000, 00000000), -57); - case 3: return DiyFp(UINT64_2PART_C(0xfa000000, 00000000), -54); - case 4: return DiyFp(UINT64_2PART_C(0x9c400000, 00000000), -50); - case 5: return DiyFp(UINT64_2PART_C(0xc3500000, 00000000), -47); - case 6: return DiyFp(UINT64_2PART_C(0xf4240000, 00000000), -44); - case 7: return DiyFp(UINT64_2PART_C(0x98968000, 00000000), -40); - default: - UNREACHABLE(); - return DiyFp(0, 0); - } + if (0 <= exponent && exponent < kExactPowersOfTenSize) { + // 10^exponent fits into a double. + *result = static_cast(ReadUint64(trimmed, &read_digits)); + ASSERT(read_digits == trimmed.length()); + *result *= exact_powers_of_ten[exponent]; + return true; } - - - // If the function returns true then the result is the correct double. - // Otherwise it is either the correct double or the double that is just below - // the correct double. - static bool DiyFpStrtod(Vector buffer, - int exponent, - double* result) { - DiyFp input; - int remaining_decimals; - ReadDiyFp(buffer, &input, &remaining_decimals); - // Since we may have dropped some digits the input is not accurate. - // If remaining_decimals is different than 0 than the error is at most - // .5 ulp (unit in the last place). - // We don't want to deal with fractions and therefore keep a common - // denominator. - const int kDenominatorLog = 3; - const int kDenominator = 1 << kDenominatorLog; - // Move the remaining decimals into the exponent. - exponent += remaining_decimals; - int error = (remaining_decimals == 0 ? 0 : kDenominator / 2); - - int old_e = input.e(); - input.Normalize(); - error <<= old_e - input.e(); - - ASSERT(exponent <= PowersOfTenCache::kMaxDecimalExponent); - if (exponent < PowersOfTenCache::kMinDecimalExponent) { - *result = 0.0; - return true; - } - DiyFp cached_power; - int cached_decimal_exponent; - PowersOfTenCache::GetCachedPowerForDecimalExponent(exponent, - &cached_power, - &cached_decimal_exponent); - - if (cached_decimal_exponent != exponent) { - int adjustment_exponent = exponent - cached_decimal_exponent; - DiyFp adjustment_power = AdjustmentPowerOfTen(adjustment_exponent); - input.Multiply(adjustment_power); - if (kMaxUint64DecimalDigits - buffer.length() >= adjustment_exponent) { - // The product of input with the adjustment power fits into a 64 bit - // integer. - ASSERT(DiyFp::kSignificandSize == 64); - } else { - // The adjustment power is exact. There is hence only an error of 0.5. - error += kDenominator / 2; - } - } - - input.Multiply(cached_power); - // The error introduced by a multiplication of a*b equals - // error_a + error_b + error_a*error_b/2^64 + 0.5 - // Substituting a with 'input' and b with 'cached_power' we have - // error_b = 0.5 (all cached powers have an error of less than 0.5 ulp), - // error_ab = 0 or 1 / kDenominator > error_a*error_b/ 2^64 - int error_b = kDenominator / 2; - int error_ab = (error == 0 ? 0 : 1); // We round up to 1. - int fixed_error = kDenominator / 2; - error += error_b + error_ab + fixed_error; - - old_e = input.e(); - input.Normalize(); - error <<= old_e - input.e(); - - // See if the double's significand changes if we add/subtract the error. - int order_of_magnitude = DiyFp::kSignificandSize + input.e(); - int effective_significand_size = - Double::SignificandSizeForOrderOfMagnitude(order_of_magnitude); - int precision_digits_count = - DiyFp::kSignificandSize - effective_significand_size; - if (precision_digits_count + kDenominatorLog >= DiyFp::kSignificandSize) { - // This can only happen for very small denormals. In this case the - // half-way multiplied by the denominator exceeds the range of an uint64. - // Simply shift everything to the right. - int shift_amount = (precision_digits_count + kDenominatorLog) - - DiyFp::kSignificandSize + 1; - input.set_f(input.f() >> shift_amount); - input.set_e(input.e() + shift_amount); - // We add 1 for the lost precision of error, and kDenominator for - // the lost precision of input.f(). - error = (error >> shift_amount) + 1 + kDenominator; - precision_digits_count -= shift_amount; - } - // We use uint64_ts now. This only works if the DiyFp uses uint64_ts too. - ASSERT(DiyFp::kSignificandSize == 64); - ASSERT(precision_digits_count < 64); - uint64_t one64 = 1; - uint64_t precision_bits_mask = (one64 << precision_digits_count) - 1; - uint64_t precision_bits = input.f() & precision_bits_mask; - uint64_t half_way = one64 << (precision_digits_count - 1); - precision_bits *= kDenominator; - half_way *= kDenominator; - DiyFp rounded_input(input.f() >> precision_digits_count, - input.e() + precision_digits_count); - if (precision_bits >= half_way + error) { - rounded_input.set_f(rounded_input.f() + 1); - } - // If the last_bits are too close to the half-way case than we are too - // inaccurate and round down. In this case we return false so that we can - // fall back to a more precise algorithm. - - *result = Double(rounded_input).value(); - if (half_way - error < precision_bits && precision_bits < half_way + error) { - // Too imprecise. The caller will have to fall back to a slower version. - // However the returned number is guaranteed to be either the correct - // double, or the next-lower double. - return false; - } else { - return true; - } - } - - - // Returns the correct double for the buffer*10^exponent. - // The variable guess should be a close guess that is either the correct double - // or its lower neighbor (the nearest double less than the correct one). - // Preconditions: - // buffer.length() + exponent <= kMaxDecimalPower + 1 - // buffer.length() + exponent > kMinDecimalPower - // buffer.length() <= kMaxDecimalSignificantDigits - static double BignumStrtod(Vector buffer, - int exponent, - double guess) { - if (guess == Double::Infinity()) { - return guess; - } - - DiyFp upper_boundary = Double(guess).UpperBoundary(); - - ASSERT(buffer.length() + exponent <= kMaxDecimalPower + 1); - ASSERT(buffer.length() + exponent > kMinDecimalPower); - ASSERT(buffer.length() <= kMaxSignificantDecimalDigits); - // Make sure that the Bignum will be able to hold all our numbers. - // Our Bignum implementation has a separate field for exponents. Shifts will - // consume at most one bigit (< 64 bits). - // ln(10) == 3.3219... - ASSERT(((kMaxDecimalPower + 1) * 333 / 100) < Bignum::kMaxSignificantBits); - Bignum input; - Bignum boundary; - input.AssignDecimalString(buffer); - boundary.AssignUInt64(upper_boundary.f()); - if (exponent >= 0) { - input.MultiplyByPowerOfTen(exponent); - } else { - boundary.MultiplyByPowerOfTen(-exponent); - } - if (upper_boundary.e() > 0) { - boundary.Shifxleft(upper_boundary.e()); - } else { - input.Shifxleft(-upper_boundary.e()); - } - int comparison = Bignum::Compare(input, boundary); - if (comparison < 0) { - return guess; - } else if (comparison > 0) { - return Double(guess).NextDouble(); - } else if ((Double(guess).Significand() & 1) == 0) { - // Round towards even. - return guess; - } else { - return Double(guess).NextDouble(); - } + int remaining_digits = + kMaxExactDoubleIntegerDecimalDigits - trimmed.length(); + if ((0 <= exponent) && + (exponent - remaining_digits < kExactPowersOfTenSize)) { + // The trimmed string was short and we can multiply it with + // 10^remaining_digits. As a result the remaining exponent now fits + // into a double too. + *result = static_cast(ReadUint64(trimmed, &read_digits)); + ASSERT(read_digits == trimmed.length()); + *result *= exact_powers_of_ten[remaining_digits]; + *result *= exact_powers_of_ten[exponent - remaining_digits]; + return true; } - - - double Strtod(Vector buffer, int exponent) { - Vector left_trimmed = TrimLeadingZeros(buffer); - Vector trimmed = TrimTrailingZeros(left_trimmed); - exponent += left_trimmed.length() - trimmed.length(); - if (trimmed.length() == 0) return 0.0; - if (trimmed.length() > kMaxSignificantDecimalDigits) { - char significant_buffer[kMaxSignificantDecimalDigits]; - int significant_exponent; - TrimToMaxSignificantDigits(trimmed, exponent, - significant_buffer, &significant_exponent); - return Strtod(Vector(significant_buffer, - kMaxSignificantDecimalDigits), - significant_exponent); - } - if (exponent + trimmed.length() - 1 >= kMaxDecimalPower) { - return Double::Infinity(); - } - if (exponent + trimmed.length() <= kMinDecimalPower) { - return 0.0; - } - - double guess; - if (DoubleStrtod(trimmed, exponent, &guess) || - DiyFpStrtod(trimmed, exponent, &guess)) { - return guess; - } - return BignumStrtod(trimmed, exponent, guess); + } + return false; +} + +// Returns 10^exponent as an exact DiyFp. +// The given exponent must be in the range [1; kDecimalExponentDistance[. +static DiyFp AdjustmentPowerOfTen(int exponent) { + ASSERT(0 < exponent); + ASSERT(exponent < PowersOfTenCache::kDecimalExponentDistance); + // Simply hardcode the remaining powers for the given decimal exponent + // distance. + ASSERT(PowersOfTenCache::kDecimalExponentDistance == 8); + switch (exponent) { + case 1: + return DiyFp(UINT64_2PART_C(0xa0000000, 00000000), -60); + case 2: + return DiyFp(UINT64_2PART_C(0xc8000000, 00000000), -57); + case 3: + return DiyFp(UINT64_2PART_C(0xfa000000, 00000000), -54); + case 4: + return DiyFp(UINT64_2PART_C(0x9c400000, 00000000), -50); + case 5: + return DiyFp(UINT64_2PART_C(0xc3500000, 00000000), -47); + case 6: + return DiyFp(UINT64_2PART_C(0xf4240000, 00000000), -44); + case 7: + return DiyFp(UINT64_2PART_C(0x98968000, 00000000), -40); + default: + UNREACHABLE(); + return DiyFp(0, 0); + } +} + +// If the function returns true then the result is the correct double. +// Otherwise it is either the correct double or the double that is just below +// the correct double. +static bool DiyFpStrtod(Vector buffer, + int exponent, + double* result) { + DiyFp input; + int remaining_decimals; + ReadDiyFp(buffer, &input, &remaining_decimals); + // Since we may have dropped some digits the input is not accurate. + // If remaining_decimals is different than 0 than the error is at most + // .5 ulp (unit in the last place). + // We don't want to deal with fractions and therefore keep a common + // denominator. + const int kDenominatorLog = 3; + const int kDenominator = 1 << kDenominatorLog; + // Move the remaining decimals into the exponent. + exponent += remaining_decimals; + int error = (remaining_decimals == 0 ? 0 : kDenominator / 2); + + int old_e = input.e(); + input.Normalize(); + error <<= old_e - input.e(); + + ASSERT(exponent <= PowersOfTenCache::kMaxDecimalExponent); + if (exponent < PowersOfTenCache::kMinDecimalExponent) { + *result = 0.0; + return true; + } + DiyFp cached_power; + int cached_decimal_exponent; + PowersOfTenCache::GetCachedPowerForDecimalExponent(exponent, &cached_power, + &cached_decimal_exponent); + + if (cached_decimal_exponent != exponent) { + int adjustment_exponent = exponent - cached_decimal_exponent; + DiyFp adjustment_power = AdjustmentPowerOfTen(adjustment_exponent); + input.Multiply(adjustment_power); + if (kMaxUint64DecimalDigits - buffer.length() >= adjustment_exponent) { + // The product of input with the adjustment power fits into a 64 bit + // integer. + ASSERT(DiyFp::kSignificandSize == 64); + } else { + // The adjustment power is exact. There is hence only an error of 0.5. + error += kDenominator / 2; } + } + + input.Multiply(cached_power); + // The error introduced by a multiplication of a*b equals + // error_a + error_b + error_a*error_b/2^64 + 0.5 + // Substituting a with 'input' and b with 'cached_power' we have + // error_b = 0.5 (all cached powers have an error of less than 0.5 ulp), + // error_ab = 0 or 1 / kDenominator > error_a*error_b/ 2^64 + int error_b = kDenominator / 2; + int error_ab = (error == 0 ? 0 : 1); // We round up to 1. + int fixed_error = kDenominator / 2; + error += error_b + error_ab + fixed_error; + + old_e = input.e(); + input.Normalize(); + error <<= old_e - input.e(); + + // See if the double's significand changes if we add/subtract the error. + int order_of_magnitude = DiyFp::kSignificandSize + input.e(); + int effective_significand_size = + Double::SignificandSizeForOrderOfMagnitude(order_of_magnitude); + int precision_digits_count = + DiyFp::kSignificandSize - effective_significand_size; + if (precision_digits_count + kDenominatorLog >= DiyFp::kSignificandSize) { + // This can only happen for very small denormals. In this case the + // half-way multiplied by the denominator exceeds the range of an uint64. + // Simply shift everything to the right. + int shift_amount = (precision_digits_count + kDenominatorLog) - + DiyFp::kSignificandSize + 1; + input.set_f(input.f() >> shift_amount); + input.set_e(input.e() + shift_amount); + // We add 1 for the lost precision of error, and kDenominator for + // the lost precision of input.f(). + error = (error >> shift_amount) + 1 + kDenominator; + precision_digits_count -= shift_amount; + } + // We use uint64_ts now. This only works if the DiyFp uses uint64_ts too. + ASSERT(DiyFp::kSignificandSize == 64); + ASSERT(precision_digits_count < 64); + uint64_t one64 = 1; + uint64_t precision_bits_mask = (one64 << precision_digits_count) - 1; + uint64_t precision_bits = input.f() & precision_bits_mask; + uint64_t half_way = one64 << (precision_digits_count - 1); + precision_bits *= kDenominator; + half_way *= kDenominator; + DiyFp rounded_input(input.f() >> precision_digits_count, + input.e() + precision_digits_count); + if (precision_bits >= half_way + error) { + rounded_input.set_f(rounded_input.f() + 1); + } + // If the last_bits are too close to the half-way case than we are too + // inaccurate and round down. In this case we return false so that we can + // fall back to a more precise algorithm. + + *result = Double(rounded_input).value(); + if (half_way - error < precision_bits && precision_bits < half_way + error) { + // Too imprecise. The caller will have to fall back to a slower version. + // However the returned number is guaranteed to be either the correct + // double, or the next-lower double. + return false; + } else { + return true; + } +} + +// Returns the correct double for the buffer*10^exponent. +// The variable guess should be a close guess that is either the correct double +// or its lower neighbor (the nearest double less than the correct one). +// Preconditions: +// buffer.length() + exponent <= kMaxDecimalPower + 1 +// buffer.length() + exponent > kMinDecimalPower +// buffer.length() <= kMaxDecimalSignificantDigits +static double BignumStrtod(Vector buffer, + int exponent, + double guess) { + if (guess == Double::Infinity()) { + return guess; + } + + DiyFp upper_boundary = Double(guess).UpperBoundary(); + + ASSERT(buffer.length() + exponent <= kMaxDecimalPower + 1); + ASSERT(buffer.length() + exponent > kMinDecimalPower); + ASSERT(buffer.length() <= kMaxSignificantDecimalDigits); + // Make sure that the Bignum will be able to hold all our numbers. + // Our Bignum implementation has a separate field for exponents. Shifts will + // consume at most one bigit (< 64 bits). + // ln(10) == 3.3219... + ASSERT(((kMaxDecimalPower + 1) * 333 / 100) < Bignum::kMaxSignificantBits); + Bignum input; + Bignum boundary; + input.AssignDecimalString(buffer); + boundary.AssignUInt64(upper_boundary.f()); + if (exponent >= 0) { + input.MultiplyByPowerOfTen(exponent); + } else { + boundary.MultiplyByPowerOfTen(-exponent); + } + if (upper_boundary.e() > 0) { + boundary.Shifxleft(upper_boundary.e()); + } else { + input.Shifxleft(-upper_boundary.e()); + } + int comparison = Bignum::Compare(input, boundary); + if (comparison < 0) { + return guess; + } else if (comparison > 0) { + return Double(guess).NextDouble(); + } else if ((Double(guess).Significand() & 1) == 0) { + // Round towards even. + return guess; + } else { + return Double(guess).NextDouble(); + } +} + +double Strtod(Vector buffer, int exponent) { + Vector left_trimmed = TrimLeadingZeros(buffer); + Vector trimmed = TrimTrailingZeros(left_trimmed); + exponent += left_trimmed.length() - trimmed.length(); + if (trimmed.length() == 0) + return 0.0; + if (trimmed.length() > kMaxSignificantDecimalDigits) { + char significant_buffer[kMaxSignificantDecimalDigits]; + int significant_exponent; + TrimToMaxSignificantDigits(trimmed, exponent, significant_buffer, + &significant_exponent); + return Strtod( + Vector(significant_buffer, kMaxSignificantDecimalDigits), + significant_exponent); + } + if (exponent + trimmed.length() - 1 >= kMaxDecimalPower) { + return Double::Infinity(); + } + if (exponent + trimmed.length() <= kMinDecimalPower) { + return 0.0; + } + + double guess; + if (DoubleStrtod(trimmed, exponent, &guess) || + DiyFpStrtod(trimmed, exponent, &guess)) { + return guess; + } + return BignumStrtod(trimmed, exponent, guess); +} } // namespace double_conversion -} // namespace WTF +} // namespace WTF diff --git a/sky/engine/wtf/dtoa/strtod.h b/sky/engine/wtf/dtoa/strtod.h index 6f9e68442837a..997a05ceb722a 100644 --- a/sky/engine/wtf/dtoa/strtod.h +++ b/sky/engine/wtf/dtoa/strtod.h @@ -34,12 +34,12 @@ namespace WTF { namespace double_conversion { - // The buffer must only contain digits in the range [0-9]. It must not - // contain a dot or a sign. It must not start with '0', and must not be empty. - double Strtod(Vector buffer, int exponent); +// The buffer must only contain digits in the range [0-9]. It must not +// contain a dot or a sign. It must not start with '0', and must not be empty. +double Strtod(Vector buffer, int exponent); } // namespace double_conversion -} // namespace WTF +} // namespace WTF #endif // SKY_ENGINE_WTF_DTOA_STRTOD_H_ diff --git a/sky/engine/wtf/dtoa/utils.h b/sky/engine/wtf/dtoa/utils.h index 00297eb0d9712..d1ea29257b17b 100644 --- a/sky/engine/wtf/dtoa/utils.h +++ b/sky/engine/wtf/dtoa/utils.h @@ -30,8 +30,8 @@ #include -#include "lib/fxl/macros.h" #include "flutter/sky/engine/wtf/Assertions.h" +#include "lib/fxl/macros.h" #define UNIMPLEMENTED ASSERT_NOT_REACHED #define UNREACHABLE ASSERT_NOT_REACHED @@ -46,9 +46,8 @@ // the output of the division with the expected result. (Inlining must be // disabled.) // On Linux,x86 89255e-22 != Div_double(89255.0/1e22) -#if defined(_M_X64) || defined(__x86_64__) || \ -defined(__ARMEL__) || defined(__aarch64__) || \ -defined(__MIPSEL__) +#if defined(_M_X64) || defined(__x86_64__) || defined(__ARMEL__) || \ + defined(__aarch64__) || defined(__MIPSEL__) #define DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS 1 #elif defined(_M_IX86) || defined(__i386__) #undef DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS @@ -63,209 +62,203 @@ defined(__MIPSEL__) // write UINT64_2PART_C(0x12345678,90123456); #define UINT64_2PART_C(a, b) (((static_cast(a) << 32) + 0x##b##u)) - // The expression ARRAY_SIZE(a) is a compile-time constant of type // size_t which represents the number of elements of the given // array. You should only use ARRAY_SIZE on statically allocated // arrays. -#define ARRAY_SIZE(a) \ -((sizeof(a) / sizeof(*(a))) / \ -static_cast(!(sizeof(a) % sizeof(*(a))))) +#define ARRAY_SIZE(a) \ + ((sizeof(a) / sizeof(*(a))) / \ + static_cast(!(sizeof(a) % sizeof(*(a))))) namespace WTF { namespace double_conversion { - static const int kCharSize = sizeof(char); - - // Returns the maximum of the two parameters. - template - static T Max(T a, T b) { - return a < b ? b : a; - } - - - // Returns the minimum of the two parameters. - template - static T Min(T a, T b) { - return a < b ? a : b; - } - - - inline int StrLength(const char* string) { - size_t length = strlen(string); - ASSERT(length == static_cast(static_cast(length))); - return static_cast(length); - } - - // This is a simplified version of V8's Vector class. - template - class Vector { - public: - Vector() : start_(NULL), length_(0) {} - Vector(T* data, int length) : start_(data), length_(length) { - ASSERT(length == 0 || (length > 0 && data != NULL)); - } - - // Returns a vector using the same backing storage as this one, - // spanning from and including 'from', to but not including 'to'. - Vector SubVector(int from, int to) { - ASSERT(to <= length_); - ASSERT(from < to); - ASSERT(0 <= from); - return Vector(start() + from, to - from); - } - - // Returns the length of the vector. - int length() const { return length_; } - - // Returns whether or not the vector is empty. - bool is_empty() const { return length_ == 0; } - - // Returns the pointer to the start of the data in the vector. - T* start() const { return start_; } - - // Access individual vector elements - checks bounds in debug mode. - T& operator[](int index) const { - ASSERT(0 <= index && index < length_); - return start_[index]; - } - - T& first() { return start_[0]; } - - T& last() { return start_[length_ - 1]; } - - private: - T* start_; - int length_; - }; - - - // Helper class for building result strings in a character buffer. The - // purpose of the class is to use safe operations that checks the - // buffer bounds on all operations in debug mode. - class StringBuilder { - public: - StringBuilder(char* buffer, int size) - : buffer_(buffer, size), position_(0) { } - - ~StringBuilder() { if (!is_finalized()) Finalize(); } - - int size() const { return buffer_.length(); } - - // Get the current position in the builder. - int position() const { - ASSERT(!is_finalized()); - return position_; - } - - // Set the current position in the builder. - void SetPosition(int position) - { - ASSERT(!is_finalized()); - ASSERT_WITH_SECURITY_IMPLICATION(position < size()); - position_ = position; - } - - // Reset the position. - void Reset() { position_ = 0; } - - // Add a single character to the builder. It is not allowed to add - // 0-characters; use the Finalize() method to terminate the string - // instead. - void AddCharacter(char c) { - ASSERT(c != '\0'); - ASSERT(!is_finalized() && position_ < buffer_.length()); - buffer_[position_++] = c; - } - - // Add an entire string to the builder. Uses strlen() internally to - // compute the length of the input string. - void AddString(const char* s) { - AddSubstring(s, StrLength(s)); - } - - // Add the first 'n' characters of the given string 's' to the - // builder. The input string must have enough characters. - void AddSubstring(const char* s, int n) { - ASSERT(!is_finalized() && position_ + n < buffer_.length()); - ASSERT_WITH_SECURITY_IMPLICATION(static_cast(n) <= strlen(s)); - memcpy(&buffer_[position_], s, n * kCharSize); - position_ += n; - } - - - // Add character padding to the builder. If count is non-positive, - // nothing is added to the builder. - void AddPadding(char c, int count) { - for (int i = 0; i < count; i++) { - AddCharacter(c); - } - } - - // Finalize the string by 0-terminating it and returning the buffer. - char* Finalize() { - ASSERT(!is_finalized() && position_ < buffer_.length()); - buffer_[position_] = '\0'; - // Make sure nobody managed to add a 0-character to the - // buffer while building the string. - ASSERT(strlen(buffer_.start()) == static_cast(position_)); - position_ = -1; - ASSERT(is_finalized()); - return buffer_.start(); - } - - private: - Vector buffer_; - int position_; - - bool is_finalized() const { return position_ < 0; } - - FXL_DISALLOW_IMPLICIT_CONSTRUCTORS(StringBuilder); - }; - - // The type-based aliasing rule allows the compiler to assume that pointers of - // different types (for some definition of different) never alias each other. - // Thus the following code does not work: - // - // float f = foo(); - // int fbits = *(int*)(&f); - // - // The compiler 'knows' that the int pointer can't refer to f since the types - // don't match, so the compiler may cache f in a register, leaving random data - // in fbits. Using C++ style casts makes no difference, however a pointer to - // char data is assumed to alias any other pointer. This is the 'memcpy - // exception'. - // - // Bit_cast uses the memcpy exception to move the bits from a variable of one - // type of a variable of another type. Of course the end result is likely to - // be implementation dependent. Most compilers (gcc-4.2 and MSVC 2005) - // will completely optimize BitCast away. - // - // There is an additional use for BitCast. - // Recent gccs will warn when they see casts that may result in breakage due to - // the type-based aliasing rule. If you have checked that there is no breakage - // you can use BitCast to cast one pointer type to another. This confuses gcc - // enough that it can no longer see that you have cast one pointer type to - // another thus avoiding the warning. - template - inline Dest BitCast(const Source& source) { - // Compile time assertion: sizeof(Dest) == sizeof(Source) - // A compile error here means your Dest and Source have different sizes. - COMPILE_ASSERT(sizeof(Dest) == sizeof(Source), VerifySizesAreEqual); - - Dest dest; - memcpy(&dest, &source, sizeof(dest)); - return dest; - } - - template - inline Dest BitCast(Source* source) { - return BitCast(reinterpret_cast(source)); +static const int kCharSize = sizeof(char); + +// Returns the maximum of the two parameters. +template +static T Max(T a, T b) { + return a < b ? b : a; +} + +// Returns the minimum of the two parameters. +template +static T Min(T a, T b) { + return a < b ? a : b; +} + +inline int StrLength(const char* string) { + size_t length = strlen(string); + ASSERT(length == static_cast(static_cast(length))); + return static_cast(length); +} + +// This is a simplified version of V8's Vector class. +template +class Vector { + public: + Vector() : start_(NULL), length_(0) {} + Vector(T* data, int length) : start_(data), length_(length) { + ASSERT(length == 0 || (length > 0 && data != NULL)); + } + + // Returns a vector using the same backing storage as this one, + // spanning from and including 'from', to but not including 'to'. + Vector SubVector(int from, int to) { + ASSERT(to <= length_); + ASSERT(from < to); + ASSERT(0 <= from); + return Vector(start() + from, to - from); + } + + // Returns the length of the vector. + int length() const { return length_; } + + // Returns whether or not the vector is empty. + bool is_empty() const { return length_ == 0; } + + // Returns the pointer to the start of the data in the vector. + T* start() const { return start_; } + + // Access individual vector elements - checks bounds in debug mode. + T& operator[](int index) const { + ASSERT(0 <= index && index < length_); + return start_[index]; + } + + T& first() { return start_[0]; } + + T& last() { return start_[length_ - 1]; } + + private: + T* start_; + int length_; +}; + +// Helper class for building result strings in a character buffer. The +// purpose of the class is to use safe operations that checks the +// buffer bounds on all operations in debug mode. +class StringBuilder { + public: + StringBuilder(char* buffer, int size) : buffer_(buffer, size), position_(0) {} + + ~StringBuilder() { + if (!is_finalized()) + Finalize(); + } + + int size() const { return buffer_.length(); } + + // Get the current position in the builder. + int position() const { + ASSERT(!is_finalized()); + return position_; + } + + // Set the current position in the builder. + void SetPosition(int position) { + ASSERT(!is_finalized()); + ASSERT_WITH_SECURITY_IMPLICATION(position < size()); + position_ = position; + } + + // Reset the position. + void Reset() { position_ = 0; } + + // Add a single character to the builder. It is not allowed to add + // 0-characters; use the Finalize() method to terminate the string + // instead. + void AddCharacter(char c) { + ASSERT(c != '\0'); + ASSERT(!is_finalized() && position_ < buffer_.length()); + buffer_[position_++] = c; + } + + // Add an entire string to the builder. Uses strlen() internally to + // compute the length of the input string. + void AddString(const char* s) { AddSubstring(s, StrLength(s)); } + + // Add the first 'n' characters of the given string 's' to the + // builder. The input string must have enough characters. + void AddSubstring(const char* s, int n) { + ASSERT(!is_finalized() && position_ + n < buffer_.length()); + ASSERT_WITH_SECURITY_IMPLICATION(static_cast(n) <= strlen(s)); + memcpy(&buffer_[position_], s, n * kCharSize); + position_ += n; + } + + // Add character padding to the builder. If count is non-positive, + // nothing is added to the builder. + void AddPadding(char c, int count) { + for (int i = 0; i < count; i++) { + AddCharacter(c); } + } + + // Finalize the string by 0-terminating it and returning the buffer. + char* Finalize() { + ASSERT(!is_finalized() && position_ < buffer_.length()); + buffer_[position_] = '\0'; + // Make sure nobody managed to add a 0-character to the + // buffer while building the string. + ASSERT(strlen(buffer_.start()) == static_cast(position_)); + position_ = -1; + ASSERT(is_finalized()); + return buffer_.start(); + } + + private: + Vector buffer_; + int position_; + + bool is_finalized() const { return position_ < 0; } + + FXL_DISALLOW_IMPLICIT_CONSTRUCTORS(StringBuilder); +}; + +// The type-based aliasing rule allows the compiler to assume that pointers of +// different types (for some definition of different) never alias each other. +// Thus the following code does not work: +// +// float f = foo(); +// int fbits = *(int*)(&f); +// +// The compiler 'knows' that the int pointer can't refer to f since the types +// don't match, so the compiler may cache f in a register, leaving random data +// in fbits. Using C++ style casts makes no difference, however a pointer to +// char data is assumed to alias any other pointer. This is the 'memcpy +// exception'. +// +// Bit_cast uses the memcpy exception to move the bits from a variable of one +// type of a variable of another type. Of course the end result is likely to +// be implementation dependent. Most compilers (gcc-4.2 and MSVC 2005) +// will completely optimize BitCast away. +// +// There is an additional use for BitCast. +// Recent gccs will warn when they see casts that may result in breakage due to +// the type-based aliasing rule. If you have checked that there is no breakage +// you can use BitCast to cast one pointer type to another. This confuses gcc +// enough that it can no longer see that you have cast one pointer type to +// another thus avoiding the warning. +template +inline Dest BitCast(const Source& source) { + // Compile time assertion: sizeof(Dest) == sizeof(Source) + // A compile error here means your Dest and Source have different sizes. + COMPILE_ASSERT(sizeof(Dest) == sizeof(Source), VerifySizesAreEqual); + + Dest dest; + memcpy(&dest, &source, sizeof(dest)); + return dest; +} + +template +inline Dest BitCast(Source* source) { + return BitCast(reinterpret_cast(source)); +} } // namespace double_conversion -} // namespace WTF +} // namespace WTF #endif // SKY_ENGINE_WTF_DTOA_UTILS_H_ diff --git a/sky/engine/wtf/testing/RunAllTests.cpp b/sky/engine/wtf/testing/RunAllTests.cpp index 01c0296a66937..1c1ff46380c94 100644 --- a/sky/engine/wtf/testing/RunAllTests.cpp +++ b/sky/engine/wtf/testing/RunAllTests.cpp @@ -28,9 +28,9 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "gtest/gtest.h" #include "flutter/sky/engine/wtf/MainThread.h" #include "flutter/sky/engine/wtf/WTF.h" +#include "gtest/gtest.h" int main(int argc, char** argv) { WTF::initialize(); diff --git a/sky/engine/wtf/testing/WTFTestHelpers.cpp b/sky/engine/wtf/testing/WTFTestHelpers.cpp index ba113da5da296..55eadd3728fb3 100644 --- a/sky/engine/wtf/testing/WTFTestHelpers.cpp +++ b/sky/engine/wtf/testing/WTFTestHelpers.cpp @@ -30,53 +30,52 @@ #include "wtf/testing/WTFTestHelpers.h" -#include // NOLINT -#include // NOLINT +#include // NOLINT +#include // NOLINT #include "flutter/sky/engine/wtf/text/WTFString.h" namespace WTF { -std::ostream& operator<<(std::ostream& out, const String& string) -{ - if (string.isNull()) - return out << ""; +std::ostream& operator<<(std::ostream& out, const String& string) { + if (string.isNull()) + return out << ""; - out << '"'; - for (unsigned index = 0; index < string.length(); ++index) { - // Print shorthands for select cases. - UChar character = string[index]; - switch (character) { - case '\t': - out << "\\t"; - break; - case '\n': - out << "\\n"; - break; - case '\r': - out << "\\r"; - break; - case '"': - out << "\\\""; - break; - case '\\': - out << "\\\\"; - break; - default: - if (character >= 0x20 && character < 0x7F) { - out << static_cast(character); - } else { - // Print "\uXXXX" for control or non-ASCII characters. - out << "\\u"; - out.width(4); - out.fill('0'); - out.setf(std::ios_base::hex, std::ios_base::basefield); - out.setf(std::ios::uppercase); - out << character; - } - break; + out << '"'; + for (unsigned index = 0; index < string.length(); ++index) { + // Print shorthands for select cases. + UChar character = string[index]; + switch (character) { + case '\t': + out << "\\t"; + break; + case '\n': + out << "\\n"; + break; + case '\r': + out << "\\r"; + break; + case '"': + out << "\\\""; + break; + case '\\': + out << "\\\\"; + break; + default: + if (character >= 0x20 && character < 0x7F) { + out << static_cast(character); + } else { + // Print "\uXXXX" for control or non-ASCII characters. + out << "\\u"; + out.width(4); + out.fill('0'); + out.setf(std::ios_base::hex, std::ios_base::basefield); + out.setf(std::ios::uppercase); + out << character; } + break; } - return out << '"'; + } + return out << '"'; } -} // namespace WTF +} // namespace WTF diff --git a/sky/engine/wtf/testing/WTFTestHelpersTest.cpp b/sky/engine/wtf/testing/WTFTestHelpersTest.cpp index a594ce0632a82..c317fa5cf7740 100644 --- a/sky/engine/wtf/testing/WTFTestHelpersTest.cpp +++ b/sky/engine/wtf/testing/WTFTestHelpersTest.cpp @@ -41,30 +41,39 @@ using namespace WTF; namespace { -CString toCStringThroughPrinter(const String& string) -{ - std::ostringstream output; - output << string; - const std::string& result = output.str(); - return CString(result.data(), result.length()); +CString toCStringThroughPrinter(const String& string) { + std::ostringstream output; + output << string; + const std::string& result = output.str(); + return CString(result.data(), result.length()); } -TEST(WTFTestHelpersTest, StringPrinter) -{ - EXPECT_EQ(CString("\"Hello!\""), toCStringThroughPrinter("Hello!")); - EXPECT_EQ(CString("\"\\\"\""), toCStringThroughPrinter("\"")); - EXPECT_EQ(CString("\"\\\\\""), toCStringThroughPrinter("\\")); - EXPECT_EQ(CString("\"\\u0000\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007\""), toCStringThroughPrinter(String("\x00\x01\x02\x03\x04\x05\x06\x07", 8))); - EXPECT_EQ(CString("\"\\u0008\\t\\n\\u000B\\u000C\\r\\u000E\\u000F\""), toCStringThroughPrinter(String("\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", 8))); - EXPECT_EQ(CString("\"\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017\""), toCStringThroughPrinter(String("\x10\x11\x12\x13\x14\x15\x16\x17", 8))); - EXPECT_EQ(CString("\"\\u0018\\u0019\\u001A\\u001B\\u001C\\u001D\\u001E\\u001F\""), toCStringThroughPrinter(String("\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F", 8))); - EXPECT_EQ(CString("\"\\u007F\\u0080\\u0081\""), toCStringThroughPrinter("\x7F\x80\x81")); - EXPECT_EQ(CString("\"\""), toCStringThroughPrinter(emptyString())); - EXPECT_EQ(CString(""), toCStringThroughPrinter(String())); - - static const UChar unicodeSample[] = { 0x30C6, 0x30B9, 0x30C8 }; // "Test" in Japanese. - EXPECT_EQ(CString("\"\\u30C6\\u30B9\\u30C8\""), toCStringThroughPrinter(String(unicodeSample, WTF_ARRAY_LENGTH(unicodeSample)))); +TEST(WTFTestHelpersTest, StringPrinter) { + EXPECT_EQ(CString("\"Hello!\""), toCStringThroughPrinter("Hello!")); + EXPECT_EQ(CString("\"\\\"\""), toCStringThroughPrinter("\"")); + EXPECT_EQ(CString("\"\\\\\""), toCStringThroughPrinter("\\")); + EXPECT_EQ( + CString("\"\\u0000\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007\""), + toCStringThroughPrinter(String("\x00\x01\x02\x03\x04\x05\x06\x07", 8))); + EXPECT_EQ( + CString("\"\\u0008\\t\\n\\u000B\\u000C\\r\\u000E\\u000F\""), + toCStringThroughPrinter(String("\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", 8))); + EXPECT_EQ( + CString("\"\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017\""), + toCStringThroughPrinter(String("\x10\x11\x12\x13\x14\x15\x16\x17", 8))); + EXPECT_EQ( + CString("\"\\u0018\\u0019\\u001A\\u001B\\u001C\\u001D\\u001E\\u001F\""), + toCStringThroughPrinter(String("\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F", 8))); + EXPECT_EQ(CString("\"\\u007F\\u0080\\u0081\""), + toCStringThroughPrinter("\x7F\x80\x81")); + EXPECT_EQ(CString("\"\""), toCStringThroughPrinter(emptyString())); + EXPECT_EQ(CString(""), toCStringThroughPrinter(String())); + static const UChar unicodeSample[] = {0x30C6, 0x30B9, + 0x30C8}; // "Test" in Japanese. + EXPECT_EQ(CString("\"\\u30C6\\u30B9\\u30C8\""), + toCStringThroughPrinter( + String(unicodeSample, WTF_ARRAY_LENGTH(unicodeSample)))); } -} +} // namespace diff --git a/sky/engine/wtf/text/ASCIIFastPath.h b/sky/engine/wtf/text/ASCIIFastPath.h index 0e595ce80ccbd..a187d87c6015d 100644 --- a/sky/engine/wtf/text/ASCIIFastPath.h +++ b/sky/engine/wtf/text/ASCIIFastPath.h @@ -35,103 +35,109 @@ namespace WTF { typedef uintptr_t MachineWord; const uintptr_t machineWordAlignmentMask = sizeof(MachineWord) - 1; -inline bool isAlignedToMachineWord(const void* pointer) -{ - return !(reinterpret_cast(pointer) & machineWordAlignmentMask); +inline bool isAlignedToMachineWord(const void* pointer) { + return !(reinterpret_cast(pointer) & machineWordAlignmentMask); } -template inline T* alignToMachineWord(T* pointer) -{ - return reinterpret_cast(reinterpret_cast(pointer) & ~machineWordAlignmentMask); +template +inline T* alignToMachineWord(T* pointer) { + return reinterpret_cast(reinterpret_cast(pointer) & + ~machineWordAlignmentMask); } -template struct NonASCIIMask; -template<> struct NonASCIIMask<4, UChar> { - static inline uint32_t value() { return 0xFF80FF80U; } +template +struct NonASCIIMask; +template <> +struct NonASCIIMask<4, UChar> { + static inline uint32_t value() { return 0xFF80FF80U; } }; -template<> struct NonASCIIMask<4, LChar> { - static inline uint32_t value() { return 0x80808080U; } +template <> +struct NonASCIIMask<4, LChar> { + static inline uint32_t value() { return 0x80808080U; } }; -template<> struct NonASCIIMask<8, UChar> { - static inline uint64_t value() { return 0xFF80FF80FF80FF80ULL; } +template <> +struct NonASCIIMask<8, UChar> { + static inline uint64_t value() { return 0xFF80FF80FF80FF80ULL; } }; -template<> struct NonASCIIMask<8, LChar> { - static inline uint64_t value() { return 0x8080808080808080ULL; } +template <> +struct NonASCIIMask<8, LChar> { + static inline uint64_t value() { return 0x8080808080808080ULL; } }; - -template -inline bool isAllASCII(MachineWord word) -{ - return !(word & NonASCIIMask::value()); +template +inline bool isAllASCII(MachineWord word) { + return !(word & NonASCIIMask::value()); } // Note: This function assume the input is likely all ASCII, and // does not leave early if it is not the case. -template -inline bool charactersAreAllASCII(const CharacterType* characters, size_t length) -{ - MachineWord allCharBits = 0; - const CharacterType* end = characters + length; - - // Prologue: align the input. - while (!isAlignedToMachineWord(characters) && characters != end) { - allCharBits |= *characters; - ++characters; - } - - // Compare the values of CPU word size. - const CharacterType* wordEnd = alignToMachineWord(end); - const size_t loopIncrement = sizeof(MachineWord) / sizeof(CharacterType); - while (characters < wordEnd) { - allCharBits |= *(reinterpret_cast_ptr(characters)); - characters += loopIncrement; - } - - // Process the remaining bytes. - while (characters != end) { - allCharBits |= *characters; - ++characters; - } - - MachineWord nonASCIIBitMask = NonASCIIMask::value(); - return !(allCharBits & nonASCIIBitMask); +template +inline bool charactersAreAllASCII(const CharacterType* characters, + size_t length) { + MachineWord allCharBits = 0; + const CharacterType* end = characters + length; + + // Prologue: align the input. + while (!isAlignedToMachineWord(characters) && characters != end) { + allCharBits |= *characters; + ++characters; + } + + // Compare the values of CPU word size. + const CharacterType* wordEnd = alignToMachineWord(end); + const size_t loopIncrement = sizeof(MachineWord) / sizeof(CharacterType); + while (characters < wordEnd) { + allCharBits |= *(reinterpret_cast_ptr(characters)); + characters += loopIncrement; + } + + // Process the remaining bytes. + while (characters != end) { + allCharBits |= *characters; + ++characters; + } + + MachineWord nonASCIIBitMask = + NonASCIIMask::value(); + return !(allCharBits & nonASCIIBitMask); } -inline void copyLCharsFromUCharSource(LChar* destination, const UChar* source, size_t length) -{ -#if COMPILER(GCC) && CPU(ARM_NEON) && !(CPU(BIG_ENDIAN) || CPU(MIDDLE_ENDIAN)) && defined(NDEBUG) - const LChar* const end = destination + length; - const uintptr_t memoryAccessSize = 8; - - if (length >= (2 * memoryAccessSize) - 1) { - // Prefix: align dst on 64 bits. - const uintptr_t memoryAccessMask = memoryAccessSize - 1; - while (!isAlignedTo(destination)) - *destination++ = static_cast(*source++); - - // Vector interleaved unpack, we only store the lower 8 bits. - const uintptr_t lengthLeft = end - destination; - const LChar* const simdEnd = end - (lengthLeft % memoryAccessSize); - do { - asm("vld2.8 { d0-d1 }, [%[SOURCE]] !\n\t" - "vst1.8 { d0 }, [%[DESTINATION],:64] !\n\t" - : [SOURCE]"+r" (source), [DESTINATION]"+r" (destination) - : - : "memory", "d0", "d1"); - } while (destination != simdEnd); - } - - while (destination != end) - *destination++ = static_cast(*source++); +inline void copyLCharsFromUCharSource(LChar* destination, + const UChar* source, + size_t length) { +#if COMPILER(GCC) && CPU(ARM_NEON) && \ + !(CPU(BIG_ENDIAN) || CPU(MIDDLE_ENDIAN)) && defined(NDEBUG) + const LChar* const end = destination + length; + const uintptr_t memoryAccessSize = 8; + + if (length >= (2 * memoryAccessSize) - 1) { + // Prefix: align dst on 64 bits. + const uintptr_t memoryAccessMask = memoryAccessSize - 1; + while (!isAlignedTo(destination)) + *destination++ = static_cast(*source++); + + // Vector interleaved unpack, we only store the lower 8 bits. + const uintptr_t lengthLeft = end - destination; + const LChar* const simdEnd = end - (lengthLeft % memoryAccessSize); + do { + asm("vld2.8 { d0-d1 }, [%[SOURCE]] !\n\t" + "vst1.8 { d0 }, [%[DESTINATION],:64] !\n\t" + : [SOURCE] "+r"(source), [DESTINATION] "+r"(destination) + : + : "memory", "d0", "d1"); + } while (destination != simdEnd); + } + + while (destination != end) + *destination++ = static_cast(*source++); #else - for (size_t i = 0; i < length; ++i) { - ASSERT(!(source[i] & 0xff00)); - destination[i] = static_cast(source[i]); - } + for (size_t i = 0; i < length; ++i) { + ASSERT(!(source[i] & 0xff00)); + destination[i] = static_cast(source[i]); + } #endif } -} // namespace WTF +} // namespace WTF #endif // SKY_ENGINE_WTF_TEXT_ASCIIFASTPATH_H_ diff --git a/sky/engine/wtf/text/AtomicString.cpp b/sky/engine/wtf/text/AtomicString.cpp index 7f89878d4c468..5eba8d146f1b9 100644 --- a/sky/engine/wtf/text/AtomicString.cpp +++ b/sky/engine/wtf/text/AtomicString.cpp @@ -1,5 +1,6 @@ /* - * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2013 Apple Inc. + * All rights reserved. * Copyright (C) 2010 Patrick Gansterer * Copyright (C) 2012 Google Inc. All rights reserved. * @@ -33,483 +34,490 @@ namespace WTF { using namespace Unicode; -COMPILE_ASSERT(sizeof(AtomicString) == sizeof(String), atomic_string_and_string_must_be_same_size); +COMPILE_ASSERT(sizeof(AtomicString) == sizeof(String), + atomic_string_and_string_must_be_same_size); class AtomicStringTable { - WTF_MAKE_NONCOPYABLE(AtomicStringTable); -public: - static AtomicStringTable* create(WTFThreadData& data) - { - data.m_atomicStringTable = new AtomicStringTable; - data.m_atomicStringTableDestructor = AtomicStringTable::destroy; - data.m_atomicStringTable->addStaticStrings(); - return data.m_atomicStringTable; - } + WTF_MAKE_NONCOPYABLE(AtomicStringTable); - StringImpl* addStringImpl(StringImpl* string) - { - if (!string->length()) - return StringImpl::empty(); + public: + static AtomicStringTable* create(WTFThreadData& data) { + data.m_atomicStringTable = new AtomicStringTable; + data.m_atomicStringTableDestructor = AtomicStringTable::destroy; + data.m_atomicStringTable->addStaticStrings(); + return data.m_atomicStringTable; + } - StringImpl* result = *m_table.add(string).storedValue; + StringImpl* addStringImpl(StringImpl* string) { + if (!string->length()) + return StringImpl::empty(); - if (!result->isAtomic()) - result->setIsAtomic(true); + StringImpl* result = *m_table.add(string).storedValue; - ASSERT(!string->isStatic() || result->isStatic()); - return result; - } + if (!result->isAtomic()) + result->setIsAtomic(true); - HashSet& table() - { - return m_table; - } + ASSERT(!string->isStatic() || result->isStatic()); + return result; + } -private: - AtomicStringTable() { } + HashSet& table() { return m_table; } - void addStaticStrings() - { - const StaticStringsTable& staticStrings = StringImpl::allStaticStrings(); + private: + AtomicStringTable() {} - StaticStringsTable::const_iterator it = staticStrings.begin(); - for (; it != staticStrings.end(); ++it) { - addStringImpl(it->value); - } - } + void addStaticStrings() { + const StaticStringsTable& staticStrings = StringImpl::allStaticStrings(); - static void destroy(AtomicStringTable* table) - { - HashSet::iterator end = table->m_table.end(); - for (HashSet::iterator iter = table->m_table.begin(); iter != end; ++iter) { - StringImpl* string = *iter; - if (!string->isStatic()) { - ASSERT(string->isAtomic()); - string->setIsAtomic(false); - } - } - delete table; + StaticStringsTable::const_iterator it = staticStrings.begin(); + for (; it != staticStrings.end(); ++it) { + addStringImpl(it->value); + } + } + + static void destroy(AtomicStringTable* table) { + HashSet::iterator end = table->m_table.end(); + for (HashSet::iterator iter = table->m_table.begin(); + iter != end; ++iter) { + StringImpl* string = *iter; + if (!string->isStatic()) { + ASSERT(string->isAtomic()); + string->setIsAtomic(false); + } } + delete table; + } - HashSet m_table; + HashSet m_table; }; -static inline AtomicStringTable& atomicStringTable() -{ - // Once possible we should make this non-lazy (constructed in WTFThreadData's constructor). - WTFThreadData& data = wtfThreadData(); - AtomicStringTable* table = data.atomicStringTable(); - if (UNLIKELY(!table)) - table = AtomicStringTable::create(data); - return *table; +static inline AtomicStringTable& atomicStringTable() { + // Once possible we should make this non-lazy (constructed in WTFThreadData's + // constructor). + WTFThreadData& data = wtfThreadData(); + AtomicStringTable* table = data.atomicStringTable(); + if (UNLIKELY(!table)) + table = AtomicStringTable::create(data); + return *table; } -static inline HashSet& atomicStrings() -{ - return atomicStringTable().table(); +static inline HashSet& atomicStrings() { + return atomicStringTable().table(); } -template -static inline PassRefPtr addToStringTable(const T& value) -{ - HashSet::AddResult addResult = atomicStrings().add(value); +template +static inline PassRefPtr addToStringTable(const T& value) { + HashSet::AddResult addResult = + atomicStrings().add(value); - // If the string is newly-translated, then we need to adopt it. - // The boolean in the pair tells us if that is so. - return addResult.isNewEntry ? adoptRef(*addResult.storedValue) : *addResult.storedValue; + // If the string is newly-translated, then we need to adopt it. + // The boolean in the pair tells us if that is so. + return addResult.isNewEntry ? adoptRef(*addResult.storedValue) + : *addResult.storedValue; } -PassRefPtr AtomicString::add(const LChar* c) -{ - if (!c) - return nullptr; - if (!*c) - return StringImpl::empty(); +PassRefPtr AtomicString::add(const LChar* c) { + if (!c) + return nullptr; + if (!*c) + return StringImpl::empty(); - return add(c, strlen(reinterpret_cast(c))); + return add(c, strlen(reinterpret_cast(c))); } -template +template struct HashTranslatorCharBuffer { - const CharacterType* s; - unsigned length; + const CharacterType* s; + unsigned length; }; typedef HashTranslatorCharBuffer UCharBuffer; struct UCharBufferTranslator { - static unsigned hash(const UCharBuffer& buf) - { - return StringHasher::computeHashAndMaskTop8Bits(buf.s, buf.length); - } - - static bool equal(StringImpl* const& str, const UCharBuffer& buf) - { - return WTF::equal(str, buf.s, buf.length); - } - - static void translate(StringImpl*& location, const UCharBuffer& buf, unsigned hash) - { - location = StringImpl::create8BitIfPossible(buf.s, buf.length).leakRef(); - location->setHash(hash); - location->setIsAtomic(true); - } + static unsigned hash(const UCharBuffer& buf) { + return StringHasher::computeHashAndMaskTop8Bits(buf.s, buf.length); + } + + static bool equal(StringImpl* const& str, const UCharBuffer& buf) { + return WTF::equal(str, buf.s, buf.length); + } + + static void translate(StringImpl*& location, + const UCharBuffer& buf, + unsigned hash) { + location = StringImpl::create8BitIfPossible(buf.s, buf.length).leakRef(); + location->setHash(hash); + location->setIsAtomic(true); + } }; -template +template struct HashAndCharacters { - unsigned hash; - const CharacterType* characters; - unsigned length; + unsigned hash; + const CharacterType* characters; + unsigned length; }; -template +template struct HashAndCharactersTranslator { - static unsigned hash(const HashAndCharacters& buffer) - { - ASSERT(buffer.hash == StringHasher::computeHashAndMaskTop8Bits(buffer.characters, buffer.length)); - return buffer.hash; - } - - static bool equal(StringImpl* const& string, const HashAndCharacters& buffer) - { - return WTF::equal(string, buffer.characters, buffer.length); - } - - static void translate(StringImpl*& location, const HashAndCharacters& buffer, unsigned hash) - { - location = StringImpl::create(buffer.characters, buffer.length).leakRef(); - location->setHash(hash); - location->setIsAtomic(true); - } + static unsigned hash(const HashAndCharacters& buffer) { + ASSERT(buffer.hash == StringHasher::computeHashAndMaskTop8Bits( + buffer.characters, buffer.length)); + return buffer.hash; + } + + static bool equal(StringImpl* const& string, + const HashAndCharacters& buffer) { + return WTF::equal(string, buffer.characters, buffer.length); + } + + static void translate(StringImpl*& location, + const HashAndCharacters& buffer, + unsigned hash) { + location = StringImpl::create(buffer.characters, buffer.length).leakRef(); + location->setHash(hash); + location->setIsAtomic(true); + } }; struct HashAndUTF8Characters { - unsigned hash; - const char* characters; - unsigned length; - unsigned utf16Length; + unsigned hash; + const char* characters; + unsigned length; + unsigned utf16Length; }; struct HashAndUTF8CharactersTranslator { - static unsigned hash(const HashAndUTF8Characters& buffer) - { - return buffer.hash; + static unsigned hash(const HashAndUTF8Characters& buffer) { + return buffer.hash; + } + + static bool equal(StringImpl* const& string, + const HashAndUTF8Characters& buffer) { + if (buffer.utf16Length != string->length()) + return false; + + // If buffer contains only ASCII characters UTF-8 and UTF16 length are the + // same. + if (buffer.utf16Length != buffer.length) { + if (string->is8Bit()) { + const LChar* characters8 = string->characters8(); + return equalLatin1WithUTF8(characters8, characters8 + string->length(), + buffer.characters, + buffer.characters + buffer.length); + } + const UChar* characters16 = string->characters16(); + return equalUTF16WithUTF8(characters16, characters16 + string->length(), + buffer.characters, + buffer.characters + buffer.length); } - static bool equal(StringImpl* const& string, const HashAndUTF8Characters& buffer) - { - if (buffer.utf16Length != string->length()) - return false; - - // If buffer contains only ASCII characters UTF-8 and UTF16 length are the same. - if (buffer.utf16Length != buffer.length) { - if (string->is8Bit()) { - const LChar* characters8 = string->characters8(); - return equalLatin1WithUTF8(characters8, characters8 + string->length(), buffer.characters, buffer.characters + buffer.length); - } - const UChar* characters16 = string->characters16(); - return equalUTF16WithUTF8(characters16, characters16 + string->length(), buffer.characters, buffer.characters + buffer.length); - } - - if (string->is8Bit()) { - const LChar* stringCharacters = string->characters8(); - - for (unsigned i = 0; i < buffer.length; ++i) { - ASSERT(isASCII(buffer.characters[i])); - if (stringCharacters[i] != buffer.characters[i]) - return false; - } - - return true; - } - - const UChar* stringCharacters = string->characters16(); - - for (unsigned i = 0; i < buffer.length; ++i) { - ASSERT(isASCII(buffer.characters[i])); - if (stringCharacters[i] != buffer.characters[i]) - return false; - } - - return true; - } + if (string->is8Bit()) { + const LChar* stringCharacters = string->characters8(); - static void translate(StringImpl*& location, const HashAndUTF8Characters& buffer, unsigned hash) - { - UChar* target; - RefPtr newString = StringImpl::createUninitialized(buffer.utf16Length, target); + for (unsigned i = 0; i < buffer.length; ++i) { + ASSERT(isASCII(buffer.characters[i])); + if (stringCharacters[i] != buffer.characters[i]) + return false; + } - bool isAllASCII; - const char* source = buffer.characters; - if (convertUTF8ToUTF16(&source, source + buffer.length, &target, target + buffer.utf16Length, &isAllASCII) != conversionOK) - ASSERT_NOT_REACHED(); + return true; + } - if (isAllASCII) - newString = StringImpl::create(buffer.characters, buffer.length); + const UChar* stringCharacters = string->characters16(); - location = newString.release().leakRef(); - location->setHash(hash); - location->setIsAtomic(true); + for (unsigned i = 0; i < buffer.length; ++i) { + ASSERT(isASCII(buffer.characters[i])); + if (stringCharacters[i] != buffer.characters[i]) + return false; } + + return true; + } + + static void translate(StringImpl*& location, + const HashAndUTF8Characters& buffer, + unsigned hash) { + UChar* target; + RefPtr newString = + StringImpl::createUninitialized(buffer.utf16Length, target); + + bool isAllASCII; + const char* source = buffer.characters; + if (convertUTF8ToUTF16(&source, source + buffer.length, &target, + target + buffer.utf16Length, + &isAllASCII) != conversionOK) + ASSERT_NOT_REACHED(); + + if (isAllASCII) + newString = StringImpl::create(buffer.characters, buffer.length); + + location = newString.release().leakRef(); + location->setHash(hash); + location->setIsAtomic(true); + } }; -PassRefPtr AtomicString::add(const UChar* s, unsigned length) -{ - if (!s) - return nullptr; +PassRefPtr AtomicString::add(const UChar* s, unsigned length) { + if (!s) + return nullptr; - if (!length) - return StringImpl::empty(); + if (!length) + return StringImpl::empty(); - UCharBuffer buffer = { s, length }; - return addToStringTable(buffer); + UCharBuffer buffer = {s, length}; + return addToStringTable(buffer); } -PassRefPtr AtomicString::add(const UChar* s, unsigned length, unsigned existingHash) -{ - ASSERT(s); - ASSERT(existingHash); +PassRefPtr AtomicString::add(const UChar* s, + unsigned length, + unsigned existingHash) { + ASSERT(s); + ASSERT(existingHash); - if (!length) - return StringImpl::empty(); + if (!length) + return StringImpl::empty(); - HashAndCharacters buffer = { existingHash, s, length }; - return addToStringTable, HashAndCharactersTranslator >(buffer); + HashAndCharacters buffer = {existingHash, s, length}; + return addToStringTable, + HashAndCharactersTranslator>(buffer); } -PassRefPtr AtomicString::add(const UChar* s) -{ - if (!s) - return nullptr; +PassRefPtr AtomicString::add(const UChar* s) { + if (!s) + return nullptr; - unsigned length = 0; - while (s[length] != UChar(0)) - ++length; + unsigned length = 0; + while (s[length] != UChar(0)) + ++length; - if (!length) - return StringImpl::empty(); + if (!length) + return StringImpl::empty(); - UCharBuffer buffer = { s, length }; - return addToStringTable(buffer); + UCharBuffer buffer = {s, length}; + return addToStringTable(buffer); } struct SubstringLocation { - StringImpl* baseString; - unsigned start; - unsigned length; + StringImpl* baseString; + unsigned start; + unsigned length; }; struct SubstringTranslator { - static unsigned hash(const SubstringLocation& buffer) - { - if (buffer.baseString->is8Bit()) - return StringHasher::computeHashAndMaskTop8Bits(buffer.baseString->characters8() + buffer.start, buffer.length); - return StringHasher::computeHashAndMaskTop8Bits(buffer.baseString->characters16() + buffer.start, buffer.length); - } - - static bool equal(StringImpl* const& string, const SubstringLocation& buffer) - { - if (buffer.baseString->is8Bit()) - return WTF::equal(string, buffer.baseString->characters8() + buffer.start, buffer.length); - return WTF::equal(string, buffer.baseString->characters16() + buffer.start, buffer.length); - } - - static void translate(StringImpl*& location, const SubstringLocation& buffer, unsigned hash) - { - location = buffer.baseString->substring(buffer.start, buffer.length).leakRef(); - location->setHash(hash); - location->setIsAtomic(true); - } + static unsigned hash(const SubstringLocation& buffer) { + if (buffer.baseString->is8Bit()) + return StringHasher::computeHashAndMaskTop8Bits( + buffer.baseString->characters8() + buffer.start, buffer.length); + return StringHasher::computeHashAndMaskTop8Bits( + buffer.baseString->characters16() + buffer.start, buffer.length); + } + + static bool equal(StringImpl* const& string, + const SubstringLocation& buffer) { + if (buffer.baseString->is8Bit()) + return WTF::equal(string, buffer.baseString->characters8() + buffer.start, + buffer.length); + return WTF::equal(string, buffer.baseString->characters16() + buffer.start, + buffer.length); + } + + static void translate(StringImpl*& location, + const SubstringLocation& buffer, + unsigned hash) { + location = + buffer.baseString->substring(buffer.start, buffer.length).leakRef(); + location->setHash(hash); + location->setIsAtomic(true); + } }; -PassRefPtr AtomicString::add(StringImpl* baseString, unsigned start, unsigned length) -{ - if (!baseString) - return nullptr; +PassRefPtr AtomicString::add(StringImpl* baseString, + unsigned start, + unsigned length) { + if (!baseString) + return nullptr; - if (!length || start >= baseString->length()) - return StringImpl::empty(); + if (!length || start >= baseString->length()) + return StringImpl::empty(); - unsigned maxLength = baseString->length() - start; - if (length >= maxLength) { - if (!start) - return add(baseString); - length = maxLength; - } + unsigned maxLength = baseString->length() - start; + if (length >= maxLength) { + if (!start) + return add(baseString); + length = maxLength; + } - SubstringLocation buffer = { baseString, start, length }; - return addToStringTable(buffer); + SubstringLocation buffer = {baseString, start, length}; + return addToStringTable(buffer); } typedef HashTranslatorCharBuffer LCharBuffer; struct LCharBufferTranslator { - static unsigned hash(const LCharBuffer& buf) - { - return StringHasher::computeHashAndMaskTop8Bits(buf.s, buf.length); - } - - static bool equal(StringImpl* const& str, const LCharBuffer& buf) - { - return WTF::equal(str, buf.s, buf.length); - } - - static void translate(StringImpl*& location, const LCharBuffer& buf, unsigned hash) - { - location = StringImpl::create(buf.s, buf.length).leakRef(); - location->setHash(hash); - location->setIsAtomic(true); - } + static unsigned hash(const LCharBuffer& buf) { + return StringHasher::computeHashAndMaskTop8Bits(buf.s, buf.length); + } + + static bool equal(StringImpl* const& str, const LCharBuffer& buf) { + return WTF::equal(str, buf.s, buf.length); + } + + static void translate(StringImpl*& location, + const LCharBuffer& buf, + unsigned hash) { + location = StringImpl::create(buf.s, buf.length).leakRef(); + location->setHash(hash); + location->setIsAtomic(true); + } }; typedef HashTranslatorCharBuffer CharBuffer; struct CharBufferFromLiteralDataTranslator { - static unsigned hash(const CharBuffer& buf) - { - return StringHasher::computeHashAndMaskTop8Bits(reinterpret_cast(buf.s), buf.length); - } - - static bool equal(StringImpl* const& str, const CharBuffer& buf) - { - return WTF::equal(str, buf.s, buf.length); - } - - static void translate(StringImpl*& location, const CharBuffer& buf, unsigned hash) - { - location = StringImpl::create(buf.s, buf.length).leakRef(); - location->setHash(hash); - location->setIsAtomic(true); - } + static unsigned hash(const CharBuffer& buf) { + return StringHasher::computeHashAndMaskTop8Bits( + reinterpret_cast(buf.s), buf.length); + } + + static bool equal(StringImpl* const& str, const CharBuffer& buf) { + return WTF::equal(str, buf.s, buf.length); + } + + static void translate(StringImpl*& location, + const CharBuffer& buf, + unsigned hash) { + location = StringImpl::create(buf.s, buf.length).leakRef(); + location->setHash(hash); + location->setIsAtomic(true); + } }; -PassRefPtr AtomicString::add(const LChar* s, unsigned length) -{ - if (!s) - return nullptr; +PassRefPtr AtomicString::add(const LChar* s, unsigned length) { + if (!s) + return nullptr; - if (!length) - return StringImpl::empty(); + if (!length) + return StringImpl::empty(); - LCharBuffer buffer = { s, length }; - return addToStringTable(buffer); + LCharBuffer buffer = {s, length}; + return addToStringTable(buffer); } -PassRefPtr AtomicString::addFromLiteralData(const char* characters, unsigned length) -{ - ASSERT(characters); - ASSERT(length); +PassRefPtr AtomicString::addFromLiteralData(const char* characters, + unsigned length) { + ASSERT(characters); + ASSERT(length); - CharBuffer buffer = { characters, length }; - return addToStringTable(buffer); + CharBuffer buffer = {characters, length}; + return addToStringTable( + buffer); } -PassRefPtr AtomicString::addSlowCase(StringImpl* string) -{ - return atomicStringTable().addStringImpl(string); +PassRefPtr AtomicString::addSlowCase(StringImpl* string) { + return atomicStringTable().addStringImpl(string); } -template -static inline HashSet::iterator findString(const StringImpl* stringImpl) -{ - HashAndCharacters buffer = { stringImpl->existingHash(), stringImpl->getCharacters(), stringImpl->length() }; - return atomicStrings().find >(buffer); +template +static inline HashSet::iterator findString( + const StringImpl* stringImpl) { + HashAndCharacters buffer = { + stringImpl->existingHash(), stringImpl->getCharacters(), + stringImpl->length()}; + return atomicStrings().find>( + buffer); } -StringImpl* AtomicString::find(const StringImpl* stringImpl) -{ - ASSERT(stringImpl); - ASSERT(stringImpl->existingHash()); - - if (!stringImpl->length()) - return StringImpl::empty(); - - HashSet::iterator iterator; - if (stringImpl->is8Bit()) - iterator = findString(stringImpl); - else - iterator = findString(stringImpl); - if (iterator == atomicStrings().end()) - return 0; - return *iterator; +StringImpl* AtomicString::find(const StringImpl* stringImpl) { + ASSERT(stringImpl); + ASSERT(stringImpl->existingHash()); + + if (!stringImpl->length()) + return StringImpl::empty(); + + HashSet::iterator iterator; + if (stringImpl->is8Bit()) + iterator = findString(stringImpl); + else + iterator = findString(stringImpl); + if (iterator == atomicStrings().end()) + return 0; + return *iterator; } -void AtomicString::remove(StringImpl* r) -{ - HashSet::iterator iterator; - if (r->is8Bit()) - iterator = findString(r); - else - iterator = findString(r); - RELEASE_ASSERT(iterator != atomicStrings().end()); - atomicStrings().remove(iterator); +void AtomicString::remove(StringImpl* r) { + HashSet::iterator iterator; + if (r->is8Bit()) + iterator = findString(r); + else + iterator = findString(r); + RELEASE_ASSERT(iterator != atomicStrings().end()); + atomicStrings().remove(iterator); } -AtomicString AtomicString::lower() const -{ - // Note: This is a hot function in the Dromaeo benchmark. - StringImpl* impl = this->impl(); - if (UNLIKELY(!impl)) - return *this; - RefPtr newImpl = impl->lower(); - if (LIKELY(newImpl == impl)) - return *this; - return AtomicString(newImpl.release()); +AtomicString AtomicString::lower() const { + // Note: This is a hot function in the Dromaeo benchmark. + StringImpl* impl = this->impl(); + if (UNLIKELY(!impl)) + return *this; + RefPtr newImpl = impl->lower(); + if (LIKELY(newImpl == impl)) + return *this; + return AtomicString(newImpl.release()); } -AtomicString AtomicString::fromUTF8Internal(const char* charactersStart, const char* charactersEnd) -{ - HashAndUTF8Characters buffer; - buffer.characters = charactersStart; - buffer.hash = calculateStringHashAndLengthFromUTF8MaskingTop8Bits(charactersStart, charactersEnd, buffer.length, buffer.utf16Length); - - if (!buffer.hash) - return nullAtom; - - AtomicString atomicString; - atomicString.m_string = addToStringTable(buffer); - return atomicString; +AtomicString AtomicString::fromUTF8Internal(const char* charactersStart, + const char* charactersEnd) { + HashAndUTF8Characters buffer; + buffer.characters = charactersStart; + buffer.hash = calculateStringHashAndLengthFromUTF8MaskingTop8Bits( + charactersStart, charactersEnd, buffer.length, buffer.utf16Length); + + if (!buffer.hash) + return nullAtom; + + AtomicString atomicString; + atomicString.m_string = + addToStringTable( + buffer); + return atomicString; } -AtomicString AtomicString::number(int number) -{ - return numberToStringSigned(number); +AtomicString AtomicString::number(int number) { + return numberToStringSigned(number); } -AtomicString AtomicString::number(unsigned number) -{ - return numberToStringUnsigned(number); +AtomicString AtomicString::number(unsigned number) { + return numberToStringUnsigned(number); } -AtomicString AtomicString::number(long number) -{ - return numberToStringSigned(number); +AtomicString AtomicString::number(long number) { + return numberToStringSigned(number); } -AtomicString AtomicString::number(unsigned long number) -{ - return numberToStringUnsigned(number); +AtomicString AtomicString::number(unsigned long number) { + return numberToStringUnsigned(number); } -AtomicString AtomicString::number(long long number) -{ - return numberToStringSigned(number); +AtomicString AtomicString::number(long long number) { + return numberToStringSigned(number); } -AtomicString AtomicString::number(unsigned long long number) -{ - return numberToStringUnsigned(number); +AtomicString AtomicString::number(unsigned long long number) { + return numberToStringUnsigned(number); } -AtomicString AtomicString::number(double number, unsigned precision, TrailingZerosTruncatingPolicy trailingZerosTruncatingPolicy) -{ - NumberToStringBuffer buffer; - return AtomicString(numberToFixedPrecisionString(number, precision, buffer, trailingZerosTruncatingPolicy == TruncateTrailingZeros)); +AtomicString AtomicString::number( + double number, + unsigned precision, + TrailingZerosTruncatingPolicy trailingZerosTruncatingPolicy) { + NumberToStringBuffer buffer; + return AtomicString(numberToFixedPrecisionString( + number, precision, buffer, + trailingZerosTruncatingPolicy == TruncateTrailingZeros)); } #ifndef NDEBUG -void AtomicString::show() const -{ - m_string.show(); +void AtomicString::show() const { + m_string.show(); } #endif -} // namespace WTF +} // namespace WTF diff --git a/sky/engine/wtf/text/AtomicString.h b/sky/engine/wtf/text/AtomicString.h index 9725042e39c11..c7859ac218458 100644 --- a/sky/engine/wtf/text/AtomicString.h +++ b/sky/engine/wtf/text/AtomicString.h @@ -31,179 +31,257 @@ namespace WTF { struct AtomicStringHash; class WTF_EXPORT AtomicString { -public: - static void init(); - - AtomicString() { } - AtomicString(const LChar* s) : m_string(add(s)) { } - AtomicString(const char* s) : m_string(add(s)) { } - AtomicString(const LChar* s, unsigned length) : m_string(add(s, length)) { } - AtomicString(const UChar* s, unsigned length) : m_string(add(s, length)) { } - AtomicString(const UChar* s, unsigned length, unsigned existingHash) : m_string(add(s, length, existingHash)) { } - AtomicString(const UChar* s) : m_string(add(s)) { } - - template - explicit AtomicString(const Vector& characters) - : m_string(add(characters.data(), characters.size())) - { - } - - // Constructing an AtomicString from a String / StringImpl can be expensive if - // the StringImpl is not already atomic. - explicit AtomicString(StringImpl* impl) : m_string(add(impl)) { } - AtomicString(const String& s) : m_string(add(s.impl())) { } - - AtomicString(StringImpl* baseString, unsigned start, unsigned length) : m_string(add(baseString, start, length)) { } - - enum ConstructFromLiteralTag { ConstructFromLiteral }; - AtomicString(const char* characters, unsigned length, ConstructFromLiteralTag) - : m_string(addFromLiteralData(characters, length)) - { - } - - template - ALWAYS_INLINE AtomicString(const char (&characters)[charactersCount], ConstructFromLiteralTag) - : m_string(addFromLiteralData(characters, charactersCount - 1)) - { - COMPILE_ASSERT(charactersCount > 1, AtomicStringFromLiteralNotEmpty); - COMPILE_ASSERT((charactersCount - 1 <= ((unsigned(~0) - sizeof(StringImpl)) / sizeof(LChar))), AtomicStringFromLiteralCannotOverflow); - } - - // Hash table deleted values, which are only constructed and never copied or destroyed. - AtomicString(WTF::HashTableDeletedValueType) : m_string(WTF::HashTableDeletedValue) { } - bool isHashTableDeletedValue() const { return m_string.isHashTableDeletedValue(); } - - static StringImpl* find(const StringImpl*); - - operator const String&() const { return m_string; } - const String& string() const { return m_string; }; - - StringImpl* impl() const { return m_string.impl(); } - - bool is8Bit() const { return m_string.is8Bit(); } - const LChar* characters8() const { return m_string.characters8(); } - const UChar* characters16() const { return m_string.characters16(); } - unsigned length() const { return m_string.length(); } - - UChar operator[](unsigned i) const { return m_string[i]; } - - bool contains(UChar c) const { return m_string.contains(c); } - bool contains(const LChar* s, bool caseSensitive = true) const - { return m_string.contains(s, caseSensitive); } - bool contains(const String& s, bool caseSensitive = true) const - { return m_string.contains(s, caseSensitive); } - - size_t find(UChar c, size_t start = 0) const { return m_string.find(c, start); } - size_t find(const LChar* s, size_t start = 0, bool caseSentitive = true) const - { return m_string.find(s, start, caseSentitive); } - size_t find(const String& s, size_t start = 0, bool caseSentitive = true) const - { return m_string.find(s, start, caseSentitive); } - - bool startsWith(const String& s, bool caseSensitive = true) const - { return m_string.startsWith(s, caseSensitive); } - bool startsWith(UChar character) const - { return m_string.startsWith(character); } - template - bool startsWith(const char (&prefix)[matchLength], bool caseSensitive = true) const - { return m_string.startsWith(prefix, caseSensitive); } - - bool endsWith(const String& s, bool caseSensitive = true) const - { return m_string.endsWith(s, caseSensitive); } - bool endsWith(UChar character) const - { return m_string.endsWith(character); } - template - bool endsWith(const char (&prefix)[matchLength], bool caseSensitive = true) const - { return m_string.endsWith(prefix, caseSensitive); } - - AtomicString lower() const; - AtomicString upper() const { return AtomicString(impl()->upper()); } - - int toInt(bool* ok = 0) const { return m_string.toInt(ok); } - double toDouble(bool* ok = 0) const { return m_string.toDouble(ok); } - float toFloat(bool* ok = 0) const { return m_string.toFloat(ok); } - bool percentage(int& p) const { return m_string.percentage(p); } - - static AtomicString number(int); - static AtomicString number(unsigned); - static AtomicString number(long); - static AtomicString number(unsigned long); - static AtomicString number(long long); - static AtomicString number(unsigned long long); - - static AtomicString number(double, unsigned precision = 6, TrailingZerosTruncatingPolicy = TruncateTrailingZeros); - - bool isNull() const { return m_string.isNull(); } - bool isEmpty() const { return m_string.isEmpty(); } - - static void remove(StringImpl*); + public: + static void init(); + + AtomicString() {} + AtomicString(const LChar* s) : m_string(add(s)) {} + AtomicString(const char* s) : m_string(add(s)) {} + AtomicString(const LChar* s, unsigned length) : m_string(add(s, length)) {} + AtomicString(const UChar* s, unsigned length) : m_string(add(s, length)) {} + AtomicString(const UChar* s, unsigned length, unsigned existingHash) + : m_string(add(s, length, existingHash)) {} + AtomicString(const UChar* s) : m_string(add(s)) {} + + template + explicit AtomicString(const Vector& characters) + : m_string(add(characters.data(), characters.size())) {} + + // Constructing an AtomicString from a String / StringImpl can be expensive if + // the StringImpl is not already atomic. + explicit AtomicString(StringImpl* impl) : m_string(add(impl)) {} + AtomicString(const String& s) : m_string(add(s.impl())) {} + + AtomicString(StringImpl* baseString, unsigned start, unsigned length) + : m_string(add(baseString, start, length)) {} + + enum ConstructFromLiteralTag { ConstructFromLiteral }; + AtomicString(const char* characters, unsigned length, ConstructFromLiteralTag) + : m_string(addFromLiteralData(characters, length)) {} + + template + ALWAYS_INLINE AtomicString(const char (&characters)[charactersCount], + ConstructFromLiteralTag) + : m_string(addFromLiteralData(characters, charactersCount - 1)) { + COMPILE_ASSERT(charactersCount > 1, AtomicStringFromLiteralNotEmpty); + COMPILE_ASSERT((charactersCount - 1 <= + ((unsigned(~0) - sizeof(StringImpl)) / sizeof(LChar))), + AtomicStringFromLiteralCannotOverflow); + } + + // Hash table deleted values, which are only constructed and never copied or + // destroyed. + AtomicString(WTF::HashTableDeletedValueType) + : m_string(WTF::HashTableDeletedValue) {} + bool isHashTableDeletedValue() const { + return m_string.isHashTableDeletedValue(); + } + + static StringImpl* find(const StringImpl*); + + operator const String&() const { return m_string; } + const String& string() const { return m_string; }; + + StringImpl* impl() const { return m_string.impl(); } + + bool is8Bit() const { return m_string.is8Bit(); } + const LChar* characters8() const { return m_string.characters8(); } + const UChar* characters16() const { return m_string.characters16(); } + unsigned length() const { return m_string.length(); } + + UChar operator[](unsigned i) const { return m_string[i]; } + + bool contains(UChar c) const { return m_string.contains(c); } + bool contains(const LChar* s, bool caseSensitive = true) const { + return m_string.contains(s, caseSensitive); + } + bool contains(const String& s, bool caseSensitive = true) const { + return m_string.contains(s, caseSensitive); + } + + size_t find(UChar c, size_t start = 0) const { + return m_string.find(c, start); + } + size_t find(const LChar* s, + size_t start = 0, + bool caseSentitive = true) const { + return m_string.find(s, start, caseSentitive); + } + size_t find(const String& s, + size_t start = 0, + bool caseSentitive = true) const { + return m_string.find(s, start, caseSentitive); + } + + bool startsWith(const String& s, bool caseSensitive = true) const { + return m_string.startsWith(s, caseSensitive); + } + bool startsWith(UChar character) const { + return m_string.startsWith(character); + } + template + bool startsWith(const char (&prefix)[matchLength], + bool caseSensitive = true) const { + return m_string.startsWith(prefix, caseSensitive); + } + + bool endsWith(const String& s, bool caseSensitive = true) const { + return m_string.endsWith(s, caseSensitive); + } + bool endsWith(UChar character) const { return m_string.endsWith(character); } + template + bool endsWith(const char (&prefix)[matchLength], + bool caseSensitive = true) const { + return m_string.endsWith(prefix, caseSensitive); + } + + AtomicString lower() const; + AtomicString upper() const { return AtomicString(impl()->upper()); } + + int toInt(bool* ok = 0) const { return m_string.toInt(ok); } + double toDouble(bool* ok = 0) const { return m_string.toDouble(ok); } + float toFloat(bool* ok = 0) const { return m_string.toFloat(ok); } + bool percentage(int& p) const { return m_string.percentage(p); } + + static AtomicString number(int); + static AtomicString number(unsigned); + static AtomicString number(long); + static AtomicString number(unsigned long); + static AtomicString number(long long); + static AtomicString number(unsigned long long); + + static AtomicString number( + double, + unsigned precision = 6, + TrailingZerosTruncatingPolicy = TruncateTrailingZeros); + + bool isNull() const { return m_string.isNull(); } + bool isEmpty() const { return m_string.isEmpty(); } + + static void remove(StringImpl*); #if USE(CF) - AtomicString(CFStringRef s) : m_string(add(s)) { } + AtomicString(CFStringRef s) : m_string(add(s)) {} #endif - // AtomicString::fromUTF8 will return a null string if - // the input data contains invalid UTF-8 sequences. - static AtomicString fromUTF8(const char*, size_t); - static AtomicString fromUTF8(const char*); + // AtomicString::fromUTF8 will return a null string if + // the input data contains invalid UTF-8 sequences. + static AtomicString fromUTF8(const char*, size_t); + static AtomicString fromUTF8(const char*); - CString ascii() const { return m_string.ascii(); } - CString latin1() const { return m_string.latin1(); } - CString utf8(UTF8ConversionMode mode = LenientUTF8Conversion) const { return m_string.utf8(mode); } + CString ascii() const { return m_string.ascii(); } + CString latin1() const { return m_string.latin1(); } + CString utf8(UTF8ConversionMode mode = LenientUTF8Conversion) const { + return m_string.utf8(mode); + } #ifndef NDEBUG - void show() const; + void show() const; #endif -private: - String m_string; - - static PassRefPtr add(const LChar*); - ALWAYS_INLINE static PassRefPtr add(const char* s) { return add(reinterpret_cast(s)); }; - static PassRefPtr add(const LChar*, unsigned length); - static PassRefPtr add(const UChar*, unsigned length); - ALWAYS_INLINE static PassRefPtr add(const char* s, unsigned length) { return add(reinterpret_cast(s), length); }; - static PassRefPtr add(const UChar*, unsigned length, unsigned existingHash); - static PassRefPtr add(const UChar*); - static PassRefPtr add(StringImpl*, unsigned offset, unsigned length); - ALWAYS_INLINE static PassRefPtr add(StringImpl* r) - { - if (!r || r->isAtomic()) - return r; - return addSlowCase(r); - } - static PassRefPtr addFromLiteralData(const char* characters, unsigned length); - static PassRefPtr addSlowCase(StringImpl*); + private: + String m_string; + + static PassRefPtr add(const LChar*); + ALWAYS_INLINE static PassRefPtr add(const char* s) { + return add(reinterpret_cast(s)); + }; + static PassRefPtr add(const LChar*, unsigned length); + static PassRefPtr add(const UChar*, unsigned length); + ALWAYS_INLINE static PassRefPtr add(const char* s, + unsigned length) { + return add(reinterpret_cast(s), length); + }; + static PassRefPtr add(const UChar*, + unsigned length, + unsigned existingHash); + static PassRefPtr add(const UChar*); + static PassRefPtr add(StringImpl*, + unsigned offset, + unsigned length); + ALWAYS_INLINE static PassRefPtr add(StringImpl* r) { + if (!r || r->isAtomic()) + return r; + return addSlowCase(r); + } + static PassRefPtr addFromLiteralData(const char* characters, + unsigned length); + static PassRefPtr addSlowCase(StringImpl*); #if USE(CF) - static PassRefPtr add(CFStringRef); + static PassRefPtr add(CFStringRef); #endif - static AtomicString fromUTF8Internal(const char*, const char*); + static AtomicString fromUTF8Internal(const char*, const char*); }; -inline bool operator==(const AtomicString& a, const AtomicString& b) { return a.impl() == b.impl(); } +inline bool operator==(const AtomicString& a, const AtomicString& b) { + return a.impl() == b.impl(); +} WTF_EXPORT bool operator==(const AtomicString&, const LChar*); -inline bool operator==(const AtomicString& a, const char* b) { return WTF::equal(a.impl(), reinterpret_cast(b)); } -inline bool operator==(const AtomicString& a, const Vector& b) { return a.impl() && equal(a.impl(), b.data(), b.size()); } -inline bool operator==(const AtomicString& a, const String& b) { return equal(a.impl(), b.impl()); } -inline bool operator==(const LChar* a, const AtomicString& b) { return b == a; } -inline bool operator==(const String& a, const AtomicString& b) { return equal(a.impl(), b.impl()); } -inline bool operator==(const Vector& a, const AtomicString& b) { return b == a; } - -inline bool operator!=(const AtomicString& a, const AtomicString& b) { return a.impl() != b.impl(); } -inline bool operator!=(const AtomicString& a, const LChar* b) { return !(a == b); } -inline bool operator!=(const AtomicString& a, const char* b) { return !(a == b); } -inline bool operator!=(const AtomicString& a, const String& b) { return !equal(a.impl(), b.impl()); } -inline bool operator!=(const AtomicString& a, const Vector& b) { return !(a == b); } -inline bool operator!=(const LChar* a, const AtomicString& b) { return !(b == a); } -inline bool operator!=(const String& a, const AtomicString& b) { return !equal(a.impl(), b.impl()); } -inline bool operator!=(const Vector& a, const AtomicString& b) { return !(a == b); } - -inline bool equalIgnoringCase(const AtomicString& a, const AtomicString& b) { return equalIgnoringCase(a.impl(), b.impl()); } -inline bool equalIgnoringCase(const AtomicString& a, const LChar* b) { return equalIgnoringCase(a.impl(), b); } -inline bool equalIgnoringCase(const AtomicString& a, const char* b) { return equalIgnoringCase(a.impl(), reinterpret_cast(b)); } -inline bool equalIgnoringCase(const AtomicString& a, const String& b) { return equalIgnoringCase(a.impl(), b.impl()); } -inline bool equalIgnoringCase(const LChar* a, const AtomicString& b) { return equalIgnoringCase(a, b.impl()); } -inline bool equalIgnoringCase(const char* a, const AtomicString& b) { return equalIgnoringCase(reinterpret_cast(a), b.impl()); } -inline bool equalIgnoringCase(const String& a, const AtomicString& b) { return equalIgnoringCase(a.impl(), b.impl()); } +inline bool operator==(const AtomicString& a, const char* b) { + return WTF::equal(a.impl(), reinterpret_cast(b)); +} +inline bool operator==(const AtomicString& a, const Vector& b) { + return a.impl() && equal(a.impl(), b.data(), b.size()); +} +inline bool operator==(const AtomicString& a, const String& b) { + return equal(a.impl(), b.impl()); +} +inline bool operator==(const LChar* a, const AtomicString& b) { + return b == a; +} +inline bool operator==(const String& a, const AtomicString& b) { + return equal(a.impl(), b.impl()); +} +inline bool operator==(const Vector& a, const AtomicString& b) { + return b == a; +} + +inline bool operator!=(const AtomicString& a, const AtomicString& b) { + return a.impl() != b.impl(); +} +inline bool operator!=(const AtomicString& a, const LChar* b) { + return !(a == b); +} +inline bool operator!=(const AtomicString& a, const char* b) { + return !(a == b); +} +inline bool operator!=(const AtomicString& a, const String& b) { + return !equal(a.impl(), b.impl()); +} +inline bool operator!=(const AtomicString& a, const Vector& b) { + return !(a == b); +} +inline bool operator!=(const LChar* a, const AtomicString& b) { + return !(b == a); +} +inline bool operator!=(const String& a, const AtomicString& b) { + return !equal(a.impl(), b.impl()); +} +inline bool operator!=(const Vector& a, const AtomicString& b) { + return !(a == b); +} + +inline bool equalIgnoringCase(const AtomicString& a, const AtomicString& b) { + return equalIgnoringCase(a.impl(), b.impl()); +} +inline bool equalIgnoringCase(const AtomicString& a, const LChar* b) { + return equalIgnoringCase(a.impl(), b); +} +inline bool equalIgnoringCase(const AtomicString& a, const char* b) { + return equalIgnoringCase(a.impl(), reinterpret_cast(b)); +} +inline bool equalIgnoringCase(const AtomicString& a, const String& b) { + return equalIgnoringCase(a.impl(), b.impl()); +} +inline bool equalIgnoringCase(const LChar* a, const AtomicString& b) { + return equalIgnoringCase(a, b.impl()); +} +inline bool equalIgnoringCase(const char* a, const AtomicString& b) { + return equalIgnoringCase(reinterpret_cast(a), b.impl()); +} +inline bool equalIgnoringCase(const String& a, const AtomicString& b) { + return equalIgnoringCase(a.impl(), b.impl()); +} // Define external global variables for the commonly used atomic strings. // These are only usable from the main thread. @@ -211,37 +289,38 @@ WTF_EXPORT extern const AtomicString& nullAtom; WTF_EXPORT extern const AtomicString& emptyAtom; WTF_EXPORT extern const AtomicString& starAtom; -inline AtomicString AtomicString::fromUTF8(const char* characters, size_t length) -{ - if (!characters) - return nullAtom; - if (!length) - return emptyAtom; - return fromUTF8Internal(characters, characters + length); +inline AtomicString AtomicString::fromUTF8(const char* characters, + size_t length) { + if (!characters) + return nullAtom; + if (!length) + return emptyAtom; + return fromUTF8Internal(characters, characters + length); } -inline AtomicString AtomicString::fromUTF8(const char* characters) -{ - if (!characters) - return nullAtom; - if (!*characters) - return emptyAtom; - return fromUTF8Internal(characters, 0); +inline AtomicString AtomicString::fromUTF8(const char* characters) { + if (!characters) + return nullAtom; + if (!*characters) + return emptyAtom; + return fromUTF8Internal(characters, 0); } // AtomicStringHash is the default hash for AtomicString -template struct DefaultHash; -template<> struct DefaultHash { - typedef AtomicStringHash Hash; +template +struct DefaultHash; +template <> +struct DefaultHash { + typedef AtomicStringHash Hash; }; -} // namespace WTF +} // namespace WTF WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(AtomicString); using WTF::AtomicString; -using WTF::nullAtom; using WTF::emptyAtom; +using WTF::nullAtom; using WTF::starAtom; #include "flutter/sky/engine/wtf/text/StringConcatenate.h" diff --git a/sky/engine/wtf/text/AtomicStringHash.h b/sky/engine/wtf/text/AtomicStringHash.h index 3f4c480723252..c9a80d5b352b6 100644 --- a/sky/engine/wtf/text/AtomicStringHash.h +++ b/sky/engine/wtf/text/AtomicStringHash.h @@ -34,34 +34,38 @@ namespace WTF { - struct AtomicStringHash { - static unsigned hash(const AtomicString& key) - { - return key.impl()->existingHash(); - } +struct AtomicStringHash { + static unsigned hash(const AtomicString& key) { + return key.impl()->existingHash(); + } - static bool equal(const AtomicString& a, const AtomicString& b) - { - return a == b; - } + static bool equal(const AtomicString& a, const AtomicString& b) { + return a == b; + } - static const bool safeToCompareToEmptyOrDeleted = false; - }; + static const bool safeToCompareToEmptyOrDeleted = false; +}; - // AtomicStringHash is the default hash for AtomicString - template<> struct HashTraits : GenericHashTraits { - // Unlike other types, we can return a const reference for AtomicString's empty value (nullAtom). - typedef const WTF::AtomicString& PeekOutType; +// AtomicStringHash is the default hash for AtomicString +template <> +struct HashTraits : GenericHashTraits { + // Unlike other types, we can return a const reference for AtomicString's + // empty value (nullAtom). + typedef const WTF::AtomicString& PeekOutType; - static const WTF::AtomicString& emptyValue() { return nullAtom; } - static PeekOutType peek(const WTF::AtomicString& value) { return value; } + static const WTF::AtomicString& emptyValue() { return nullAtom; } + static PeekOutType peek(const WTF::AtomicString& value) { return value; } - static const bool emptyValueIsZero = true; - static void constructDeletedValue(WTF::AtomicString& slot, bool) { new (NotNull, &slot) WTF::AtomicString(HashTableDeletedValue); } - static bool isDeletedValue(const WTF::AtomicString& slot) { return slot.isHashTableDeletedValue(); } - }; + static const bool emptyValueIsZero = true; + static void constructDeletedValue(WTF::AtomicString& slot, bool) { + new (NotNull, &slot) WTF::AtomicString(HashTableDeletedValue); + } + static bool isDeletedValue(const WTF::AtomicString& slot) { + return slot.isHashTableDeletedValue(); + } +}; -} +} // namespace WTF using WTF::AtomicStringHash; diff --git a/sky/engine/wtf/text/AtomicStringTest.cpp b/sky/engine/wtf/text/AtomicStringTest.cpp index 62abad0ebaa88..a26a16b5b9ede 100644 --- a/sky/engine/wtf/text/AtomicStringTest.cpp +++ b/sky/engine/wtf/text/AtomicStringTest.cpp @@ -34,46 +34,45 @@ namespace { -TEST(AtomicStringTest, Number) -{ - int intValue = 1234; - ASSERT_EQ(AtomicString::number(intValue), "1234"); - intValue = -1234; - ASSERT_EQ(AtomicString::number(intValue), "-1234"); - unsigned unsignedValue = 1234u; - ASSERT_EQ(AtomicString::number(unsignedValue), "1234"); - long longValue = 6553500; - ASSERT_EQ(AtomicString::number(longValue), "6553500"); - longValue = -6553500; - ASSERT_EQ(AtomicString::number(longValue), "-6553500"); - unsigned long unsignedLongValue = 4294967295u; - ASSERT_EQ(AtomicString::number(unsignedLongValue), "4294967295"); - long long longlongValue = 9223372036854775807; - ASSERT_EQ(AtomicString::number(longlongValue), "9223372036854775807"); - longlongValue = -9223372036854775807; - ASSERT_EQ(AtomicString::number(longlongValue), "-9223372036854775807"); - unsigned long long unsignedLongLongValue = 18446744073709551615u; - ASSERT_EQ(AtomicString::number(unsignedLongLongValue), "18446744073709551615"); - double doubleValue = 1234.56; - ASSERT_EQ(AtomicString::number(doubleValue), "1234.56"); - doubleValue = 1234.56789; - ASSERT_EQ(AtomicString::number(doubleValue, 9), "1234.56789"); +TEST(AtomicStringTest, Number) { + int intValue = 1234; + ASSERT_EQ(AtomicString::number(intValue), "1234"); + intValue = -1234; + ASSERT_EQ(AtomicString::number(intValue), "-1234"); + unsigned unsignedValue = 1234u; + ASSERT_EQ(AtomicString::number(unsignedValue), "1234"); + long longValue = 6553500; + ASSERT_EQ(AtomicString::number(longValue), "6553500"); + longValue = -6553500; + ASSERT_EQ(AtomicString::number(longValue), "-6553500"); + unsigned long unsignedLongValue = 4294967295u; + ASSERT_EQ(AtomicString::number(unsignedLongValue), "4294967295"); + long long longlongValue = 9223372036854775807; + ASSERT_EQ(AtomicString::number(longlongValue), "9223372036854775807"); + longlongValue = -9223372036854775807; + ASSERT_EQ(AtomicString::number(longlongValue), "-9223372036854775807"); + unsigned long long unsignedLongLongValue = 18446744073709551615u; + ASSERT_EQ(AtomicString::number(unsignedLongLongValue), + "18446744073709551615"); + double doubleValue = 1234.56; + ASSERT_EQ(AtomicString::number(doubleValue), "1234.56"); + doubleValue = 1234.56789; + ASSERT_EQ(AtomicString::number(doubleValue, 9), "1234.56789"); } -TEST(AtomicStringTest, ImplEquality) -{ - AtomicString foo("foo"); - AtomicString bar("bar"); - AtomicString baz("baz"); - AtomicString foo2("foo"); - AtomicString baz2("baz"); - AtomicString bar2("bar"); - ASSERT_EQ(foo.impl(), foo2.impl()); - ASSERT_EQ(bar.impl(), bar2.impl()); - ASSERT_EQ(baz.impl(), baz2.impl()); - ASSERT_NE(foo.impl(), bar.impl()); - ASSERT_NE(foo.impl(), baz.impl()); - ASSERT_NE(bar.impl(), baz.impl()); +TEST(AtomicStringTest, ImplEquality) { + AtomicString foo("foo"); + AtomicString bar("bar"); + AtomicString baz("baz"); + AtomicString foo2("foo"); + AtomicString baz2("baz"); + AtomicString bar2("bar"); + ASSERT_EQ(foo.impl(), foo2.impl()); + ASSERT_EQ(bar.impl(), bar2.impl()); + ASSERT_EQ(baz.impl(), baz2.impl()); + ASSERT_NE(foo.impl(), bar.impl()); + ASSERT_NE(foo.impl(), baz.impl()); + ASSERT_NE(bar.impl(), baz.impl()); } -} // namespace +} // namespace diff --git a/sky/engine/wtf/text/CString.cpp b/sky/engine/wtf/text/CString.cpp index bcd18853bb738..52399cccb41d8 100644 --- a/sky/engine/wtf/text/CString.cpp +++ b/sky/engine/wtf/text/CString.cpp @@ -1,5 +1,6 @@ /* - * Copyright (C) 2003, 2006, 2008, 2009, 2010, 2012 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2006, 2008, 2009, 2010, 2012 Apple Inc. + * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -23,7 +24,6 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #include "flutter/sky/engine/wtf/text/CString.h" #include @@ -34,98 +34,89 @@ using namespace std; namespace WTF { -PassRefPtr CStringBuffer::createUninitialized(size_t length) -{ - RELEASE_ASSERT(length < (numeric_limits::max() - sizeof(CStringBuffer))); +PassRefPtr CStringBuffer::createUninitialized(size_t length) { + RELEASE_ASSERT(length < + (numeric_limits::max() - sizeof(CStringBuffer))); - // The +1 is for the terminating NUL character. - size_t size = sizeof(CStringBuffer) + length + 1; - CStringBuffer* stringBuffer = static_cast(partitionAllocGeneric(Partitions::getBufferPartition(), size)); - return adoptRef(new (stringBuffer) CStringBuffer(length)); + // The +1 is for the terminating NUL character. + size_t size = sizeof(CStringBuffer) + length + 1; + CStringBuffer* stringBuffer = static_cast( + partitionAllocGeneric(Partitions::getBufferPartition(), size)); + return adoptRef(new (stringBuffer) CStringBuffer(length)); } -void CStringBuffer::operator delete(void* ptr) -{ - partitionFreeGeneric(Partitions::getBufferPartition(), ptr); +void CStringBuffer::operator delete(void* ptr) { + partitionFreeGeneric(Partitions::getBufferPartition(), ptr); } -CString::CString(const char* str) -{ - if (!str) - return; +CString::CString(const char* str) { + if (!str) + return; - init(str, strlen(str)); + init(str, strlen(str)); } -CString::CString(const char* str, size_t length) -{ - if (!str) { - ASSERT(!length); - return; - } +CString::CString(const char* str, size_t length) { + if (!str) { + ASSERT(!length); + return; + } - init(str, length); + init(str, length); } -void CString::init(const char* str, size_t length) -{ - ASSERT(str); +void CString::init(const char* str, size_t length) { + ASSERT(str); - m_buffer = CStringBuffer::createUninitialized(length); - memcpy(m_buffer->mutableData(), str, length); - m_buffer->mutableData()[length] = '\0'; + m_buffer = CStringBuffer::createUninitialized(length); + memcpy(m_buffer->mutableData(), str, length); + m_buffer->mutableData()[length] = '\0'; } -char* CString::mutableData() -{ - copyBufferIfNeeded(); - if (!m_buffer) - return 0; - return m_buffer->mutableData(); +char* CString::mutableData() { + copyBufferIfNeeded(); + if (!m_buffer) + return 0; + return m_buffer->mutableData(); } -CString CString::newUninitialized(size_t length, char*& characterBuffer) -{ - CString result; - result.m_buffer = CStringBuffer::createUninitialized(length); - char* bytes = result.m_buffer->mutableData(); - bytes[length] = '\0'; - characterBuffer = bytes; - return result; +CString CString::newUninitialized(size_t length, char*& characterBuffer) { + CString result; + result.m_buffer = CStringBuffer::createUninitialized(length); + char* bytes = result.m_buffer->mutableData(); + bytes[length] = '\0'; + characterBuffer = bytes; + return result; } -void CString::copyBufferIfNeeded() -{ - if (!m_buffer || m_buffer->hasOneRef()) - return; +void CString::copyBufferIfNeeded() { + if (!m_buffer || m_buffer->hasOneRef()) + return; - RefPtr buffer = m_buffer.release(); - size_t length = buffer->length(); - m_buffer = CStringBuffer::createUninitialized(length); - memcpy(m_buffer->mutableData(), buffer->data(), length + 1); + RefPtr buffer = m_buffer.release(); + size_t length = buffer->length(); + m_buffer = CStringBuffer::createUninitialized(length); + memcpy(m_buffer->mutableData(), buffer->data(), length + 1); } -bool CString::isSafeToSendToAnotherThread() const -{ - return !m_buffer || m_buffer->hasOneRef(); +bool CString::isSafeToSendToAnotherThread() const { + return !m_buffer || m_buffer->hasOneRef(); } -bool operator==(const CString& a, const CString& b) -{ - if (a.isNull() != b.isNull()) - return false; - if (a.length() != b.length()) - return false; - return !memcmp(a.data(), b.data(), a.length()); +bool operator==(const CString& a, const CString& b) { + if (a.isNull() != b.isNull()) + return false; + if (a.length() != b.length()) + return false; + return !memcmp(a.data(), b.data(), a.length()); } -bool operator==(const CString& a, const char* b) -{ - if (a.isNull() != !b) - return false; - if (!b) - return true; - return !strcmp(a.data(), b); +bool operator==(const CString& a, const char* b) { + if (a.isNull() != !b) + return false; + if (!b) + return true; + return !strcmp(a.data(), b); } -} // namespace WTF +} // namespace WTF diff --git a/sky/engine/wtf/text/CString.h b/sky/engine/wtf/text/CString.h index 6fa04064b725e..938dbd36a8034 100644 --- a/sky/engine/wtf/text/CString.h +++ b/sky/engine/wtf/text/CString.h @@ -1,5 +1,6 @@ /* - * Copyright (C) 2003, 2006, 2008, 2009, 2010, 2012 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2006, 2008, 2009, 2010, 2012 Apple Inc. + * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,70 +34,66 @@ namespace WTF { -// CStringBuffer is the ref-counted storage class for the characters in a CString. -// The data is implicitly allocated 1 character longer than length(), as it is zero-terminated. +// CStringBuffer is the ref-counted storage class for the characters in a +// CString. The data is implicitly allocated 1 character longer than length(), +// as it is zero-terminated. class WTF_EXPORT CStringBuffer : public RefCounted { -public: - const char* data() { return mutableData(); } - size_t length() const { return m_length; } + public: + const char* data() { return mutableData(); } + size_t length() const { return m_length; } -private: - friend class CString; - friend class RefCounted; - // CStringBuffers are allocated out of the WTF buffer partition. - void* operator new(size_t, void* ptr) { return ptr; }; - void operator delete(void*); + private: + friend class CString; + friend class RefCounted; + // CStringBuffers are allocated out of the WTF buffer partition. + void* operator new(size_t, void* ptr) { return ptr; }; + void operator delete(void*); - static PassRefPtr createUninitialized(size_t length); + static PassRefPtr createUninitialized(size_t length); - CStringBuffer(size_t length) : m_length(length) { } - char* mutableData() { return reinterpret_cast(this + 1); } + CStringBuffer(size_t length) : m_length(length) {} + char* mutableData() { return reinterpret_cast(this + 1); } - const unsigned m_length; + const unsigned m_length; }; // A container for a null-terminated char array supporting copy-on-write // assignment. The contained char array may be null. class WTF_EXPORT CString { -public: - CString() { } - CString(const char*); - CString(const char*, size_t length); - CString(CStringBuffer* buffer) : m_buffer(buffer) { } - static CString newUninitialized(size_t length, char*& characterBuffer); - - std::string toStdString() const - { - return std::string(data(), length()); - } - - const char* data() const - { - return m_buffer ? m_buffer->data() : 0; - } - char* mutableData(); - size_t length() const - { - return m_buffer ? m_buffer->length() : 0; - } - - bool isNull() const { return !m_buffer; } - bool isSafeToSendToAnotherThread() const; - - CStringBuffer* buffer() const { return m_buffer.get(); } - -private: - void copyBufferIfNeeded(); - void init(const char*, size_t length); - RefPtr m_buffer; + public: + CString() {} + CString(const char*); + CString(const char*, size_t length); + CString(CStringBuffer* buffer) : m_buffer(buffer) {} + static CString newUninitialized(size_t length, char*& characterBuffer); + + std::string toStdString() const { return std::string(data(), length()); } + + const char* data() const { return m_buffer ? m_buffer->data() : 0; } + char* mutableData(); + size_t length() const { return m_buffer ? m_buffer->length() : 0; } + + bool isNull() const { return !m_buffer; } + bool isSafeToSendToAnotherThread() const; + + CStringBuffer* buffer() const { return m_buffer.get(); } + + private: + void copyBufferIfNeeded(); + void init(const char*, size_t length); + RefPtr m_buffer; }; WTF_EXPORT bool operator==(const CString& a, const CString& b); -inline bool operator!=(const CString& a, const CString& b) { return !(a == b); } +inline bool operator!=(const CString& a, const CString& b) { + return !(a == b); +} WTF_EXPORT bool operator==(const CString& a, const char* b); -inline bool operator!=(const CString& a, const char* b) { return !(a == b); } +inline bool operator!=(const CString& a, const char* b) { + return !(a == b); +} -} // namespace WTF +} // namespace WTF using WTF::CString; diff --git a/sky/engine/wtf/text/CStringTest.cpp b/sky/engine/wtf/text/CStringTest.cpp index 9f80bbacca794..5abfdddfba23f 100644 --- a/sky/engine/wtf/text/CStringTest.cpp +++ b/sky/engine/wtf/text/CStringTest.cpp @@ -23,178 +23,170 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ - #include #include "flutter/sky/engine/wtf/text/CString.h" namespace { -TEST(WTF, CStringNullStringConstructor) -{ - CString string; - ASSERT_TRUE(string.isNull()); - ASSERT_EQ(string.data(), static_cast(0)); - ASSERT_EQ(string.length(), static_cast(0)); - - CString stringFromCharPointer(static_cast(0)); - ASSERT_TRUE(stringFromCharPointer.isNull()); - ASSERT_EQ(stringFromCharPointer.data(), static_cast(0)); - ASSERT_EQ(stringFromCharPointer.length(), static_cast(0)); - - CString stringFromCharAndLength(static_cast(0), 0); - ASSERT_TRUE(stringFromCharAndLength.isNull()); - ASSERT_EQ(stringFromCharAndLength.data(), static_cast(0)); - ASSERT_EQ(stringFromCharAndLength.length(), static_cast(0)); +TEST(WTF, CStringNullStringConstructor) { + CString string; + ASSERT_TRUE(string.isNull()); + ASSERT_EQ(string.data(), static_cast(0)); + ASSERT_EQ(string.length(), static_cast(0)); + + CString stringFromCharPointer(static_cast(0)); + ASSERT_TRUE(stringFromCharPointer.isNull()); + ASSERT_EQ(stringFromCharPointer.data(), static_cast(0)); + ASSERT_EQ(stringFromCharPointer.length(), static_cast(0)); + + CString stringFromCharAndLength(static_cast(0), 0); + ASSERT_TRUE(stringFromCharAndLength.isNull()); + ASSERT_EQ(stringFromCharAndLength.data(), static_cast(0)); + ASSERT_EQ(stringFromCharAndLength.length(), static_cast(0)); } -TEST(WTF, CStringEmptyEmptyConstructor) -{ - const char* emptyString = ""; - CString string(emptyString); - ASSERT_FALSE(string.isNull()); - ASSERT_EQ(string.length(), static_cast(0)); - ASSERT_EQ(string.data()[0], 0); - - CString stringWithLength(emptyString, 0); - ASSERT_FALSE(stringWithLength.isNull()); - ASSERT_EQ(stringWithLength.length(), static_cast(0)); - ASSERT_EQ(stringWithLength.data()[0], 0); +TEST(WTF, CStringEmptyEmptyConstructor) { + const char* emptyString = ""; + CString string(emptyString); + ASSERT_FALSE(string.isNull()); + ASSERT_EQ(string.length(), static_cast(0)); + ASSERT_EQ(string.data()[0], 0); + + CString stringWithLength(emptyString, 0); + ASSERT_FALSE(stringWithLength.isNull()); + ASSERT_EQ(stringWithLength.length(), static_cast(0)); + ASSERT_EQ(stringWithLength.data()[0], 0); } -TEST(WTF, CStringEmptyRegularConstructor) -{ - const char* referenceString = "WebKit"; +TEST(WTF, CStringEmptyRegularConstructor) { + const char* referenceString = "WebKit"; - CString string(referenceString); - ASSERT_FALSE(string.isNull()); - ASSERT_EQ(string.length(), strlen(referenceString)); - ASSERT_STREQ(referenceString, string.data()); + CString string(referenceString); + ASSERT_FALSE(string.isNull()); + ASSERT_EQ(string.length(), strlen(referenceString)); + ASSERT_STREQ(referenceString, string.data()); - CString stringWithLength(referenceString, 6); - ASSERT_FALSE(stringWithLength.isNull()); - ASSERT_EQ(stringWithLength.length(), strlen(referenceString)); - ASSERT_STREQ(referenceString, stringWithLength.data()); + CString stringWithLength(referenceString, 6); + ASSERT_FALSE(stringWithLength.isNull()); + ASSERT_EQ(stringWithLength.length(), strlen(referenceString)); + ASSERT_STREQ(referenceString, stringWithLength.data()); } -TEST(WTF, CStringUninitializedConstructor) -{ - char* buffer; - CString emptyString = CString::newUninitialized(0, buffer); - ASSERT_FALSE(emptyString.isNull()); - ASSERT_EQ(buffer, emptyString.data()); - ASSERT_EQ(buffer[0], 0); - - const size_t length = 25; - CString uninitializedString = CString::newUninitialized(length, buffer); - ASSERT_FALSE(uninitializedString.isNull()); - ASSERT_EQ(buffer, uninitializedString.data()); - ASSERT_EQ(uninitializedString.data()[length], 0); +TEST(WTF, CStringUninitializedConstructor) { + char* buffer; + CString emptyString = CString::newUninitialized(0, buffer); + ASSERT_FALSE(emptyString.isNull()); + ASSERT_EQ(buffer, emptyString.data()); + ASSERT_EQ(buffer[0], 0); + + const size_t length = 25; + CString uninitializedString = CString::newUninitialized(length, buffer); + ASSERT_FALSE(uninitializedString.isNull()); + ASSERT_EQ(buffer, uninitializedString.data()); + ASSERT_EQ(uninitializedString.data()[length], 0); } -TEST(WTF, CStringZeroTerminated) -{ - const char* referenceString = "WebKit"; - CString stringWithLength(referenceString, 3); - ASSERT_EQ(stringWithLength.data()[3], 0); +TEST(WTF, CStringZeroTerminated) { + const char* referenceString = "WebKit"; + CString stringWithLength(referenceString, 3); + ASSERT_EQ(stringWithLength.data()[3], 0); } -TEST(WTF, CStringCopyOnWrite) -{ - const char* initialString = "Webkit"; - CString string(initialString); - CString copy = string; +TEST(WTF, CStringCopyOnWrite) { + const char* initialString = "Webkit"; + CString string(initialString); + CString copy = string; - string.mutableData()[3] = 'K'; - ASSERT_TRUE(string != copy); - ASSERT_STREQ(string.data(), "WebKit"); - ASSERT_STREQ(copy.data(), initialString); + string.mutableData()[3] = 'K'; + ASSERT_TRUE(string != copy); + ASSERT_STREQ(string.data(), "WebKit"); + ASSERT_STREQ(copy.data(), initialString); } -TEST(WTF, CStringComparison) -{ - // Comparison with another CString. - CString a; - CString b; - ASSERT_TRUE(a == b); - ASSERT_FALSE(a != b); - a = "a"; - b = CString(); - ASSERT_FALSE(a == b); - ASSERT_TRUE(a != b); - a = "a"; - b = "b"; - ASSERT_FALSE(a == b); - ASSERT_TRUE(a != b); - a = "a"; - b = "a"; - ASSERT_TRUE(a == b); - ASSERT_FALSE(a != b); - a = "a"; - b = "aa"; - ASSERT_FALSE(a == b); - ASSERT_TRUE(a != b); - a = ""; - b = ""; - ASSERT_TRUE(a == b); - ASSERT_FALSE(a != b); - a = ""; - b = CString(); - ASSERT_FALSE(a == b); - ASSERT_TRUE(a != b); - a = "a"; - b = ""; - ASSERT_FALSE(a == b); - ASSERT_TRUE(a != b); - - // Comparison with a const char*. - CString c; - const char* d = 0; - ASSERT_TRUE(c == d); - ASSERT_FALSE(c != d); - c = "c"; - d = 0; - ASSERT_FALSE(c == d); - ASSERT_TRUE(c != d); - c = CString(); - d = "d"; - ASSERT_FALSE(c == d); - ASSERT_TRUE(c != d); - c = "c"; - d = "d"; - ASSERT_FALSE(c == d); - ASSERT_TRUE(c != d); - c = "c"; - d = "c"; - ASSERT_TRUE(c == d); - ASSERT_FALSE(c != d); - c = "c"; - d = "cc"; - ASSERT_FALSE(c == d); - ASSERT_TRUE(c != d); - c = "cc"; - d = "c"; - ASSERT_FALSE(c == d); - ASSERT_TRUE(c != d); - c = ""; - d = ""; - ASSERT_TRUE(c == d); - ASSERT_FALSE(c != d); - c = ""; - d = 0; - ASSERT_FALSE(c == d); - ASSERT_TRUE(c != d); - c = CString(); - d = ""; - ASSERT_FALSE(c == d); - ASSERT_TRUE(c != d); - c = "a"; - d = ""; - ASSERT_FALSE(c == d); - ASSERT_TRUE(c != d); - c = ""; - d = "b"; - ASSERT_FALSE(c == d); - ASSERT_TRUE(c != d); +TEST(WTF, CStringComparison) { + // Comparison with another CString. + CString a; + CString b; + ASSERT_TRUE(a == b); + ASSERT_FALSE(a != b); + a = "a"; + b = CString(); + ASSERT_FALSE(a == b); + ASSERT_TRUE(a != b); + a = "a"; + b = "b"; + ASSERT_FALSE(a == b); + ASSERT_TRUE(a != b); + a = "a"; + b = "a"; + ASSERT_TRUE(a == b); + ASSERT_FALSE(a != b); + a = "a"; + b = "aa"; + ASSERT_FALSE(a == b); + ASSERT_TRUE(a != b); + a = ""; + b = ""; + ASSERT_TRUE(a == b); + ASSERT_FALSE(a != b); + a = ""; + b = CString(); + ASSERT_FALSE(a == b); + ASSERT_TRUE(a != b); + a = "a"; + b = ""; + ASSERT_FALSE(a == b); + ASSERT_TRUE(a != b); + + // Comparison with a const char*. + CString c; + const char* d = 0; + ASSERT_TRUE(c == d); + ASSERT_FALSE(c != d); + c = "c"; + d = 0; + ASSERT_FALSE(c == d); + ASSERT_TRUE(c != d); + c = CString(); + d = "d"; + ASSERT_FALSE(c == d); + ASSERT_TRUE(c != d); + c = "c"; + d = "d"; + ASSERT_FALSE(c == d); + ASSERT_TRUE(c != d); + c = "c"; + d = "c"; + ASSERT_TRUE(c == d); + ASSERT_FALSE(c != d); + c = "c"; + d = "cc"; + ASSERT_FALSE(c == d); + ASSERT_TRUE(c != d); + c = "cc"; + d = "c"; + ASSERT_FALSE(c == d); + ASSERT_TRUE(c != d); + c = ""; + d = ""; + ASSERT_TRUE(c == d); + ASSERT_FALSE(c != d); + c = ""; + d = 0; + ASSERT_FALSE(c == d); + ASSERT_TRUE(c != d); + c = CString(); + d = ""; + ASSERT_FALSE(c == d); + ASSERT_TRUE(c != d); + c = "a"; + d = ""; + ASSERT_FALSE(c == d); + ASSERT_TRUE(c != d); + c = ""; + d = "b"; + ASSERT_FALSE(c == d); + ASSERT_TRUE(c != d); } -} // namespace +} // namespace diff --git a/sky/engine/wtf/text/IntegerToStringConversion.h b/sky/engine/wtf/text/IntegerToStringConversion.h index 454f459d3f967..121ff056e3aeb 100644 --- a/sky/engine/wtf/text/IntegerToStringConversion.h +++ b/sky/engine/wtf/text/IntegerToStringConversion.h @@ -27,73 +27,99 @@ namespace WTF { -enum PositiveOrNegativeNumber { - PositiveNumber, - NegativeNumber +enum PositiveOrNegativeNumber { PositiveNumber, NegativeNumber }; + +template +struct ConversionTrait; + +template <> +struct ConversionTrait { + typedef PassRefPtr ReturnType; + typedef void AdditionalArgumentType; + static inline ReturnType flush(LChar* characters, unsigned length, void*) { + return StringImpl::create(characters, length); + } }; - -template struct ConversionTrait; - -template<> struct ConversionTrait { - typedef PassRefPtr ReturnType; - typedef void AdditionalArgumentType; - static inline ReturnType flush(LChar* characters, unsigned length, void*) { return StringImpl::create(characters, length); } -}; -template<> struct ConversionTrait { - typedef void ReturnType; - typedef StringBuilder AdditionalArgumentType; - static inline ReturnType flush(LChar* characters, unsigned length, StringBuilder* stringBuilder) { stringBuilder->append(characters, length); } +template <> +struct ConversionTrait { + typedef void ReturnType; + typedef StringBuilder AdditionalArgumentType; + static inline ReturnType flush(LChar* characters, + unsigned length, + StringBuilder* stringBuilder) { + stringBuilder->append(characters, length); + } }; -template<> struct ConversionTrait { - typedef AtomicString ReturnType; - typedef void AdditionalArgumentType; - static inline ReturnType flush(LChar* characters, unsigned length, void*) { return AtomicString(characters, length); } +template <> +struct ConversionTrait { + typedef AtomicString ReturnType; + typedef void AdditionalArgumentType; + static inline ReturnType flush(LChar* characters, unsigned length, void*) { + return AtomicString(characters, length); + } }; -template struct UnsignedIntegerTrait; +template +struct UnsignedIntegerTrait; -template<> struct UnsignedIntegerTrait { - typedef unsigned Type; +template <> +struct UnsignedIntegerTrait { + typedef unsigned Type; }; -template<> struct UnsignedIntegerTrait { - typedef unsigned long Type; +template <> +struct UnsignedIntegerTrait { + typedef unsigned long Type; }; -template<> struct UnsignedIntegerTrait { - typedef unsigned long long Type; +template <> +struct UnsignedIntegerTrait { + typedef unsigned long long Type; }; -template -static typename ConversionTrait::ReturnType numberToStringImpl(UnsignedIntegerType number, typename ConversionTrait::AdditionalArgumentType* additionalArgument) -{ - LChar buf[sizeof(UnsignedIntegerType) * 3 + 1]; - LChar* end = buf + WTF_ARRAY_LENGTH(buf); - LChar* p = end; - - do { - *--p = static_cast((number % 10) + '0'); - number /= 10; - } while (number); - - if (NumberType == NegativeNumber) - *--p = '-'; - - return ConversionTrait::flush(p, static_cast(end - p), additionalArgument); +template +static typename ConversionTrait::ReturnType numberToStringImpl( + UnsignedIntegerType number, + typename ConversionTrait::AdditionalArgumentType* additionalArgument) { + LChar buf[sizeof(UnsignedIntegerType) * 3 + 1]; + LChar* end = buf + WTF_ARRAY_LENGTH(buf); + LChar* p = end; + + do { + *--p = static_cast((number % 10) + '0'); + number /= 10; + } while (number); + + if (NumberType == NegativeNumber) + *--p = '-'; + + return ConversionTrait::flush(p, static_cast(end - p), + additionalArgument); } -template -inline typename ConversionTrait::ReturnType numberToStringSigned(SignedIntegerType number, typename ConversionTrait::AdditionalArgumentType* additionalArgument = 0) -{ - if (number < 0) - return numberToStringImpl::Type, NegativeNumber>(-number, additionalArgument); - return numberToStringImpl::Type, PositiveNumber>(number, additionalArgument); +template +inline typename ConversionTrait::ReturnType numberToStringSigned( + SignedIntegerType number, + typename ConversionTrait::AdditionalArgumentType* additionalArgument = + 0) { + if (number < 0) + return numberToStringImpl< + T, typename UnsignedIntegerTrait::Type, + NegativeNumber>(-number, additionalArgument); + return numberToStringImpl< + T, typename UnsignedIntegerTrait::Type, + PositiveNumber>(number, additionalArgument); } -template -inline typename ConversionTrait::ReturnType numberToStringUnsigned(UnsignedIntegerType number, typename ConversionTrait::AdditionalArgumentType* additionalArgument = 0) -{ - return numberToStringImpl(number, additionalArgument); +template +inline typename ConversionTrait::ReturnType numberToStringUnsigned( + UnsignedIntegerType number, + typename ConversionTrait::AdditionalArgumentType* additionalArgument = + 0) { + return numberToStringImpl( + number, additionalArgument); } -} // namespace WTF +} // namespace WTF #endif // SKY_ENGINE_WTF_TEXT_INTEGERTOSTRINGCONVERSION_H_ diff --git a/sky/engine/wtf/text/StringBuffer.h b/sky/engine/wtf/text/StringBuffer.h index bcd7821662b19..5cdf1d54826b6 100644 --- a/sky/engine/wtf/text/StringBuffer.h +++ b/sky/engine/wtf/text/StringBuffer.h @@ -37,56 +37,58 @@ namespace WTF { template class StringBuffer { - WTF_MAKE_NONCOPYABLE(StringBuffer); -public: - StringBuffer() { } - - explicit StringBuffer(unsigned length) - { - CharType* characters; - m_data = StringImpl::createUninitialized(length, characters); - } + WTF_MAKE_NONCOPYABLE(StringBuffer); - ~StringBuffer() - { - } + public: + StringBuffer() {} + + explicit StringBuffer(unsigned length) { + CharType* characters; + m_data = StringImpl::createUninitialized(length, characters); + } - void shrink(unsigned newLength); - void resize(unsigned newLength) - { - if (!m_data) { - CharType* characters; - m_data = StringImpl::createUninitialized(newLength, characters); - return; - } - if (newLength > m_data->length()) { - m_data = StringImpl::reallocate(m_data.release(), newLength); - return; - } - shrink(newLength); + ~StringBuffer() {} + + void shrink(unsigned newLength); + void resize(unsigned newLength) { + if (!m_data) { + CharType* characters; + m_data = StringImpl::createUninitialized(newLength, characters); + return; + } + if (newLength > m_data->length()) { + m_data = StringImpl::reallocate(m_data.release(), newLength); + return; } + shrink(newLength); + } - unsigned length() const { return m_data ? m_data->length() : 0; } - CharType* characters() { return length() ? const_cast(m_data->getCharacters()) : 0; } + unsigned length() const { return m_data ? m_data->length() : 0; } + CharType* characters() { + return length() ? const_cast(m_data->getCharacters()) + : 0; + } - CharType& operator[](unsigned i) { ASSERT_WITH_SECURITY_IMPLICATION(i < length()); return characters()[i]; } + CharType& operator[](unsigned i) { + ASSERT_WITH_SECURITY_IMPLICATION(i < length()); + return characters()[i]; + } - PassRefPtr release() { return m_data.release(); } + PassRefPtr release() { return m_data.release(); } -private: - RefPtr m_data; + private: + RefPtr m_data; }; template -void StringBuffer::shrink(unsigned newLength) -{ - ASSERT(m_data); - if (m_data->length() == newLength) - return; - m_data->truncateAssumingIsolated(newLength); +void StringBuffer::shrink(unsigned newLength) { + ASSERT(m_data); + if (m_data->length() == newLength) + return; + m_data->truncateAssumingIsolated(newLength); } -} // namespace WTF +} // namespace WTF using WTF::StringBuffer; diff --git a/sky/engine/wtf/text/StringBufferTest.cpp b/sky/engine/wtf/text/StringBufferTest.cpp index 12b02cb77e571..124c132916e8e 100644 --- a/sky/engine/wtf/text/StringBufferTest.cpp +++ b/sky/engine/wtf/text/StringBufferTest.cpp @@ -4,42 +4,38 @@ * found in the LICENSE file. */ - #include "flutter/sky/engine/wtf/text/StringBuffer.h" #include namespace { +TEST(StringBuffer, Initial) { + StringBuffer buf1; + EXPECT_EQ(0u, buf1.length()); + EXPECT_FALSE(buf1.characters()); -TEST(StringBuffer, Initial) -{ - StringBuffer buf1; - EXPECT_EQ(0u, buf1.length()); - EXPECT_FALSE(buf1.characters()); - - StringBuffer buf2(0); - EXPECT_EQ(0u, buf2.length()); - EXPECT_FALSE(buf2.characters()); + StringBuffer buf2(0); + EXPECT_EQ(0u, buf2.length()); + EXPECT_FALSE(buf2.characters()); - StringBuffer buf3(1); - EXPECT_EQ(1u, buf3.length()); - EXPECT_TRUE(buf3.characters()); + StringBuffer buf3(1); + EXPECT_EQ(1u, buf3.length()); + EXPECT_TRUE(buf3.characters()); } -TEST(StringBuffer, shrink) -{ - StringBuffer buf(2); - EXPECT_EQ(2u, buf.length()); - buf[0] = 'a'; - buf[1] = 'b'; +TEST(StringBuffer, shrink) { + StringBuffer buf(2); + EXPECT_EQ(2u, buf.length()); + buf[0] = 'a'; + buf[1] = 'b'; - buf.shrink(1); - EXPECT_EQ(1u, buf.length()); - EXPECT_EQ('a', buf[0]); + buf.shrink(1); + EXPECT_EQ(1u, buf.length()); + EXPECT_EQ('a', buf[0]); - buf.shrink(0); - EXPECT_EQ(0u, buf.length()); + buf.shrink(0); + EXPECT_EQ(0u, buf.length()); } -} // namespace +} // namespace diff --git a/sky/engine/wtf/text/StringBuilder.cpp b/sky/engine/wtf/text/StringBuilder.cpp index a4f2525425f1a..98e9c588e3627 100644 --- a/sky/engine/wtf/text/StringBuilder.cpp +++ b/sky/engine/wtf/text/StringBuilder.cpp @@ -32,364 +32,377 @@ namespace WTF { -static unsigned expandedCapacity(unsigned capacity, unsigned requiredLength) -{ - static const unsigned minimumCapacity = 16; - return std::max(requiredLength, std::max(minimumCapacity, capacity * 2)); +static unsigned expandedCapacity(unsigned capacity, unsigned requiredLength) { + static const unsigned minimumCapacity = 16; + return std::max(requiredLength, std::max(minimumCapacity, capacity * 2)); } -void StringBuilder::reifyString() -{ - if (!m_string.isNull()) { - ASSERT(m_string.length() == m_length); - return; - } +void StringBuilder::reifyString() { + if (!m_string.isNull()) { + ASSERT(m_string.length() == m_length); + return; + } - if (!m_length) { - m_string = StringImpl::empty(); - return; - } + if (!m_length) { + m_string = StringImpl::empty(); + return; + } - ASSERT(m_buffer && m_length <= m_buffer->length()); - if (m_length == m_buffer->length()) { - m_string = m_buffer.release(); - return; - } + ASSERT(m_buffer && m_length <= m_buffer->length()); + if (m_length == m_buffer->length()) { + m_string = m_buffer.release(); + return; + } - if (m_buffer->hasOneRef()) { - m_buffer->truncateAssumingIsolated(m_length); - m_string = m_buffer.release(); - return; - } + if (m_buffer->hasOneRef()) { + m_buffer->truncateAssumingIsolated(m_length); + m_string = m_buffer.release(); + return; + } - m_string = m_buffer->substring(0, m_length); + m_string = m_buffer->substring(0, m_length); } -String StringBuilder::reifySubstring(unsigned position, unsigned length) const -{ - ASSERT(m_string.isNull()); - ASSERT(m_buffer); - unsigned substringLength = std::min(length, m_length - position); - return m_buffer->substring(position, substringLength); +String StringBuilder::reifySubstring(unsigned position, unsigned length) const { + ASSERT(m_string.isNull()); + ASSERT(m_buffer); + unsigned substringLength = std::min(length, m_length - position); + return m_buffer->substring(position, substringLength); } -void StringBuilder::resize(unsigned newSize) -{ - // Check newSize < m_length, hence m_length > 0. - ASSERT(newSize <= m_length); - if (newSize == m_length) - return; - ASSERT(m_length); - - // If there is a buffer, we only need to duplicate it if it has more than one ref. - if (m_buffer) { - m_string = String(); // Clear the string to remove the reference to m_buffer if any before checking the reference count of m_buffer. - if (!m_buffer->hasOneRef()) { - if (m_buffer->is8Bit()) - allocateBuffer(m_buffer->characters8(), m_buffer->length()); - else - allocateBuffer(m_buffer->characters16(), m_buffer->length()); - } - m_length = newSize; - return; +void StringBuilder::resize(unsigned newSize) { + // Check newSize < m_length, hence m_length > 0. + ASSERT(newSize <= m_length); + if (newSize == m_length) + return; + ASSERT(m_length); + + // If there is a buffer, we only need to duplicate it if it has more than one + // ref. + if (m_buffer) { + m_string = String(); // Clear the string to remove the reference to + // m_buffer if any before checking the reference count + // of m_buffer. + if (!m_buffer->hasOneRef()) { + if (m_buffer->is8Bit()) + allocateBuffer(m_buffer->characters8(), m_buffer->length()); + else + allocateBuffer(m_buffer->characters16(), m_buffer->length()); } - - // Since m_length && !m_buffer, the string must be valid in m_string, and m_string.length() > 0. - ASSERT(!m_string.isEmpty()); - ASSERT(m_length == m_string.length()); - ASSERT(newSize < m_string.length()); m_length = newSize; - RefPtr string = m_string.releaseImpl(); - if (string->hasOneRef()) { - // If we're the only ones with a reference to the string, we can - // re-purpose the string as m_buffer and continue mutating it. - m_buffer = string; - } else { - // Otherwise, we need to make a copy of the string so that we don't - // mutate a String that's held elsewhere. - m_buffer = string->substring(0, m_length); - } + return; + } + + // Since m_length && !m_buffer, the string must be valid in m_string, and + // m_string.length() > 0. + ASSERT(!m_string.isEmpty()); + ASSERT(m_length == m_string.length()); + ASSERT(newSize < m_string.length()); + m_length = newSize; + RefPtr string = m_string.releaseImpl(); + if (string->hasOneRef()) { + // If we're the only ones with a reference to the string, we can + // re-purpose the string as m_buffer and continue mutating it. + m_buffer = string; + } else { + // Otherwise, we need to make a copy of the string so that we don't + // mutate a String that's held elsewhere. + m_buffer = string->substring(0, m_length); + } } -// Allocate a new 8 bit buffer, copying in currentCharacters (these may come from either m_string -// or m_buffer, neither will be reassigned until the copy has completed). -void StringBuilder::allocateBuffer(const LChar* currentCharacters, unsigned requiredLength) -{ - ASSERT(m_is8Bit); - // Copy the existing data into a new buffer, set result to point to the end of the existing data. - RefPtr buffer = StringImpl::createUninitialized(requiredLength, m_bufferCharacters8); - memcpy(m_bufferCharacters8, currentCharacters, static_cast(m_length) * sizeof(LChar)); // This can't overflow. - - // Update the builder state. - m_buffer = buffer.release(); - m_string = String(); +// Allocate a new 8 bit buffer, copying in currentCharacters (these may come +// from either m_string or m_buffer, neither will be reassigned until the copy +// has completed). +void StringBuilder::allocateBuffer(const LChar* currentCharacters, + unsigned requiredLength) { + ASSERT(m_is8Bit); + // Copy the existing data into a new buffer, set result to point to the end of + // the existing data. + RefPtr buffer = + StringImpl::createUninitialized(requiredLength, m_bufferCharacters8); + memcpy( + m_bufferCharacters8, currentCharacters, + static_cast(m_length) * sizeof(LChar)); // This can't overflow. + + // Update the builder state. + m_buffer = buffer.release(); + m_string = String(); } -// Allocate a new 16 bit buffer, copying in currentCharacters (these may come from either m_string -// or m_buffer, neither will be reassigned until the copy has completed). -void StringBuilder::allocateBuffer(const UChar* currentCharacters, unsigned requiredLength) -{ - ASSERT(!m_is8Bit); - // Copy the existing data into a new buffer, set result to point to the end of the existing data. - RefPtr buffer = StringImpl::createUninitialized(requiredLength, m_bufferCharacters16); - memcpy(m_bufferCharacters16, currentCharacters, static_cast(m_length) * sizeof(UChar)); // This can't overflow. - - // Update the builder state. - m_buffer = buffer.release(); - m_string = String(); +// Allocate a new 16 bit buffer, copying in currentCharacters (these may come +// from either m_string or m_buffer, neither will be reassigned until the copy +// has completed). +void StringBuilder::allocateBuffer(const UChar* currentCharacters, + unsigned requiredLength) { + ASSERT(!m_is8Bit); + // Copy the existing data into a new buffer, set result to point to the end of + // the existing data. + RefPtr buffer = + StringImpl::createUninitialized(requiredLength, m_bufferCharacters16); + memcpy( + m_bufferCharacters16, currentCharacters, + static_cast(m_length) * sizeof(UChar)); // This can't overflow. + + // Update the builder state. + m_buffer = buffer.release(); + m_string = String(); } -// Allocate a new 16 bit buffer, copying in currentCharacters (which is 8 bit and may come -// from either m_string or m_buffer, neither will be reassigned until the copy has completed). -void StringBuilder::allocateBufferUpConvert(const LChar* currentCharacters, unsigned requiredLength) -{ - ASSERT(m_is8Bit); - // Copy the existing data into a new buffer, set result to point to the end of the existing data. - RefPtr buffer = StringImpl::createUninitialized(requiredLength, m_bufferCharacters16); - for (unsigned i = 0; i < m_length; ++i) - m_bufferCharacters16[i] = currentCharacters[i]; - - m_is8Bit = false; - - // Update the builder state. - m_buffer = buffer.release(); - m_string = String(); +// Allocate a new 16 bit buffer, copying in currentCharacters (which is 8 bit +// and may come from either m_string or m_buffer, neither will be reassigned +// until the copy has completed). +void StringBuilder::allocateBufferUpConvert(const LChar* currentCharacters, + unsigned requiredLength) { + ASSERT(m_is8Bit); + // Copy the existing data into a new buffer, set result to point to the end of + // the existing data. + RefPtr buffer = + StringImpl::createUninitialized(requiredLength, m_bufferCharacters16); + for (unsigned i = 0; i < m_length; ++i) + m_bufferCharacters16[i] = currentCharacters[i]; + + m_is8Bit = false; + + // Update the builder state. + m_buffer = buffer.release(); + m_string = String(); } template <> -void StringBuilder::reallocateBuffer(unsigned requiredLength) -{ - // If the buffer has only one ref (by this StringBuilder), reallocate it, - // otherwise fall back to "allocate and copy" method. - m_string = String(); +void StringBuilder::reallocateBuffer(unsigned requiredLength) { + // If the buffer has only one ref (by this StringBuilder), reallocate it, + // otherwise fall back to "allocate and copy" method. + m_string = String(); + + ASSERT(m_is8Bit); + ASSERT(m_buffer->is8Bit()); + + if (m_buffer->hasOneRef()) { + m_buffer = StringImpl::reallocate(m_buffer.release(), requiredLength); + m_bufferCharacters8 = const_cast(m_buffer->characters8()); + } else + allocateBuffer(m_buffer->characters8(), requiredLength); +} - ASSERT(m_is8Bit); - ASSERT(m_buffer->is8Bit()); +template <> +void StringBuilder::reallocateBuffer(unsigned requiredLength) { + // If the buffer has only one ref (by this StringBuilder), reallocate it, + // otherwise fall back to "allocate and copy" method. + m_string = String(); + + if (m_buffer->is8Bit()) { + allocateBufferUpConvert(m_buffer->characters8(), requiredLength); + } else if (m_buffer->hasOneRef()) { + m_buffer = StringImpl::reallocate(m_buffer.release(), requiredLength); + m_bufferCharacters16 = const_cast(m_buffer->characters16()); + } else + allocateBuffer(m_buffer->characters16(), requiredLength); +} - if (m_buffer->hasOneRef()) { - m_buffer = StringImpl::reallocate(m_buffer.release(), requiredLength); - m_bufferCharacters8 = const_cast(m_buffer->characters8()); - } else - allocateBuffer(m_buffer->characters8(), requiredLength); +void StringBuilder::reserveCapacity(unsigned newCapacity) { + if (m_buffer) { + // If there is already a buffer, then grow if necessary. + if (newCapacity > m_buffer->length()) { + if (m_buffer->is8Bit()) + reallocateBuffer(newCapacity); + else + reallocateBuffer(newCapacity); + } + } else { + // Grow the string, if necessary. + if (newCapacity > m_length) { + if (!m_length) { + LChar* nullPlaceholder = 0; + allocateBuffer(nullPlaceholder, newCapacity); + } else if (m_string.is8Bit()) + allocateBuffer(m_string.characters8(), newCapacity); + else + allocateBuffer(m_string.characters16(), newCapacity); + } + } } -template <> -void StringBuilder::reallocateBuffer(unsigned requiredLength) -{ - // If the buffer has only one ref (by this StringBuilder), reallocate it, - // otherwise fall back to "allocate and copy" method. +// Make 'length' additional capacity be available in m_buffer, update m_string & +// m_length, return a pointer to the newly allocated storage. +template +ALWAYS_INLINE CharType* StringBuilder::appendUninitialized(unsigned length) { + ASSERT(length); + + // Calculate the new size of the builder after appending. + unsigned requiredLength = length + m_length; + RELEASE_ASSERT(requiredLength >= length); + + if ((m_buffer) && (requiredLength <= m_buffer->length())) { + // If the buffer is valid it must be at least as long as the current builder + // contents! + ASSERT(m_buffer->length() >= m_length); + unsigned currentLength = m_length; m_string = String(); + m_length = requiredLength; + return getBufferCharacters() + currentLength; + } - if (m_buffer->is8Bit()) { - allocateBufferUpConvert(m_buffer->characters8(), requiredLength); - } else if (m_buffer->hasOneRef()) { - m_buffer = StringImpl::reallocate(m_buffer.release(), requiredLength); - m_bufferCharacters16 = const_cast(m_buffer->characters16()); - } else - allocateBuffer(m_buffer->characters16(), requiredLength); + return appendUninitializedSlow(requiredLength); } -void StringBuilder::reserveCapacity(unsigned newCapacity) -{ - if (m_buffer) { - // If there is already a buffer, then grow if necessary. - if (newCapacity > m_buffer->length()) { - if (m_buffer->is8Bit()) - reallocateBuffer(newCapacity); - else - reallocateBuffer(newCapacity); - } - } else { - // Grow the string, if necessary. - if (newCapacity > m_length) { - if (!m_length) { - LChar* nullPlaceholder = 0; - allocateBuffer(nullPlaceholder, newCapacity); - } else if (m_string.is8Bit()) - allocateBuffer(m_string.characters8(), newCapacity); - else - allocateBuffer(m_string.characters16(), newCapacity); - } - } +// Make 'length' additional capacity be available in m_buffer, update m_string & +// m_length, return a pointer to the newly allocated storage. +template +CharType* StringBuilder::appendUninitializedSlow(unsigned requiredLength) { + ASSERT(requiredLength); + + if (m_buffer) { + // If the buffer is valid it must be at least as long as the current builder + // contents! + ASSERT(m_buffer->length() >= m_length); + + reallocateBuffer(expandedCapacity(capacity(), requiredLength)); + } else { + ASSERT(m_string.length() == m_length); + allocateBuffer(m_length ? m_string.getCharacters() : 0, + expandedCapacity(capacity(), requiredLength)); + } + + CharType* result = getBufferCharacters() + m_length; + m_length = requiredLength; + return result; } -// Make 'length' additional capacity be available in m_buffer, update m_string & m_length, -// return a pointer to the newly allocated storage. -template -ALWAYS_INLINE CharType* StringBuilder::appendUninitialized(unsigned length) -{ - ASSERT(length); +void StringBuilder::append(const UChar* characters, unsigned length) { + if (!length) + return; - // Calculate the new size of the builder after appending. - unsigned requiredLength = length + m_length; - RELEASE_ASSERT(requiredLength >= length); + ASSERT(characters); - if ((m_buffer) && (requiredLength <= m_buffer->length())) { - // If the buffer is valid it must be at least as long as the current builder contents! - ASSERT(m_buffer->length() >= m_length); - unsigned currentLength = m_length; - m_string = String(); - m_length = requiredLength; - return getBufferCharacters() + currentLength; + if (m_is8Bit) { + if (length == 1 && !(*characters & ~0xff)) { + // Append as 8 bit character + LChar lChar = static_cast(*characters); + append(&lChar, 1); + return; } - return appendUninitializedSlow(requiredLength); -} - -// Make 'length' additional capacity be available in m_buffer, update m_string & m_length, -// return a pointer to the newly allocated storage. -template -CharType* StringBuilder::appendUninitializedSlow(unsigned requiredLength) -{ - ASSERT(requiredLength); + // Calculate the new size of the builder after appending. + unsigned requiredLength = length + m_length; + RELEASE_ASSERT(requiredLength >= length); if (m_buffer) { - // If the buffer is valid it must be at least as long as the current builder contents! - ASSERT(m_buffer->length() >= m_length); + // If the buffer is valid it must be at least as long as the current + // builder contents! + ASSERT(m_buffer->length() >= m_length); - reallocateBuffer(expandedCapacity(capacity(), requiredLength)); + allocateBufferUpConvert(m_buffer->characters8(), + expandedCapacity(capacity(), requiredLength)); } else { - ASSERT(m_string.length() == m_length); - allocateBuffer(m_length ? m_string.getCharacters() : 0, expandedCapacity(capacity(), requiredLength)); + ASSERT(m_string.length() == m_length); + allocateBufferUpConvert(m_string.isNull() ? 0 : m_string.characters8(), + expandedCapacity(capacity(), requiredLength)); } - CharType* result = getBufferCharacters() + m_length; + memcpy(m_bufferCharacters16 + m_length, characters, + static_cast(length) * sizeof(UChar)); m_length = requiredLength; - return result; + } else + memcpy(appendUninitialized(length), characters, + static_cast(length) * sizeof(UChar)); } -void StringBuilder::append(const UChar* characters, unsigned length) -{ - if (!length) - return; - - ASSERT(characters); - - if (m_is8Bit) { - if (length == 1 && !(*characters & ~0xff)) { - // Append as 8 bit character - LChar lChar = static_cast(*characters); - append(&lChar, 1); - return; - } - - // Calculate the new size of the builder after appending. - unsigned requiredLength = length + m_length; - RELEASE_ASSERT(requiredLength >= length); - - if (m_buffer) { - // If the buffer is valid it must be at least as long as the current builder contents! - ASSERT(m_buffer->length() >= m_length); - - allocateBufferUpConvert(m_buffer->characters8(), expandedCapacity(capacity(), requiredLength)); - } else { - ASSERT(m_string.length() == m_length); - allocateBufferUpConvert(m_string.isNull() ? 0 : m_string.characters8(), expandedCapacity(capacity(), requiredLength)); - } - - memcpy(m_bufferCharacters16 + m_length, characters, static_cast(length) * sizeof(UChar)); - m_length = requiredLength; - } else - memcpy(appendUninitialized(length), characters, static_cast(length) * sizeof(UChar)); -} - -void StringBuilder::append(const LChar* characters, unsigned length) -{ - if (!length) - return; - ASSERT(characters); - - if (m_is8Bit) { - LChar* dest = appendUninitialized(length); - if (length > 8) - memcpy(dest, characters, static_cast(length) * sizeof(LChar)); - else { - const LChar* end = characters + length; - while (characters < end) - *(dest++) = *(characters++); - } - } else { - UChar* dest = appendUninitialized(length); - const LChar* end = characters + length; - while (characters < end) - *(dest++) = *(characters++); +void StringBuilder::append(const LChar* characters, unsigned length) { + if (!length) + return; + ASSERT(characters); + + if (m_is8Bit) { + LChar* dest = appendUninitialized(length); + if (length > 8) + memcpy(dest, characters, static_cast(length) * sizeof(LChar)); + else { + const LChar* end = characters + length; + while (characters < end) + *(dest++) = *(characters++); } + } else { + UChar* dest = appendUninitialized(length); + const LChar* end = characters + length; + while (characters < end) + *(dest++) = *(characters++); + } } -void StringBuilder::appendNumber(int number) -{ - numberToStringSigned(number, this); +void StringBuilder::appendNumber(int number) { + numberToStringSigned(number, this); } -void StringBuilder::appendNumber(unsigned number) -{ - numberToStringUnsigned(number, this); +void StringBuilder::appendNumber(unsigned number) { + numberToStringUnsigned(number, this); } -void StringBuilder::appendNumber(long number) -{ - numberToStringSigned(number, this); +void StringBuilder::appendNumber(long number) { + numberToStringSigned(number, this); } -void StringBuilder::appendNumber(unsigned long number) -{ - numberToStringUnsigned(number, this); +void StringBuilder::appendNumber(unsigned long number) { + numberToStringUnsigned(number, this); } -void StringBuilder::appendNumber(long long number) -{ - numberToStringSigned(number, this); +void StringBuilder::appendNumber(long long number) { + numberToStringSigned(number, this); } -void StringBuilder::appendNumber(unsigned long long number) -{ - numberToStringUnsigned(number, this); +void StringBuilder::appendNumber(unsigned long long number) { + numberToStringUnsigned(number, this); } -static void expandLCharToUCharInplace(UChar* buffer, size_t length) -{ - const LChar* sourceEnd = reinterpret_cast(buffer) + length; - UChar* current = buffer + length; - while (current != buffer) - *--current = *--sourceEnd; +static void expandLCharToUCharInplace(UChar* buffer, size_t length) { + const LChar* sourceEnd = reinterpret_cast(buffer) + length; + UChar* current = buffer + length; + while (current != buffer) + *--current = *--sourceEnd; } -void StringBuilder::appendNumber(double number, unsigned precision, TrailingZerosTruncatingPolicy trailingZerosTruncatingPolicy) -{ - bool truncateTrailingZeros = trailingZerosTruncatingPolicy == TruncateTrailingZeros; - size_t numberLength; - if (m_is8Bit) { - LChar* dest = appendUninitialized(NumberToStringBufferLength); - const char* result = numberToFixedPrecisionString(number, precision, reinterpret_cast(dest), truncateTrailingZeros); - numberLength = strlen(result); - } else { - UChar* dest = appendUninitialized(NumberToStringBufferLength); - const char* result = numberToFixedPrecisionString(number, precision, reinterpret_cast(dest), truncateTrailingZeros); - numberLength = strlen(result); - expandLCharToUCharInplace(dest, numberLength); - } - ASSERT(m_length >= NumberToStringBufferLength); - // Remove what appendUninitialized added. - m_length -= NumberToStringBufferLength; - ASSERT(numberLength <= NumberToStringBufferLength); - m_length += numberLength; +void StringBuilder::appendNumber( + double number, + unsigned precision, + TrailingZerosTruncatingPolicy trailingZerosTruncatingPolicy) { + bool truncateTrailingZeros = + trailingZerosTruncatingPolicy == TruncateTrailingZeros; + size_t numberLength; + if (m_is8Bit) { + LChar* dest = appendUninitialized(NumberToStringBufferLength); + const char* result = numberToFixedPrecisionString( + number, precision, reinterpret_cast(dest), + truncateTrailingZeros); + numberLength = strlen(result); + } else { + UChar* dest = appendUninitialized(NumberToStringBufferLength); + const char* result = numberToFixedPrecisionString( + number, precision, reinterpret_cast(dest), + truncateTrailingZeros); + numberLength = strlen(result); + expandLCharToUCharInplace(dest, numberLength); + } + ASSERT(m_length >= NumberToStringBufferLength); + // Remove what appendUninitialized added. + m_length -= NumberToStringBufferLength; + ASSERT(numberLength <= NumberToStringBufferLength); + m_length += numberLength; } -bool StringBuilder::canShrink() const -{ - // Only shrink the buffer if it's less than 80% full. Need to tune this heuristic! - return m_buffer && m_buffer->length() > (m_length + (m_length >> 2)); +bool StringBuilder::canShrink() const { + // Only shrink the buffer if it's less than 80% full. Need to tune this + // heuristic! + return m_buffer && m_buffer->length() > (m_length + (m_length >> 2)); } -void StringBuilder::shrinkToFit() -{ - if (!canShrink()) - return; - if (m_is8Bit) - reallocateBuffer(m_length); - else - reallocateBuffer(m_length); - m_string = m_buffer.release(); +void StringBuilder::shrinkToFit() { + if (!canShrink()) + return; + if (m_is8Bit) + reallocateBuffer(m_length); + else + reallocateBuffer(m_length); + m_string = m_buffer.release(); } -} // namespace WTF +} // namespace WTF diff --git a/sky/engine/wtf/text/StringBuilder.h b/sky/engine/wtf/text/StringBuilder.h index 5290ee74d4dad..b4777eed9abc4 100644 --- a/sky/engine/wtf/text/StringBuilder.h +++ b/sky/engine/wtf/text/StringBuilder.h @@ -34,368 +34,355 @@ namespace WTF { class WTF_EXPORT StringBuilder { - // Disallow copying since it's expensive and we don't want code to do it by accident. - WTF_MAKE_NONCOPYABLE(StringBuilder); - -public: - StringBuilder() - : m_bufferCharacters8(0) - , m_length(0) - , m_is8Bit(true) - { + // Disallow copying since it's expensive and we don't want code to do it by + // accident. + WTF_MAKE_NONCOPYABLE(StringBuilder); + + public: + StringBuilder() : m_bufferCharacters8(0), m_length(0), m_is8Bit(true) {} + + void append(const UChar*, unsigned); + void append(const LChar*, unsigned); + + ALWAYS_INLINE void append(const char* characters, unsigned length) { + append(reinterpret_cast(characters), length); + } + + void append(const String& string) { + if (!string.length()) + return; + + // If we're appending to an empty string, and there is not a buffer + // (reserveCapacity has not been called) then just retain the string. + if (!m_length && !m_buffer) { + m_string = string; + m_length = string.length(); + m_is8Bit = m_string.is8Bit(); + return; } - void append(const UChar*, unsigned); - void append(const LChar*, unsigned); - - ALWAYS_INLINE void append(const char* characters, unsigned length) { append(reinterpret_cast(characters), length); } - - void append(const String& string) - { - if (!string.length()) - return; - - // If we're appending to an empty string, and there is not a buffer (reserveCapacity has not been called) - // then just retain the string. - if (!m_length && !m_buffer) { - m_string = string; - m_length = string.length(); - m_is8Bit = m_string.is8Bit(); - return; - } - - if (string.is8Bit()) - append(string.characters8(), string.length()); - else - append(string.characters16(), string.length()); + if (string.is8Bit()) + append(string.characters8(), string.length()); + else + append(string.characters16(), string.length()); + } + + void append(const StringBuilder& other) { + if (!other.m_length) + return; + + // If we're appending to an empty string, and there is not a buffer + // (reserveCapacity has not been called) then just retain the string. + if (!m_length && !m_buffer && !other.m_string.isNull()) { + m_string = other.m_string; + m_length = other.m_length; + return; } - void append(const StringBuilder& other) - { - if (!other.m_length) - return; - - // If we're appending to an empty string, and there is not a buffer (reserveCapacity has not been called) - // then just retain the string. - if (!m_length && !m_buffer && !other.m_string.isNull()) { - m_string = other.m_string; - m_length = other.m_length; - return; - } - - if (other.is8Bit()) - append(other.characters8(), other.m_length); - else - append(other.characters16(), other.m_length); + if (other.is8Bit()) + append(other.characters8(), other.m_length); + else + append(other.characters16(), other.m_length); + } + + void append(const String& string, unsigned offset, unsigned length) { + if (!string.length()) + return; + + unsigned extent = offset + length; + if (extent < offset || extent > string.length()) + return; + + if (string.is8Bit()) + append(string.characters8() + offset, length); + else + append(string.characters16() + offset, length); + } + + void append(const StringView& string) { + if (!string.length()) + return; + + if (string.is8Bit()) + append(string.characters8(), string.length()); + else + append(string.characters16(), string.length()); + } + + void append(const char* characters) { + if (characters) + append(characters, strlen(characters)); + } + + void append(UChar c) { + if (m_buffer && m_length < m_buffer->length() && m_string.isNull()) { + if (!m_is8Bit) { + m_bufferCharacters16[m_length++] = c; + return; + } + + if (!(c & ~0xff)) { + m_bufferCharacters8[m_length++] = static_cast(c); + return; + } } - - void append(const String& string, unsigned offset, unsigned length) - { - if (!string.length()) - return; - - unsigned extent = offset + length; - if (extent < offset || extent > string.length()) - return; - - if (string.is8Bit()) - append(string.characters8() + offset, length); - else - append(string.characters16() + offset, length); - } - - void append(const StringView& string) - { - if (!string.length()) - return; - - if (string.is8Bit()) - append(string.characters8(), string.length()); - else - append(string.characters16(), string.length()); - } - - void append(const char* characters) - { - if (characters) - append(characters, strlen(characters)); - } - - void append(UChar c) - { - if (m_buffer && m_length < m_buffer->length() && m_string.isNull()) { - if (!m_is8Bit) { - m_bufferCharacters16[m_length++] = c; - return; - } - - if (!(c & ~0xff)) { - m_bufferCharacters8[m_length++] = static_cast(c); - return; - } - } - append(&c, 1); - } - - void append(LChar c) - { - if (m_buffer && m_length < m_buffer->length() && m_string.isNull()) { - if (m_is8Bit) - m_bufferCharacters8[m_length++] = c; - else - m_bufferCharacters16[m_length++] = c; - } else - append(&c, 1); - } - - void append(char c) - { - append(static_cast(c)); + append(&c, 1); + } + + void append(LChar c) { + if (m_buffer && m_length < m_buffer->length() && m_string.isNull()) { + if (m_is8Bit) + m_bufferCharacters8[m_length++] = c; + else + m_bufferCharacters16[m_length++] = c; + } else + append(&c, 1); + } + + void append(char c) { append(static_cast(c)); } + + void append(UChar32 c) { + if (U_IS_BMP(c)) { + append(static_cast(c)); + return; } - - void append(UChar32 c) - { - if (U_IS_BMP(c)) { - append(static_cast(c)); - return; - } - append(U16_LEAD(c)); - append(U16_TRAIL(c)); + append(U16_LEAD(c)); + append(U16_TRAIL(c)); + } + + template + ALWAYS_INLINE void appendLiteral(const char (&characters)[charactersCount]) { + append(characters, charactersCount - 1); + } + + void appendNumber(int); + void appendNumber(unsigned); + void appendNumber(long); + void appendNumber(unsigned long); + void appendNumber(long long); + void appendNumber(unsigned long long); + void appendNumber(double, + unsigned precision = 6, + TrailingZerosTruncatingPolicy = TruncateTrailingZeros); + + String toString() { + shrinkToFit(); + if (m_string.isNull()) + reifyString(); + return m_string; + } + + String substring(unsigned position, unsigned length) const { + if (!m_length) + return emptyString(); + if (!m_string.isNull()) + return m_string.substring(position, length); + return reifySubstring(position, length); + } + + AtomicString toAtomicString() const { + if (!m_length) + return emptyAtom; + + // If the buffer is sufficiently over-allocated, make a new AtomicString + // from a copy so its buffer is not so large. + if (canShrink()) { + if (is8Bit()) + return AtomicString(characters8(), length()); + return AtomicString(characters16(), length()); } - template - ALWAYS_INLINE void appendLiteral(const char (&characters)[charactersCount]) { append(characters, charactersCount - 1); } - - void appendNumber(int); - void appendNumber(unsigned); - void appendNumber(long); - void appendNumber(unsigned long); - void appendNumber(long long); - void appendNumber(unsigned long long); - void appendNumber(double, unsigned precision = 6, TrailingZerosTruncatingPolicy = TruncateTrailingZeros); - - String toString() - { - shrinkToFit(); - if (m_string.isNull()) - reifyString(); - return m_string; - } + if (!m_string.isNull()) + return AtomicString(m_string); - String substring(unsigned position, unsigned length) const - { - if (!m_length) - return emptyString(); - if (!m_string.isNull()) - return m_string.substring(position, length); - return reifySubstring(position, length); - } + ASSERT(m_buffer); + return AtomicString(m_buffer.get(), 0, m_length); + } - AtomicString toAtomicString() const - { - if (!m_length) - return emptyAtom; + unsigned length() const { return m_length; } - // If the buffer is sufficiently over-allocated, make a new AtomicString from a copy so its buffer is not so large. - if (canShrink()) { - if (is8Bit()) - return AtomicString(characters8(), length()); - return AtomicString(characters16(), length()); - } + bool isEmpty() const { return !m_length; } - if (!m_string.isNull()) - return AtomicString(m_string); + void reserveCapacity(unsigned newCapacity); - ASSERT(m_buffer); - return AtomicString(m_buffer.get(), 0, m_length); - } + unsigned capacity() const { return m_buffer ? m_buffer->length() : m_length; } - unsigned length() const - { - return m_length; - } + void resize(unsigned newSize); - bool isEmpty() const { return !m_length; } + bool canShrink() const; - void reserveCapacity(unsigned newCapacity); + void shrinkToFit(); - unsigned capacity() const - { - return m_buffer ? m_buffer->length() : m_length; - } + UChar operator[](unsigned i) const { + ASSERT_WITH_SECURITY_IMPLICATION(i < m_length); + if (m_is8Bit) + return characters8()[i]; + return characters16()[i]; + } - void resize(unsigned newSize); - - bool canShrink() const; - - void shrinkToFit(); - - UChar operator[](unsigned i) const - { - ASSERT_WITH_SECURITY_IMPLICATION(i < m_length); - if (m_is8Bit) - return characters8()[i]; - return characters16()[i]; - } - - const LChar* characters8() const - { - ASSERT(m_is8Bit); - if (!m_length) - return 0; - if (!m_string.isNull()) - return m_string.characters8(); - ASSERT(m_buffer); - return m_buffer->characters8(); - } - - const UChar* characters16() const - { - ASSERT(!m_is8Bit); - if (!m_length) - return 0; - if (!m_string.isNull()) - return m_string.characters16(); - ASSERT(m_buffer); - return m_buffer->characters16(); - } - - bool is8Bit() const { return m_is8Bit; } - - void clear() - { - m_length = 0; - m_string = String(); - m_buffer = nullptr; - m_bufferCharacters8 = 0; - m_is8Bit = true; - } - - void swap(StringBuilder& stringBuilder) - { - std::swap(m_length, stringBuilder.m_length); - m_string.swap(stringBuilder.m_string); - m_buffer.swap(stringBuilder.m_buffer); - std::swap(m_is8Bit, stringBuilder.m_is8Bit); - std::swap(m_bufferCharacters8, stringBuilder.m_bufferCharacters8); - } - -private: - void allocateBuffer(const LChar* currentCharacters, unsigned requiredLength); - void allocateBuffer(const UChar* currentCharacters, unsigned requiredLength); - void allocateBufferUpConvert(const LChar* currentCharacters, unsigned requiredLength); - template - void reallocateBuffer(unsigned requiredLength); - template - ALWAYS_INLINE CharType* appendUninitialized(unsigned length); - template - CharType* appendUninitializedSlow(unsigned length); - template - ALWAYS_INLINE CharType * getBufferCharacters(); - void reifyString(); - String reifySubstring(unsigned position, unsigned length) const; - - String m_string; // Pointers first: crbug.com/232031 - RefPtr m_buffer; - union { - LChar* m_bufferCharacters8; - UChar* m_bufferCharacters16; - }; - unsigned m_length; - bool m_is8Bit; + const LChar* characters8() const { + ASSERT(m_is8Bit); + if (!m_length) + return 0; + if (!m_string.isNull()) + return m_string.characters8(); + ASSERT(m_buffer); + return m_buffer->characters8(); + } + + const UChar* characters16() const { + ASSERT(!m_is8Bit); + if (!m_length) + return 0; + if (!m_string.isNull()) + return m_string.characters16(); + ASSERT(m_buffer); + return m_buffer->characters16(); + } + + bool is8Bit() const { return m_is8Bit; } + + void clear() { + m_length = 0; + m_string = String(); + m_buffer = nullptr; + m_bufferCharacters8 = 0; + m_is8Bit = true; + } + + void swap(StringBuilder& stringBuilder) { + std::swap(m_length, stringBuilder.m_length); + m_string.swap(stringBuilder.m_string); + m_buffer.swap(stringBuilder.m_buffer); + std::swap(m_is8Bit, stringBuilder.m_is8Bit); + std::swap(m_bufferCharacters8, stringBuilder.m_bufferCharacters8); + } + + private: + void allocateBuffer(const LChar* currentCharacters, unsigned requiredLength); + void allocateBuffer(const UChar* currentCharacters, unsigned requiredLength); + void allocateBufferUpConvert(const LChar* currentCharacters, + unsigned requiredLength); + template + void reallocateBuffer(unsigned requiredLength); + template + ALWAYS_INLINE CharType* appendUninitialized(unsigned length); + template + CharType* appendUninitializedSlow(unsigned length); + template + ALWAYS_INLINE CharType* getBufferCharacters(); + void reifyString(); + String reifySubstring(unsigned position, unsigned length) const; + + String m_string; // Pointers first: crbug.com/232031 + RefPtr m_buffer; + union { + LChar* m_bufferCharacters8; + UChar* m_bufferCharacters16; + }; + unsigned m_length; + bool m_is8Bit; }; template <> -ALWAYS_INLINE LChar* StringBuilder::getBufferCharacters() -{ - ASSERT(m_is8Bit); - return m_bufferCharacters8; +ALWAYS_INLINE LChar* StringBuilder::getBufferCharacters() { + ASSERT(m_is8Bit); + return m_bufferCharacters8; } template <> -ALWAYS_INLINE UChar* StringBuilder::getBufferCharacters() -{ - ASSERT(!m_is8Bit); - return m_bufferCharacters16; +ALWAYS_INLINE UChar* StringBuilder::getBufferCharacters() { + ASSERT(!m_is8Bit); + return m_bufferCharacters16; } template -bool equal(const StringBuilder& s, const CharType* buffer, unsigned length) -{ - if (s.length() != length) - return false; +bool equal(const StringBuilder& s, const CharType* buffer, unsigned length) { + if (s.length() != length) + return false; - if (s.is8Bit()) - return equal(s.characters8(), buffer, length); + if (s.is8Bit()) + return equal(s.characters8(), buffer, length); - return equal(s.characters16(), buffer, length); + return equal(s.characters16(), buffer, length); } -template -bool equalIgnoringCase(const StringBuilder& s, const CharType* buffer, unsigned length) -{ - if (s.length() != length) - return false; +template +bool equalIgnoringCase(const StringBuilder& s, + const CharType* buffer, + unsigned length) { + if (s.length() != length) + return false; - if (s.is8Bit()) - return equalIgnoringCase(s.characters8(), buffer, length); + if (s.is8Bit()) + return equalIgnoringCase(s.characters8(), buffer, length); - return equalIgnoringCase(s.characters16(), buffer, length); + return equalIgnoringCase(s.characters16(), buffer, length); } -inline bool equalIgnoringCase(const StringBuilder& s, const char* string) -{ - return equalIgnoringCase(s, reinterpret_cast(string), strlen(string)); +inline bool equalIgnoringCase(const StringBuilder& s, const char* string) { + return equalIgnoringCase(s, reinterpret_cast(string), + strlen(string)); } template -bool equal(const StringBuilder& a, const StringType& b) -{ - if (a.length() != b.length()) - return false; - - if (!a.length()) - return true; - - if (a.is8Bit()) { - if (b.is8Bit()) - return equal(a.characters8(), b.characters8(), a.length()); - return equal(a.characters8(), b.characters16(), a.length()); - } +bool equal(const StringBuilder& a, const StringType& b) { + if (a.length() != b.length()) + return false; + + if (!a.length()) + return true; + if (a.is8Bit()) { if (b.is8Bit()) - return equal(a.characters16(), b.characters8(), a.length()); - return equal(a.characters16(), b.characters16(), a.length()); + return equal(a.characters8(), b.characters8(), a.length()); + return equal(a.characters8(), b.characters16(), a.length()); + } + + if (b.is8Bit()) + return equal(a.characters16(), b.characters8(), a.length()); + return equal(a.characters16(), b.characters16(), a.length()); } template -bool equalIgnoringCase(const StringBuilder& a, const StringType& b) -{ - if (a.length() != b.length()) - return false; - - if (!a.length()) - return true; - - if (a.is8Bit()) { - if (b.is8Bit()) - return equalIgnoringCase(a.characters8(), b.characters8(), a.length()); - return equalIgnoringCase(a.characters8(), b.characters16(), a.length()); - } +bool equalIgnoringCase(const StringBuilder& a, const StringType& b) { + if (a.length() != b.length()) + return false; + + if (!a.length()) + return true; + if (a.is8Bit()) { if (b.is8Bit()) - return equalIgnoringCase(a.characters16(), b.characters8(), a.length()); - return equalIgnoringCase(a.characters16(), b.characters16(), a.length()); + return equalIgnoringCase(a.characters8(), b.characters8(), a.length()); + return equalIgnoringCase(a.characters8(), b.characters16(), a.length()); + } + + if (b.is8Bit()) + return equalIgnoringCase(a.characters16(), b.characters8(), a.length()); + return equalIgnoringCase(a.characters16(), b.characters16(), a.length()); } -inline bool operator==(const StringBuilder& a, const StringBuilder& b) { return equal(a, b); } -inline bool operator!=(const StringBuilder& a, const StringBuilder& b) { return !equal(a, b); } -inline bool operator==(const StringBuilder& a, const String& b) { return equal(a, b); } -inline bool operator!=(const StringBuilder& a, const String& b) { return !equal(a, b); } -inline bool operator==(const String& a, const StringBuilder& b) { return equal(b, a); } -inline bool operator!=(const String& a, const StringBuilder& b) { return !equal(b, a); } +inline bool operator==(const StringBuilder& a, const StringBuilder& b) { + return equal(a, b); +} +inline bool operator!=(const StringBuilder& a, const StringBuilder& b) { + return !equal(a, b); +} +inline bool operator==(const StringBuilder& a, const String& b) { + return equal(a, b); +} +inline bool operator!=(const StringBuilder& a, const String& b) { + return !equal(a, b); +} +inline bool operator==(const String& a, const StringBuilder& b) { + return equal(b, a); +} +inline bool operator!=(const String& a, const StringBuilder& b) { + return !equal(b, a); +} -} // namespace WTF +} // namespace WTF using WTF::StringBuilder; diff --git a/sky/engine/wtf/text/StringBuilderTest.cpp b/sky/engine/wtf/text/StringBuilderTest.cpp index 7201bc438677d..59aab44b96196 100644 --- a/sky/engine/wtf/text/StringBuilderTest.cpp +++ b/sky/engine/wtf/text/StringBuilderTest.cpp @@ -29,7 +29,6 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #include #include "flutter/sky/engine/wtf/Assertions.h" #include "flutter/sky/engine/wtf/text/CString.h" @@ -39,293 +38,288 @@ namespace WTF { -static std::ostream& operator<<(std::ostream& os, const String& string) -{ - return os << string.utf8().data(); +static std::ostream& operator<<(std::ostream& os, const String& string) { + return os << string.utf8().data(); } -} +} // namespace WTF namespace { -static void expectBuilderContent(const String& expected, const StringBuilder& builder) -{ - // Not using builder.toString() because it changes internal state of builder. - if (builder.is8Bit()) - EXPECT_EQ(expected, String(builder.characters8(), builder.length())); - else - EXPECT_EQ(expected, String(builder.characters16(), builder.length())); +static void expectBuilderContent(const String& expected, + const StringBuilder& builder) { + // Not using builder.toString() because it changes internal state of builder. + if (builder.is8Bit()) + EXPECT_EQ(expected, String(builder.characters8(), builder.length())); + else + EXPECT_EQ(expected, String(builder.characters16(), builder.length())); } -void expectEmpty(const StringBuilder& builder) -{ - EXPECT_EQ(0U, builder.length()); - EXPECT_TRUE(builder.isEmpty()); - EXPECT_EQ(0, builder.characters8()); +void expectEmpty(const StringBuilder& builder) { + EXPECT_EQ(0U, builder.length()); + EXPECT_TRUE(builder.isEmpty()); + EXPECT_EQ(0, builder.characters8()); } -TEST(StringBuilderTest, DefaultConstructor) -{ - StringBuilder builder; - expectEmpty(builder); +TEST(StringBuilderTest, DefaultConstructor) { + StringBuilder builder; + expectEmpty(builder); } -TEST(StringBuilderTest, Append) -{ - StringBuilder builder; - builder.append(String("0123456789")); - expectBuilderContent("0123456789", builder); - builder.append("abcd"); - expectBuilderContent("0123456789abcd", builder); - builder.append("efgh", 3); - expectBuilderContent("0123456789abcdefg", builder); - builder.append(""); - expectBuilderContent("0123456789abcdefg", builder); - builder.append('#'); - expectBuilderContent("0123456789abcdefg#", builder); - - builder.toString(); // Test after reifyString(). - StringBuilder builder1; - builder.append("", 0); - expectBuilderContent("0123456789abcdefg#", builder); - builder1.append(builder.characters8(), builder.length()); - builder1.append("XYZ"); - builder.append(builder1.characters8(), builder1.length()); - expectBuilderContent("0123456789abcdefg#0123456789abcdefg#XYZ", builder); - - StringBuilder builder2; - builder2.reserveCapacity(100); - builder2.append("xyz"); - const LChar* characters = builder2.characters8(); - builder2.append("0123456789"); - ASSERT_EQ(characters, builder2.characters8()); - - // Test appending UChar32 characters to StringBuilder. - StringBuilder builderForUChar32Append; - UChar32 frakturAChar = 0x1D504; - builderForUChar32Append.append(frakturAChar); // The fraktur A is not in the BMP, so it's two UTF-16 code units long. - ASSERT_EQ(2U, builderForUChar32Append.length()); - builderForUChar32Append.append(static_cast('A')); - ASSERT_EQ(3U, builderForUChar32Append.length()); - const UChar resultArray[] = { U16_LEAD(frakturAChar), U16_TRAIL(frakturAChar), 'A' }; - expectBuilderContent(String(resultArray, WTF_ARRAY_LENGTH(resultArray)), builderForUChar32Append); +TEST(StringBuilderTest, Append) { + StringBuilder builder; + builder.append(String("0123456789")); + expectBuilderContent("0123456789", builder); + builder.append("abcd"); + expectBuilderContent("0123456789abcd", builder); + builder.append("efgh", 3); + expectBuilderContent("0123456789abcdefg", builder); + builder.append(""); + expectBuilderContent("0123456789abcdefg", builder); + builder.append('#'); + expectBuilderContent("0123456789abcdefg#", builder); + + builder.toString(); // Test after reifyString(). + StringBuilder builder1; + builder.append("", 0); + expectBuilderContent("0123456789abcdefg#", builder); + builder1.append(builder.characters8(), builder.length()); + builder1.append("XYZ"); + builder.append(builder1.characters8(), builder1.length()); + expectBuilderContent("0123456789abcdefg#0123456789abcdefg#XYZ", builder); + + StringBuilder builder2; + builder2.reserveCapacity(100); + builder2.append("xyz"); + const LChar* characters = builder2.characters8(); + builder2.append("0123456789"); + ASSERT_EQ(characters, builder2.characters8()); + + // Test appending UChar32 characters to StringBuilder. + StringBuilder builderForUChar32Append; + UChar32 frakturAChar = 0x1D504; + builderForUChar32Append.append(frakturAChar); // The fraktur A is not in the + // BMP, so it's two UTF-16 code + // units long. + ASSERT_EQ(2U, builderForUChar32Append.length()); + builderForUChar32Append.append(static_cast('A')); + ASSERT_EQ(3U, builderForUChar32Append.length()); + const UChar resultArray[] = {U16_LEAD(frakturAChar), U16_TRAIL(frakturAChar), + 'A'}; + expectBuilderContent(String(resultArray, WTF_ARRAY_LENGTH(resultArray)), + builderForUChar32Append); } -TEST(StringBuilderTest, ToString) -{ - StringBuilder builder; - builder.append("0123456789"); - String string = builder.toString(); - ASSERT_EQ(String("0123456789"), string); - ASSERT_EQ(string.impl(), builder.toString().impl()); - - // Changing the StringBuilder should not affect the original result of toString(). - builder.append("abcdefghijklmnopqrstuvwxyz"); - ASSERT_EQ(String("0123456789"), string); - - // Changing the StringBuilder should not affect the original result of toString() in case the capacity is not changed. - builder.reserveCapacity(200); - string = builder.toString(); - ASSERT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyz"), string); - builder.append("ABC"); - ASSERT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyz"), string); - - // Changing the original result of toString() should not affect the content of the StringBuilder. - String string1 = builder.toString(); - ASSERT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyzABC"), string1); - string1.append("DEF"); - ASSERT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyzABC"), builder.toString()); - ASSERT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyzABCDEF"), string1); - - // Resizing the StringBuilder should not affect the original result of toString(). - string1 = builder.toString(); - builder.resize(10); - builder.append("###"); - ASSERT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyzABC"), string1); +TEST(StringBuilderTest, ToString) { + StringBuilder builder; + builder.append("0123456789"); + String string = builder.toString(); + ASSERT_EQ(String("0123456789"), string); + ASSERT_EQ(string.impl(), builder.toString().impl()); + + // Changing the StringBuilder should not affect the original result of + // toString(). + builder.append("abcdefghijklmnopqrstuvwxyz"); + ASSERT_EQ(String("0123456789"), string); + + // Changing the StringBuilder should not affect the original result of + // toString() in case the capacity is not changed. + builder.reserveCapacity(200); + string = builder.toString(); + ASSERT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyz"), string); + builder.append("ABC"); + ASSERT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyz"), string); + + // Changing the original result of toString() should not affect the content of + // the StringBuilder. + String string1 = builder.toString(); + ASSERT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyzABC"), string1); + string1.append("DEF"); + ASSERT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyzABC"), + builder.toString()); + ASSERT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyzABCDEF"), string1); + + // Resizing the StringBuilder should not affect the original result of + // toString(). + string1 = builder.toString(); + builder.resize(10); + builder.append("###"); + ASSERT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyzABC"), string1); } -TEST(StringBuilderTest, Clear) -{ - StringBuilder builder; - builder.append("0123456789"); - builder.clear(); - expectEmpty(builder); +TEST(StringBuilderTest, Clear) { + StringBuilder builder; + builder.append("0123456789"); + builder.clear(); + expectEmpty(builder); } -TEST(StringBuilderTest, Array) -{ - StringBuilder builder; - builder.append("0123456789"); - EXPECT_EQ('0', static_cast(builder[0])); - EXPECT_EQ('9', static_cast(builder[9])); - builder.toString(); // Test after reifyString(). - EXPECT_EQ('0', static_cast(builder[0])); - EXPECT_EQ('9', static_cast(builder[9])); +TEST(StringBuilderTest, Array) { + StringBuilder builder; + builder.append("0123456789"); + EXPECT_EQ('0', static_cast(builder[0])); + EXPECT_EQ('9', static_cast(builder[9])); + builder.toString(); // Test after reifyString(). + EXPECT_EQ('0', static_cast(builder[0])); + EXPECT_EQ('9', static_cast(builder[9])); } -TEST(StringBuilderTest, Resize) -{ - StringBuilder builder; - builder.append("0123456789"); - builder.resize(10); - EXPECT_EQ(10U, builder.length()); - expectBuilderContent("0123456789", builder); - builder.resize(8); - EXPECT_EQ(8U, builder.length()); - expectBuilderContent("01234567", builder); - - builder.toString(); - builder.resize(7); - EXPECT_EQ(7U, builder.length()); - expectBuilderContent("0123456", builder); - builder.resize(0); - expectEmpty(builder); +TEST(StringBuilderTest, Resize) { + StringBuilder builder; + builder.append("0123456789"); + builder.resize(10); + EXPECT_EQ(10U, builder.length()); + expectBuilderContent("0123456789", builder); + builder.resize(8); + EXPECT_EQ(8U, builder.length()); + expectBuilderContent("01234567", builder); + + builder.toString(); + builder.resize(7); + EXPECT_EQ(7U, builder.length()); + expectBuilderContent("0123456", builder); + builder.resize(0); + expectEmpty(builder); } -TEST(StringBuilderTest, Equal) -{ - StringBuilder builder1; - StringBuilder builder2; - ASSERT_TRUE(builder1 == builder2); - ASSERT_TRUE(equal(builder1, static_cast(0), 0)); - ASSERT_TRUE(builder1 == String()); - ASSERT_TRUE(String() == builder1); - ASSERT_TRUE(builder1 != String("abc")); - - builder1.append("123"); - builder1.reserveCapacity(32); - builder2.append("123"); - builder1.reserveCapacity(64); - ASSERT_TRUE(builder1 == builder2); - ASSERT_TRUE(builder1 == String("123")); - ASSERT_TRUE(String("123") == builder1); - - builder2.append("456"); - ASSERT_TRUE(builder1 != builder2); - ASSERT_TRUE(builder2 != builder1); - ASSERT_TRUE(String("123") != builder2); - ASSERT_TRUE(builder2 != String("123")); - builder2.toString(); // Test after reifyString(). - ASSERT_TRUE(builder1 != builder2); - - builder2.resize(3); - ASSERT_TRUE(builder1 == builder2); - - builder1.toString(); // Test after reifyString(). - ASSERT_TRUE(builder1 == builder2); +TEST(StringBuilderTest, Equal) { + StringBuilder builder1; + StringBuilder builder2; + ASSERT_TRUE(builder1 == builder2); + ASSERT_TRUE(equal(builder1, static_cast(0), 0)); + ASSERT_TRUE(builder1 == String()); + ASSERT_TRUE(String() == builder1); + ASSERT_TRUE(builder1 != String("abc")); + + builder1.append("123"); + builder1.reserveCapacity(32); + builder2.append("123"); + builder1.reserveCapacity(64); + ASSERT_TRUE(builder1 == builder2); + ASSERT_TRUE(builder1 == String("123")); + ASSERT_TRUE(String("123") == builder1); + + builder2.append("456"); + ASSERT_TRUE(builder1 != builder2); + ASSERT_TRUE(builder2 != builder1); + ASSERT_TRUE(String("123") != builder2); + ASSERT_TRUE(builder2 != String("123")); + builder2.toString(); // Test after reifyString(). + ASSERT_TRUE(builder1 != builder2); + + builder2.resize(3); + ASSERT_TRUE(builder1 == builder2); + + builder1.toString(); // Test after reifyString(). + ASSERT_TRUE(builder1 == builder2); } -TEST(StringBuilderTest, CanShrink) -{ - StringBuilder builder; - builder.reserveCapacity(256); - ASSERT_TRUE(builder.canShrink()); - for (int i = 0; i < 256; i++) - builder.append('x'); - ASSERT_EQ(builder.length(), builder.capacity()); - ASSERT_FALSE(builder.canShrink()); +TEST(StringBuilderTest, CanShrink) { + StringBuilder builder; + builder.reserveCapacity(256); + ASSERT_TRUE(builder.canShrink()); + for (int i = 0; i < 256; i++) + builder.append('x'); + ASSERT_EQ(builder.length(), builder.capacity()); + ASSERT_FALSE(builder.canShrink()); } -TEST(StringBuilderTest, ToAtomicString) -{ - StringBuilder builder; - builder.append("123"); - AtomicString atomicString = builder.toAtomicString(); - ASSERT_EQ(String("123"), atomicString); - - builder.reserveCapacity(256); - ASSERT_TRUE(builder.canShrink()); - for (int i = builder.length(); i < 128; i++) - builder.append('x'); - AtomicString atomicString1 = builder.toAtomicString(); - ASSERT_EQ(128u, atomicString1.length()); - ASSERT_EQ('x', atomicString1[127]); - - // Later change of builder should not affect the atomic string. - for (int i = builder.length(); i < 256; i++) - builder.append('x'); - ASSERT_EQ(128u, atomicString1.length()); - - ASSERT_FALSE(builder.canShrink()); - String string = builder.toString(); - AtomicString atomicString2 = builder.toAtomicString(); - // They should share the same StringImpl. - ASSERT_EQ(atomicString2.impl(), string.impl()); +TEST(StringBuilderTest, ToAtomicString) { + StringBuilder builder; + builder.append("123"); + AtomicString atomicString = builder.toAtomicString(); + ASSERT_EQ(String("123"), atomicString); + + builder.reserveCapacity(256); + ASSERT_TRUE(builder.canShrink()); + for (int i = builder.length(); i < 128; i++) + builder.append('x'); + AtomicString atomicString1 = builder.toAtomicString(); + ASSERT_EQ(128u, atomicString1.length()); + ASSERT_EQ('x', atomicString1[127]); + + // Later change of builder should not affect the atomic string. + for (int i = builder.length(); i < 256; i++) + builder.append('x'); + ASSERT_EQ(128u, atomicString1.length()); + + ASSERT_FALSE(builder.canShrink()); + String string = builder.toString(); + AtomicString atomicString2 = builder.toAtomicString(); + // They should share the same StringImpl. + ASSERT_EQ(atomicString2.impl(), string.impl()); } -TEST(StringBuilderTest, ToAtomicStringOnEmpty) -{ - { // Default constructed. - StringBuilder builder; - AtomicString atomicString = builder.toAtomicString(); - ASSERT_EQ(emptyAtom, atomicString); - } - { // With capacity. - StringBuilder builder; - builder.reserveCapacity(64); - AtomicString atomicString = builder.toAtomicString(); - ASSERT_EQ(emptyAtom, atomicString); - } - { // AtomicString constructed from a null string. - StringBuilder builder; - builder.append(String()); - AtomicString atomicString = builder.toAtomicString(); - ASSERT_EQ(emptyAtom, atomicString); - } - { // AtomicString constructed from an empty string. - StringBuilder builder; - builder.append(emptyString()); - AtomicString atomicString = builder.toAtomicString(); - ASSERT_EQ(emptyAtom, atomicString); - } - { // AtomicString constructed from an empty StringBuilder. - StringBuilder builder; - StringBuilder emptyBuilder; - builder.append(emptyBuilder); - AtomicString atomicString = builder.toAtomicString(); - ASSERT_EQ(emptyAtom, atomicString); - } - { // AtomicString constructed from an empty char* string. - StringBuilder builder; - builder.append("", 0); - AtomicString atomicString = builder.toAtomicString(); - ASSERT_EQ(emptyAtom, atomicString); - } - { // Cleared StringBuilder. - StringBuilder builder; - builder.appendLiteral("WebKit"); - builder.clear(); - AtomicString atomicString = builder.toAtomicString(); - ASSERT_EQ(emptyAtom, atomicString); - } +TEST(StringBuilderTest, ToAtomicStringOnEmpty) { + { // Default constructed. + StringBuilder builder; + AtomicString atomicString = builder.toAtomicString(); + ASSERT_EQ(emptyAtom, atomicString); + } + { // With capacity. + StringBuilder builder; + builder.reserveCapacity(64); + AtomicString atomicString = builder.toAtomicString(); + ASSERT_EQ(emptyAtom, atomicString); + } + { // AtomicString constructed from a null string. + StringBuilder builder; + builder.append(String()); + AtomicString atomicString = builder.toAtomicString(); + ASSERT_EQ(emptyAtom, atomicString); + } + { // AtomicString constructed from an empty string. + StringBuilder builder; + builder.append(emptyString()); + AtomicString atomicString = builder.toAtomicString(); + ASSERT_EQ(emptyAtom, atomicString); + } + { // AtomicString constructed from an empty StringBuilder. + StringBuilder builder; + StringBuilder emptyBuilder; + builder.append(emptyBuilder); + AtomicString atomicString = builder.toAtomicString(); + ASSERT_EQ(emptyAtom, atomicString); + } + { // AtomicString constructed from an empty char* string. + StringBuilder builder; + builder.append("", 0); + AtomicString atomicString = builder.toAtomicString(); + ASSERT_EQ(emptyAtom, atomicString); + } + { // Cleared StringBuilder. + StringBuilder builder; + builder.appendLiteral("WebKit"); + builder.clear(); + AtomicString atomicString = builder.toAtomicString(); + ASSERT_EQ(emptyAtom, atomicString); + } } -TEST(StringBuilderTest, Substring) -{ - { // Default constructed. - StringBuilder builder; - String substring = builder.substring(0, 10); - ASSERT_EQ(emptyString(), substring); - } - { // With capacity. - StringBuilder builder; - builder.reserveCapacity(64); - builder.append("abc"); - String substring = builder.substring(2, 10); - ASSERT_EQ(String("c"), substring); - } +TEST(StringBuilderTest, Substring) { + { // Default constructed. + StringBuilder builder; + String substring = builder.substring(0, 10); + ASSERT_EQ(emptyString(), substring); + } + { // With capacity. + StringBuilder builder; + builder.reserveCapacity(64); + builder.append("abc"); + String substring = builder.substring(2, 10); + ASSERT_EQ(String("c"), substring); + } } -TEST(StringBuilderTest, AppendNumberDoubleUChar) -{ - const double someNumber = 1.2345; - StringBuilder reference; - reference.append(replacementCharacter); // Make it UTF-16. - reference.append(String::number(someNumber)); - StringBuilder test; - test.append(replacementCharacter); - test.appendNumber(someNumber); - ASSERT_EQ(reference, test); +TEST(StringBuilderTest, AppendNumberDoubleUChar) { + const double someNumber = 1.2345; + StringBuilder reference; + reference.append(replacementCharacter); // Make it UTF-16. + reference.append(String::number(someNumber)); + StringBuilder test; + test.append(replacementCharacter); + test.appendNumber(someNumber); + ASSERT_EQ(reference, test); } -} // namespace +} // namespace diff --git a/sky/engine/wtf/text/StringConcatenate.h b/sky/engine/wtf/text/StringConcatenate.h index 0bef51ed1c14a..4b22204cc7bad 100644 --- a/sky/engine/wtf/text/StringConcatenate.h +++ b/sky/engine/wtf/text/StringConcatenate.h @@ -32,409 +32,354 @@ #include "flutter/sky/engine/wtf/text/AtomicString.h" #endif -// This macro is helpful for testing how many intermediate Strings are created while evaluating an -// expression containing operator+. +// This macro is helpful for testing how many intermediate Strings are created +// while evaluating an expression containing operator+. #ifndef WTF_STRINGTYPEADAPTER_COPIED_WTF_STRING #define WTF_STRINGTYPEADAPTER_COPIED_WTF_STRING() ((void)0) #endif namespace WTF { -template -class StringTypeAdapter { -}; +template +class StringTypeAdapter {}; -template<> +template <> class StringTypeAdapter { -public: - StringTypeAdapter(char buffer) - : m_buffer(buffer) - { - } + public: + StringTypeAdapter(char buffer) : m_buffer(buffer) {} - unsigned length() { return 1; } + unsigned length() { return 1; } - bool is8Bit() { return true; } + bool is8Bit() { return true; } - void writeTo(LChar* destination) - { - *destination = m_buffer; - } + void writeTo(LChar* destination) { *destination = m_buffer; } - void writeTo(UChar* destination) { *destination = m_buffer; } + void writeTo(UChar* destination) { *destination = m_buffer; } -private: - unsigned char m_buffer; + private: + unsigned char m_buffer; }; -template<> +template <> class StringTypeAdapter { -public: - StringTypeAdapter(LChar buffer) - : m_buffer(buffer) - { - } + public: + StringTypeAdapter(LChar buffer) : m_buffer(buffer) {} - unsigned length() { return 1; } + unsigned length() { return 1; } - bool is8Bit() { return true; } + bool is8Bit() { return true; } - void writeTo(LChar* destination) - { - *destination = m_buffer; - } + void writeTo(LChar* destination) { *destination = m_buffer; } - void writeTo(UChar* destination) { *destination = m_buffer; } + void writeTo(UChar* destination) { *destination = m_buffer; } -private: - LChar m_buffer; + private: + LChar m_buffer; }; -template<> +template <> class StringTypeAdapter { -public: - StringTypeAdapter(UChar buffer) - : m_buffer(buffer) - { - } + public: + StringTypeAdapter(UChar buffer) : m_buffer(buffer) {} - unsigned length() { return 1; } + unsigned length() { return 1; } - bool is8Bit() { return m_buffer <= 0xff; } + bool is8Bit() { return m_buffer <= 0xff; } - void writeTo(LChar* destination) - { - ASSERT(is8Bit()); - *destination = static_cast(m_buffer); - } + void writeTo(LChar* destination) { + ASSERT(is8Bit()); + *destination = static_cast(m_buffer); + } - void writeTo(UChar* destination) { *destination = m_buffer; } + void writeTo(UChar* destination) { *destination = m_buffer; } -private: - UChar m_buffer; + private: + UChar m_buffer; }; -template<> +template <> class StringTypeAdapter { -public: - StringTypeAdapter(char* buffer) - : m_buffer(buffer) - , m_length(strlen(buffer)) - { - } + public: + StringTypeAdapter(char* buffer) + : m_buffer(buffer), m_length(strlen(buffer)) {} - unsigned length() { return m_length; } + unsigned length() { return m_length; } - bool is8Bit() { return true; } + bool is8Bit() { return true; } - void writeTo(LChar* destination) - { - for (unsigned i = 0; i < m_length; ++i) - destination[i] = static_cast(m_buffer[i]); - } + void writeTo(LChar* destination) { + for (unsigned i = 0; i < m_length; ++i) + destination[i] = static_cast(m_buffer[i]); + } - void writeTo(UChar* destination) - { - for (unsigned i = 0; i < m_length; ++i) { - unsigned char c = m_buffer[i]; - destination[i] = c; - } + void writeTo(UChar* destination) { + for (unsigned i = 0; i < m_length; ++i) { + unsigned char c = m_buffer[i]; + destination[i] = c; } + } -private: - const char* m_buffer; - unsigned m_length; + private: + const char* m_buffer; + unsigned m_length; }; -template<> +template <> class StringTypeAdapter { -public: - StringTypeAdapter(LChar* buffer) - : m_buffer(buffer) - , m_length(strlen(reinterpret_cast(buffer))) - { - } + public: + StringTypeAdapter(LChar* buffer) + : m_buffer(buffer), m_length(strlen(reinterpret_cast(buffer))) {} - unsigned length() { return m_length; } + unsigned length() { return m_length; } - bool is8Bit() { return true; } + bool is8Bit() { return true; } - void writeTo(LChar* destination) - { - memcpy(destination, m_buffer, m_length * sizeof(LChar)); - } + void writeTo(LChar* destination) { + memcpy(destination, m_buffer, m_length * sizeof(LChar)); + } - void writeTo(UChar* destination) - { - StringImpl::copyChars(destination, m_buffer, m_length); - } + void writeTo(UChar* destination) { + StringImpl::copyChars(destination, m_buffer, m_length); + } -private: - const LChar* m_buffer; - unsigned m_length; + private: + const LChar* m_buffer; + unsigned m_length; }; -template<> +template <> class StringTypeAdapter { -public: - StringTypeAdapter(const UChar* buffer) - : m_buffer(buffer) - { - size_t len = 0; - while (m_buffer[len] != UChar(0)) - ++len; + public: + StringTypeAdapter(const UChar* buffer) : m_buffer(buffer) { + size_t len = 0; + while (m_buffer[len] != UChar(0)) + ++len; - RELEASE_ASSERT(len <= std::numeric_limits::max()); + RELEASE_ASSERT(len <= std::numeric_limits::max()); - m_length = len; - } + m_length = len; + } - unsigned length() { return m_length; } + unsigned length() { return m_length; } - bool is8Bit() { return false; } + bool is8Bit() { return false; } - NO_RETURN_DUE_TO_CRASH void writeTo(LChar*) - { - RELEASE_ASSERT(false); - } + NO_RETURN_DUE_TO_CRASH void writeTo(LChar*) { RELEASE_ASSERT(false); } - void writeTo(UChar* destination) - { - memcpy(destination, m_buffer, m_length * sizeof(UChar)); - } + void writeTo(UChar* destination) { + memcpy(destination, m_buffer, m_length * sizeof(UChar)); + } -private: - const UChar* m_buffer; - unsigned m_length; + private: + const UChar* m_buffer; + unsigned m_length; }; -template<> +template <> class StringTypeAdapter { -public: - StringTypeAdapter(const char* buffer) - : m_buffer(buffer) - , m_length(strlen(buffer)) - { - } + public: + StringTypeAdapter(const char* buffer) + : m_buffer(buffer), m_length(strlen(buffer)) {} - unsigned length() { return m_length; } + unsigned length() { return m_length; } - bool is8Bit() { return true; } + bool is8Bit() { return true; } - void writeTo(LChar* destination) - { - memcpy(destination, m_buffer, static_cast(m_length) * sizeof(LChar)); - } + void writeTo(LChar* destination) { + memcpy(destination, m_buffer, + static_cast(m_length) * sizeof(LChar)); + } - void writeTo(UChar* destination) - { - for (unsigned i = 0; i < m_length; ++i) { - unsigned char c = m_buffer[i]; - destination[i] = c; - } + void writeTo(UChar* destination) { + for (unsigned i = 0; i < m_length; ++i) { + unsigned char c = m_buffer[i]; + destination[i] = c; } + } -private: - const char* m_buffer; - unsigned m_length; + private: + const char* m_buffer; + unsigned m_length; }; -template<> +template <> class StringTypeAdapter { -public: - StringTypeAdapter(const LChar* buffer) - : m_buffer(buffer) - , m_length(strlen(reinterpret_cast(buffer))) - { - } + public: + StringTypeAdapter(const LChar* buffer) + : m_buffer(buffer), + m_length(strlen(reinterpret_cast(buffer))) {} - unsigned length() { return m_length; } + unsigned length() { return m_length; } - bool is8Bit() { return true; } + bool is8Bit() { return true; } - void writeTo(LChar* destination) - { - memcpy(destination, m_buffer, static_cast(m_length) * sizeof(LChar)); - } + void writeTo(LChar* destination) { + memcpy(destination, m_buffer, + static_cast(m_length) * sizeof(LChar)); + } - void writeTo(UChar* destination) - { - StringImpl::copyChars(destination, m_buffer, m_length); - } + void writeTo(UChar* destination) { + StringImpl::copyChars(destination, m_buffer, m_length); + } -private: - const LChar* m_buffer; - unsigned m_length; + private: + const LChar* m_buffer; + unsigned m_length; }; -template<> -class StringTypeAdapter > { -public: - StringTypeAdapter >(const Vector& buffer) - : m_buffer(buffer) - { - } +template <> +class StringTypeAdapter> { + public: + StringTypeAdapter>(const Vector& buffer) + : m_buffer(buffer) {} - size_t length() { return m_buffer.size(); } + size_t length() { return m_buffer.size(); } - bool is8Bit() { return true; } + bool is8Bit() { return true; } - void writeTo(LChar* destination) - { - for (size_t i = 0; i < m_buffer.size(); ++i) - destination[i] = static_cast(m_buffer[i]); - } + void writeTo(LChar* destination) { + for (size_t i = 0; i < m_buffer.size(); ++i) + destination[i] = static_cast(m_buffer[i]); + } - void writeTo(UChar* destination) - { - for (size_t i = 0; i < m_buffer.size(); ++i) - destination[i] = static_cast(m_buffer[i]); - } + void writeTo(UChar* destination) { + for (size_t i = 0; i < m_buffer.size(); ++i) + destination[i] = static_cast(m_buffer[i]); + } -private: - const Vector& m_buffer; + private: + const Vector& m_buffer; }; -template<> -class StringTypeAdapter > { -public: - StringTypeAdapter >(const Vector& buffer) - : m_buffer(buffer) - { - } +template <> +class StringTypeAdapter> { + public: + StringTypeAdapter>(const Vector& buffer) + : m_buffer(buffer) {} - size_t length() { return m_buffer.size(); } + size_t length() { return m_buffer.size(); } - bool is8Bit() { return true; } + bool is8Bit() { return true; } - void writeTo(LChar* destination) - { - for (size_t i = 0; i < m_buffer.size(); ++i) - destination[i] = m_buffer[i]; - } + void writeTo(LChar* destination) { + for (size_t i = 0; i < m_buffer.size(); ++i) + destination[i] = m_buffer[i]; + } - void writeTo(UChar* destination) - { - for (size_t i = 0; i < m_buffer.size(); ++i) - destination[i] = m_buffer[i]; - } + void writeTo(UChar* destination) { + for (size_t i = 0; i < m_buffer.size(); ++i) + destination[i] = m_buffer[i]; + } -private: - const Vector& m_buffer; + private: + const Vector& m_buffer; }; -template<> +template <> class StringTypeAdapter { -public: - StringTypeAdapter(const String& string) - : m_buffer(string) - { - } + public: + StringTypeAdapter(const String& string) : m_buffer(string) {} - unsigned length() { return m_buffer.length(); } + unsigned length() { return m_buffer.length(); } - bool is8Bit() { return m_buffer.isNull() || m_buffer.is8Bit(); } + bool is8Bit() { return m_buffer.isNull() || m_buffer.is8Bit(); } - void writeTo(LChar* destination) - { - unsigned length = m_buffer.length(); + void writeTo(LChar* destination) { + unsigned length = m_buffer.length(); - ASSERT(is8Bit()); - const LChar* data = m_buffer.characters8(); - for (unsigned i = 0; i < length; ++i) - destination[i] = data[i]; + ASSERT(is8Bit()); + const LChar* data = m_buffer.characters8(); + for (unsigned i = 0; i < length; ++i) + destination[i] = data[i]; - WTF_STRINGTYPEADAPTER_COPIED_WTF_STRING(); - } + WTF_STRINGTYPEADAPTER_COPIED_WTF_STRING(); + } + + void writeTo(UChar* destination) { + unsigned length = m_buffer.length(); - void writeTo(UChar* destination) - { - unsigned length = m_buffer.length(); - - if (is8Bit()) { - const LChar* data = m_buffer.characters8(); - for (unsigned i = 0; i < length; ++i) - destination[i] = data[i]; - } else { - const UChar* data = m_buffer.characters16(); - for (unsigned i = 0; i < length; ++i) - destination[i] = data[i]; - } - - WTF_STRINGTYPEADAPTER_COPIED_WTF_STRING(); + if (is8Bit()) { + const LChar* data = m_buffer.characters8(); + for (unsigned i = 0; i < length; ++i) + destination[i] = data[i]; + } else { + const UChar* data = m_buffer.characters16(); + for (unsigned i = 0; i < length; ++i) + destination[i] = data[i]; } -private: - const String& m_buffer; + WTF_STRINGTYPEADAPTER_COPIED_WTF_STRING(); + } + + private: + const String& m_buffer; }; -template<> +template <> class StringTypeAdapter { -public: - StringTypeAdapter(const AtomicString& string) - : m_adapter(string.string()) - { - } + public: + StringTypeAdapter(const AtomicString& string) + : m_adapter(string.string()) {} - unsigned length() { return m_adapter.length(); } + unsigned length() { return m_adapter.length(); } - bool is8Bit() { return m_adapter.is8Bit(); } + bool is8Bit() { return m_adapter.is8Bit(); } - void writeTo(LChar* destination) { m_adapter.writeTo(destination); } - void writeTo(UChar* destination) { m_adapter.writeTo(destination); } + void writeTo(LChar* destination) { m_adapter.writeTo(destination); } + void writeTo(UChar* destination) { m_adapter.writeTo(destination); } -private: - StringTypeAdapter m_adapter; + private: + StringTypeAdapter m_adapter; }; -inline void sumWithOverflow(unsigned& total, unsigned addend, bool& overflow) -{ - unsigned oldTotal = total; - total = oldTotal + addend; - if (total < oldTotal) - overflow = true; +inline void sumWithOverflow(unsigned& total, unsigned addend, bool& overflow) { + unsigned oldTotal = total; + total = oldTotal + addend; + if (total < oldTotal) + overflow = true; } -template -PassRefPtr makeString(StringType1 string1, StringType2 string2) -{ - StringTypeAdapter adapter1(string1); - StringTypeAdapter adapter2(string2); - - bool overflow = false; - unsigned length = adapter1.length(); - sumWithOverflow(length, adapter2.length(), overflow); - if (overflow) - return nullptr; - - if (adapter1.is8Bit() && adapter2.is8Bit()) { - LChar* buffer; - RefPtr resultImpl = StringImpl::createUninitialized(length, buffer); - if (!resultImpl) - return nullptr; - - LChar* result = buffer; - adapter1.writeTo(result); - result += adapter1.length(); - adapter2.writeTo(result); - - return resultImpl.release(); - } - - UChar* buffer; - RefPtr resultImpl = StringImpl::createUninitialized(length, buffer); +template +PassRefPtr makeString(StringType1 string1, StringType2 string2) { + StringTypeAdapter adapter1(string1); + StringTypeAdapter adapter2(string2); + + bool overflow = false; + unsigned length = adapter1.length(); + sumWithOverflow(length, adapter2.length(), overflow); + if (overflow) + return nullptr; + + if (adapter1.is8Bit() && adapter2.is8Bit()) { + LChar* buffer; + RefPtr resultImpl = + StringImpl::createUninitialized(length, buffer); if (!resultImpl) - return nullptr; + return nullptr; - UChar* result = buffer; + LChar* result = buffer; adapter1.writeTo(result); result += adapter1.length(); adapter2.writeTo(result); return resultImpl.release(); + } + + UChar* buffer; + RefPtr resultImpl = + StringImpl::createUninitialized(length, buffer); + if (!resultImpl) + return nullptr; + + UChar* result = buffer; + adapter1.writeTo(result); + result += adapter1.length(); + adapter2.writeTo(result); + + return resultImpl.release(); } -} // namespace WTF +} // namespace WTF #include "flutter/sky/engine/wtf/text/StringOperators.h" #endif // SKY_ENGINE_WTF_TEXT_STRINGCONCATENATE_H_ diff --git a/sky/engine/wtf/text/StringHash.h b/sky/engine/wtf/text/StringHash.h index 998a1cf10078c..3b02cb84ccaa9 100644 --- a/sky/engine/wtf/text/StringHash.h +++ b/sky/engine/wtf/text/StringHash.h @@ -28,127 +28,110 @@ namespace WTF { - inline bool HashTraits::isEmptyValue(const String& value) - { - return value.isNull(); - } - - // The hash() functions on StringHash and CaseFoldingHash do not support - // null strings. get(), contains(), and add() on HashMap - // cause a null-pointer dereference when passed null strings. - - // FIXME: We should really figure out a way to put the computeHash function that's - // currently a member function of StringImpl into this file so we can be a little - // closer to having all the nearly-identical hash functions in one place. - - struct StringHash { - static unsigned hash(StringImpl* key) { return key->hash(); } - static inline bool equal(const StringImpl* a, const StringImpl* b) - { - return equalNonNull(a, b); - } - - static unsigned hash(const RefPtr& key) { return key->hash(); } - static bool equal(const RefPtr& a, const RefPtr& b) - { - return equal(a.get(), b.get()); - } - - static unsigned hash(const String& key) { return key.impl()->hash(); } - static bool equal(const String& a, const String& b) - { - return equal(a.impl(), b.impl()); - } - - static const bool safeToCompareToEmptyOrDeleted = false; - }; - - class CaseFoldingHash { - public: - template static inline UChar foldCase(T ch) - { - return WTF::Unicode::foldCase(ch); - } - - static unsigned hash(const UChar* data, unsigned length) - { - return StringHasher::computeHashAndMaskTop8Bits >(data, length); - } - - static unsigned hash(StringImpl* str) - { - if (str->is8Bit()) - return hash(str->characters8(), str->length()); - return hash(str->characters16(), str->length()); - } - - static unsigned hash(const LChar* data, unsigned length) - { - return StringHasher::computeHashAndMaskTop8Bits >(data, length); - } - - static inline unsigned hash(const char* data, unsigned length) - { - return CaseFoldingHash::hash(reinterpret_cast(data), length); - } - - static inline bool equal(const StringImpl* a, const StringImpl* b) - { - return equalIgnoringCaseNonNull(a, b); - } - - static unsigned hash(const RefPtr& key) - { - return hash(key.get()); - } - - static bool equal(const RefPtr& a, const RefPtr& b) - { - return equal(a.get(), b.get()); - } - - static unsigned hash(const String& key) - { - return hash(key.impl()); - } - static unsigned hash(const AtomicString& key) - { - return hash(key.impl()); - } - static bool equal(const String& a, const String& b) - { - return equal(a.impl(), b.impl()); - } - static bool equal(const AtomicString& a, const AtomicString& b) - { - return (a == b) || equal(a.impl(), b.impl()); - } - - static const bool safeToCompareToEmptyOrDeleted = false; - }; - - // This hash can be used in cases where the key is a hash of a string, but we don't - // want to store the string. It's not really specific to string hashing, but all our - // current uses of it are for strings. - struct AlreadyHashed : IntHash { - static unsigned hash(unsigned key) { return key; } - - // To use a hash value as a key for a hash table, we need to eliminate the - // "deleted" value, which is negative one. That could be done by changing - // the string hash function to never generate negative one, but this works - // and is still relatively efficient. - static unsigned avoidDeletedValue(unsigned hash) - { - ASSERT(hash); - unsigned newHash = hash | (!(hash + 1) << 31); - ASSERT(newHash); - ASSERT(newHash != 0xFFFFFFFF); - return newHash; - } - }; - +inline bool HashTraits::isEmptyValue(const String& value) { + return value.isNull(); } +// The hash() functions on StringHash and CaseFoldingHash do not support +// null strings. get(), contains(), and add() on HashMap +// cause a null-pointer dereference when passed null strings. + +// FIXME: We should really figure out a way to put the computeHash function +// that's currently a member function of StringImpl into this file so we can be +// a little closer to having all the nearly-identical hash functions in one +// place. + +struct StringHash { + static unsigned hash(StringImpl* key) { return key->hash(); } + static inline bool equal(const StringImpl* a, const StringImpl* b) { + return equalNonNull(a, b); + } + + static unsigned hash(const RefPtr& key) { return key->hash(); } + static bool equal(const RefPtr& a, const RefPtr& b) { + return equal(a.get(), b.get()); + } + + static unsigned hash(const String& key) { return key.impl()->hash(); } + static bool equal(const String& a, const String& b) { + return equal(a.impl(), b.impl()); + } + + static const bool safeToCompareToEmptyOrDeleted = false; +}; + +class CaseFoldingHash { + public: + template + static inline UChar foldCase(T ch) { + return WTF::Unicode::foldCase(ch); + } + + static unsigned hash(const UChar* data, unsigned length) { + return StringHasher::computeHashAndMaskTop8Bits>( + data, length); + } + + static unsigned hash(StringImpl* str) { + if (str->is8Bit()) + return hash(str->characters8(), str->length()); + return hash(str->characters16(), str->length()); + } + + static unsigned hash(const LChar* data, unsigned length) { + return StringHasher::computeHashAndMaskTop8Bits>( + data, length); + } + + static inline unsigned hash(const char* data, unsigned length) { + return CaseFoldingHash::hash(reinterpret_cast(data), length); + } + + static inline bool equal(const StringImpl* a, const StringImpl* b) { + return equalIgnoringCaseNonNull(a, b); + } + + static unsigned hash(const RefPtr& key) { + return hash(key.get()); + } + + static bool equal(const RefPtr& a, const RefPtr& b) { + return equal(a.get(), b.get()); + } + + static unsigned hash(const String& key) { return hash(key.impl()); } + static unsigned hash(const AtomicString& key) { return hash(key.impl()); } + static bool equal(const String& a, const String& b) { + return equal(a.impl(), b.impl()); + } + static bool equal(const AtomicString& a, const AtomicString& b) { + return (a == b) || equal(a.impl(), b.impl()); + } + + static const bool safeToCompareToEmptyOrDeleted = false; +}; + +// This hash can be used in cases where the key is a hash of a string, but we +// don't want to store the string. It's not really specific to string hashing, +// but all our current uses of it are for strings. +struct AlreadyHashed : IntHash { + static unsigned hash(unsigned key) { return key; } + + // To use a hash value as a key for a hash table, we need to eliminate the + // "deleted" value, which is negative one. That could be done by changing + // the string hash function to never generate negative one, but this works + // and is still relatively efficient. + static unsigned avoidDeletedValue(unsigned hash) { + ASSERT(hash); + unsigned newHash = hash | (!(hash + 1) << 31); + ASSERT(newHash); + ASSERT(newHash != 0xFFFFFFFF); + return newHash; + } +}; + +} // namespace WTF + using WTF::AlreadyHashed; using WTF::CaseFoldingHash; using WTF::StringHash; diff --git a/sky/engine/wtf/text/StringImpl.cpp b/sky/engine/wtf/text/StringImpl.cpp index 006cdb87b7522..83c90dc6f716a 100644 --- a/sky/engine/wtf/text/StringImpl.cpp +++ b/sky/engine/wtf/text/StringImpl.cpp @@ -2,7 +2,8 @@ * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2001 Dirk Mueller ( mueller@kde.org ) - * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2013 Apple Inc. All + * rights reserved. * Copyright (C) 2006 Andrew Wellington (proton@wiretapped.net) * * This library is free software; you can redistribute it and/or @@ -54,2053 +55,2164 @@ namespace WTF { using namespace Unicode; -COMPILE_ASSERT(sizeof(StringImpl) == 3 * sizeof(int), StringImpl_should_stay_small); +COMPILE_ASSERT(sizeof(StringImpl) == 3 * sizeof(int), + StringImpl_should_stay_small); #ifdef STRING_STATS -static Mutex& statsMutex() -{ - DEFINE_STATIC_LOCAL(Mutex, mutex, ()); - return mutex; +static Mutex& statsMutex() { + DEFINE_STATIC_LOCAL(Mutex, mutex, ()); + return mutex; } -static HashSet& liveStrings() -{ - // Notice that we can't use HashSet because then HashSet would dedup identical strings. - DEFINE_STATIC_LOCAL(HashSet, strings, ()); - return strings; +static HashSet& liveStrings() { + // Notice that we can't use HashSet because then HashSet would + // dedup identical strings. + DEFINE_STATIC_LOCAL(HashSet, strings, ()); + return strings; } -void addStringForStats(StringImpl* string) -{ - MutexLocker locker(statsMutex()); - liveStrings().add(string); +void addStringForStats(StringImpl* string) { + MutexLocker locker(statsMutex()); + liveStrings().add(string); } -void removeStringForStats(StringImpl* string) -{ - MutexLocker locker(statsMutex()); - liveStrings().remove(string); +void removeStringForStats(StringImpl* string) { + MutexLocker locker(statsMutex()); + liveStrings().remove(string); } -static void fillWithSnippet(const StringImpl* string, Vector& snippet) -{ - const unsigned kMaxSnippetLength = 64; - snippet.clear(); +static void fillWithSnippet(const StringImpl* string, Vector& snippet) { + const unsigned kMaxSnippetLength = 64; + snippet.clear(); - size_t expectedLength = std::min(string->length(), kMaxSnippetLength); - if (expectedLength == kMaxSnippetLength) - expectedLength += 3; // For the "...". - ++expectedLength; // For the terminating '\0'. - snippet.reserveCapacity(expectedLength); + size_t expectedLength = std::min(string->length(), kMaxSnippetLength); + if (expectedLength == kMaxSnippetLength) + expectedLength += 3; // For the "...". + ++expectedLength; // For the terminating '\0'. + snippet.reserveCapacity(expectedLength); - size_t i; - for (i = 0; i < string->length() && i < kMaxSnippetLength; ++i) { - UChar c = (*string)[i]; - if (isASCIIPrintable(c)) - snippet.append(c); - else - snippet.append('?'); - } - if (i < string->length()) { - snippet.append('.'); - snippet.append('.'); - snippet.append('.'); - } - snippet.append('\0'); -} - -static bool isUnnecessarilyWide(const StringImpl* string) -{ - if (string->is8Bit()) - return false; - UChar c = 0; - for (unsigned i = 0; i < string->length(); ++i) - c |= (*string)[i] >> 8; - return !c; + size_t i; + for (i = 0; i < string->length() && i < kMaxSnippetLength; ++i) { + UChar c = (*string)[i]; + if (isASCIIPrintable(c)) + snippet.append(c); + else + snippet.append('?'); + } + if (i < string->length()) { + snippet.append('.'); + snippet.append('.'); + snippet.append('.'); + } + snippet.append('\0'); +} + +static bool isUnnecessarilyWide(const StringImpl* string) { + if (string->is8Bit()) + return false; + UChar c = 0; + for (unsigned i = 0; i < string->length(); ++i) + c |= (*string)[i] >> 8; + return !c; } class PerStringStats : public RefCounted { -public: - static PassRefPtr create() - { - return adoptRef(new PerStringStats); - } - - void add(const StringImpl* string) - { - ++m_numberOfCopies; - if (!m_length) { - m_length = string->length(); - fillWithSnippet(string, m_snippet); - } - if (string->isAtomic()) - ++m_numberOfAtomicCopies; - if (isUnnecessarilyWide(string)) - m_unnecessarilyWide = true; - } - - size_t totalCharacters() const - { - return m_numberOfCopies * m_length; - } - - void print() - { - const char* status = "ok"; - if (m_unnecessarilyWide) - status = "16"; - dataLogF("%8u copies (%s) of length %8u %s\n", m_numberOfCopies, status, m_length, m_snippet.data()); - } - - bool m_unnecessarilyWide; - unsigned m_numberOfCopies; - unsigned m_length; - unsigned m_numberOfAtomicCopies; - Vector m_snippet; - -private: - PerStringStats() - : m_unnecessarilyWide(false) - , m_numberOfCopies(0) - , m_length(0) - , m_numberOfAtomicCopies(0) - { - } + public: + static PassRefPtr create() { + return adoptRef(new PerStringStats); + } + + void add(const StringImpl* string) { + ++m_numberOfCopies; + if (!m_length) { + m_length = string->length(); + fillWithSnippet(string, m_snippet); + } + if (string->isAtomic()) + ++m_numberOfAtomicCopies; + if (isUnnecessarilyWide(string)) + m_unnecessarilyWide = true; + } + + size_t totalCharacters() const { return m_numberOfCopies * m_length; } + + void print() { + const char* status = "ok"; + if (m_unnecessarilyWide) + status = "16"; + dataLogF("%8u copies (%s) of length %8u %s\n", m_numberOfCopies, status, + m_length, m_snippet.data()); + } + + bool m_unnecessarilyWide; + unsigned m_numberOfCopies; + unsigned m_length; + unsigned m_numberOfAtomicCopies; + Vector m_snippet; + + private: + PerStringStats() + : m_unnecessarilyWide(false), + m_numberOfCopies(0), + m_length(0), + m_numberOfAtomicCopies(0) {} }; -bool operator<(const RefPtr& a, const RefPtr& b) -{ - if (a->m_unnecessarilyWide != b->m_unnecessarilyWide) - return !a->m_unnecessarilyWide && b->m_unnecessarilyWide; - if (a->totalCharacters() != b->totalCharacters()) - return a->totalCharacters() < b->totalCharacters(); - if (a->m_numberOfCopies != b->m_numberOfCopies) - return a->m_numberOfCopies < b->m_numberOfCopies; - if (a->m_length != b->m_length) - return a->m_length < b->m_length; - return a->m_numberOfAtomicCopies < b->m_numberOfAtomicCopies; -} - -static void printLiveStringStats() -{ - MutexLocker locker(statsMutex()); - HashSet& strings = liveStrings(); - - HashMap > stats; - for (HashSet::iterator iter = strings.begin(); iter != strings.end(); ++iter) { - StringImpl* string = static_cast(*iter); - HashMap >::iterator entry = stats.find(string); - RefPtr value = entry == stats.end() ? RefPtr(PerStringStats::create()) : entry->value; - value->add(string); - stats.set(string, value.release()); - } - - Vector > all; - for (HashMap >::iterator iter = stats.begin(); iter != stats.end(); ++iter) - all.append(iter->value); - - std::sort(all.begin(), all.end()); - std::reverse(all.begin(), all.end()); - for (size_t i = 0; i < 20 && i < all.size(); ++i) - all[i]->print(); +bool operator<(const RefPtr& a, + const RefPtr& b) { + if (a->m_unnecessarilyWide != b->m_unnecessarilyWide) + return !a->m_unnecessarilyWide && b->m_unnecessarilyWide; + if (a->totalCharacters() != b->totalCharacters()) + return a->totalCharacters() < b->totalCharacters(); + if (a->m_numberOfCopies != b->m_numberOfCopies) + return a->m_numberOfCopies < b->m_numberOfCopies; + if (a->m_length != b->m_length) + return a->m_length < b->m_length; + return a->m_numberOfAtomicCopies < b->m_numberOfAtomicCopies; +} + +static void printLiveStringStats() { + MutexLocker locker(statsMutex()); + HashSet& strings = liveStrings(); + + HashMap> stats; + for (HashSet::iterator iter = strings.begin(); iter != strings.end(); + ++iter) { + StringImpl* string = static_cast(*iter); + HashMap>::iterator entry = + stats.find(string); + RefPtr value = + entry == stats.end() ? RefPtr(PerStringStats::create()) + : entry->value; + value->add(string); + stats.set(string, value.release()); + } + + Vector> all; + for (HashMap>::iterator iter = + stats.begin(); + iter != stats.end(); ++iter) + all.append(iter->value); + + std::sort(all.begin(), all.end()); + std::reverse(all.begin(), all.end()); + for (size_t i = 0; i < 20 && i < all.size(); ++i) + all[i]->print(); } StringStats StringImpl::m_stringStats; -unsigned StringStats::s_stringRemovesTillPrintStats = StringStats::s_printStringStatsFrequency; - -void StringStats::removeString(StringImpl* string) -{ - unsigned length = string->length(); - --m_totalNumberStrings; - - if (string->is8Bit()) { - --m_number8BitStrings; - m_total8BitData -= length; - } else { - --m_number16BitStrings; - m_total16BitData -= length; - } - - if (!--s_stringRemovesTillPrintStats) { - s_stringRemovesTillPrintStats = s_printStringStatsFrequency; - printStats(); - } -} - -void StringStats::printStats() -{ - dataLogF("String stats\n"); - - unsigned long long totalNumberCharacters = m_total8BitData + m_total16BitData; - double percent8Bit = m_totalNumberStrings ? ((double)m_number8BitStrings * 100) / (double)m_totalNumberStrings : 0.0; - double average8bitLength = m_number8BitStrings ? (double)m_total8BitData / (double)m_number8BitStrings : 0.0; - dataLogF("%8u (%5.2f%%) 8 bit %12llu chars %12llu bytes avg length %6.1f\n", m_number8BitStrings, percent8Bit, m_total8BitData, m_total8BitData, average8bitLength); - - double percent16Bit = m_totalNumberStrings ? ((double)m_number16BitStrings * 100) / (double)m_totalNumberStrings : 0.0; - double average16bitLength = m_number16BitStrings ? (double)m_total16BitData / (double)m_number16BitStrings : 0.0; - dataLogF("%8u (%5.2f%%) 16 bit %12llu chars %12llu bytes avg length %6.1f\n", m_number16BitStrings, percent16Bit, m_total16BitData, m_total16BitData * 2, average16bitLength); - - double averageLength = m_totalNumberStrings ? (double)totalNumberCharacters / (double)m_totalNumberStrings : 0.0; - unsigned long long totalDataBytes = m_total8BitData + m_total16BitData * 2; - dataLogF("%8u Total %12llu chars %12llu bytes avg length %6.1f\n", m_totalNumberStrings, totalNumberCharacters, totalDataBytes, averageLength); - unsigned long long totalSavedBytes = m_total8BitData; - double percentSavings = totalSavedBytes ? ((double)totalSavedBytes * 100) / (double)(totalDataBytes + totalSavedBytes) : 0.0; - dataLogF(" Total savings %12llu bytes (%5.2f%%)\n", totalSavedBytes, percentSavings); - - unsigned totalOverhead = m_totalNumberStrings * sizeof(StringImpl); - double overheadPercent = (double)totalOverhead / (double)totalDataBytes * 100; - dataLogF(" StringImpl overheader: %8u (%5.2f%%)\n", totalOverhead, overheadPercent); - - printLiveStringStats(); +unsigned StringStats::s_stringRemovesTillPrintStats = + StringStats::s_printStringStatsFrequency; + +void StringStats::removeString(StringImpl* string) { + unsigned length = string->length(); + --m_totalNumberStrings; + + if (string->is8Bit()) { + --m_number8BitStrings; + m_total8BitData -= length; + } else { + --m_number16BitStrings; + m_total16BitData -= length; + } + + if (!--s_stringRemovesTillPrintStats) { + s_stringRemovesTillPrintStats = s_printStringStatsFrequency; + printStats(); + } +} + +void StringStats::printStats() { + dataLogF("String stats\n"); + + unsigned long long totalNumberCharacters = m_total8BitData + m_total16BitData; + double percent8Bit = + m_totalNumberStrings + ? ((double)m_number8BitStrings * 100) / (double)m_totalNumberStrings + : 0.0; + double average8bitLength = + m_number8BitStrings + ? (double)m_total8BitData / (double)m_number8BitStrings + : 0.0; + dataLogF( + "%8u (%5.2f%%) 8 bit %12llu chars %12llu bytes avg length " + "%6.1f\n", + m_number8BitStrings, percent8Bit, m_total8BitData, m_total8BitData, + average8bitLength); + + double percent16Bit = + m_totalNumberStrings + ? ((double)m_number16BitStrings * 100) / (double)m_totalNumberStrings + : 0.0; + double average16bitLength = + m_number16BitStrings + ? (double)m_total16BitData / (double)m_number16BitStrings + : 0.0; + dataLogF( + "%8u (%5.2f%%) 16 bit %12llu chars %12llu bytes avg length " + "%6.1f\n", + m_number16BitStrings, percent16Bit, m_total16BitData, + m_total16BitData * 2, average16bitLength); + + double averageLength = m_totalNumberStrings ? (double)totalNumberCharacters / + (double)m_totalNumberStrings + : 0.0; + unsigned long long totalDataBytes = m_total8BitData + m_total16BitData * 2; + dataLogF( + "%8u Total %12llu chars %12llu bytes avg length " + "%6.1f\n", + m_totalNumberStrings, totalNumberCharacters, totalDataBytes, + averageLength); + unsigned long long totalSavedBytes = m_total8BitData; + double percentSavings = totalSavedBytes + ? ((double)totalSavedBytes * 100) / + (double)(totalDataBytes + totalSavedBytes) + : 0.0; + dataLogF(" Total savings %12llu bytes (%5.2f%%)\n", totalSavedBytes, + percentSavings); + + unsigned totalOverhead = m_totalNumberStrings * sizeof(StringImpl); + double overheadPercent = (double)totalOverhead / (double)totalDataBytes * 100; + dataLogF(" StringImpl overheader: %8u (%5.2f%%)\n", totalOverhead, + overheadPercent); + + printLiveStringStats(); } #endif -void* StringImpl::operator new(size_t size) -{ - ASSERT(size == sizeof(StringImpl)); - return partitionAllocGeneric(Partitions::getBufferPartition(), size); +void* StringImpl::operator new(size_t size) { + ASSERT(size == sizeof(StringImpl)); + return partitionAllocGeneric(Partitions::getBufferPartition(), size); } -void StringImpl::operator delete(void* ptr) -{ - partitionFreeGeneric(Partitions::getBufferPartition(), ptr); +void StringImpl::operator delete(void* ptr) { + partitionFreeGeneric(Partitions::getBufferPartition(), ptr); } -inline StringImpl::~StringImpl() -{ - ASSERT(!isStatic()); +inline StringImpl::~StringImpl() { + ASSERT(!isStatic()); - STRING_STATS_REMOVE_STRING(this); + STRING_STATS_REMOVE_STRING(this); - if (isAtomic()) - AtomicString::remove(this); + if (isAtomic()) + AtomicString::remove(this); } -void StringImpl::destroyIfNotStatic() -{ - if (!isStatic()) - delete this; +void StringImpl::destroyIfNotStatic() { + if (!isStatic()) + delete this; } -PassRefPtr StringImpl::createUninitialized(unsigned length, LChar*& data) -{ - if (!length) { - data = 0; - return empty(); - } +PassRefPtr StringImpl::createUninitialized(unsigned length, + LChar*& data) { + if (!length) { + data = 0; + return empty(); + } - // Allocate a single buffer large enough to contain the StringImpl - // struct as well as the data which it contains. This removes one - // heap allocation from this call. - StringImpl* string = static_cast(partitionAllocGeneric(Partitions::getBufferPartition(), allocationSize(length))); + // Allocate a single buffer large enough to contain the StringImpl + // struct as well as the data which it contains. This removes one + // heap allocation from this call. + StringImpl* string = static_cast(partitionAllocGeneric( + Partitions::getBufferPartition(), allocationSize(length))); - data = reinterpret_cast(string + 1); - return adoptRef(new (string) StringImpl(length, Force8BitConstructor)); + data = reinterpret_cast(string + 1); + return adoptRef(new (string) StringImpl(length, Force8BitConstructor)); } -PassRefPtr StringImpl::createUninitialized(unsigned length, UChar*& data) -{ - if (!length) { - data = 0; - return empty(); - } +PassRefPtr StringImpl::createUninitialized(unsigned length, + UChar*& data) { + if (!length) { + data = 0; + return empty(); + } - // Allocate a single buffer large enough to contain the StringImpl - // struct as well as the data which it contains. This removes one - // heap allocation from this call. - StringImpl* string = static_cast(partitionAllocGeneric(Partitions::getBufferPartition(), allocationSize(length))); + // Allocate a single buffer large enough to contain the StringImpl + // struct as well as the data which it contains. This removes one + // heap allocation from this call. + StringImpl* string = static_cast(partitionAllocGeneric( + Partitions::getBufferPartition(), allocationSize(length))); - data = reinterpret_cast(string + 1); - return adoptRef(new (string) StringImpl(length)); + data = reinterpret_cast(string + 1); + return adoptRef(new (string) StringImpl(length)); } -PassRefPtr StringImpl::reallocate(PassRefPtr originalString, unsigned length) -{ - ASSERT(originalString->hasOneRef()); +PassRefPtr StringImpl::reallocate( + PassRefPtr originalString, + unsigned length) { + ASSERT(originalString->hasOneRef()); - if (!length) - return empty(); + if (!length) + return empty(); - bool is8Bit = originalString->is8Bit(); - // Same as createUninitialized() except here we use realloc. - size_t size = is8Bit ? allocationSize(length) : allocationSize(length); - originalString->~StringImpl(); - StringImpl* string = static_cast(partitionReallocGeneric(Partitions::getBufferPartition(), originalString.leakRef(), size)); - if (is8Bit) - return adoptRef(new (string) StringImpl(length, Force8BitConstructor)); - return adoptRef(new (string) StringImpl(length)); + bool is8Bit = originalString->is8Bit(); + // Same as createUninitialized() except here we use realloc. + size_t size = + is8Bit ? allocationSize(length) : allocationSize(length); + originalString->~StringImpl(); + StringImpl* string = static_cast(partitionReallocGeneric( + Partitions::getBufferPartition(), originalString.leakRef(), size)); + if (is8Bit) + return adoptRef(new (string) StringImpl(length, Force8BitConstructor)); + return adoptRef(new (string) StringImpl(length)); } -static StaticStringsTable& staticStrings() -{ - DEFINE_STATIC_LOCAL(StaticStringsTable, staticStrings, ()); - return staticStrings; +static StaticStringsTable& staticStrings() { + DEFINE_STATIC_LOCAL(StaticStringsTable, staticStrings, ()); + return staticStrings; } #if ENABLE(ASSERT) static bool s_allowCreationOfStaticStrings = true; #endif -const StaticStringsTable& StringImpl::allStaticStrings() -{ - return staticStrings(); +const StaticStringsTable& StringImpl::allStaticStrings() { + return staticStrings(); } -void StringImpl::freezeStaticStrings() -{ - ASSERT(isMainThread()); +void StringImpl::freezeStaticStrings() { + ASSERT(isMainThread()); #if ENABLE(ASSERT) - s_allowCreationOfStaticStrings = false; + s_allowCreationOfStaticStrings = false; #endif } unsigned StringImpl::m_highestStaticStringLength = 0; -StringImpl* StringImpl::createStatic(const char* string, unsigned length, unsigned hash) -{ - ASSERT(s_allowCreationOfStaticStrings); - ASSERT(string); - ASSERT(length); - - StaticStringsTable::const_iterator it = staticStrings().find(hash); - if (it != staticStrings().end()) { - ASSERT(!memcmp(string, it->value + 1, length * sizeof(LChar))); - return it->value; - } - - // Allocate a single buffer large enough to contain the StringImpl - // struct as well as the data which it contains. This removes one - // heap allocation from this call. - RELEASE_ASSERT(length <= ((std::numeric_limits::max() - sizeof(StringImpl)) / sizeof(LChar))); - size_t size = sizeof(StringImpl) + length * sizeof(LChar); - - WTF_ANNOTATE_SCOPED_MEMORY_LEAK; - StringImpl* impl = static_cast(partitionAllocGeneric(Partitions::getBufferPartition(), size)); - - LChar* data = reinterpret_cast(impl + 1); - impl = new (impl) StringImpl(length, hash, StaticString); - memcpy(data, string, length * sizeof(LChar)); +StringImpl* StringImpl::createStatic(const char* string, + unsigned length, + unsigned hash) { + ASSERT(s_allowCreationOfStaticStrings); + ASSERT(string); + ASSERT(length); + + StaticStringsTable::const_iterator it = staticStrings().find(hash); + if (it != staticStrings().end()) { + ASSERT(!memcmp(string, it->value + 1, length * sizeof(LChar))); + return it->value; + } + + // Allocate a single buffer large enough to contain the StringImpl + // struct as well as the data which it contains. This removes one + // heap allocation from this call. + RELEASE_ASSERT(length <= + ((std::numeric_limits::max() - sizeof(StringImpl)) / + sizeof(LChar))); + size_t size = sizeof(StringImpl) + length * sizeof(LChar); + + WTF_ANNOTATE_SCOPED_MEMORY_LEAK; + StringImpl* impl = static_cast( + partitionAllocGeneric(Partitions::getBufferPartition(), size)); + + LChar* data = reinterpret_cast(impl + 1); + impl = new (impl) StringImpl(length, hash, StaticString); + memcpy(data, string, length * sizeof(LChar)); #if ENABLE(ASSERT) - impl->assertHashIsCorrect(); + impl->assertHashIsCorrect(); #endif - ASSERT(isMainThread()); - m_highestStaticStringLength = std::max(m_highestStaticStringLength, length); - staticStrings().add(hash, impl); - WTF_ANNOTATE_BENIGN_RACE(impl, - "Benign race on the reference counter of a static string created by StringImpl::createStatic"); + ASSERT(isMainThread()); + m_highestStaticStringLength = std::max(m_highestStaticStringLength, length); + staticStrings().add(hash, impl); + WTF_ANNOTATE_BENIGN_RACE(impl, + "Benign race on the reference counter of a static " + "string created by StringImpl::createStatic"); - return impl; + return impl; } -PassRefPtr StringImpl::create(const UChar* characters, unsigned length) -{ - if (!characters || !length) - return empty(); +PassRefPtr StringImpl::create(const UChar* characters, + unsigned length) { + if (!characters || !length) + return empty(); - UChar* data; - RefPtr string = createUninitialized(length, data); - memcpy(data, characters, length * sizeof(UChar)); - return string.release(); + UChar* data; + RefPtr string = createUninitialized(length, data); + memcpy(data, characters, length * sizeof(UChar)); + return string.release(); } -PassRefPtr StringImpl::create(const LChar* characters, unsigned length) -{ - if (!characters || !length) - return empty(); +PassRefPtr StringImpl::create(const LChar* characters, + unsigned length) { + if (!characters || !length) + return empty(); - LChar* data; - RefPtr string = createUninitialized(length, data); - memcpy(data, characters, length * sizeof(LChar)); - return string.release(); + LChar* data; + RefPtr string = createUninitialized(length, data); + memcpy(data, characters, length * sizeof(LChar)); + return string.release(); } -PassRefPtr StringImpl::create8BitIfPossible(const UChar* characters, unsigned length) -{ - if (!characters || !length) - return empty(); +PassRefPtr StringImpl::create8BitIfPossible(const UChar* characters, + unsigned length) { + if (!characters || !length) + return empty(); - LChar* data; - RefPtr string = createUninitialized(length, data); + LChar* data; + RefPtr string = createUninitialized(length, data); - for (size_t i = 0; i < length; ++i) { - if (characters[i] & 0xff00) - return create(characters, length); - data[i] = static_cast(characters[i]); - } + for (size_t i = 0; i < length; ++i) { + if (characters[i] & 0xff00) + return create(characters, length); + data[i] = static_cast(characters[i]); + } - return string.release(); + return string.release(); } -PassRefPtr StringImpl::create(const LChar* string) -{ - if (!string) - return empty(); - size_t length = strlen(reinterpret_cast(string)); - RELEASE_ASSERT(length <= numeric_limits::max()); - return create(string, length); +PassRefPtr StringImpl::create(const LChar* string) { + if (!string) + return empty(); + size_t length = strlen(reinterpret_cast(string)); + RELEASE_ASSERT(length <= numeric_limits::max()); + return create(string, length); } -bool StringImpl::containsOnlyWhitespace() -{ - // FIXME: The definition of whitespace here includes a number of characters - // that are not whitespace from the point of view of RenderText; I wonder if - // that's a problem in practice. - if (is8Bit()) { - for (unsigned i = 0; i < m_length; ++i) { - UChar c = characters8()[i]; - if (!isASCIISpace(c)) - return false; - } - - return true; - } - +bool StringImpl::containsOnlyWhitespace() { + // FIXME: The definition of whitespace here includes a number of characters + // that are not whitespace from the point of view of RenderText; I wonder if + // that's a problem in practice. + if (is8Bit()) { for (unsigned i = 0; i < m_length; ++i) { - UChar c = characters16()[i]; - if (!isASCIISpace(c)) - return false; + UChar c = characters8()[i]; + if (!isASCIISpace(c)) + return false; } - return true; -} -PassRefPtr StringImpl::substring(unsigned start, unsigned length) -{ - if (start >= m_length) - return empty(); - unsigned maxLength = m_length - start; - if (length >= maxLength) { - if (!start) - return this; - length = maxLength; + return true; + } + + for (unsigned i = 0; i < m_length; ++i) { + UChar c = characters16()[i]; + if (!isASCIISpace(c)) + return false; + } + return true; +} + +PassRefPtr StringImpl::substring(unsigned start, unsigned length) { + if (start >= m_length) + return empty(); + unsigned maxLength = m_length - start; + if (length >= maxLength) { + if (!start) + return this; + length = maxLength; + } + if (is8Bit()) + return create(characters8() + start, length); + + return create(characters16() + start, length); +} + +UChar32 StringImpl::characterStartingAt(unsigned i) { + if (is8Bit()) + return characters8()[i]; + if (U16_IS_SINGLE(characters16()[i])) + return characters16()[i]; + if (i + 1 < m_length && U16_IS_LEAD(characters16()[i]) && + U16_IS_TRAIL(characters16()[i + 1])) + return U16_GET_SUPPLEMENTARY(characters16()[i], characters16()[i + 1]); + return 0; +} + +PassRefPtr StringImpl::lower() { + // Note: This is a hot function in the Dromaeo benchmark, specifically the + // no-op code path up through the first 'return' statement. + + // First scan the string for uppercase and non-ASCII characters: + bool noUpper = true; + UChar ored = 0; + if (is8Bit()) { + const LChar* end = characters8() + m_length; + for (const LChar* chp = characters8(); chp != end; ++chp) { + if (UNLIKELY(isASCIIUpper(*chp))) + noUpper = false; + ored |= *chp; } - if (is8Bit()) - return create(characters8() + start, length); + // Nothing to do if the string is all ASCII with no uppercase. + if (noUpper && !(ored & ~0x7F)) + return this; - return create(characters16() + start, length); -} + RELEASE_ASSERT(m_length <= + static_cast(numeric_limits::max())); + int32_t length = m_length; -UChar32 StringImpl::characterStartingAt(unsigned i) -{ - if (is8Bit()) - return characters8()[i]; - if (U16_IS_SINGLE(characters16()[i])) - return characters16()[i]; - if (i + 1 < m_length && U16_IS_LEAD(characters16()[i]) && U16_IS_TRAIL(characters16()[i + 1])) - return U16_GET_SUPPLEMENTARY(characters16()[i], characters16()[i + 1]); - return 0; -} + LChar* data8; + RefPtr newImpl = createUninitialized(length, data8); -PassRefPtr StringImpl::lower() -{ - // Note: This is a hot function in the Dromaeo benchmark, specifically the - // no-op code path up through the first 'return' statement. + if (!(ored & ~0x7F)) { + for (int32_t i = 0; i < length; ++i) + data8[i] = toASCIILower(characters8()[i]); - // First scan the string for uppercase and non-ASCII characters: - bool noUpper = true; - UChar ored = 0; - if (is8Bit()) { - const LChar* end = characters8() + m_length; - for (const LChar* chp = characters8(); chp != end; ++chp) { - if (UNLIKELY(isASCIIUpper(*chp))) - noUpper = false; - ored |= *chp; - } - // Nothing to do if the string is all ASCII with no uppercase. - if (noUpper && !(ored & ~0x7F)) - return this; - - RELEASE_ASSERT(m_length <= static_cast(numeric_limits::max())); - int32_t length = m_length; - - LChar* data8; - RefPtr newImpl = createUninitialized(length, data8); - - if (!(ored & ~0x7F)) { - for (int32_t i = 0; i < length; ++i) - data8[i] = toASCIILower(characters8()[i]); - - return newImpl.release(); - } - - // Do a slower implementation for cases that include non-ASCII Latin-1 characters. - for (int32_t i = 0; i < length; ++i) - data8[i] = static_cast(Unicode::toLower(characters8()[i])); - - return newImpl.release(); + return newImpl.release(); } - const UChar* end = characters16() + m_length; - for (const UChar* chp = characters16(); chp != end; ++chp) { - if (UNLIKELY(isASCIIUpper(*chp))) - noUpper = false; - ored |= *chp; - } - // Nothing to do if the string is all ASCII with no uppercase. - if (noUpper && !(ored & ~0x7F)) - return this; + // Do a slower implementation for cases that include non-ASCII Latin-1 + // characters. + for (int32_t i = 0; i < length; ++i) + data8[i] = static_cast(Unicode::toLower(characters8()[i])); - RELEASE_ASSERT(m_length <= static_cast(numeric_limits::max())); - int32_t length = m_length; + return newImpl.release(); + } + + const UChar* end = characters16() + m_length; + for (const UChar* chp = characters16(); chp != end; ++chp) { + if (UNLIKELY(isASCIIUpper(*chp))) + noUpper = false; + ored |= *chp; + } + // Nothing to do if the string is all ASCII with no uppercase. + if (noUpper && !(ored & ~0x7F)) + return this; - if (!(ored & ~0x7F)) { - UChar* data16; - RefPtr newImpl = createUninitialized(m_length, data16); - - for (int32_t i = 0; i < length; ++i) { - UChar c = characters16()[i]; - data16[i] = toASCIILower(c); - } - return newImpl.release(); - } + RELEASE_ASSERT(m_length <= + static_cast(numeric_limits::max())); + int32_t length = m_length; - // Do a slower implementation for cases that include non-ASCII characters. + if (!(ored & ~0x7F)) { UChar* data16; RefPtr newImpl = createUninitialized(m_length, data16); - bool error; - int32_t realLength = Unicode::toLower(data16, length, characters16(), m_length, &error); - if (!error && realLength == length) - return newImpl.release(); - - newImpl = createUninitialized(realLength, data16); - Unicode::toLower(data16, realLength, characters16(), m_length, &error); - if (error) - return this; + for (int32_t i = 0; i < length; ++i) { + UChar c = characters16()[i]; + data16[i] = toASCIILower(c); + } return newImpl.release(); -} + } -PassRefPtr StringImpl::upper() -{ - // This function could be optimized for no-op cases the way lower() is, - // but in empirical testing, few actual calls to upper() are no-ops, so - // it wouldn't be worth the extra time for pre-scanning. + // Do a slower implementation for cases that include non-ASCII characters. + UChar* data16; + RefPtr newImpl = createUninitialized(m_length, data16); - RELEASE_ASSERT(m_length <= static_cast(numeric_limits::max())); - int32_t length = m_length; + bool error; + int32_t realLength = + Unicode::toLower(data16, length, characters16(), m_length, &error); + if (!error && realLength == length) + return newImpl.release(); - if (is8Bit()) { - LChar* data8; - RefPtr newImpl = createUninitialized(m_length, data8); - - // Do a faster loop for the case where all the characters are ASCII. - LChar ored = 0; - for (int i = 0; i < length; ++i) { - LChar c = characters8()[i]; - ored |= c; - data8[i] = toASCIIUpper(c); - } - if (!(ored & ~0x7F)) - return newImpl.release(); - - // Do a slower implementation for cases that include non-ASCII Latin-1 characters. - int numberSharpSCharacters = 0; - - // There are two special cases. - // 1. latin-1 characters when converted to upper case are 16 bit characters. - // 2. Lower case sharp-S converts to "SS" (two characters) - for (int32_t i = 0; i < length; ++i) { - LChar c = characters8()[i]; - if (UNLIKELY(c == smallLetterSharpS)) - ++numberSharpSCharacters; - UChar upper = Unicode::toUpper(c); - if (UNLIKELY(upper > 0xff)) { - // Since this upper-cased character does not fit in an 8-bit string, we need to take the 16-bit path. - goto upconvert; - } - data8[i] = static_cast(upper); - } - - if (!numberSharpSCharacters) - return newImpl.release(); - - // We have numberSSCharacters sharp-s characters, but none of the other special characters. - newImpl = createUninitialized(m_length + numberSharpSCharacters, data8); - - LChar* dest = data8; - - for (int32_t i = 0; i < length; ++i) { - LChar c = characters8()[i]; - if (c == smallLetterSharpS) { - *dest++ = 'S'; - *dest++ = 'S'; - } else - *dest++ = static_cast(Unicode::toUpper(c)); - } - - return newImpl.release(); - } + newImpl = createUninitialized(realLength, data16); + Unicode::toLower(data16, realLength, characters16(), m_length, &error); + if (error) + return this; + return newImpl.release(); +} -upconvert: - RefPtr upconverted = upconvertedString(); - const UChar* source16 = upconverted->characters16(); +PassRefPtr StringImpl::upper() { + // This function could be optimized for no-op cases the way lower() is, + // but in empirical testing, few actual calls to upper() are no-ops, so + // it wouldn't be worth the extra time for pre-scanning. - UChar* data16; - RefPtr newImpl = createUninitialized(m_length, data16); + RELEASE_ASSERT(m_length <= + static_cast(numeric_limits::max())); + int32_t length = m_length; + + if (is8Bit()) { + LChar* data8; + RefPtr newImpl = createUninitialized(m_length, data8); // Do a faster loop for the case where all the characters are ASCII. - UChar ored = 0; + LChar ored = 0; for (int i = 0; i < length; ++i) { - UChar c = source16[i]; - ored |= c; - data16[i] = toASCIIUpper(c); + LChar c = characters8()[i]; + ored |= c; + data8[i] = toASCIIUpper(c); } if (!(ored & ~0x7F)) - return newImpl.release(); + return newImpl.release(); - // Do a slower implementation for cases that include non-ASCII characters. - bool error; - int32_t realLength = Unicode::toUpper(data16, length, source16, m_length, &error); - if (!error && realLength == length) - return newImpl; - newImpl = createUninitialized(realLength, data16); - Unicode::toUpper(data16, realLength, source16, m_length, &error); - if (error) - return this; - return newImpl.release(); -} + // Do a slower implementation for cases that include non-ASCII Latin-1 + // characters. + int numberSharpSCharacters = 0; -static bool inline localeIdMatchesLang(const AtomicString& localeId, const char* lang) -{ - if (equalIgnoringCase(localeId, lang)) - return true; - static char localeIdPrefix[4]; - static const char delimeter[4] = "-_@"; - - size_t langLength = strlen(lang); - RELEASE_ASSERT(langLength >= 2 && langLength <= 3); - strncpy(localeIdPrefix, lang, langLength); - for (int i = 0; i < 3; ++i) { - localeIdPrefix[langLength] = delimeter[i]; - // case-insensitive comparison - if (localeId.impl() && localeId.impl()->startsWith(localeIdPrefix, langLength + 1, false)) - return true; + // There are two special cases. + // 1. latin-1 characters when converted to upper case are 16 bit + // characters. + // 2. Lower case sharp-S converts to "SS" (two characters) + for (int32_t i = 0; i < length; ++i) { + LChar c = characters8()[i]; + if (UNLIKELY(c == smallLetterSharpS)) + ++numberSharpSCharacters; + UChar upper = Unicode::toUpper(c); + if (UNLIKELY(upper > 0xff)) { + // Since this upper-cased character does not fit in an 8-bit string, we + // need to take the 16-bit path. + goto upconvert; + } + data8[i] = static_cast(upper); } - return false; -} -typedef int32_t (*icuCaseConverter)(UChar*, int32_t, const UChar*, int32_t, const char*, UErrorCode*); + if (!numberSharpSCharacters) + return newImpl.release(); -static PassRefPtr caseConvert(const UChar* source16, size_t length, icuCaseConverter converter, const char* locale, StringImpl* originalString) -{ - UChar* data16; - int32_t targetLength = length; - RefPtr output = StringImpl::createUninitialized(length, data16); - do { - UErrorCode status = U_ZERO_ERROR; - targetLength = converter(data16, targetLength, source16, length, locale, &status); - if (U_SUCCESS(status)) { - output->truncateAssumingIsolated(targetLength); - return output.release(); - } - if (status != U_BUFFER_OVERFLOW_ERROR) - return originalString; - // Expand the buffer. - output = StringImpl::createUninitialized(targetLength, data16); - } while (true); -} - -PassRefPtr StringImpl::lower(const AtomicString& localeIdentifier) -{ - // Use the more-optimized code path most of the time. - // Only Turkic (tr and az) languages and Lithuanian requires - // locale-specific lowercasing rules. Even though CLDR has el-Lower, - // it's identical to the locale-agnostic lowercasing. Context-dependent - // handling of Greek capital sigma is built into the common lowercasing - // function in ICU. - const char* localeForConversion = 0; - if (localeIdMatchesLang(localeIdentifier, "tr") || localeIdMatchesLang(localeIdentifier, "az")) - localeForConversion = "tr"; - else if (localeIdMatchesLang(localeIdentifier, "lt")) - localeForConversion = "lt"; - else - return lower(); - - if (m_length > static_cast(numeric_limits::max())) - CRASH(); - int length = m_length; - - RefPtr upconverted = upconvertedString(); - const UChar* source16 = upconverted->characters16(); - return caseConvert(source16, length, u_strToLower, localeForConversion, this); -} - -PassRefPtr StringImpl::upper(const AtomicString& localeIdentifier) -{ - // Use the more-optimized code path most of the time. - // Only Turkic (tr and az) languages and Greek require locale-specific - // lowercasing rules. - icu::UnicodeString transliteratorId; - const char* localeForConversion = 0; - if (localeIdMatchesLang(localeIdentifier, "tr") || localeIdMatchesLang(localeIdentifier, "az")) - localeForConversion = "tr"; - else if (localeIdMatchesLang(localeIdentifier, "el")) - transliteratorId = UNICODE_STRING_SIMPLE("el-Upper"); - else if (localeIdMatchesLang(localeIdentifier, "lt")) - localeForConversion = "lt"; - else - return upper(); + // We have numberSSCharacters sharp-s characters, but none of the other + // special characters. + newImpl = createUninitialized(m_length + numberSharpSCharacters, data8); + + LChar* dest = data8; + + for (int32_t i = 0; i < length; ++i) { + LChar c = characters8()[i]; + if (c == smallLetterSharpS) { + *dest++ = 'S'; + *dest++ = 'S'; + } else + *dest++ = static_cast(Unicode::toUpper(c)); + } - if (m_length > static_cast(numeric_limits::max())) - CRASH(); - int length = m_length; + return newImpl.release(); + } - RefPtr upconverted = upconvertedString(); - const UChar* source16 = upconverted->characters16(); +upconvert: + RefPtr upconverted = upconvertedString(); + const UChar* source16 = upconverted->characters16(); + + UChar* data16; + RefPtr newImpl = createUninitialized(m_length, data16); + + // Do a faster loop for the case where all the characters are ASCII. + UChar ored = 0; + for (int i = 0; i < length; ++i) { + UChar c = source16[i]; + ored |= c; + data16[i] = toASCIIUpper(c); + } + if (!(ored & ~0x7F)) + return newImpl.release(); - if (localeForConversion) - return caseConvert(source16, length, u_strToUpper, localeForConversion, this); + // Do a slower implementation for cases that include non-ASCII characters. + bool error; + int32_t realLength = + Unicode::toUpper(data16, length, source16, m_length, &error); + if (!error && realLength == length) + return newImpl; + newImpl = createUninitialized(realLength, data16); + Unicode::toUpper(data16, realLength, source16, m_length, &error); + if (error) + return this; + return newImpl.release(); +} - // TODO(jungshik): Cache transliterator if perf penaly warrants it for Greek. +static bool inline localeIdMatchesLang(const AtomicString& localeId, + const char* lang) { + if (equalIgnoringCase(localeId, lang)) + return true; + static char localeIdPrefix[4]; + static const char delimeter[4] = "-_@"; + + size_t langLength = strlen(lang); + RELEASE_ASSERT(langLength >= 2 && langLength <= 3); + strncpy(localeIdPrefix, lang, langLength); + for (int i = 0; i < 3; ++i) { + localeIdPrefix[langLength] = delimeter[i]; + // case-insensitive comparison + if (localeId.impl() && + localeId.impl()->startsWith(localeIdPrefix, langLength + 1, false)) + return true; + } + return false; +} + +typedef int32_t (*icuCaseConverter)(UChar*, + int32_t, + const UChar*, + int32_t, + const char*, + UErrorCode*); + +static PassRefPtr caseConvert(const UChar* source16, + size_t length, + icuCaseConverter converter, + const char* locale, + StringImpl* originalString) { + UChar* data16; + int32_t targetLength = length; + RefPtr output = StringImpl::createUninitialized(length, data16); + do { UErrorCode status = U_ZERO_ERROR; - OwnPtr translit = - adoptPtr(icu::Transliterator::createInstance(transliteratorId, UTRANS_FORWARD, status)); - if (U_FAILURE(status)) - return upper(); - - // target will be copy-on-write. - icu::UnicodeString target(false, source16, length); - translit->transliterate(target); - - return create(reinterpret_cast(target.getBuffer()), target.length()); -} - -PassRefPtr StringImpl::fill(UChar character) -{ - if (!(character & ~0x7F)) { - LChar* data; - RefPtr newImpl = createUninitialized(m_length, data); - for (unsigned i = 0; i < m_length; ++i) - data[i] = character; - return newImpl.release(); - } - UChar* data; + targetLength = + converter(data16, targetLength, source16, length, locale, &status); + if (U_SUCCESS(status)) { + output->truncateAssumingIsolated(targetLength); + return output.release(); + } + if (status != U_BUFFER_OVERFLOW_ERROR) + return originalString; + // Expand the buffer. + output = StringImpl::createUninitialized(targetLength, data16); + } while (true); +} + +PassRefPtr StringImpl::lower(const AtomicString& localeIdentifier) { + // Use the more-optimized code path most of the time. + // Only Turkic (tr and az) languages and Lithuanian requires + // locale-specific lowercasing rules. Even though CLDR has el-Lower, + // it's identical to the locale-agnostic lowercasing. Context-dependent + // handling of Greek capital sigma is built into the common lowercasing + // function in ICU. + const char* localeForConversion = 0; + if (localeIdMatchesLang(localeIdentifier, "tr") || + localeIdMatchesLang(localeIdentifier, "az")) + localeForConversion = "tr"; + else if (localeIdMatchesLang(localeIdentifier, "lt")) + localeForConversion = "lt"; + else + return lower(); + + if (m_length > static_cast(numeric_limits::max())) + CRASH(); + int length = m_length; + + RefPtr upconverted = upconvertedString(); + const UChar* source16 = upconverted->characters16(); + return caseConvert(source16, length, u_strToLower, localeForConversion, this); +} + +PassRefPtr StringImpl::upper(const AtomicString& localeIdentifier) { + // Use the more-optimized code path most of the time. + // Only Turkic (tr and az) languages and Greek require locale-specific + // lowercasing rules. + icu::UnicodeString transliteratorId; + const char* localeForConversion = 0; + if (localeIdMatchesLang(localeIdentifier, "tr") || + localeIdMatchesLang(localeIdentifier, "az")) + localeForConversion = "tr"; + else if (localeIdMatchesLang(localeIdentifier, "el")) + transliteratorId = UNICODE_STRING_SIMPLE("el-Upper"); + else if (localeIdMatchesLang(localeIdentifier, "lt")) + localeForConversion = "lt"; + else + return upper(); + + if (m_length > static_cast(numeric_limits::max())) + CRASH(); + int length = m_length; + + RefPtr upconverted = upconvertedString(); + const UChar* source16 = upconverted->characters16(); + + if (localeForConversion) + return caseConvert(source16, length, u_strToUpper, localeForConversion, + this); + + // TODO(jungshik): Cache transliterator if perf penaly warrants it for Greek. + UErrorCode status = U_ZERO_ERROR; + OwnPtr translit = + adoptPtr(icu::Transliterator::createInstance(transliteratorId, + UTRANS_FORWARD, status)); + if (U_FAILURE(status)) + return upper(); + + // target will be copy-on-write. + icu::UnicodeString target(false, source16, length); + translit->transliterate(target); + + return create(reinterpret_cast(target.getBuffer()), + target.length()); +} + +PassRefPtr StringImpl::fill(UChar character) { + if (!(character & ~0x7F)) { + LChar* data; RefPtr newImpl = createUninitialized(m_length, data); for (unsigned i = 0; i < m_length; ++i) - data[i] = character; + data[i] = character; return newImpl.release(); + } + UChar* data; + RefPtr newImpl = createUninitialized(m_length, data); + for (unsigned i = 0; i < m_length; ++i) + data[i] = character; + return newImpl.release(); } -PassRefPtr StringImpl::foldCase() -{ - RELEASE_ASSERT(m_length <= static_cast(numeric_limits::max())); - int32_t length = m_length; - - if (is8Bit()) { - // Do a faster loop for the case where all the characters are ASCII. - LChar* data; - RefPtr newImpl = createUninitialized(m_length, data); - LChar ored = 0; - - for (int32_t i = 0; i < length; ++i) { - LChar c = characters8()[i]; - data[i] = toASCIILower(c); - ored |= c; - } - - if (!(ored & ~0x7F)) - return newImpl.release(); - - // Do a slower implementation for cases that include non-ASCII Latin-1 characters. - for (int32_t i = 0; i < length; ++i) - data[i] = static_cast(Unicode::toLower(characters8()[i])); - - return newImpl.release(); - } +PassRefPtr StringImpl::foldCase() { + RELEASE_ASSERT(m_length <= + static_cast(numeric_limits::max())); + int32_t length = m_length; + if (is8Bit()) { // Do a faster loop for the case where all the characters are ASCII. - UChar* data; + LChar* data; RefPtr newImpl = createUninitialized(m_length, data); - UChar ored = 0; + LChar ored = 0; + for (int32_t i = 0; i < length; ++i) { - UChar c = characters16()[i]; - ored |= c; - data[i] = toASCIILower(c); + LChar c = characters8()[i]; + data[i] = toASCIILower(c); + ored |= c; } + if (!(ored & ~0x7F)) - return newImpl.release(); + return newImpl.release(); - // Do a slower implementation for cases that include non-ASCII characters. - bool error; - int32_t realLength = Unicode::foldCase(data, length, characters16(), m_length, &error); - if (!error && realLength == length) - return newImpl.release(); - newImpl = createUninitialized(realLength, data); - Unicode::foldCase(data, realLength, characters16(), m_length, &error); - if (error) - return this; + // Do a slower implementation for cases that include non-ASCII Latin-1 + // characters. + for (int32_t i = 0; i < length; ++i) + data[i] = static_cast(Unicode::toLower(characters8()[i])); + + return newImpl.release(); + } + + // Do a faster loop for the case where all the characters are ASCII. + UChar* data; + RefPtr newImpl = createUninitialized(m_length, data); + UChar ored = 0; + for (int32_t i = 0; i < length; ++i) { + UChar c = characters16()[i]; + ored |= c; + data[i] = toASCIILower(c); + } + if (!(ored & ~0x7F)) + return newImpl.release(); + + // Do a slower implementation for cases that include non-ASCII characters. + bool error; + int32_t realLength = + Unicode::foldCase(data, length, characters16(), m_length, &error); + if (!error && realLength == length) return newImpl.release(); + newImpl = createUninitialized(realLength, data); + Unicode::foldCase(data, realLength, characters16(), m_length, &error); + if (error) + return this; + return newImpl.release(); } template -inline PassRefPtr StringImpl::stripMatchedCharacters(UCharPredicate predicate) -{ - if (!m_length) - return empty(); +inline PassRefPtr StringImpl::stripMatchedCharacters( + UCharPredicate predicate) { + if (!m_length) + return empty(); - unsigned start = 0; - unsigned end = m_length - 1; + unsigned start = 0; + unsigned end = m_length - 1; - // skip white space from start - while (start <= end && predicate(is8Bit() ? characters8()[start] : characters16()[start])) - ++start; + // skip white space from start + while (start <= end && + predicate(is8Bit() ? characters8()[start] : characters16()[start])) + ++start; - // only white space - if (start > end) - return empty(); + // only white space + if (start > end) + return empty(); - // skip white space from end - while (end && predicate(is8Bit() ? characters8()[end] : characters16()[end])) - --end; + // skip white space from end + while (end && predicate(is8Bit() ? characters8()[end] : characters16()[end])) + --end; - if (!start && end == m_length - 1) - return this; - if (is8Bit()) - return create(characters8() + start, end + 1 - start); - return create(characters16() + start, end + 1 - start); + if (!start && end == m_length - 1) + return this; + if (is8Bit()) + return create(characters8() + start, end + 1 - start); + return create(characters16() + start, end + 1 - start); } class UCharPredicate { -public: - inline UCharPredicate(CharacterMatchFunctionPtr function): m_function(function) { } + public: + inline UCharPredicate(CharacterMatchFunctionPtr function) + : m_function(function) {} - inline bool operator()(UChar ch) const - { - return m_function(ch); - } + inline bool operator()(UChar ch) const { return m_function(ch); } -private: - const CharacterMatchFunctionPtr m_function; + private: + const CharacterMatchFunctionPtr m_function; }; class SpaceOrNewlinePredicate { -public: - inline bool operator()(UChar ch) const - { - return isSpaceOrNewline(ch); - } + public: + inline bool operator()(UChar ch) const { return isSpaceOrNewline(ch); } }; -PassRefPtr StringImpl::stripWhiteSpace() -{ - return stripMatchedCharacters(SpaceOrNewlinePredicate()); +PassRefPtr StringImpl::stripWhiteSpace() { + return stripMatchedCharacters(SpaceOrNewlinePredicate()); } -PassRefPtr StringImpl::stripWhiteSpace(IsWhiteSpaceFunctionPtr isWhiteSpace) -{ - return stripMatchedCharacters(UCharPredicate(isWhiteSpace)); +PassRefPtr StringImpl::stripWhiteSpace( + IsWhiteSpaceFunctionPtr isWhiteSpace) { + return stripMatchedCharacters(UCharPredicate(isWhiteSpace)); } template -ALWAYS_INLINE PassRefPtr StringImpl::removeCharacters(const CharType* characters, CharacterMatchFunctionPtr findMatch) -{ - const CharType* from = characters; - const CharType* fromend = from + m_length; - - // Assume the common case will not remove any characters - while (from != fromend && !findMatch(*from)) - ++from; - if (from == fromend) - return this; +ALWAYS_INLINE PassRefPtr StringImpl::removeCharacters( + const CharType* characters, + CharacterMatchFunctionPtr findMatch) { + const CharType* from = characters; + const CharType* fromend = from + m_length; + + // Assume the common case will not remove any characters + while (from != fromend && !findMatch(*from)) + ++from; + if (from == fromend) + return this; - StringBuffer data(m_length); - CharType* to = data.characters(); - unsigned outc = from - characters; + StringBuffer data(m_length); + CharType* to = data.characters(); + unsigned outc = from - characters; - if (outc) - memcpy(to, characters, outc * sizeof(CharType)); + if (outc) + memcpy(to, characters, outc * sizeof(CharType)); - while (true) { - while (from != fromend && findMatch(*from)) - ++from; - while (from != fromend && !findMatch(*from)) - to[outc++] = *from++; - if (from == fromend) - break; - } + while (true) { + while (from != fromend && findMatch(*from)) + ++from; + while (from != fromend && !findMatch(*from)) + to[outc++] = *from++; + if (from == fromend) + break; + } - data.shrink(outc); + data.shrink(outc); - return data.release(); + return data.release(); } -PassRefPtr StringImpl::removeCharacters(CharacterMatchFunctionPtr findMatch) -{ - if (is8Bit()) - return removeCharacters(characters8(), findMatch); - return removeCharacters(characters16(), findMatch); +PassRefPtr StringImpl::removeCharacters( + CharacterMatchFunctionPtr findMatch) { + if (is8Bit()) + return removeCharacters(characters8(), findMatch); + return removeCharacters(characters16(), findMatch); } template -inline PassRefPtr StringImpl::simplifyMatchedCharactersToSpace(UCharPredicate predicate, StripBehavior stripBehavior) -{ - StringBuffer data(m_length); - - const CharType* from = getCharacters(); - const CharType* fromend = from + m_length; - int outc = 0; - bool changedToSpace = false; - - CharType* to = data.characters(); - - if (stripBehavior == StripExtraWhiteSpace) { - while (true) { - while (from != fromend && predicate(*from)) { - if (*from != ' ') - changedToSpace = true; - ++from; - } - while (from != fromend && !predicate(*from)) - to[outc++] = *from++; - if (from != fromend) - to[outc++] = ' '; - else - break; - } - - if (outc > 0 && to[outc - 1] == ' ') - --outc; - } else { - for (; from != fromend; ++from) { - if (predicate(*from)) { - if (*from != ' ') - changedToSpace = true; - to[outc++] = ' '; - } else { - to[outc++] = *from; - } - } - } +inline PassRefPtr StringImpl::simplifyMatchedCharactersToSpace( + UCharPredicate predicate, + StripBehavior stripBehavior) { + StringBuffer data(m_length); - if (static_cast(outc) == m_length && !changedToSpace) - return this; + const CharType* from = getCharacters(); + const CharType* fromend = from + m_length; + int outc = 0; + bool changedToSpace = false; - data.shrink(outc); + CharType* to = data.characters(); - return data.release(); -} + if (stripBehavior == StripExtraWhiteSpace) { + while (true) { + while (from != fromend && predicate(*from)) { + if (*from != ' ') + changedToSpace = true; + ++from; + } + while (from != fromend && !predicate(*from)) + to[outc++] = *from++; + if (from != fromend) + to[outc++] = ' '; + else + break; + } + + if (outc > 0 && to[outc - 1] == ' ') + --outc; + } else { + for (; from != fromend; ++from) { + if (predicate(*from)) { + if (*from != ' ') + changedToSpace = true; + to[outc++] = ' '; + } else { + to[outc++] = *from; + } + } + } + + if (static_cast(outc) == m_length && !changedToSpace) + return this; -PassRefPtr StringImpl::simplifyWhiteSpace(StripBehavior stripBehavior) -{ - if (is8Bit()) - return StringImpl::simplifyMatchedCharactersToSpace(SpaceOrNewlinePredicate(), stripBehavior); - return StringImpl::simplifyMatchedCharactersToSpace(SpaceOrNewlinePredicate(), stripBehavior); -} + data.shrink(outc); -PassRefPtr StringImpl::simplifyWhiteSpace(IsWhiteSpaceFunctionPtr isWhiteSpace, StripBehavior stripBehavior) -{ - if (is8Bit()) - return StringImpl::simplifyMatchedCharactersToSpace(UCharPredicate(isWhiteSpace), stripBehavior); - return StringImpl::simplifyMatchedCharactersToSpace(UCharPredicate(isWhiteSpace), stripBehavior); + return data.release(); } -int StringImpl::toIntStrict(bool* ok, int base) -{ - if (is8Bit()) - return charactersToIntStrict(characters8(), m_length, ok, base); - return charactersToIntStrict(characters16(), m_length, ok, base); +PassRefPtr StringImpl::simplifyWhiteSpace( + StripBehavior stripBehavior) { + if (is8Bit()) + return StringImpl::simplifyMatchedCharactersToSpace( + SpaceOrNewlinePredicate(), stripBehavior); + return StringImpl::simplifyMatchedCharactersToSpace( + SpaceOrNewlinePredicate(), stripBehavior); } -unsigned StringImpl::toUIntStrict(bool* ok, int base) -{ - if (is8Bit()) - return charactersToUIntStrict(characters8(), m_length, ok, base); - return charactersToUIntStrict(characters16(), m_length, ok, base); +PassRefPtr StringImpl::simplifyWhiteSpace( + IsWhiteSpaceFunctionPtr isWhiteSpace, + StripBehavior stripBehavior) { + if (is8Bit()) + return StringImpl::simplifyMatchedCharactersToSpace( + UCharPredicate(isWhiteSpace), stripBehavior); + return StringImpl::simplifyMatchedCharactersToSpace( + UCharPredicate(isWhiteSpace), stripBehavior); } -int64_t StringImpl::toInt64Strict(bool* ok, int base) -{ - if (is8Bit()) - return charactersToInt64Strict(characters8(), m_length, ok, base); - return charactersToInt64Strict(characters16(), m_length, ok, base); +int StringImpl::toIntStrict(bool* ok, int base) { + if (is8Bit()) + return charactersToIntStrict(characters8(), m_length, ok, base); + return charactersToIntStrict(characters16(), m_length, ok, base); } -uint64_t StringImpl::toUInt64Strict(bool* ok, int base) -{ - if (is8Bit()) - return charactersToUInt64Strict(characters8(), m_length, ok, base); - return charactersToUInt64Strict(characters16(), m_length, ok, base); +unsigned StringImpl::toUIntStrict(bool* ok, int base) { + if (is8Bit()) + return charactersToUIntStrict(characters8(), m_length, ok, base); + return charactersToUIntStrict(characters16(), m_length, ok, base); } -intptr_t StringImpl::toIntPtrStrict(bool* ok, int base) -{ - if (is8Bit()) - return charactersToIntPtrStrict(characters8(), m_length, ok, base); - return charactersToIntPtrStrict(characters16(), m_length, ok, base); +int64_t StringImpl::toInt64Strict(bool* ok, int base) { + if (is8Bit()) + return charactersToInt64Strict(characters8(), m_length, ok, base); + return charactersToInt64Strict(characters16(), m_length, ok, base); } -int StringImpl::toInt(bool* ok) -{ - if (is8Bit()) - return charactersToInt(characters8(), m_length, ok); - return charactersToInt(characters16(), m_length, ok); +uint64_t StringImpl::toUInt64Strict(bool* ok, int base) { + if (is8Bit()) + return charactersToUInt64Strict(characters8(), m_length, ok, base); + return charactersToUInt64Strict(characters16(), m_length, ok, base); } -unsigned StringImpl::toUInt(bool* ok) -{ - if (is8Bit()) - return charactersToUInt(characters8(), m_length, ok); - return charactersToUInt(characters16(), m_length, ok); +intptr_t StringImpl::toIntPtrStrict(bool* ok, int base) { + if (is8Bit()) + return charactersToIntPtrStrict(characters8(), m_length, ok, base); + return charactersToIntPtrStrict(characters16(), m_length, ok, base); } -int64_t StringImpl::toInt64(bool* ok) -{ - if (is8Bit()) - return charactersToInt64(characters8(), m_length, ok); - return charactersToInt64(characters16(), m_length, ok); +int StringImpl::toInt(bool* ok) { + if (is8Bit()) + return charactersToInt(characters8(), m_length, ok); + return charactersToInt(characters16(), m_length, ok); } -uint64_t StringImpl::toUInt64(bool* ok) -{ - if (is8Bit()) - return charactersToUInt64(characters8(), m_length, ok); - return charactersToUInt64(characters16(), m_length, ok); +unsigned StringImpl::toUInt(bool* ok) { + if (is8Bit()) + return charactersToUInt(characters8(), m_length, ok); + return charactersToUInt(characters16(), m_length, ok); } -intptr_t StringImpl::toIntPtr(bool* ok) -{ - if (is8Bit()) - return charactersToIntPtr(characters8(), m_length, ok); - return charactersToIntPtr(characters16(), m_length, ok); +int64_t StringImpl::toInt64(bool* ok) { + if (is8Bit()) + return charactersToInt64(characters8(), m_length, ok); + return charactersToInt64(characters16(), m_length, ok); } -double StringImpl::toDouble(bool* ok) -{ - if (is8Bit()) - return charactersToDouble(characters8(), m_length, ok); - return charactersToDouble(characters16(), m_length, ok); +uint64_t StringImpl::toUInt64(bool* ok) { + if (is8Bit()) + return charactersToUInt64(characters8(), m_length, ok); + return charactersToUInt64(characters16(), m_length, ok); } -float StringImpl::toFloat(bool* ok) -{ - if (is8Bit()) - return charactersToFloat(characters8(), m_length, ok); - return charactersToFloat(characters16(), m_length, ok); +intptr_t StringImpl::toIntPtr(bool* ok) { + if (is8Bit()) + return charactersToIntPtr(characters8(), m_length, ok); + return charactersToIntPtr(characters16(), m_length, ok); } -bool equalIgnoringCase(const LChar* a, const LChar* b, unsigned length) -{ - while (length--) { - LChar bc = *b++; - if (foldCase(*a++) != foldCase(bc)) - return false; - } - return true; +double StringImpl::toDouble(bool* ok) { + if (is8Bit()) + return charactersToDouble(characters8(), m_length, ok); + return charactersToDouble(characters16(), m_length, ok); } -bool equalIgnoringCase(const UChar* a, const LChar* b, unsigned length) -{ - while (length--) { - LChar bc = *b++; - if (foldCase(*a++) != foldCase(bc)) - return false; - } - return true; +float StringImpl::toFloat(bool* ok) { + if (is8Bit()) + return charactersToFloat(characters8(), m_length, ok); + return charactersToFloat(characters16(), m_length, ok); } -size_t StringImpl::find(CharacterMatchFunctionPtr matchFunction, unsigned start) -{ - if (is8Bit()) - return WTF::find(characters8(), m_length, matchFunction, start); - return WTF::find(characters16(), m_length, matchFunction, start); -} - -size_t StringImpl::find(const LChar* matchString, unsigned index) -{ - // Check for null or empty string to match against - if (!matchString) - return kNotFound; - size_t matchStringLength = strlen(reinterpret_cast(matchString)); - RELEASE_ASSERT(matchStringLength <= numeric_limits::max()); - unsigned matchLength = matchStringLength; - if (!matchLength) - return min(index, length()); - - // Optimization 1: fast case for strings of length 1. - if (matchLength == 1) - return WTF::find(characters16(), length(), *matchString, index); - - // Check index & matchLength are in range. - if (index > length()) - return kNotFound; - unsigned searchLength = length() - index; - if (matchLength > searchLength) - return kNotFound; - // delta is the number of additional times to test; delta == 0 means test only once. - unsigned delta = searchLength - matchLength; - - const UChar* searchCharacters = characters16() + index; - - // Optimization 2: keep a running hash of the strings, - // only call equal if the hashes match. - unsigned searchHash = 0; - unsigned matchHash = 0; - for (unsigned i = 0; i < matchLength; ++i) { - searchHash += searchCharacters[i]; - matchHash += matchString[i]; - } - - unsigned i = 0; - // keep looping until we match - while (searchHash != matchHash || !equal(searchCharacters + i, matchString, matchLength)) { - if (i == delta) - return kNotFound; - searchHash += searchCharacters[i + matchLength]; - searchHash -= searchCharacters[i]; - ++i; - } - return index + i; +bool equalIgnoringCase(const LChar* a, const LChar* b, unsigned length) { + while (length--) { + LChar bc = *b++; + if (foldCase(*a++) != foldCase(bc)) + return false; + } + return true; } -template -ALWAYS_INLINE size_t findIgnoringCaseInternal(const CharType* searchCharacters, const LChar* matchString, unsigned index, unsigned searchLength, unsigned matchLength) -{ - // delta is the number of additional times to test; delta == 0 means test only once. - unsigned delta = searchLength - matchLength; - - unsigned i = 0; - while (!equalIgnoringCase(searchCharacters + i, matchString, matchLength)) { - if (i == delta) - return kNotFound; - ++i; - } - return index + i; -} - -size_t StringImpl::findIgnoringCase(const LChar* matchString, unsigned index) -{ - // Check for null or empty string to match against - if (!matchString) - return kNotFound; - size_t matchStringLength = strlen(reinterpret_cast(matchString)); - RELEASE_ASSERT(matchStringLength <= numeric_limits::max()); - unsigned matchLength = matchStringLength; - if (!matchLength) - return min(index, length()); - - // Check index & matchLength are in range. - if (index > length()) - return kNotFound; - unsigned searchLength = length() - index; - if (matchLength > searchLength) - return kNotFound; - - if (is8Bit()) - return findIgnoringCaseInternal(characters8() + index, matchString, index, searchLength, matchLength); - return findIgnoringCaseInternal(characters16() + index, matchString, index, searchLength, matchLength); +bool equalIgnoringCase(const UChar* a, const LChar* b, unsigned length) { + while (length--) { + LChar bc = *b++; + if (foldCase(*a++) != foldCase(bc)) + return false; + } + return true; } -template -ALWAYS_INLINE static size_t findInternal(const SearchCharacterType* searchCharacters, const MatchCharacterType* matchCharacters, unsigned index, unsigned searchLength, unsigned matchLength) -{ - // Optimization: keep a running hash of the strings, - // only call equal() if the hashes match. +size_t StringImpl::find(CharacterMatchFunctionPtr matchFunction, + unsigned start) { + if (is8Bit()) + return WTF::find(characters8(), m_length, matchFunction, start); + return WTF::find(characters16(), m_length, matchFunction, start); +} - // delta is the number of additional times to test; delta == 0 means test only once. - unsigned delta = searchLength - matchLength; +size_t StringImpl::find(const LChar* matchString, unsigned index) { + // Check for null or empty string to match against + if (!matchString) + return kNotFound; + size_t matchStringLength = strlen(reinterpret_cast(matchString)); + RELEASE_ASSERT(matchStringLength <= numeric_limits::max()); + unsigned matchLength = matchStringLength; + if (!matchLength) + return min(index, length()); - unsigned searchHash = 0; - unsigned matchHash = 0; + // Optimization 1: fast case for strings of length 1. + if (matchLength == 1) + return WTF::find(characters16(), length(), *matchString, index); - for (unsigned i = 0; i < matchLength; ++i) { - searchHash += searchCharacters[i]; - matchHash += matchCharacters[i]; - } + // Check index & matchLength are in range. + if (index > length()) + return kNotFound; + unsigned searchLength = length() - index; + if (matchLength > searchLength) + return kNotFound; + // delta is the number of additional times to test; delta == 0 means test only + // once. + unsigned delta = searchLength - matchLength; - unsigned i = 0; - // keep looping until we match - while (searchHash != matchHash || !equal(searchCharacters + i, matchCharacters, matchLength)) { - if (i == delta) - return kNotFound; - searchHash += searchCharacters[i + matchLength]; - searchHash -= searchCharacters[i]; - ++i; - } - return index + i; -} - -size_t StringImpl::find(StringImpl* matchString) -{ - // Check for null string to match against - if (UNLIKELY(!matchString)) - return kNotFound; - unsigned matchLength = matchString->length(); - - // Optimization 1: fast case for strings of length 1. - if (matchLength == 1) { - if (is8Bit()) { - if (matchString->is8Bit()) - return WTF::find(characters8(), length(), matchString->characters8()[0]); - return WTF::find(characters8(), length(), matchString->characters16()[0]); - } - if (matchString->is8Bit()) - return WTF::find(characters16(), length(), matchString->characters8()[0]); - return WTF::find(characters16(), length(), matchString->characters16()[0]); - } + const UChar* searchCharacters = characters16() + index; - // Check matchLength is in range. - if (matchLength > length()) - return kNotFound; + // Optimization 2: keep a running hash of the strings, + // only call equal if the hashes match. + unsigned searchHash = 0; + unsigned matchHash = 0; + for (unsigned i = 0; i < matchLength; ++i) { + searchHash += searchCharacters[i]; + matchHash += matchString[i]; + } + + unsigned i = 0; + // keep looping until we match + while (searchHash != matchHash || + !equal(searchCharacters + i, matchString, matchLength)) { + if (i == delta) + return kNotFound; + searchHash += searchCharacters[i + matchLength]; + searchHash -= searchCharacters[i]; + ++i; + } + return index + i; +} - // Check for empty string to match against - if (UNLIKELY(!matchLength)) - return 0; +template +ALWAYS_INLINE size_t findIgnoringCaseInternal(const CharType* searchCharacters, + const LChar* matchString, + unsigned index, + unsigned searchLength, + unsigned matchLength) { + // delta is the number of additional times to test; delta == 0 means test only + // once. + unsigned delta = searchLength - matchLength; + + unsigned i = 0; + while (!equalIgnoringCase(searchCharacters + i, matchString, matchLength)) { + if (i == delta) + return kNotFound; + ++i; + } + return index + i; +} + +size_t StringImpl::findIgnoringCase(const LChar* matchString, unsigned index) { + // Check for null or empty string to match against + if (!matchString) + return kNotFound; + size_t matchStringLength = strlen(reinterpret_cast(matchString)); + RELEASE_ASSERT(matchStringLength <= numeric_limits::max()); + unsigned matchLength = matchStringLength; + if (!matchLength) + return min(index, length()); + + // Check index & matchLength are in range. + if (index > length()) + return kNotFound; + unsigned searchLength = length() - index; + if (matchLength > searchLength) + return kNotFound; + + if (is8Bit()) + return findIgnoringCaseInternal(characters8() + index, matchString, index, + searchLength, matchLength); + return findIgnoringCaseInternal(characters16() + index, matchString, index, + searchLength, matchLength); +} +template +ALWAYS_INLINE static size_t findInternal( + const SearchCharacterType* searchCharacters, + const MatchCharacterType* matchCharacters, + unsigned index, + unsigned searchLength, + unsigned matchLength) { + // Optimization: keep a running hash of the strings, + // only call equal() if the hashes match. + + // delta is the number of additional times to test; delta == 0 means test only + // once. + unsigned delta = searchLength - matchLength; + + unsigned searchHash = 0; + unsigned matchHash = 0; + + for (unsigned i = 0; i < matchLength; ++i) { + searchHash += searchCharacters[i]; + matchHash += matchCharacters[i]; + } + + unsigned i = 0; + // keep looping until we match + while (searchHash != matchHash || + !equal(searchCharacters + i, matchCharacters, matchLength)) { + if (i == delta) + return kNotFound; + searchHash += searchCharacters[i + matchLength]; + searchHash -= searchCharacters[i]; + ++i; + } + return index + i; +} + +size_t StringImpl::find(StringImpl* matchString) { + // Check for null string to match against + if (UNLIKELY(!matchString)) + return kNotFound; + unsigned matchLength = matchString->length(); + + // Optimization 1: fast case for strings of length 1. + if (matchLength == 1) { if (is8Bit()) { - if (matchString->is8Bit()) - return findInternal(characters8(), matchString->characters8(), 0, length(), matchLength); - return findInternal(characters8(), matchString->characters16(), 0, length(), matchLength); + if (matchString->is8Bit()) + return WTF::find(characters8(), length(), + matchString->characters8()[0]); + return WTF::find(characters8(), length(), matchString->characters16()[0]); } - if (matchString->is8Bit()) - return findInternal(characters16(), matchString->characters8(), 0, length(), matchLength); + return WTF::find(characters16(), length(), matchString->characters8()[0]); + return WTF::find(characters16(), length(), matchString->characters16()[0]); + } - return findInternal(characters16(), matchString->characters16(), 0, length(), matchLength); -} + // Check matchLength is in range. + if (matchLength > length()) + return kNotFound; -size_t StringImpl::find(StringImpl* matchString, unsigned index) -{ - // Check for null or empty string to match against - if (UNLIKELY(!matchString)) - return kNotFound; + // Check for empty string to match against + if (UNLIKELY(!matchLength)) + return 0; - unsigned matchLength = matchString->length(); + if (is8Bit()) { + if (matchString->is8Bit()) + return findInternal(characters8(), matchString->characters8(), 0, + length(), matchLength); + return findInternal(characters8(), matchString->characters16(), 0, length(), + matchLength); + } - // Optimization 1: fast case for strings of length 1. - if (matchLength == 1) { - if (is8Bit()) - return WTF::find(characters8(), length(), (*matchString)[0], index); - return WTF::find(characters16(), length(), (*matchString)[0], index); - } + if (matchString->is8Bit()) + return findInternal(characters16(), matchString->characters8(), 0, length(), + matchLength); - if (UNLIKELY(!matchLength)) - return min(index, length()); + return findInternal(characters16(), matchString->characters16(), 0, length(), + matchLength); +} - // Check index & matchLength are in range. - if (index > length()) - return kNotFound; - unsigned searchLength = length() - index; - if (matchLength > searchLength) - return kNotFound; +size_t StringImpl::find(StringImpl* matchString, unsigned index) { + // Check for null or empty string to match against + if (UNLIKELY(!matchString)) + return kNotFound; - if (is8Bit()) { - if (matchString->is8Bit()) - return findInternal(characters8() + index, matchString->characters8(), index, searchLength, matchLength); - return findInternal(characters8() + index, matchString->characters16(), index, searchLength, matchLength); - } + unsigned matchLength = matchString->length(); - if (matchString->is8Bit()) - return findInternal(characters16() + index, matchString->characters8(), index, searchLength, matchLength); + // Optimization 1: fast case for strings of length 1. + if (matchLength == 1) { + if (is8Bit()) + return WTF::find(characters8(), length(), (*matchString)[0], index); + return WTF::find(characters16(), length(), (*matchString)[0], index); + } - return findInternal(characters16() + index, matchString->characters16(), index, searchLength, matchLength); -} + if (UNLIKELY(!matchLength)) + return min(index, length()); -template -ALWAYS_INLINE static size_t findIgnoringCaseInner(const SearchCharacterType* searchCharacters, const MatchCharacterType* matchCharacters, unsigned index, unsigned searchLength, unsigned matchLength) -{ - // delta is the number of additional times to test; delta == 0 means test only once. - unsigned delta = searchLength - matchLength; - - unsigned i = 0; - // keep looping until we match - while (!equalIgnoringCase(searchCharacters + i, matchCharacters, matchLength)) { - if (i == delta) - return kNotFound; - ++i; - } - return index + i; -} + // Check index & matchLength are in range. + if (index > length()) + return kNotFound; + unsigned searchLength = length() - index; + if (matchLength > searchLength) + return kNotFound; -size_t StringImpl::findIgnoringCase(StringImpl* matchString, unsigned index) -{ - // Check for null or empty string to match against - if (!matchString) - return kNotFound; - unsigned matchLength = matchString->length(); - if (!matchLength) - return min(index, length()); + if (is8Bit()) { + if (matchString->is8Bit()) + return findInternal(characters8() + index, matchString->characters8(), + index, searchLength, matchLength); + return findInternal(characters8() + index, matchString->characters16(), + index, searchLength, matchLength); + } - // Check index & matchLength are in range. - if (index > length()) - return kNotFound; - unsigned searchLength = length() - index; - if (matchLength > searchLength) - return kNotFound; + if (matchString->is8Bit()) + return findInternal(characters16() + index, matchString->characters8(), + index, searchLength, matchLength); - if (is8Bit()) { - if (matchString->is8Bit()) - return findIgnoringCaseInner(characters8() + index, matchString->characters8(), index, searchLength, matchLength); - return findIgnoringCaseInner(characters8() + index, matchString->characters16(), index, searchLength, matchLength); - } + return findInternal(characters16() + index, matchString->characters16(), + index, searchLength, matchLength); +} +template +ALWAYS_INLINE static size_t findIgnoringCaseInner( + const SearchCharacterType* searchCharacters, + const MatchCharacterType* matchCharacters, + unsigned index, + unsigned searchLength, + unsigned matchLength) { + // delta is the number of additional times to test; delta == 0 means test only + // once. + unsigned delta = searchLength - matchLength; + + unsigned i = 0; + // keep looping until we match + while ( + !equalIgnoringCase(searchCharacters + i, matchCharacters, matchLength)) { + if (i == delta) + return kNotFound; + ++i; + } + return index + i; +} + +size_t StringImpl::findIgnoringCase(StringImpl* matchString, unsigned index) { + // Check for null or empty string to match against + if (!matchString) + return kNotFound; + unsigned matchLength = matchString->length(); + if (!matchLength) + return min(index, length()); + + // Check index & matchLength are in range. + if (index > length()) + return kNotFound; + unsigned searchLength = length() - index; + if (matchLength > searchLength) + return kNotFound; + + if (is8Bit()) { if (matchString->is8Bit()) - return findIgnoringCaseInner(characters16() + index, matchString->characters8(), index, searchLength, matchLength); + return findIgnoringCaseInner(characters8() + index, + matchString->characters8(), index, + searchLength, matchLength); + return findIgnoringCaseInner(characters8() + index, + matchString->characters16(), index, + searchLength, matchLength); + } + + if (matchString->is8Bit()) + return findIgnoringCaseInner(characters16() + index, + matchString->characters8(), index, + searchLength, matchLength); - return findIgnoringCaseInner(characters16() + index, matchString->characters16(), index, searchLength, matchLength); + return findIgnoringCaseInner(characters16() + index, + matchString->characters16(), index, searchLength, + matchLength); } -size_t StringImpl::findNextLineStart(unsigned index) -{ - if (is8Bit()) - return WTF::findNextLineStart(characters8(), m_length, index); - return WTF::findNextLineStart(characters16(), m_length, index); +size_t StringImpl::findNextLineStart(unsigned index) { + if (is8Bit()) + return WTF::findNextLineStart(characters8(), m_length, index); + return WTF::findNextLineStart(characters16(), m_length, index); } -size_t StringImpl::count(LChar c) const -{ - int count = 0; - if (is8Bit()) { - for (size_t i = 0; i < m_length; ++i) - count += characters8()[i] == c; - } else { - for (size_t i = 0; i < m_length; ++i) - count += characters16()[i] == c; - } - return count; +size_t StringImpl::count(LChar c) const { + int count = 0; + if (is8Bit()) { + for (size_t i = 0; i < m_length; ++i) + count += characters8()[i] == c; + } else { + for (size_t i = 0; i < m_length; ++i) + count += characters16()[i] == c; + } + return count; } -size_t StringImpl::reverseFind(UChar c, unsigned index) -{ - if (is8Bit()) - return WTF::reverseFind(characters8(), m_length, c, index); - return WTF::reverseFind(characters16(), m_length, c, index); +size_t StringImpl::reverseFind(UChar c, unsigned index) { + if (is8Bit()) + return WTF::reverseFind(characters8(), m_length, c, index); + return WTF::reverseFind(characters16(), m_length, c, index); } template -ALWAYS_INLINE static size_t reverseFindInner(const SearchCharacterType* searchCharacters, const MatchCharacterType* matchCharacters, unsigned index, unsigned length, unsigned matchLength) -{ - // Optimization: keep a running hash of the strings, - // only call equal if the hashes match. - - // delta is the number of additional times to test; delta == 0 means test only once. - unsigned delta = min(index, length - matchLength); - - unsigned searchHash = 0; - unsigned matchHash = 0; - for (unsigned i = 0; i < matchLength; ++i) { - searchHash += searchCharacters[delta + i]; - matchHash += matchCharacters[i]; - } - - // keep looping until we match - while (searchHash != matchHash || !equal(searchCharacters + delta, matchCharacters, matchLength)) { - if (!delta) - return kNotFound; - --delta; - searchHash -= searchCharacters[delta + matchLength]; - searchHash += searchCharacters[delta]; - } - return delta; -} - -size_t StringImpl::reverseFind(StringImpl* matchString, unsigned index) -{ - // Check for null or empty string to match against - if (!matchString) - return kNotFound; - unsigned matchLength = matchString->length(); - unsigned ourLength = length(); - if (!matchLength) - return min(index, ourLength); - - // Optimization 1: fast case for strings of length 1. - if (matchLength == 1) { - if (is8Bit()) - return WTF::reverseFind(characters8(), ourLength, (*matchString)[0], index); - return WTF::reverseFind(characters16(), ourLength, (*matchString)[0], index); - } - - // Check index & matchLength are in range. - if (matchLength > ourLength) - return kNotFound; +ALWAYS_INLINE static size_t reverseFindInner( + const SearchCharacterType* searchCharacters, + const MatchCharacterType* matchCharacters, + unsigned index, + unsigned length, + unsigned matchLength) { + // Optimization: keep a running hash of the strings, + // only call equal if the hashes match. + + // delta is the number of additional times to test; delta == 0 means test only + // once. + unsigned delta = min(index, length - matchLength); + + unsigned searchHash = 0; + unsigned matchHash = 0; + for (unsigned i = 0; i < matchLength; ++i) { + searchHash += searchCharacters[delta + i]; + matchHash += matchCharacters[i]; + } + + // keep looping until we match + while (searchHash != matchHash || + !equal(searchCharacters + delta, matchCharacters, matchLength)) { + if (!delta) + return kNotFound; + --delta; + searchHash -= searchCharacters[delta + matchLength]; + searchHash += searchCharacters[delta]; + } + return delta; +} + +size_t StringImpl::reverseFind(StringImpl* matchString, unsigned index) { + // Check for null or empty string to match against + if (!matchString) + return kNotFound; + unsigned matchLength = matchString->length(); + unsigned ourLength = length(); + if (!matchLength) + return min(index, ourLength); + + // Optimization 1: fast case for strings of length 1. + if (matchLength == 1) { + if (is8Bit()) + return WTF::reverseFind(characters8(), ourLength, (*matchString)[0], + index); + return WTF::reverseFind(characters16(), ourLength, (*matchString)[0], + index); + } - if (is8Bit()) { - if (matchString->is8Bit()) - return reverseFindInner(characters8(), matchString->characters8(), index, ourLength, matchLength); - return reverseFindInner(characters8(), matchString->characters16(), index, ourLength, matchLength); - } + // Check index & matchLength are in range. + if (matchLength > ourLength) + return kNotFound; + if (is8Bit()) { if (matchString->is8Bit()) - return reverseFindInner(characters16(), matchString->characters8(), index, ourLength, matchLength); + return reverseFindInner(characters8(), matchString->characters8(), index, + ourLength, matchLength); + return reverseFindInner(characters8(), matchString->characters16(), index, + ourLength, matchLength); + } - return reverseFindInner(characters16(), matchString->characters16(), index, ourLength, matchLength); -} + if (matchString->is8Bit()) + return reverseFindInner(characters16(), matchString->characters8(), index, + ourLength, matchLength); -template -ALWAYS_INLINE static size_t reverseFindIgnoringCaseInner(const SearchCharacterType* searchCharacters, const MatchCharacterType* matchCharacters, unsigned index, unsigned length, unsigned matchLength) -{ - // delta is the number of additional times to test; delta == 0 means test only once. - unsigned delta = min(index, length - matchLength); - - // keep looping until we match - while (!equalIgnoringCase(searchCharacters + delta, matchCharacters, matchLength)) { - if (!delta) - return kNotFound; - --delta; - } - return delta; + return reverseFindInner(characters16(), matchString->characters16(), index, + ourLength, matchLength); } -size_t StringImpl::reverseFindIgnoringCase(StringImpl* matchString, unsigned index) -{ - // Check for null or empty string to match against - if (!matchString) - return kNotFound; - unsigned matchLength = matchString->length(); - unsigned ourLength = length(); - if (!matchLength) - return min(index, ourLength); - - // Check index & matchLength are in range. - if (matchLength > ourLength) - return kNotFound; - - if (is8Bit()) { - if (matchString->is8Bit()) - return reverseFindIgnoringCaseInner(characters8(), matchString->characters8(), index, ourLength, matchLength); - return reverseFindIgnoringCaseInner(characters8(), matchString->characters16(), index, ourLength, matchLength); - } - +template +ALWAYS_INLINE static size_t reverseFindIgnoringCaseInner( + const SearchCharacterType* searchCharacters, + const MatchCharacterType* matchCharacters, + unsigned index, + unsigned length, + unsigned matchLength) { + // delta is the number of additional times to test; delta == 0 means test only + // once. + unsigned delta = min(index, length - matchLength); + + // keep looping until we match + while (!equalIgnoringCase(searchCharacters + delta, matchCharacters, + matchLength)) { + if (!delta) + return kNotFound; + --delta; + } + return delta; +} + +size_t StringImpl::reverseFindIgnoringCase(StringImpl* matchString, + unsigned index) { + // Check for null or empty string to match against + if (!matchString) + return kNotFound; + unsigned matchLength = matchString->length(); + unsigned ourLength = length(); + if (!matchLength) + return min(index, ourLength); + + // Check index & matchLength are in range. + if (matchLength > ourLength) + return kNotFound; + + if (is8Bit()) { if (matchString->is8Bit()) - return reverseFindIgnoringCaseInner(characters16(), matchString->characters8(), index, ourLength, matchLength); - - return reverseFindIgnoringCaseInner(characters16(), matchString->characters16(), index, ourLength, matchLength); -} - -ALWAYS_INLINE static bool equalInner(const StringImpl* stringImpl, unsigned startOffset, const char* matchString, unsigned matchLength, bool caseSensitive) -{ - ASSERT(stringImpl); - ASSERT(matchLength <= stringImpl->length()); - ASSERT(startOffset + matchLength <= stringImpl->length()); - - if (caseSensitive) { - if (stringImpl->is8Bit()) - return equal(stringImpl->characters8() + startOffset, reinterpret_cast(matchString), matchLength); - return equal(stringImpl->characters16() + startOffset, reinterpret_cast(matchString), matchLength); - } + return reverseFindIgnoringCaseInner(characters8(), + matchString->characters8(), index, + ourLength, matchLength); + return reverseFindIgnoringCaseInner(characters8(), + matchString->characters16(), index, + ourLength, matchLength); + } + + if (matchString->is8Bit()) + return reverseFindIgnoringCaseInner(characters16(), + matchString->characters8(), index, + ourLength, matchLength); + + return reverseFindIgnoringCaseInner(characters16(), + matchString->characters16(), index, + ourLength, matchLength); +} + +ALWAYS_INLINE static bool equalInner(const StringImpl* stringImpl, + unsigned startOffset, + const char* matchString, + unsigned matchLength, + bool caseSensitive) { + ASSERT(stringImpl); + ASSERT(matchLength <= stringImpl->length()); + ASSERT(startOffset + matchLength <= stringImpl->length()); + + if (caseSensitive) { if (stringImpl->is8Bit()) - return equalIgnoringCase(stringImpl->characters8() + startOffset, reinterpret_cast(matchString), matchLength); - return equalIgnoringCase(stringImpl->characters16() + startOffset, reinterpret_cast(matchString), matchLength); + return equal(stringImpl->characters8() + startOffset, + reinterpret_cast(matchString), matchLength); + return equal(stringImpl->characters16() + startOffset, + reinterpret_cast(matchString), matchLength); + } + if (stringImpl->is8Bit()) + return equalIgnoringCase(stringImpl->characters8() + startOffset, + reinterpret_cast(matchString), + matchLength); + return equalIgnoringCase(stringImpl->characters16() + startOffset, + reinterpret_cast(matchString), + matchLength); +} + +bool StringImpl::startsWith(UChar character) const { + return m_length && (*this)[0] == character; +} + +bool StringImpl::startsWith(const char* matchString, + unsigned matchLength, + bool caseSensitive) const { + ASSERT(matchLength); + if (matchLength > length()) + return false; + return equalInner(this, 0, matchString, matchLength, caseSensitive); } -bool StringImpl::startsWith(UChar character) const -{ - return m_length && (*this)[0] == character; +bool StringImpl::endsWith(StringImpl* matchString, bool caseSensitive) { + ASSERT(matchString); + if (m_length >= matchString->m_length) { + unsigned start = m_length - matchString->m_length; + return (caseSensitive ? find(matchString, start) + : findIgnoringCase(matchString, start)) == start; + } + return false; } -bool StringImpl::startsWith(const char* matchString, unsigned matchLength, bool caseSensitive) const -{ - ASSERT(matchLength); - if (matchLength > length()) - return false; - return equalInner(this, 0, matchString, matchLength, caseSensitive); +bool StringImpl::endsWith(UChar character) const { + return m_length && (*this)[m_length - 1] == character; } -bool StringImpl::endsWith(StringImpl* matchString, bool caseSensitive) -{ - ASSERT(matchString); - if (m_length >= matchString->m_length) { - unsigned start = m_length - matchString->m_length; - return (caseSensitive ? find(matchString, start) : findIgnoringCase(matchString, start)) == start; - } +bool StringImpl::endsWith(const char* matchString, + unsigned matchLength, + bool caseSensitive) const { + ASSERT(matchLength); + if (matchLength > length()) return false; + unsigned startOffset = length() - matchLength; + return equalInner(this, startOffset, matchString, matchLength, caseSensitive); } -bool StringImpl::endsWith(UChar character) const -{ - return m_length && (*this)[m_length - 1] == character; -} +PassRefPtr StringImpl::replace(UChar oldC, UChar newC) { + if (oldC == newC) + return this; -bool StringImpl::endsWith(const char* matchString, unsigned matchLength, bool caseSensitive) const -{ - ASSERT(matchLength); - if (matchLength > length()) - return false; - unsigned startOffset = length() - matchLength; - return equalInner(this, startOffset, matchString, matchLength, caseSensitive); -} + if (find(oldC) == kNotFound) + return this; -PassRefPtr StringImpl::replace(UChar oldC, UChar newC) -{ - if (oldC == newC) - return this; + unsigned i; + if (is8Bit()) { + if (newC <= 0xff) { + LChar* data; + LChar oldChar = static_cast(oldC); + LChar newChar = static_cast(newC); - if (find(oldC) == kNotFound) - return this; + RefPtr newImpl = createUninitialized(m_length, data); - unsigned i; - if (is8Bit()) { - if (newC <= 0xff) { - LChar* data; - LChar oldChar = static_cast(oldC); - LChar newChar = static_cast(newC); - - RefPtr newImpl = createUninitialized(m_length, data); - - for (i = 0; i != m_length; ++i) { - LChar ch = characters8()[i]; - if (ch == oldChar) - ch = newChar; - data[i] = ch; - } - return newImpl.release(); - } - - // There is the possibility we need to up convert from 8 to 16 bit, - // create a 16 bit string for the result. - UChar* data; - RefPtr newImpl = createUninitialized(m_length, data); - - for (i = 0; i != m_length; ++i) { - UChar ch = characters8()[i]; - if (ch == oldC) - ch = newC; - data[i] = ch; - } - - return newImpl.release(); + for (i = 0; i != m_length; ++i) { + LChar ch = characters8()[i]; + if (ch == oldChar) + ch = newChar; + data[i] = ch; + } + return newImpl.release(); } + // There is the possibility we need to up convert from 8 to 16 bit, + // create a 16 bit string for the result. UChar* data; RefPtr newImpl = createUninitialized(m_length, data); for (i = 0; i != m_length; ++i) { - UChar ch = characters16()[i]; - if (ch == oldC) - ch = newC; - data[i] = ch; + UChar ch = characters8()[i]; + if (ch == oldC) + ch = newC; + data[i] = ch; } - return newImpl.release(); -} -PassRefPtr StringImpl::replace(unsigned position, unsigned lengthToReplace, StringImpl* str) -{ - position = min(position, length()); - lengthToReplace = min(lengthToReplace, length() - position); - unsigned lengthToInsert = str ? str->length() : 0; - if (!lengthToReplace && !lengthToInsert) - return this; + return newImpl.release(); + } + + UChar* data; + RefPtr newImpl = createUninitialized(m_length, data); + + for (i = 0; i != m_length; ++i) { + UChar ch = characters16()[i]; + if (ch == oldC) + ch = newC; + data[i] = ch; + } + return newImpl.release(); +} + +PassRefPtr StringImpl::replace(unsigned position, + unsigned lengthToReplace, + StringImpl* str) { + position = min(position, length()); + lengthToReplace = min(lengthToReplace, length() - position); + unsigned lengthToInsert = str ? str->length() : 0; + if (!lengthToReplace && !lengthToInsert) + return this; - RELEASE_ASSERT((length() - lengthToReplace) < (numeric_limits::max() - lengthToInsert)); + RELEASE_ASSERT((length() - lengthToReplace) < + (numeric_limits::max() - lengthToInsert)); - if (is8Bit() && (!str || str->is8Bit())) { - LChar* data; - RefPtr newImpl = - createUninitialized(length() - lengthToReplace + lengthToInsert, data); - memcpy(data, characters8(), position * sizeof(LChar)); - if (str) - memcpy(data + position, str->characters8(), lengthToInsert * sizeof(LChar)); - memcpy(data + position + lengthToInsert, characters8() + position + lengthToReplace, - (length() - position - lengthToReplace) * sizeof(LChar)); - return newImpl.release(); - } - UChar* data; + if (is8Bit() && (!str || str->is8Bit())) { + LChar* data; RefPtr newImpl = createUninitialized(length() - lengthToReplace + lengthToInsert, data); - if (is8Bit()) - for (unsigned i = 0; i < position; ++i) - data[i] = characters8()[i]; - else - memcpy(data, characters16(), position * sizeof(UChar)); - if (str) { - if (str->is8Bit()) - for (unsigned i = 0; i < lengthToInsert; ++i) - data[i + position] = str->characters8()[i]; - else - memcpy(data + position, str->characters16(), lengthToInsert * sizeof(UChar)); - } - if (is8Bit()) { - for (unsigned i = 0; i < length() - position - lengthToReplace; ++i) - data[i + position + lengthToInsert] = characters8()[i + position + lengthToReplace]; - } else { - memcpy(data + position + lengthToInsert, characters16() + position + lengthToReplace, - (length() - position - lengthToReplace) * sizeof(UChar)); - } + memcpy(data, characters8(), position * sizeof(LChar)); + if (str) + memcpy(data + position, str->characters8(), + lengthToInsert * sizeof(LChar)); + memcpy(data + position + lengthToInsert, + characters8() + position + lengthToReplace, + (length() - position - lengthToReplace) * sizeof(LChar)); return newImpl.release(); -} - -PassRefPtr StringImpl::replace(UChar pattern, StringImpl* replacement) -{ - if (!replacement) - return this; + } + UChar* data; + RefPtr newImpl = + createUninitialized(length() - lengthToReplace + lengthToInsert, data); + if (is8Bit()) + for (unsigned i = 0; i < position; ++i) + data[i] = characters8()[i]; + else + memcpy(data, characters16(), position * sizeof(UChar)); + if (str) { + if (str->is8Bit()) + for (unsigned i = 0; i < lengthToInsert; ++i) + data[i + position] = str->characters8()[i]; + else + memcpy(data + position, str->characters16(), + lengthToInsert * sizeof(UChar)); + } + if (is8Bit()) { + for (unsigned i = 0; i < length() - position - lengthToReplace; ++i) + data[i + position + lengthToInsert] = + characters8()[i + position + lengthToReplace]; + } else { + memcpy(data + position + lengthToInsert, + characters16() + position + lengthToReplace, + (length() - position - lengthToReplace) * sizeof(UChar)); + } + return newImpl.release(); +} + +PassRefPtr StringImpl::replace(UChar pattern, + StringImpl* replacement) { + if (!replacement) + return this; - if (replacement->is8Bit()) - return replace(pattern, replacement->characters8(), replacement->length()); + if (replacement->is8Bit()) + return replace(pattern, replacement->characters8(), replacement->length()); - return replace(pattern, replacement->characters16(), replacement->length()); + return replace(pattern, replacement->characters16(), replacement->length()); } -PassRefPtr StringImpl::replace(UChar pattern, const LChar* replacement, unsigned repStrLength) -{ - ASSERT(replacement); - - size_t srcSegmentStart = 0; - unsigned matchCount = 0; - - // Count the matches. - while ((srcSegmentStart = find(pattern, srcSegmentStart)) != kNotFound) { - ++matchCount; - ++srcSegmentStart; - } - - // If we have 0 matches then we don't have to do any more work. - if (!matchCount) - return this; - - RELEASE_ASSERT(!repStrLength || matchCount <= numeric_limits::max() / repStrLength); +PassRefPtr StringImpl::replace(UChar pattern, + const LChar* replacement, + unsigned repStrLength) { + ASSERT(replacement); - unsigned replaceSize = matchCount * repStrLength; - unsigned newSize = m_length - matchCount; - RELEASE_ASSERT(newSize < (numeric_limits::max() - replaceSize)); + size_t srcSegmentStart = 0; + unsigned matchCount = 0; - newSize += replaceSize; + // Count the matches. + while ((srcSegmentStart = find(pattern, srcSegmentStart)) != kNotFound) { + ++matchCount; + ++srcSegmentStart; + } - // Construct the new data. - size_t srcSegmentEnd; - unsigned srcSegmentLength; - srcSegmentStart = 0; - unsigned dstOffset = 0; + // If we have 0 matches then we don't have to do any more work. + if (!matchCount) + return this; - if (is8Bit()) { - LChar* data; - RefPtr newImpl = createUninitialized(newSize, data); + RELEASE_ASSERT(!repStrLength || + matchCount <= numeric_limits::max() / repStrLength); - while ((srcSegmentEnd = find(pattern, srcSegmentStart)) != kNotFound) { - srcSegmentLength = srcSegmentEnd - srcSegmentStart; - memcpy(data + dstOffset, characters8() + srcSegmentStart, srcSegmentLength * sizeof(LChar)); - dstOffset += srcSegmentLength; - memcpy(data + dstOffset, replacement, repStrLength * sizeof(LChar)); - dstOffset += repStrLength; - srcSegmentStart = srcSegmentEnd + 1; - } + unsigned replaceSize = matchCount * repStrLength; + unsigned newSize = m_length - matchCount; + RELEASE_ASSERT(newSize < (numeric_limits::max() - replaceSize)); - srcSegmentLength = m_length - srcSegmentStart; - memcpy(data + dstOffset, characters8() + srcSegmentStart, srcSegmentLength * sizeof(LChar)); + newSize += replaceSize; - ASSERT(dstOffset + srcSegmentLength == newImpl->length()); + // Construct the new data. + size_t srcSegmentEnd; + unsigned srcSegmentLength; + srcSegmentStart = 0; + unsigned dstOffset = 0; - return newImpl.release(); - } - - UChar* data; + if (is8Bit()) { + LChar* data; RefPtr newImpl = createUninitialized(newSize, data); while ((srcSegmentEnd = find(pattern, srcSegmentStart)) != kNotFound) { - srcSegmentLength = srcSegmentEnd - srcSegmentStart; - memcpy(data + dstOffset, characters16() + srcSegmentStart, srcSegmentLength * sizeof(UChar)); - - dstOffset += srcSegmentLength; - for (unsigned i = 0; i < repStrLength; ++i) - data[i + dstOffset] = replacement[i]; - - dstOffset += repStrLength; - srcSegmentStart = srcSegmentEnd + 1; + srcSegmentLength = srcSegmentEnd - srcSegmentStart; + memcpy(data + dstOffset, characters8() + srcSegmentStart, + srcSegmentLength * sizeof(LChar)); + dstOffset += srcSegmentLength; + memcpy(data + dstOffset, replacement, repStrLength * sizeof(LChar)); + dstOffset += repStrLength; + srcSegmentStart = srcSegmentEnd + 1; } srcSegmentLength = m_length - srcSegmentStart; - memcpy(data + dstOffset, characters16() + srcSegmentStart, srcSegmentLength * sizeof(UChar)); + memcpy(data + dstOffset, characters8() + srcSegmentStart, + srcSegmentLength * sizeof(LChar)); ASSERT(dstOffset + srcSegmentLength == newImpl->length()); return newImpl.release(); -} + } -PassRefPtr StringImpl::replace(UChar pattern, const UChar* replacement, unsigned repStrLength) -{ - ASSERT(replacement); + UChar* data; + RefPtr newImpl = createUninitialized(newSize, data); - size_t srcSegmentStart = 0; - unsigned matchCount = 0; + while ((srcSegmentEnd = find(pattern, srcSegmentStart)) != kNotFound) { + srcSegmentLength = srcSegmentEnd - srcSegmentStart; + memcpy(data + dstOffset, characters16() + srcSegmentStart, + srcSegmentLength * sizeof(UChar)); - // Count the matches. - while ((srcSegmentStart = find(pattern, srcSegmentStart)) != kNotFound) { - ++matchCount; - ++srcSegmentStart; - } + dstOffset += srcSegmentLength; + for (unsigned i = 0; i < repStrLength; ++i) + data[i + dstOffset] = replacement[i]; - // If we have 0 matches then we don't have to do any more work. - if (!matchCount) - return this; + dstOffset += repStrLength; + srcSegmentStart = srcSegmentEnd + 1; + } - RELEASE_ASSERT(!repStrLength || matchCount <= numeric_limits::max() / repStrLength); + srcSegmentLength = m_length - srcSegmentStart; + memcpy(data + dstOffset, characters16() + srcSegmentStart, + srcSegmentLength * sizeof(UChar)); - unsigned replaceSize = matchCount * repStrLength; - unsigned newSize = m_length - matchCount; - RELEASE_ASSERT(newSize < (numeric_limits::max() - replaceSize)); + ASSERT(dstOffset + srcSegmentLength == newImpl->length()); - newSize += replaceSize; + return newImpl.release(); +} - // Construct the new data. - size_t srcSegmentEnd; - unsigned srcSegmentLength; - srcSegmentStart = 0; - unsigned dstOffset = 0; +PassRefPtr StringImpl::replace(UChar pattern, + const UChar* replacement, + unsigned repStrLength) { + ASSERT(replacement); - if (is8Bit()) { - UChar* data; - RefPtr newImpl = createUninitialized(newSize, data); + size_t srcSegmentStart = 0; + unsigned matchCount = 0; - while ((srcSegmentEnd = find(pattern, srcSegmentStart)) != kNotFound) { - srcSegmentLength = srcSegmentEnd - srcSegmentStart; - for (unsigned i = 0; i < srcSegmentLength; ++i) - data[i + dstOffset] = characters8()[i + srcSegmentStart]; + // Count the matches. + while ((srcSegmentStart = find(pattern, srcSegmentStart)) != kNotFound) { + ++matchCount; + ++srcSegmentStart; + } - dstOffset += srcSegmentLength; - memcpy(data + dstOffset, replacement, repStrLength * sizeof(UChar)); + // If we have 0 matches then we don't have to do any more work. + if (!matchCount) + return this; - dstOffset += repStrLength; - srcSegmentStart = srcSegmentEnd + 1; - } + RELEASE_ASSERT(!repStrLength || + matchCount <= numeric_limits::max() / repStrLength); - srcSegmentLength = m_length - srcSegmentStart; - for (unsigned i = 0; i < srcSegmentLength; ++i) - data[i + dstOffset] = characters8()[i + srcSegmentStart]; + unsigned replaceSize = matchCount * repStrLength; + unsigned newSize = m_length - matchCount; + RELEASE_ASSERT(newSize < (numeric_limits::max() - replaceSize)); - ASSERT(dstOffset + srcSegmentLength == newImpl->length()); + newSize += replaceSize; - return newImpl.release(); - } + // Construct the new data. + size_t srcSegmentEnd; + unsigned srcSegmentLength; + srcSegmentStart = 0; + unsigned dstOffset = 0; + if (is8Bit()) { UChar* data; RefPtr newImpl = createUninitialized(newSize, data); while ((srcSegmentEnd = find(pattern, srcSegmentStart)) != kNotFound) { - srcSegmentLength = srcSegmentEnd - srcSegmentStart; - memcpy(data + dstOffset, characters16() + srcSegmentStart, srcSegmentLength * sizeof(UChar)); + srcSegmentLength = srcSegmentEnd - srcSegmentStart; + for (unsigned i = 0; i < srcSegmentLength; ++i) + data[i + dstOffset] = characters8()[i + srcSegmentStart]; - dstOffset += srcSegmentLength; - memcpy(data + dstOffset, replacement, repStrLength * sizeof(UChar)); + dstOffset += srcSegmentLength; + memcpy(data + dstOffset, replacement, repStrLength * sizeof(UChar)); - dstOffset += repStrLength; - srcSegmentStart = srcSegmentEnd + 1; + dstOffset += repStrLength; + srcSegmentStart = srcSegmentEnd + 1; } srcSegmentLength = m_length - srcSegmentStart; - memcpy(data + dstOffset, characters16() + srcSegmentStart, srcSegmentLength * sizeof(UChar)); + for (unsigned i = 0; i < srcSegmentLength; ++i) + data[i + dstOffset] = characters8()[i + srcSegmentStart]; ASSERT(dstOffset + srcSegmentLength == newImpl->length()); return newImpl.release(); + } + + UChar* data; + RefPtr newImpl = createUninitialized(newSize, data); + + while ((srcSegmentEnd = find(pattern, srcSegmentStart)) != kNotFound) { + srcSegmentLength = srcSegmentEnd - srcSegmentStart; + memcpy(data + dstOffset, characters16() + srcSegmentStart, + srcSegmentLength * sizeof(UChar)); + + dstOffset += srcSegmentLength; + memcpy(data + dstOffset, replacement, repStrLength * sizeof(UChar)); + + dstOffset += repStrLength; + srcSegmentStart = srcSegmentEnd + 1; + } + + srcSegmentLength = m_length - srcSegmentStart; + memcpy(data + dstOffset, characters16() + srcSegmentStart, + srcSegmentLength * sizeof(UChar)); + + ASSERT(dstOffset + srcSegmentLength == newImpl->length()); + + return newImpl.release(); } -PassRefPtr StringImpl::replace(StringImpl* pattern, StringImpl* replacement) -{ - if (!pattern || !replacement) - return this; +PassRefPtr StringImpl::replace(StringImpl* pattern, + StringImpl* replacement) { + if (!pattern || !replacement) + return this; - unsigned patternLength = pattern->length(); - if (!patternLength) - return this; + unsigned patternLength = pattern->length(); + if (!patternLength) + return this; - unsigned repStrLength = replacement->length(); - size_t srcSegmentStart = 0; - unsigned matchCount = 0; + unsigned repStrLength = replacement->length(); + size_t srcSegmentStart = 0; + unsigned matchCount = 0; - // Count the matches. - while ((srcSegmentStart = find(pattern, srcSegmentStart)) != kNotFound) { - ++matchCount; - srcSegmentStart += patternLength; - } + // Count the matches. + while ((srcSegmentStart = find(pattern, srcSegmentStart)) != kNotFound) { + ++matchCount; + srcSegmentStart += patternLength; + } - // If we have 0 matches, we don't have to do any more work - if (!matchCount) - return this; - - unsigned newSize = m_length - matchCount * patternLength; - RELEASE_ASSERT(!repStrLength || matchCount <= numeric_limits::max() / repStrLength); - - RELEASE_ASSERT(newSize <= (numeric_limits::max() - matchCount * repStrLength)); - - newSize += matchCount * repStrLength; - - - // Construct the new data - size_t srcSegmentEnd; - unsigned srcSegmentLength; - srcSegmentStart = 0; - unsigned dstOffset = 0; - bool srcIs8Bit = is8Bit(); - bool replacementIs8Bit = replacement->is8Bit(); - - // There are 4 cases: - // 1. This and replacement are both 8 bit. - // 2. This and replacement are both 16 bit. - // 3. This is 8 bit and replacement is 16 bit. - // 4. This is 16 bit and replacement is 8 bit. - if (srcIs8Bit && replacementIs8Bit) { - // Case 1 - LChar* data; - RefPtr newImpl = createUninitialized(newSize, data); - while ((srcSegmentEnd = find(pattern, srcSegmentStart)) != kNotFound) { - srcSegmentLength = srcSegmentEnd - srcSegmentStart; - memcpy(data + dstOffset, characters8() + srcSegmentStart, srcSegmentLength * sizeof(LChar)); - dstOffset += srcSegmentLength; - memcpy(data + dstOffset, replacement->characters8(), repStrLength * sizeof(LChar)); - dstOffset += repStrLength; - srcSegmentStart = srcSegmentEnd + patternLength; - } - - srcSegmentLength = m_length - srcSegmentStart; - memcpy(data + dstOffset, characters8() + srcSegmentStart, srcSegmentLength * sizeof(LChar)); - - ASSERT(dstOffset + srcSegmentLength == newImpl->length()); - - return newImpl.release(); - } + // If we have 0 matches, we don't have to do any more work + if (!matchCount) + return this; - UChar* data; + unsigned newSize = m_length - matchCount * patternLength; + RELEASE_ASSERT(!repStrLength || + matchCount <= numeric_limits::max() / repStrLength); + + RELEASE_ASSERT(newSize <= + (numeric_limits::max() - matchCount * repStrLength)); + + newSize += matchCount * repStrLength; + + // Construct the new data + size_t srcSegmentEnd; + unsigned srcSegmentLength; + srcSegmentStart = 0; + unsigned dstOffset = 0; + bool srcIs8Bit = is8Bit(); + bool replacementIs8Bit = replacement->is8Bit(); + + // There are 4 cases: + // 1. This and replacement are both 8 bit. + // 2. This and replacement are both 16 bit. + // 3. This is 8 bit and replacement is 16 bit. + // 4. This is 16 bit and replacement is 8 bit. + if (srcIs8Bit && replacementIs8Bit) { + // Case 1 + LChar* data; RefPtr newImpl = createUninitialized(newSize, data); while ((srcSegmentEnd = find(pattern, srcSegmentStart)) != kNotFound) { - srcSegmentLength = srcSegmentEnd - srcSegmentStart; - if (srcIs8Bit) { - // Case 3. - for (unsigned i = 0; i < srcSegmentLength; ++i) - data[i + dstOffset] = characters8()[i + srcSegmentStart]; - } else { - // Case 2 & 4. - memcpy(data + dstOffset, characters16() + srcSegmentStart, srcSegmentLength * sizeof(UChar)); - } - dstOffset += srcSegmentLength; - if (replacementIs8Bit) { - // Cases 2 & 3. - for (unsigned i = 0; i < repStrLength; ++i) - data[i + dstOffset] = replacement->characters8()[i]; - } else { - // Case 4 - memcpy(data + dstOffset, replacement->characters16(), repStrLength * sizeof(UChar)); - } - dstOffset += repStrLength; - srcSegmentStart = srcSegmentEnd + patternLength; + srcSegmentLength = srcSegmentEnd - srcSegmentStart; + memcpy(data + dstOffset, characters8() + srcSegmentStart, + srcSegmentLength * sizeof(LChar)); + dstOffset += srcSegmentLength; + memcpy(data + dstOffset, replacement->characters8(), + repStrLength * sizeof(LChar)); + dstOffset += repStrLength; + srcSegmentStart = srcSegmentEnd + patternLength; } srcSegmentLength = m_length - srcSegmentStart; - if (srcIs8Bit) { - // Case 3. - for (unsigned i = 0; i < srcSegmentLength; ++i) - data[i + dstOffset] = characters8()[i + srcSegmentStart]; - } else { - // Cases 2 & 4. - memcpy(data + dstOffset, characters16() + srcSegmentStart, srcSegmentLength * sizeof(UChar)); - } + memcpy(data + dstOffset, characters8() + srcSegmentStart, + srcSegmentLength * sizeof(LChar)); ASSERT(dstOffset + srcSegmentLength == newImpl->length()); return newImpl.release(); -} + } -PassRefPtr StringImpl::upconvertedString() -{ - if (is8Bit()) - return String::make16BitFrom8BitSource(characters8(), m_length).releaseImpl(); - return this; -} - -static inline bool stringImplContentEqual(const StringImpl* a, const StringImpl* b) -{ - unsigned aLength = a->length(); - unsigned bLength = b->length(); - if (aLength != bLength) - return false; + UChar* data; + RefPtr newImpl = createUninitialized(newSize, data); + while ((srcSegmentEnd = find(pattern, srcSegmentStart)) != kNotFound) { + srcSegmentLength = srcSegmentEnd - srcSegmentStart; + if (srcIs8Bit) { + // Case 3. + for (unsigned i = 0; i < srcSegmentLength; ++i) + data[i + dstOffset] = characters8()[i + srcSegmentStart]; + } else { + // Case 2 & 4. + memcpy(data + dstOffset, characters16() + srcSegmentStart, + srcSegmentLength * sizeof(UChar)); + } + dstOffset += srcSegmentLength; + if (replacementIs8Bit) { + // Cases 2 & 3. + for (unsigned i = 0; i < repStrLength; ++i) + data[i + dstOffset] = replacement->characters8()[i]; + } else { + // Case 4 + memcpy(data + dstOffset, replacement->characters16(), + repStrLength * sizeof(UChar)); + } + dstOffset += repStrLength; + srcSegmentStart = srcSegmentEnd + patternLength; + } + + srcSegmentLength = m_length - srcSegmentStart; + if (srcIs8Bit) { + // Case 3. + for (unsigned i = 0; i < srcSegmentLength; ++i) + data[i + dstOffset] = characters8()[i + srcSegmentStart]; + } else { + // Cases 2 & 4. + memcpy(data + dstOffset, characters16() + srcSegmentStart, + srcSegmentLength * sizeof(UChar)); + } + + ASSERT(dstOffset + srcSegmentLength == newImpl->length()); + + return newImpl.release(); +} + +PassRefPtr StringImpl::upconvertedString() { + if (is8Bit()) + return String::make16BitFrom8BitSource(characters8(), m_length) + .releaseImpl(); + return this; +} + +static inline bool stringImplContentEqual(const StringImpl* a, + const StringImpl* b) { + unsigned aLength = a->length(); + unsigned bLength = b->length(); + if (aLength != bLength) + return false; - if (a->is8Bit()) { - if (b->is8Bit()) - return equal(a->characters8(), b->characters8(), aLength); + if (a->is8Bit()) { + if (b->is8Bit()) + return equal(a->characters8(), b->characters8(), aLength); - return equal(a->characters8(), b->characters16(), aLength); - } + return equal(a->characters8(), b->characters16(), aLength); + } - if (b->is8Bit()) - return equal(a->characters16(), b->characters8(), aLength); + if (b->is8Bit()) + return equal(a->characters16(), b->characters8(), aLength); - return equal(a->characters16(), b->characters16(), aLength); + return equal(a->characters16(), b->characters16(), aLength); } -bool equal(const StringImpl* a, const StringImpl* b) -{ - if (a == b) - return true; - if (!a || !b) - return false; - if (a->isAtomic() && b->isAtomic()) - return false; +bool equal(const StringImpl* a, const StringImpl* b) { + if (a == b) + return true; + if (!a || !b) + return false; + if (a->isAtomic() && b->isAtomic()) + return false; - return stringImplContentEqual(a, b); + return stringImplContentEqual(a, b); } template -inline bool equalInternal(const StringImpl* a, const CharType* b, unsigned length) -{ - if (!a) - return !b; - if (!b) - return false; +inline bool equalInternal(const StringImpl* a, + const CharType* b, + unsigned length) { + if (!a) + return !b; + if (!b) + return false; - if (a->length() != length) - return false; - if (a->is8Bit()) - return equal(a->characters8(), b, length); - return equal(a->characters16(), b, length); + if (a->length() != length) + return false; + if (a->is8Bit()) + return equal(a->characters8(), b, length); + return equal(a->characters16(), b, length); } -bool equal(const StringImpl* a, const LChar* b, unsigned length) -{ - return equalInternal(a, b, length); +bool equal(const StringImpl* a, const LChar* b, unsigned length) { + return equalInternal(a, b, length); } -bool equal(const StringImpl* a, const UChar* b, unsigned length) -{ - return equalInternal(a, b, length); +bool equal(const StringImpl* a, const UChar* b, unsigned length) { + return equalInternal(a, b, length); } -bool equal(const StringImpl* a, const LChar* b) -{ - if (!a) - return !b; - if (!b) - return !a; +bool equal(const StringImpl* a, const LChar* b) { + if (!a) + return !b; + if (!b) + return !a; - unsigned length = a->length(); + unsigned length = a->length(); - if (a->is8Bit()) { - const LChar* aPtr = a->characters8(); - for (unsigned i = 0; i != length; ++i) { - LChar bc = b[i]; - LChar ac = aPtr[i]; - if (!bc) - return false; - if (ac != bc) - return false; - } - - return !b[length]; - } - - const UChar* aPtr = a->characters16(); + if (a->is8Bit()) { + const LChar* aPtr = a->characters8(); for (unsigned i = 0; i != length; ++i) { - LChar bc = b[i]; - if (!bc) - return false; - if (aPtr[i] != bc) - return false; + LChar bc = b[i]; + LChar ac = aPtr[i]; + if (!bc) + return false; + if (ac != bc) + return false; } return !b[length]; -} + } -bool equalNonNull(const StringImpl* a, const StringImpl* b) -{ - ASSERT(a && b); - if (a == b) - return true; + const UChar* aPtr = a->characters16(); + for (unsigned i = 0; i != length; ++i) { + LChar bc = b[i]; + if (!bc) + return false; + if (aPtr[i] != bc) + return false; + } - return stringImplContentEqual(a, b); + return !b[length]; } -bool equalIgnoringCase(const StringImpl* a, const StringImpl* b) -{ - if (a == b) - return true; - if (!a || !b) - return false; +bool equalNonNull(const StringImpl* a, const StringImpl* b) { + ASSERT(a && b); + if (a == b) + return true; - return CaseFoldingHash::equal(a, b); + return stringImplContentEqual(a, b); } -bool equalIgnoringCase(const StringImpl* a, const LChar* b) -{ - if (!a) - return !b; - if (!b) - return !a; +bool equalIgnoringCase(const StringImpl* a, const StringImpl* b) { + if (a == b) + return true; + if (!a || !b) + return false; - unsigned length = a->length(); + return CaseFoldingHash::equal(a, b); +} - // Do a faster loop for the case where all the characters are ASCII. - UChar ored = 0; - bool equal = true; - if (a->is8Bit()) { - const LChar* as = a->characters8(); - for (unsigned i = 0; i != length; ++i) { - LChar bc = b[i]; - if (!bc) - return false; - UChar ac = as[i]; - ored |= ac; - equal = equal && (toASCIILower(ac) == toASCIILower(bc)); - } - - // Do a slower implementation for cases that include non-ASCII characters. - if (ored & ~0x7F) { - equal = true; - for (unsigned i = 0; i != length; ++i) - equal = equal && (foldCase(as[i]) == foldCase(b[i])); - } - - return equal && !b[length]; - } +bool equalIgnoringCase(const StringImpl* a, const LChar* b) { + if (!a) + return !b; + if (!b) + return !a; + + unsigned length = a->length(); - const UChar* as = a->characters16(); + // Do a faster loop for the case where all the characters are ASCII. + UChar ored = 0; + bool equal = true; + if (a->is8Bit()) { + const LChar* as = a->characters8(); for (unsigned i = 0; i != length; ++i) { - LChar bc = b[i]; - if (!bc) - return false; - UChar ac = as[i]; - ored |= ac; - equal = equal && (toASCIILower(ac) == toASCIILower(bc)); + LChar bc = b[i]; + if (!bc) + return false; + UChar ac = as[i]; + ored |= ac; + equal = equal && (toASCIILower(ac) == toASCIILower(bc)); } // Do a slower implementation for cases that include non-ASCII characters. if (ored & ~0x7F) { - equal = true; - for (unsigned i = 0; i != length; ++i) { - equal = equal && (foldCase(as[i]) == foldCase(b[i])); - } + equal = true; + for (unsigned i = 0; i != length; ++i) + equal = equal && (foldCase(as[i]) == foldCase(b[i])); } return equal && !b[length]; + } + + const UChar* as = a->characters16(); + for (unsigned i = 0; i != length; ++i) { + LChar bc = b[i]; + if (!bc) + return false; + UChar ac = as[i]; + ored |= ac; + equal = equal && (toASCIILower(ac) == toASCIILower(bc)); + } + + // Do a slower implementation for cases that include non-ASCII characters. + if (ored & ~0x7F) { + equal = true; + for (unsigned i = 0; i != length; ++i) { + equal = equal && (foldCase(as[i]) == foldCase(b[i])); + } + } + + return equal && !b[length]; } -bool equalIgnoringCaseNonNull(const StringImpl* a, const StringImpl* b) -{ - ASSERT(a && b); - if (a == b) - return true; +bool equalIgnoringCaseNonNull(const StringImpl* a, const StringImpl* b) { + ASSERT(a && b); + if (a == b) + return true; - unsigned length = a->length(); - if (length != b->length()) - return false; + unsigned length = a->length(); + if (length != b->length()) + return false; - if (a->is8Bit()) { - if (b->is8Bit()) - return equalIgnoringCase(a->characters8(), b->characters8(), length); + if (a->is8Bit()) { + if (b->is8Bit()) + return equalIgnoringCase(a->characters8(), b->characters8(), length); - return equalIgnoringCase(b->characters16(), a->characters8(), length); - } + return equalIgnoringCase(b->characters16(), a->characters8(), length); + } - if (b->is8Bit()) - return equalIgnoringCase(a->characters16(), b->characters8(), length); + if (b->is8Bit()) + return equalIgnoringCase(a->characters16(), b->characters8(), length); - return equalIgnoringCase(a->characters16(), b->characters16(), length); + return equalIgnoringCase(a->characters16(), b->characters16(), length); } -bool equalIgnoringNullity(StringImpl* a, StringImpl* b) -{ - if (!a && b && !b->length()) - return true; - if (!b && a && !a->length()) - return true; - return equal(a, b); +bool equalIgnoringNullity(StringImpl* a, StringImpl* b) { + if (!a && b && !b->length()) + return true; + if (!b && a && !a->length()) + return true; + return equal(a, b); } -size_t StringImpl::sizeInBytes() const -{ - size_t size = length(); - if (!is8Bit()) - size *= 2; - return size + sizeof(*this); +size_t StringImpl::sizeInBytes() const { + size_t size = length(); + if (!is8Bit()) + size *= 2; + return size + sizeof(*this); } -} // namespace WTF +} // namespace WTF diff --git a/sky/engine/wtf/text/StringImpl.h b/sky/engine/wtf/text/StringImpl.h index cce6f78fd105a..0244a0317407c 100644 --- a/sky/engine/wtf/text/StringImpl.h +++ b/sky/engine/wtf/text/StringImpl.h @@ -1,6 +1,7 @@ /* * Copyright (C) 1999 Lars Knoll (knoll@kde.org) - * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2013 Apple Inc. + * All rights reserved. * Copyright (C) 2009 Google Inc. All rights reserved. * * This library is free software; you can redistribute it and/or @@ -36,7 +37,8 @@ namespace WTF { struct AlreadyHashed; struct CStringTranslator; -template struct HashAndCharactersTranslator; +template +struct HashAndCharactersTranslator; struct HashAndUTF8CharactersTranslator; struct LCharBufferTranslator; struct CharBufferFromLiteralDataTranslator; @@ -51,44 +53,49 @@ typedef bool (*CharacterMatchFunctionPtr)(UChar); typedef bool (*IsWhiteSpaceFunctionPtr)(UChar); typedef HashMap StaticStringsTable; -// Define STRING_STATS to turn on run time statistics of string sizes and memory usage +// Define STRING_STATS to turn on run time statistics of string sizes and memory +// usage #undef STRING_STATS #ifdef STRING_STATS struct StringStats { - inline void add8BitString(unsigned length) - { - ++m_totalNumberStrings; - ++m_number8BitStrings; - m_total8BitData += length; - } - - inline void add16BitString(unsigned length) - { - ++m_totalNumberStrings; - ++m_number16BitStrings; - m_total16BitData += length; - } - - void removeString(StringImpl*); - void printStats(); - - static const unsigned s_printStringStatsFrequency = 5000; - static unsigned s_stringRemovesTillPrintStats; - - unsigned m_totalNumberStrings; - unsigned m_number8BitStrings; - unsigned m_number16BitStrings; - unsigned long long m_total8BitData; - unsigned long long m_total16BitData; + inline void add8BitString(unsigned length) { + ++m_totalNumberStrings; + ++m_number8BitStrings; + m_total8BitData += length; + } + + inline void add16BitString(unsigned length) { + ++m_totalNumberStrings; + ++m_number16BitStrings; + m_total16BitData += length; + } + + void removeString(StringImpl*); + void printStats(); + + static const unsigned s_printStringStatsFrequency = 5000; + static unsigned s_stringRemovesTillPrintStats; + + unsigned m_totalNumberStrings; + unsigned m_number8BitStrings; + unsigned m_number16BitStrings; + unsigned long long m_total8BitData; + unsigned long long m_total16BitData; }; void addStringForStats(StringImpl*); void removeStringForStats(StringImpl*); -#define STRING_STATS_ADD_8BIT_STRING(length) StringImpl::stringStats().add8BitString(length); addStringForStats(this) -#define STRING_STATS_ADD_16BIT_STRING(length) StringImpl::stringStats().add16BitString(length); addStringForStats(this) -#define STRING_STATS_REMOVE_STRING(string) StringImpl::stringStats().removeString(string); removeStringForStats(this) +#define STRING_STATS_ADD_8BIT_STRING(length) \ + StringImpl::stringStats().add8BitString(length); \ + addStringForStats(this) +#define STRING_STATS_ADD_16BIT_STRING(length) \ + StringImpl::stringStats().add16BitString(length); \ + addStringForStats(this) +#define STRING_STATS_REMOVE_STRING(string) \ + StringImpl::stringStats().removeString(string); \ + removeStringForStats(this) #else #define STRING_STATS_ADD_8BIT_STRING(length) ((void)0) #define STRING_STATS_ADD_16BIT_STRING(length) ((void)0) @@ -98,627 +105,719 @@ void removeStringForStats(StringImpl*); // You can find documentation about this class in this doc: // https://docs.google.com/document/d/1kOCUlJdh2WJMJGDf-WoEQhmnjKLaOYRbiHz5TiGJl14/edit?usp=sharing class WTF_EXPORT StringImpl { - WTF_MAKE_NONCOPYABLE(StringImpl); - friend struct WTF::CStringTranslator; - template friend struct WTF::HashAndCharactersTranslator; - friend struct WTF::HashAndUTF8CharactersTranslator; - friend struct WTF::CharBufferFromLiteralDataTranslator; - friend struct WTF::LCharBufferTranslator; - friend struct WTF::SubstringTranslator; - friend struct WTF::UCharBufferTranslator; - -private: - // StringImpls are allocated out of the WTF buffer partition. - void* operator new(size_t); - void* operator new(size_t, void* ptr) { return ptr; }; - void operator delete(void*); - - // Used to construct static strings, which have an special refCount that can never hit zero. - // This means that the static string will never be destroyed, which is important because - // static strings will be shared across threads & ref-counted in a non-threadsafe manner. - enum ConstructEmptyStringTag { ConstructEmptyString }; - explicit StringImpl(ConstructEmptyStringTag) - : m_refCount(1) - , m_length(0) - , m_hash(0) - , m_isAtomic(false) - , m_is8Bit(true) - , m_isStatic(true) - { - // Ensure that the hash is computed so that AtomicStringHash can call existingHash() - // with impunity. The empty string is special because it is never entered into - // AtomicString's HashKey, but still needs to compare correctly. - STRING_STATS_ADD_8BIT_STRING(m_length); - hash(); - } - - // FIXME: there has to be a less hacky way to do this. - enum Force8Bit { Force8BitConstructor }; - StringImpl(unsigned length, Force8Bit) - : m_refCount(1) - , m_length(length) - , m_hash(0) - , m_isAtomic(false) - , m_is8Bit(true) - , m_isStatic(false) - { - ASSERT(m_length); - STRING_STATS_ADD_8BIT_STRING(m_length); - } - - StringImpl(unsigned length) - : m_refCount(1) - , m_length(length) - , m_hash(0) - , m_isAtomic(false) - , m_is8Bit(false) - , m_isStatic(false) - { - ASSERT(m_length); - STRING_STATS_ADD_16BIT_STRING(m_length); - } - - enum StaticStringTag { StaticString }; - StringImpl(unsigned length, unsigned hash, StaticStringTag) - : m_refCount(1) - , m_length(length) - , m_hash(hash) - , m_isAtomic(false) - , m_is8Bit(true) - , m_isStatic(true) - { - } - -public: - ~StringImpl(); - - static StringImpl* createStatic(const char* string, unsigned length, unsigned hash); - static void freezeStaticStrings(); - static const StaticStringsTable& allStaticStrings(); - static unsigned highestStaticStringLength() { return m_highestStaticStringLength; } - - static PassRefPtr create(const UChar*, unsigned length); - static PassRefPtr create(const LChar*, unsigned length); - static PassRefPtr create8BitIfPossible(const UChar*, unsigned length); - template - static PassRefPtr create8BitIfPossible(const Vector& vector) - { - return create8BitIfPossible(vector.data(), vector.size()); - } - - ALWAYS_INLINE static PassRefPtr create(const char* s, unsigned length) { return create(reinterpret_cast(s), length); } - static PassRefPtr create(const LChar*); - ALWAYS_INLINE static PassRefPtr create(const char* s) { return create(reinterpret_cast(s)); } - - static PassRefPtr createUninitialized(unsigned length, LChar*& data); - static PassRefPtr createUninitialized(unsigned length, UChar*& data); - - // Reallocate the StringImpl. The originalString must be only owned by the PassRefPtr. - // Just like the input pointer of realloc(), the originalString can't be used after this function. - static PassRefPtr reallocate(PassRefPtr originalString, unsigned length); - - // If this StringImpl has only one reference, we can truncate the string by updating - // its m_length property without actually re-allocating its buffer. - void truncateAssumingIsolated(unsigned length) - { - ASSERT(hasOneRef()); - ASSERT(length <= m_length); - m_length = length; - } - - unsigned length() const { return m_length; } - bool is8Bit() const { return m_is8Bit; } - - ALWAYS_INLINE const LChar* characters8() const { ASSERT(is8Bit()); return reinterpret_cast(this + 1); } - ALWAYS_INLINE const UChar* characters16() const { ASSERT(!is8Bit()); return reinterpret_cast(this + 1); } - - template - ALWAYS_INLINE const CharType * getCharacters() const; - - size_t sizeInBytes() const; - - bool isAtomic() const { return m_isAtomic; } - void setIsAtomic(bool isAtomic) { m_isAtomic = isAtomic; } - - bool isStatic() const { return m_isStatic; } - -private: - // The high bits of 'hash' are always empty, but we prefer to store our flags - // in the low bits because it makes them slightly more efficient to access. - // So, we shift left and right when setting and getting our hash code. - void setHash(unsigned hash) const - { - ASSERT(!hasHash()); - // Multiple clients assume that StringHasher is the canonical string hash function. - ASSERT(hash == (is8Bit() ? StringHasher::computeHashAndMaskTop8Bits(characters8(), m_length) : StringHasher::computeHashAndMaskTop8Bits(characters16(), m_length))); - m_hash = hash; - ASSERT(hash); // Verify that 0 is a valid sentinel hash value. - } - - unsigned rawHash() const - { - return m_hash; + WTF_MAKE_NONCOPYABLE(StringImpl); + friend struct WTF::CStringTranslator; + template + friend struct WTF::HashAndCharactersTranslator; + friend struct WTF::HashAndUTF8CharactersTranslator; + friend struct WTF::CharBufferFromLiteralDataTranslator; + friend struct WTF::LCharBufferTranslator; + friend struct WTF::SubstringTranslator; + friend struct WTF::UCharBufferTranslator; + + private: + // StringImpls are allocated out of the WTF buffer partition. + void* operator new(size_t); + void* operator new(size_t, void* ptr) { return ptr; }; + void operator delete(void*); + + // Used to construct static strings, which have an special refCount that can + // never hit zero. This means that the static string will never be destroyed, + // which is important because static strings will be shared across threads & + // ref-counted in a non-threadsafe manner. + enum ConstructEmptyStringTag { ConstructEmptyString }; + explicit StringImpl(ConstructEmptyStringTag) + : m_refCount(1), + m_length(0), + m_hash(0), + m_isAtomic(false), + m_is8Bit(true), + m_isStatic(true) { + // Ensure that the hash is computed so that AtomicStringHash can call + // existingHash() with impunity. The empty string is special because it is + // never entered into AtomicString's HashKey, but still needs to compare + // correctly. + STRING_STATS_ADD_8BIT_STRING(m_length); + hash(); + } + + // FIXME: there has to be a less hacky way to do this. + enum Force8Bit { Force8BitConstructor }; + StringImpl(unsigned length, Force8Bit) + : m_refCount(1), + m_length(length), + m_hash(0), + m_isAtomic(false), + m_is8Bit(true), + m_isStatic(false) { + ASSERT(m_length); + STRING_STATS_ADD_8BIT_STRING(m_length); + } + + StringImpl(unsigned length) + : m_refCount(1), + m_length(length), + m_hash(0), + m_isAtomic(false), + m_is8Bit(false), + m_isStatic(false) { + ASSERT(m_length); + STRING_STATS_ADD_16BIT_STRING(m_length); + } + + enum StaticStringTag { StaticString }; + StringImpl(unsigned length, unsigned hash, StaticStringTag) + : m_refCount(1), + m_length(length), + m_hash(hash), + m_isAtomic(false), + m_is8Bit(true), + m_isStatic(true) {} + + public: + ~StringImpl(); + + static StringImpl* createStatic(const char* string, + unsigned length, + unsigned hash); + static void freezeStaticStrings(); + static const StaticStringsTable& allStaticStrings(); + static unsigned highestStaticStringLength() { + return m_highestStaticStringLength; + } + + static PassRefPtr create(const UChar*, unsigned length); + static PassRefPtr create(const LChar*, unsigned length); + static PassRefPtr create8BitIfPossible(const UChar*, + unsigned length); + template + static PassRefPtr create8BitIfPossible( + const Vector& vector) { + return create8BitIfPossible(vector.data(), vector.size()); + } + + ALWAYS_INLINE static PassRefPtr create(const char* s, + unsigned length) { + return create(reinterpret_cast(s), length); + } + static PassRefPtr create(const LChar*); + ALWAYS_INLINE static PassRefPtr create(const char* s) { + return create(reinterpret_cast(s)); + } + + static PassRefPtr createUninitialized(unsigned length, + LChar*& data); + static PassRefPtr createUninitialized(unsigned length, + UChar*& data); + + // Reallocate the StringImpl. The originalString must be only owned by the + // PassRefPtr. Just like the input pointer of realloc(), the originalString + // can't be used after this function. + static PassRefPtr reallocate( + PassRefPtr originalString, + unsigned length); + + // If this StringImpl has only one reference, we can truncate the string by + // updating its m_length property without actually re-allocating its buffer. + void truncateAssumingIsolated(unsigned length) { + ASSERT(hasOneRef()); + ASSERT(length <= m_length); + m_length = length; + } + + unsigned length() const { return m_length; } + bool is8Bit() const { return m_is8Bit; } + + ALWAYS_INLINE const LChar* characters8() const { + ASSERT(is8Bit()); + return reinterpret_cast(this + 1); + } + ALWAYS_INLINE const UChar* characters16() const { + ASSERT(!is8Bit()); + return reinterpret_cast(this + 1); + } + + template + ALWAYS_INLINE const CharType* getCharacters() const; + + size_t sizeInBytes() const; + + bool isAtomic() const { return m_isAtomic; } + void setIsAtomic(bool isAtomic) { m_isAtomic = isAtomic; } + + bool isStatic() const { return m_isStatic; } + + private: + // The high bits of 'hash' are always empty, but we prefer to store our flags + // in the low bits because it makes them slightly more efficient to access. + // So, we shift left and right when setting and getting our hash code. + void setHash(unsigned hash) const { + ASSERT(!hasHash()); + // Multiple clients assume that StringHasher is the canonical string hash + // function. + ASSERT(hash == (is8Bit() ? StringHasher::computeHashAndMaskTop8Bits( + characters8(), m_length) + : StringHasher::computeHashAndMaskTop8Bits( + characters16(), m_length))); + m_hash = hash; + ASSERT(hash); // Verify that 0 is a valid sentinel hash value. + } + + unsigned rawHash() const { return m_hash; } + + void destroyIfNotStatic(); + + public: + bool hasHash() const { return rawHash() != 0; } + + unsigned existingHash() const { + ASSERT(hasHash()); + return rawHash(); + } + + unsigned hash() const { + if (hasHash()) + return existingHash(); + return hashSlowCase(); + } + + ALWAYS_INLINE bool hasOneRef() const { return m_refCount == 1; } + + ALWAYS_INLINE void ref() { ++m_refCount; } + + ALWAYS_INLINE void deref() { + if (hasOneRef()) { + destroyIfNotStatic(); + return; } - void destroyIfNotStatic(); + --m_refCount; + } -public: - bool hasHash() const - { - return rawHash() != 0; - } - - unsigned existingHash() const - { - ASSERT(hasHash()); - return rawHash(); - } + static StringImpl* empty(); - unsigned hash() const - { - if (hasHash()) - return existingHash(); - return hashSlowCase(); - } + // FIXME: Does this really belong in StringImpl? + template + static void copyChars(T* destination, + const T* source, + unsigned numCharacters) { + memcpy(destination, source, numCharacters * sizeof(T)); + } - ALWAYS_INLINE bool hasOneRef() const - { - return m_refCount == 1; - } + ALWAYS_INLINE static void copyChars(UChar* destination, + const LChar* source, + unsigned numCharacters) { + for (unsigned i = 0; i < numCharacters; ++i) + destination[i] = source[i]; + } - ALWAYS_INLINE void ref() - { - ++m_refCount; - } + // Some string features, like refcounting and the atomicity flag, are not + // thread-safe. We achieve thread safety by isolation, giving each thread + // its own copy of the string. + PassRefPtr isolatedCopy() const; - ALWAYS_INLINE void deref() - { - if (hasOneRef()) { - destroyIfNotStatic(); - return; - } + PassRefPtr substring(unsigned pos, unsigned len = UINT_MAX); - --m_refCount; - } - - static StringImpl* empty(); - - // FIXME: Does this really belong in StringImpl? - template static void copyChars(T* destination, const T* source, unsigned numCharacters) - { - memcpy(destination, source, numCharacters * sizeof(T)); - } - - ALWAYS_INLINE static void copyChars(UChar* destination, const LChar* source, unsigned numCharacters) - { - for (unsigned i = 0; i < numCharacters; ++i) - destination[i] = source[i]; - } - - // Some string features, like refcounting and the atomicity flag, are not - // thread-safe. We achieve thread safety by isolation, giving each thread - // its own copy of the string. - PassRefPtr isolatedCopy() const; - - PassRefPtr substring(unsigned pos, unsigned len = UINT_MAX); - - UChar operator[](unsigned i) const - { - ASSERT_WITH_SECURITY_IMPLICATION(i < m_length); - if (is8Bit()) - return characters8()[i]; - return characters16()[i]; - } - UChar32 characterStartingAt(unsigned); - - bool containsOnlyWhitespace(); - - int toIntStrict(bool* ok = 0, int base = 10); - unsigned toUIntStrict(bool* ok = 0, int base = 10); - int64_t toInt64Strict(bool* ok = 0, int base = 10); - uint64_t toUInt64Strict(bool* ok = 0, int base = 10); - intptr_t toIntPtrStrict(bool* ok = 0, int base = 10); - - int toInt(bool* ok = 0); // ignores trailing garbage - unsigned toUInt(bool* ok = 0); // ignores trailing garbage - int64_t toInt64(bool* ok = 0); // ignores trailing garbage - uint64_t toUInt64(bool* ok = 0); // ignores trailing garbage - intptr_t toIntPtr(bool* ok = 0); // ignores trailing garbage - - // FIXME: Like the strict functions above, these give false for "ok" when there is trailing garbage. - // Like the non-strict functions above, these return the value when there is trailing garbage. - // It would be better if these were more consistent with the above functions instead. - double toDouble(bool* ok = 0); - float toFloat(bool* ok = 0); - - PassRefPtr lower(); - PassRefPtr upper(); - PassRefPtr lower(const AtomicString& localeIdentifier); - PassRefPtr upper(const AtomicString& localeIdentifier); - - PassRefPtr fill(UChar); - // FIXME: Do we need fill(char) or can we just do the right thing if UChar is ASCII? - PassRefPtr foldCase(); - - PassRefPtr stripWhiteSpace(); - PassRefPtr stripWhiteSpace(IsWhiteSpaceFunctionPtr); - PassRefPtr simplifyWhiteSpace(StripBehavior stripBehavior = StripExtraWhiteSpace); - PassRefPtr simplifyWhiteSpace(IsWhiteSpaceFunctionPtr, StripBehavior stripBehavior = StripExtraWhiteSpace); - - PassRefPtr removeCharacters(CharacterMatchFunctionPtr); - template - ALWAYS_INLINE PassRefPtr removeCharacters(const CharType* characters, CharacterMatchFunctionPtr); - - size_t find(LChar character, unsigned start = 0); - size_t find(char character, unsigned start = 0); - size_t find(UChar character, unsigned start = 0); - size_t find(CharacterMatchFunctionPtr, unsigned index = 0); - size_t find(const LChar*, unsigned index = 0); - ALWAYS_INLINE size_t find(const char* s, unsigned index = 0) { return find(reinterpret_cast(s), index); } - size_t find(StringImpl*); - size_t find(StringImpl*, unsigned index); - size_t findIgnoringCase(const LChar*, unsigned index = 0); - ALWAYS_INLINE size_t findIgnoringCase(const char* s, unsigned index = 0) { return findIgnoringCase(reinterpret_cast(s), index); } - size_t findIgnoringCase(StringImpl*, unsigned index = 0); - - size_t findNextLineStart(unsigned index = UINT_MAX); - - size_t reverseFind(UChar, unsigned index = UINT_MAX); - size_t reverseFind(StringImpl*, unsigned index = UINT_MAX); - size_t reverseFindIgnoringCase(StringImpl*, unsigned index = UINT_MAX); - - size_t count(LChar) const; - - bool startsWith(StringImpl* str, bool caseSensitive = true) { return (caseSensitive ? reverseFind(str, 0) : reverseFindIgnoringCase(str, 0)) == 0; } - bool startsWith(UChar) const; - bool startsWith(const char*, unsigned matchLength, bool caseSensitive) const; - template - bool startsWith(const char (&prefix)[matchLength], bool caseSensitive = true) const { return startsWith(prefix, matchLength - 1, caseSensitive); } - - bool endsWith(StringImpl*, bool caseSensitive = true); - bool endsWith(UChar) const; - bool endsWith(const char*, unsigned matchLength, bool caseSensitive) const; - template - bool endsWith(const char (&prefix)[matchLength], bool caseSensitive = true) const { return endsWith(prefix, matchLength - 1, caseSensitive); } - - PassRefPtr replace(UChar, UChar); - PassRefPtr replace(UChar, StringImpl*); - ALWAYS_INLINE PassRefPtr replace(UChar pattern, const char* replacement, unsigned replacementLength) { return replace(pattern, reinterpret_cast(replacement), replacementLength); } - PassRefPtr replace(UChar, const LChar*, unsigned replacementLength); - PassRefPtr replace(UChar, const UChar*, unsigned replacementLength); - PassRefPtr replace(StringImpl*, StringImpl*); - PassRefPtr replace(unsigned index, unsigned len, StringImpl*); - PassRefPtr upconvertedString(); + UChar operator[](unsigned i) const { + ASSERT_WITH_SECURITY_IMPLICATION(i < m_length); + if (is8Bit()) + return characters8()[i]; + return characters16()[i]; + } + UChar32 characterStartingAt(unsigned); + + bool containsOnlyWhitespace(); + + int toIntStrict(bool* ok = 0, int base = 10); + unsigned toUIntStrict(bool* ok = 0, int base = 10); + int64_t toInt64Strict(bool* ok = 0, int base = 10); + uint64_t toUInt64Strict(bool* ok = 0, int base = 10); + intptr_t toIntPtrStrict(bool* ok = 0, int base = 10); + + int toInt(bool* ok = 0); // ignores trailing garbage + unsigned toUInt(bool* ok = 0); // ignores trailing garbage + int64_t toInt64(bool* ok = 0); // ignores trailing garbage + uint64_t toUInt64(bool* ok = 0); // ignores trailing garbage + intptr_t toIntPtr(bool* ok = 0); // ignores trailing garbage + + // FIXME: Like the strict functions above, these give false for "ok" when + // there is trailing garbage. Like the non-strict functions above, these + // return the value when there is trailing garbage. It would be better if + // these were more consistent with the above functions instead. + double toDouble(bool* ok = 0); + float toFloat(bool* ok = 0); + + PassRefPtr lower(); + PassRefPtr upper(); + PassRefPtr lower(const AtomicString& localeIdentifier); + PassRefPtr upper(const AtomicString& localeIdentifier); + + PassRefPtr fill(UChar); + // FIXME: Do we need fill(char) or can we just do the right thing if UChar is + // ASCII? + PassRefPtr foldCase(); + + PassRefPtr stripWhiteSpace(); + PassRefPtr stripWhiteSpace(IsWhiteSpaceFunctionPtr); + PassRefPtr simplifyWhiteSpace( + StripBehavior stripBehavior = StripExtraWhiteSpace); + PassRefPtr simplifyWhiteSpace( + IsWhiteSpaceFunctionPtr, + StripBehavior stripBehavior = StripExtraWhiteSpace); + + PassRefPtr removeCharacters(CharacterMatchFunctionPtr); + template + ALWAYS_INLINE PassRefPtr removeCharacters( + const CharType* characters, + CharacterMatchFunctionPtr); + + size_t find(LChar character, unsigned start = 0); + size_t find(char character, unsigned start = 0); + size_t find(UChar character, unsigned start = 0); + size_t find(CharacterMatchFunctionPtr, unsigned index = 0); + size_t find(const LChar*, unsigned index = 0); + ALWAYS_INLINE size_t find(const char* s, unsigned index = 0) { + return find(reinterpret_cast(s), index); + } + size_t find(StringImpl*); + size_t find(StringImpl*, unsigned index); + size_t findIgnoringCase(const LChar*, unsigned index = 0); + ALWAYS_INLINE size_t findIgnoringCase(const char* s, unsigned index = 0) { + return findIgnoringCase(reinterpret_cast(s), index); + } + size_t findIgnoringCase(StringImpl*, unsigned index = 0); + + size_t findNextLineStart(unsigned index = UINT_MAX); + + size_t reverseFind(UChar, unsigned index = UINT_MAX); + size_t reverseFind(StringImpl*, unsigned index = UINT_MAX); + size_t reverseFindIgnoringCase(StringImpl*, unsigned index = UINT_MAX); + + size_t count(LChar) const; + + bool startsWith(StringImpl* str, bool caseSensitive = true) { + return (caseSensitive ? reverseFind(str, 0) + : reverseFindIgnoringCase(str, 0)) == 0; + } + bool startsWith(UChar) const; + bool startsWith(const char*, unsigned matchLength, bool caseSensitive) const; + template + bool startsWith(const char (&prefix)[matchLength], + bool caseSensitive = true) const { + return startsWith(prefix, matchLength - 1, caseSensitive); + } + + bool endsWith(StringImpl*, bool caseSensitive = true); + bool endsWith(UChar) const; + bool endsWith(const char*, unsigned matchLength, bool caseSensitive) const; + template + bool endsWith(const char (&prefix)[matchLength], + bool caseSensitive = true) const { + return endsWith(prefix, matchLength - 1, caseSensitive); + } + + PassRefPtr replace(UChar, UChar); + PassRefPtr replace(UChar, StringImpl*); + ALWAYS_INLINE PassRefPtr replace(UChar pattern, + const char* replacement, + unsigned replacementLength) { + return replace(pattern, reinterpret_cast(replacement), + replacementLength); + } + PassRefPtr replace(UChar, + const LChar*, + unsigned replacementLength); + PassRefPtr replace(UChar, + const UChar*, + unsigned replacementLength); + PassRefPtr replace(StringImpl*, StringImpl*); + PassRefPtr replace(unsigned index, unsigned len, StringImpl*); + PassRefPtr upconvertedString(); #ifdef STRING_STATS - ALWAYS_INLINE static StringStats& stringStats() { return m_stringStats; } + ALWAYS_INLINE static StringStats& stringStats() { return m_stringStats; } #endif -private: - template static size_t allocationSize(unsigned length) - { - RELEASE_ASSERT(length <= ((std::numeric_limits::max() - sizeof(StringImpl)) / sizeof(CharType))); - return sizeof(StringImpl) + length * sizeof(CharType); - } - - template PassRefPtr stripMatchedCharacters(UCharPredicate); - template PassRefPtr simplifyMatchedCharactersToSpace(UCharPredicate, StripBehavior); - NEVER_INLINE unsigned hashSlowCase() const; + private: + template + static size_t allocationSize(unsigned length) { + RELEASE_ASSERT( + length <= ((std::numeric_limits::max() - sizeof(StringImpl)) / + sizeof(CharType))); + return sizeof(StringImpl) + length * sizeof(CharType); + } + + template + PassRefPtr stripMatchedCharacters(UCharPredicate); + template + PassRefPtr simplifyMatchedCharactersToSpace(UCharPredicate, + StripBehavior); + NEVER_INLINE unsigned hashSlowCase() const; #ifdef STRING_STATS - static StringStats m_stringStats; + static StringStats m_stringStats; #endif - static unsigned m_highestStaticStringLength; + static unsigned m_highestStaticStringLength; #if ENABLE(ASSERT) - void assertHashIsCorrect() - { - ASSERT(hasHash()); - ASSERT(existingHash() == StringHasher::computeHashAndMaskTop8Bits(characters8(), length())); - } + void assertHashIsCorrect() { + ASSERT(hasHash()); + ASSERT(existingHash() == + StringHasher::computeHashAndMaskTop8Bits(characters8(), length())); + } #endif -private: - unsigned m_refCount; - unsigned m_length; - mutable unsigned m_hash : 24; - unsigned m_isAtomic : 1; - unsigned m_is8Bit : 1; - unsigned m_isStatic : 1; + private: + unsigned m_refCount; + unsigned m_length; + mutable unsigned m_hash : 24; + unsigned m_isAtomic : 1; + unsigned m_is8Bit : 1; + unsigned m_isStatic : 1; }; template <> -ALWAYS_INLINE const LChar* StringImpl::getCharacters() const { return characters8(); } +ALWAYS_INLINE const LChar* StringImpl::getCharacters() const { + return characters8(); +} template <> -ALWAYS_INLINE const UChar* StringImpl::getCharacters() const { return characters16(); } +ALWAYS_INLINE const UChar* StringImpl::getCharacters() const { + return characters16(); +} WTF_EXPORT bool equal(const StringImpl*, const StringImpl*); WTF_EXPORT bool equal(const StringImpl*, const LChar*); -inline bool equal(const StringImpl* a, const char* b) { return equal(a, reinterpret_cast(b)); } +inline bool equal(const StringImpl* a, const char* b) { + return equal(a, reinterpret_cast(b)); +} WTF_EXPORT bool equal(const StringImpl*, const LChar*, unsigned); WTF_EXPORT bool equal(const StringImpl*, const UChar*, unsigned); -inline bool equal(const StringImpl* a, const char* b, unsigned length) { return equal(a, reinterpret_cast(b), length); } -inline bool equal(const LChar* a, StringImpl* b) { return equal(b, a); } -inline bool equal(const char* a, StringImpl* b) { return equal(b, reinterpret_cast(a)); } +inline bool equal(const StringImpl* a, const char* b, unsigned length) { + return equal(a, reinterpret_cast(b), length); +} +inline bool equal(const LChar* a, StringImpl* b) { + return equal(b, a); +} +inline bool equal(const char* a, StringImpl* b) { + return equal(b, reinterpret_cast(a)); +} WTF_EXPORT bool equalNonNull(const StringImpl* a, const StringImpl* b); -template -ALWAYS_INLINE bool equal(const CharType* a, const CharType* b, unsigned length) { return !memcmp(a, b, length * sizeof(CharType)); } +template +ALWAYS_INLINE bool equal(const CharType* a, + const CharType* b, + unsigned length) { + return !memcmp(a, b, length * sizeof(CharType)); +} -ALWAYS_INLINE bool equal(const LChar* a, const UChar* b, unsigned length) -{ - for (unsigned i = 0; i < length; ++i) { - if (a[i] != b[i]) - return false; - } - return true; +ALWAYS_INLINE bool equal(const LChar* a, const UChar* b, unsigned length) { + for (unsigned i = 0; i < length; ++i) { + if (a[i] != b[i]) + return false; + } + return true; } -ALWAYS_INLINE bool equal(const UChar* a, const LChar* b, unsigned length) { return equal(b, a, length); } +ALWAYS_INLINE bool equal(const UChar* a, const LChar* b, unsigned length) { + return equal(b, a, length); +} WTF_EXPORT bool equalIgnoringCase(const StringImpl*, const StringImpl*); WTF_EXPORT bool equalIgnoringCase(const StringImpl*, const LChar*); -inline bool equalIgnoringCase(const LChar* a, const StringImpl* b) { return equalIgnoringCase(b, a); } +inline bool equalIgnoringCase(const LChar* a, const StringImpl* b) { + return equalIgnoringCase(b, a); +} WTF_EXPORT bool equalIgnoringCase(const LChar*, const LChar*, unsigned); WTF_EXPORT bool equalIgnoringCase(const UChar*, const LChar*, unsigned); -inline bool equalIgnoringCase(const UChar* a, const char* b, unsigned length) { return equalIgnoringCase(a, reinterpret_cast(b), length); } -inline bool equalIgnoringCase(const LChar* a, const UChar* b, unsigned length) { return equalIgnoringCase(b, a, length); } -inline bool equalIgnoringCase(const char* a, const UChar* b, unsigned length) { return equalIgnoringCase(b, reinterpret_cast(a), length); } -inline bool equalIgnoringCase(const char* a, const LChar* b, unsigned length) { return equalIgnoringCase(b, reinterpret_cast(a), length); } -inline bool equalIgnoringCase(const UChar* a, const UChar* b, int length) -{ - ASSERT(length >= 0); - return !Unicode::umemcasecmp(a, b, length); +inline bool equalIgnoringCase(const UChar* a, const char* b, unsigned length) { + return equalIgnoringCase(a, reinterpret_cast(b), length); +} +inline bool equalIgnoringCase(const LChar* a, const UChar* b, unsigned length) { + return equalIgnoringCase(b, a, length); +} +inline bool equalIgnoringCase(const char* a, const UChar* b, unsigned length) { + return equalIgnoringCase(b, reinterpret_cast(a), length); +} +inline bool equalIgnoringCase(const char* a, const LChar* b, unsigned length) { + return equalIgnoringCase(b, reinterpret_cast(a), length); +} +inline bool equalIgnoringCase(const UChar* a, const UChar* b, int length) { + ASSERT(length >= 0); + return !Unicode::umemcasecmp(a, b, length); } WTF_EXPORT bool equalIgnoringCaseNonNull(const StringImpl*, const StringImpl*); WTF_EXPORT bool equalIgnoringNullity(StringImpl*, StringImpl*); -template -inline size_t find(const CharacterType* characters, unsigned length, CharacterType matchCharacter, unsigned index = 0) -{ - while (index < length) { - if (characters[index] == matchCharacter) - return index; - ++index; - } +template +inline size_t find(const CharacterType* characters, + unsigned length, + CharacterType matchCharacter, + unsigned index = 0) { + while (index < length) { + if (characters[index] == matchCharacter) + return index; + ++index; + } + return kNotFound; +} + +ALWAYS_INLINE size_t find(const UChar* characters, + unsigned length, + LChar matchCharacter, + unsigned index = 0) { + return find(characters, length, static_cast(matchCharacter), index); +} + +inline size_t find(const LChar* characters, + unsigned length, + UChar matchCharacter, + unsigned index = 0) { + if (matchCharacter & ~0xFF) return kNotFound; + return find(characters, length, static_cast(matchCharacter), index); } -ALWAYS_INLINE size_t find(const UChar* characters, unsigned length, LChar matchCharacter, unsigned index = 0) -{ - return find(characters, length, static_cast(matchCharacter), index); +inline size_t find(const LChar* characters, + unsigned length, + CharacterMatchFunctionPtr matchFunction, + unsigned index = 0) { + while (index < length) { + if (matchFunction(characters[index])) + return index; + ++index; + } + return kNotFound; } -inline size_t find(const LChar* characters, unsigned length, UChar matchCharacter, unsigned index = 0) -{ - if (matchCharacter & ~0xFF) - return kNotFound; - return find(characters, length, static_cast(matchCharacter), index); +inline size_t find(const UChar* characters, + unsigned length, + CharacterMatchFunctionPtr matchFunction, + unsigned index = 0) { + while (index < length) { + if (matchFunction(characters[index])) + return index; + ++index; + } + return kNotFound; } -inline size_t find(const LChar* characters, unsigned length, CharacterMatchFunctionPtr matchFunction, unsigned index = 0) -{ - while (index < length) { - if (matchFunction(characters[index])) - return index; - ++index; +template +inline size_t findNextLineStart(const CharacterType* characters, + unsigned length, + unsigned index = 0) { + while (index < length) { + CharacterType c = characters[index++]; + if ((c != '\n') && (c != '\r')) + continue; + + // There can only be a start of a new line if there are more characters + // beyond the current character. + if (index < length) { + // The 3 common types of line terminators are 1. \r\n (Windows), + // 2. \r (old MacOS) and 3. \n (Unix'es). + + if (c == '\n') + return index; // Case 3: just \n. + + CharacterType c2 = characters[index]; + if (c2 != '\n') + return index; // Case 2: just \r. + + // Case 1: \r\n. + // But, there's only a start of a new line if there are more + // characters beyond the \r\n. + if (++index < length) + return index; } - return kNotFound; + } + return kNotFound; } -inline size_t find(const UChar* characters, unsigned length, CharacterMatchFunctionPtr matchFunction, unsigned index = 0) -{ - while (index < length) { - if (matchFunction(characters[index])) - return index; - ++index; - } +template +inline size_t reverseFindLineTerminator(const CharacterType* characters, + unsigned length, + unsigned index = UINT_MAX) { + if (!length) return kNotFound; + if (index >= length) + index = length - 1; + CharacterType c = characters[index]; + while ((c != '\n') && (c != '\r')) { + if (!index--) + return kNotFound; + c = characters[index]; + } + return index; } -template -inline size_t findNextLineStart(const CharacterType* characters, unsigned length, unsigned index = 0) -{ - while (index < length) { - CharacterType c = characters[index++]; - if ((c != '\n') && (c != '\r')) - continue; - - // There can only be a start of a new line if there are more characters - // beyond the current character. - if (index < length) { - // The 3 common types of line terminators are 1. \r\n (Windows), - // 2. \r (old MacOS) and 3. \n (Unix'es). - - if (c == '\n') - return index; // Case 3: just \n. - - CharacterType c2 = characters[index]; - if (c2 != '\n') - return index; // Case 2: just \r. - - // Case 1: \r\n. - // But, there's only a start of a new line if there are more - // characters beyond the \r\n. - if (++index < length) - return index; - } - } +template +inline size_t reverseFind(const CharacterType* characters, + unsigned length, + CharacterType matchCharacter, + unsigned index = UINT_MAX) { + if (!length) return kNotFound; + if (index >= length) + index = length - 1; + while (characters[index] != matchCharacter) { + if (!index--) + return kNotFound; + } + return index; } -template -inline size_t reverseFindLineTerminator(const CharacterType* characters, unsigned length, unsigned index = UINT_MAX) -{ - if (!length) - return kNotFound; - if (index >= length) - index = length - 1; - CharacterType c = characters[index]; - while ((c != '\n') && (c != '\r')) { - if (!index--) - return kNotFound; - c = characters[index]; - } - return index; -} - -template -inline size_t reverseFind(const CharacterType* characters, unsigned length, CharacterType matchCharacter, unsigned index = UINT_MAX) -{ - if (!length) - return kNotFound; - if (index >= length) - index = length - 1; - while (characters[index] != matchCharacter) { - if (!index--) - return kNotFound; - } - return index; +ALWAYS_INLINE size_t reverseFind(const UChar* characters, + unsigned length, + LChar matchCharacter, + unsigned index = UINT_MAX) { + return reverseFind(characters, length, static_cast(matchCharacter), + index); } -ALWAYS_INLINE size_t reverseFind(const UChar* characters, unsigned length, LChar matchCharacter, unsigned index = UINT_MAX) -{ - return reverseFind(characters, length, static_cast(matchCharacter), index); +inline size_t reverseFind(const LChar* characters, + unsigned length, + UChar matchCharacter, + unsigned index = UINT_MAX) { + if (matchCharacter & ~0xFF) + return kNotFound; + return reverseFind(characters, length, static_cast(matchCharacter), + index); } -inline size_t reverseFind(const LChar* characters, unsigned length, UChar matchCharacter, unsigned index = UINT_MAX) -{ - if (matchCharacter & ~0xFF) - return kNotFound; - return reverseFind(characters, length, static_cast(matchCharacter), index); +inline size_t StringImpl::find(LChar character, unsigned start) { + if (is8Bit()) + return WTF::find(characters8(), m_length, character, start); + return WTF::find(characters16(), m_length, character, start); } -inline size_t StringImpl::find(LChar character, unsigned start) -{ - if (is8Bit()) - return WTF::find(characters8(), m_length, character, start); - return WTF::find(characters16(), m_length, character, start); +ALWAYS_INLINE size_t StringImpl::find(char character, unsigned start) { + return find(static_cast(character), start); } -ALWAYS_INLINE size_t StringImpl::find(char character, unsigned start) -{ - return find(static_cast(character), start); +inline size_t StringImpl::find(UChar character, unsigned start) { + if (is8Bit()) + return WTF::find(characters8(), m_length, character, start); + return WTF::find(characters16(), m_length, character, start); } -inline size_t StringImpl::find(UChar character, unsigned start) -{ - if (is8Bit()) - return WTF::find(characters8(), m_length, character, start); - return WTF::find(characters16(), m_length, character, start); -} - -inline unsigned lengthOfNullTerminatedString(const UChar* string) -{ - size_t length = 0; - while (string[length] != UChar(0)) - ++length; - RELEASE_ASSERT(length <= std::numeric_limits::max()); - return static_cast(length); -} - -template -bool equalIgnoringNullity(const Vector& a, StringImpl* b) -{ - if (!b) - return !a.size(); - if (a.size() != b->length()) - return false; - if (b->is8Bit()) - return equal(a.data(), b->characters8(), b->length()); - return equal(a.data(), b->characters16(), b->length()); -} - -template -static inline int codePointCompare(unsigned l1, unsigned l2, const CharacterType1* c1, const CharacterType2* c2) -{ - const unsigned lmin = l1 < l2 ? l1 : l2; - unsigned pos = 0; - while (pos < lmin && *c1 == *c2) { - ++c1; - ++c2; - ++pos; - } - - if (pos < lmin) - return (c1[0] > c2[0]) ? 1 : -1; +inline unsigned lengthOfNullTerminatedString(const UChar* string) { + size_t length = 0; + while (string[length] != UChar(0)) + ++length; + RELEASE_ASSERT(length <= std::numeric_limits::max()); + return static_cast(length); +} - if (l1 == l2) - return 0; +template +bool equalIgnoringNullity(const Vector& a, + StringImpl* b) { + if (!b) + return !a.size(); + if (a.size() != b->length()) + return false; + if (b->is8Bit()) + return equal(a.data(), b->characters8(), b->length()); + return equal(a.data(), b->characters16(), b->length()); +} - return (l1 > l2) ? 1 : -1; +template +static inline int codePointCompare(unsigned l1, + unsigned l2, + const CharacterType1* c1, + const CharacterType2* c2) { + const unsigned lmin = l1 < l2 ? l1 : l2; + unsigned pos = 0; + while (pos < lmin && *c1 == *c2) { + ++c1; + ++c2; + ++pos; + } + + if (pos < lmin) + return (c1[0] > c2[0]) ? 1 : -1; + + if (l1 == l2) + return 0; + + return (l1 > l2) ? 1 : -1; } -static inline int codePointCompare8(const StringImpl* string1, const StringImpl* string2) -{ - return codePointCompare(string1->length(), string2->length(), string1->characters8(), string2->characters8()); +static inline int codePointCompare8(const StringImpl* string1, + const StringImpl* string2) { + return codePointCompare(string1->length(), string2->length(), + string1->characters8(), string2->characters8()); } -static inline int codePointCompare16(const StringImpl* string1, const StringImpl* string2) -{ - return codePointCompare(string1->length(), string2->length(), string1->characters16(), string2->characters16()); +static inline int codePointCompare16(const StringImpl* string1, + const StringImpl* string2) { + return codePointCompare(string1->length(), string2->length(), + string1->characters16(), string2->characters16()); } -static inline int codePointCompare8To16(const StringImpl* string1, const StringImpl* string2) -{ - return codePointCompare(string1->length(), string2->length(), string1->characters8(), string2->characters16()); +static inline int codePointCompare8To16(const StringImpl* string1, + const StringImpl* string2) { + return codePointCompare(string1->length(), string2->length(), + string1->characters8(), string2->characters16()); } -static inline int codePointCompare(const StringImpl* string1, const StringImpl* string2) -{ - if (!string1) - return (string2 && string2->length()) ? -1 : 0; +static inline int codePointCompare(const StringImpl* string1, + const StringImpl* string2) { + if (!string1) + return (string2 && string2->length()) ? -1 : 0; - if (!string2) - return string1->length() ? 1 : 0; + if (!string2) + return string1->length() ? 1 : 0; - bool string1Is8Bit = string1->is8Bit(); - bool string2Is8Bit = string2->is8Bit(); - if (string1Is8Bit) { - if (string2Is8Bit) - return codePointCompare8(string1, string2); - return codePointCompare8To16(string1, string2); - } + bool string1Is8Bit = string1->is8Bit(); + bool string2Is8Bit = string2->is8Bit(); + if (string1Is8Bit) { if (string2Is8Bit) - return -codePointCompare8To16(string2, string1); - return codePointCompare16(string1, string2); + return codePointCompare8(string1, string2); + return codePointCompare8To16(string1, string2); + } + if (string2Is8Bit) + return -codePointCompare8To16(string2, string1); + return codePointCompare16(string1, string2); } -static inline bool isSpaceOrNewline(UChar c) -{ - // Use isASCIISpace() for basic Latin-1. - // This will include newlines, which aren't included in Unicode DirWS. - return c <= 0x7F ? WTF::isASCIISpace(c) : WTF::Unicode::direction(c) == WTF::Unicode::WhiteSpaceNeutral; +static inline bool isSpaceOrNewline(UChar c) { + // Use isASCIISpace() for basic Latin-1. + // This will include newlines, which aren't included in Unicode DirWS. + return c <= 0x7F + ? WTF::isASCIISpace(c) + : WTF::Unicode::direction(c) == WTF::Unicode::WhiteSpaceNeutral; } -inline PassRefPtr StringImpl::isolatedCopy() const -{ - if (is8Bit()) - return create(characters8(), m_length); - return create(characters16(), m_length); +inline PassRefPtr StringImpl::isolatedCopy() const { + if (is8Bit()) + return create(characters8(), m_length); + return create(characters16(), m_length); } struct StringHash; // StringHash is the default hash for StringImpl* and RefPtr -template struct DefaultHash; -template<> struct DefaultHash { - typedef StringHash Hash; +template +struct DefaultHash; +template <> +struct DefaultHash { + typedef StringHash Hash; }; -template<> struct DefaultHash > { - typedef StringHash Hash; +template <> +struct DefaultHash> { + typedef StringHash Hash; }; -} +} // namespace WTF using WTF::StringImpl; +using WTF::TextCaseInsensitive; +using WTF::TextCaseSensitive; +using WTF::TextCaseSensitivity; using WTF::equal; using WTF::equalNonNull; -using WTF::TextCaseSensitivity; -using WTF::TextCaseSensitive; -using WTF::TextCaseInsensitive; #endif // SKY_ENGINE_WTF_TEXT_STRINGIMPL_H_ diff --git a/sky/engine/wtf/text/StringImplTest.cpp b/sky/engine/wtf/text/StringImplTest.cpp index a15c091ff9853..45099248b5150 100644 --- a/sky/engine/wtf/text/StringImplTest.cpp +++ b/sky/engine/wtf/text/StringImplTest.cpp @@ -23,17 +23,15 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ - #include #include "flutter/sky/engine/wtf/text/StringImpl.h" #include "flutter/sky/engine/wtf/text/WTFString.h" namespace { -TEST(WTF, StringImplCreate8Bit) -{ - RefPtr testStringImpl = StringImpl::create("1224"); - ASSERT_TRUE(testStringImpl->is8Bit()); +TEST(WTF, StringImplCreate8Bit) { + RefPtr testStringImpl = StringImpl::create("1224"); + ASSERT_TRUE(testStringImpl->is8Bit()); } -} // namespace +} // namespace diff --git a/sky/engine/wtf/text/StringOperators.h b/sky/engine/wtf/text/StringOperators.h index d0a2d65b3dc3d..2fe9bf3cd4ac9 100644 --- a/sky/engine/wtf/text/StringOperators.h +++ b/sky/engine/wtf/text/StringOperators.h @@ -1,5 +1,6 @@ /* - * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. + * All rights reserved. * Copyright (C) Research In Motion Limited 2011. All rights reserved. * * This library is free software; you can redistribute it and/or @@ -24,124 +25,115 @@ namespace WTF { -template +template class StringAppend { -public: - StringAppend(StringType1 string1, StringType2 string2) - : m_string1(string1) - , m_string2(string2) - { - } - - operator String() const - { - return String(makeString(m_string1, m_string2)); - } - - operator AtomicString() const - { - return AtomicString(makeString(m_string1, m_string2)); - } - - bool is8Bit() - { - StringTypeAdapter adapter1(m_string1); - StringTypeAdapter adapter2(m_string2); - return adapter1.is8Bit() && adapter2.is8Bit(); - } - - void writeTo(LChar* destination) - { - ASSERT(is8Bit()); - StringTypeAdapter adapter1(m_string1); - StringTypeAdapter adapter2(m_string2); - adapter1.writeTo(destination); - adapter2.writeTo(destination + adapter1.length()); - } - - void writeTo(UChar* destination) - { - StringTypeAdapter adapter1(m_string1); - StringTypeAdapter adapter2(m_string2); - adapter1.writeTo(destination); - adapter2.writeTo(destination + adapter1.length()); - } - - unsigned length() - { - StringTypeAdapter adapter1(m_string1); - StringTypeAdapter adapter2(m_string2); - return adapter1.length() + adapter2.length(); - } - -private: - StringType1 m_string1; - StringType2 m_string2; + public: + StringAppend(StringType1 string1, StringType2 string2) + : m_string1(string1), m_string2(string2) {} + + operator String() const { return String(makeString(m_string1, m_string2)); } + + operator AtomicString() const { + return AtomicString(makeString(m_string1, m_string2)); + } + + bool is8Bit() { + StringTypeAdapter adapter1(m_string1); + StringTypeAdapter adapter2(m_string2); + return adapter1.is8Bit() && adapter2.is8Bit(); + } + + void writeTo(LChar* destination) { + ASSERT(is8Bit()); + StringTypeAdapter adapter1(m_string1); + StringTypeAdapter adapter2(m_string2); + adapter1.writeTo(destination); + adapter2.writeTo(destination + adapter1.length()); + } + + void writeTo(UChar* destination) { + StringTypeAdapter adapter1(m_string1); + StringTypeAdapter adapter2(m_string2); + adapter1.writeTo(destination); + adapter2.writeTo(destination + adapter1.length()); + } + + unsigned length() { + StringTypeAdapter adapter1(m_string1); + StringTypeAdapter adapter2(m_string2); + return adapter1.length() + adapter2.length(); + } + + private: + StringType1 m_string1; + StringType2 m_string2; }; -template -class StringTypeAdapter > { -public: - StringTypeAdapter >(StringAppend& buffer) - : m_buffer(buffer) - { - } +template +class StringTypeAdapter> { + public: + StringTypeAdapter>( + StringAppend& buffer) + : m_buffer(buffer) {} - unsigned length() { return m_buffer.length(); } + unsigned length() { return m_buffer.length(); } - bool is8Bit() { return m_buffer.is8Bit(); } + bool is8Bit() { return m_buffer.is8Bit(); } - void writeTo(LChar* destination) { m_buffer.writeTo(destination); } - void writeTo(UChar* destination) { m_buffer.writeTo(destination); } + void writeTo(LChar* destination) { m_buffer.writeTo(destination); } + void writeTo(UChar* destination) { m_buffer.writeTo(destination); } -private: - StringAppend& m_buffer; + private: + StringAppend& m_buffer; }; -inline StringAppend operator+(const char* string1, const String& string2) -{ - return StringAppend(string1, string2); +inline StringAppend operator+(const char* string1, + const String& string2) { + return StringAppend(string1, string2); } -inline StringAppend operator+(const char* string1, const AtomicString& string2) -{ - return StringAppend(string1, string2); +inline StringAppend operator+( + const char* string1, + const AtomicString& string2) { + return StringAppend(string1, string2); } -template -inline StringAppend > operator+(const char* string1, const StringAppend& string2) -{ - return StringAppend >(string1, string2); +template +inline StringAppend> operator+( + const char* string1, + const StringAppend& string2) { + return StringAppend>(string1, string2); } -inline StringAppend operator+(const UChar* string1, const String& string2) -{ - return StringAppend(string1, string2); +inline StringAppend operator+(const UChar* string1, + const String& string2) { + return StringAppend(string1, string2); } -inline StringAppend operator+(const UChar* string1, const AtomicString& string2) -{ - return StringAppend(string1, string2); +inline StringAppend operator+( + const UChar* string1, + const AtomicString& string2) { + return StringAppend(string1, string2); } -template -inline StringAppend > operator+(const UChar* string1, const StringAppend& string2) -{ - return StringAppend >(string1, string2); +template +inline StringAppend> operator+( + const UChar* string1, + const StringAppend& string2) { + return StringAppend>(string1, string2); } -template -StringAppend operator+(const String& string1, T string2) -{ - return StringAppend(string1, string2); +template +StringAppend operator+(const String& string1, T string2) { + return StringAppend(string1, string2); } -template -StringAppend, W> operator+(const StringAppend& string1, W string2) -{ - return StringAppend, W>(string1, string2); +template +StringAppend, W> operator+(const StringAppend& string1, + W string2) { + return StringAppend, W>(string1, string2); } -} // namespace WTF +} // namespace WTF #endif // SKY_ENGINE_WTF_TEXT_STRINGOPERATORS_H_ diff --git a/sky/engine/wtf/text/StringOperatorsTest.cpp b/sky/engine/wtf/text/StringOperatorsTest.cpp index e7cad587a0eb7..f87b9b1c94ebc 100644 --- a/sky/engine/wtf/text/StringOperatorsTest.cpp +++ b/sky/engine/wtf/text/StringOperatorsTest.cpp @@ -23,7 +23,6 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ - #define WTF_STRINGTYPEADAPTER_COPIED_WTF_STRING() (++wtfStringCopyCount) static int wtfStringCopyCount; @@ -33,120 +32,153 @@ static int wtfStringCopyCount; namespace { -#define EXPECT_N_WTF_STRING_COPIES(count, expr) \ - do { \ - wtfStringCopyCount = 0; \ - String __testString = expr; \ - (void)__testString; \ - EXPECT_EQ(count, wtfStringCopyCount) << #expr; \ - } while (false) - -TEST(WTF, DISABLED_StringOperators) -{ - String string("String"); - AtomicString atomicString("AtomicString"); - const char* literal = "ASCIILiteral"; - - EXPECT_EQ(0, wtfStringCopyCount); - - EXPECT_N_WTF_STRING_COPIES(2, string + string); - EXPECT_N_WTF_STRING_COPIES(2, string + atomicString); - EXPECT_N_WTF_STRING_COPIES(2, atomicString + string); - EXPECT_N_WTF_STRING_COPIES(2, atomicString + atomicString); - - EXPECT_N_WTF_STRING_COPIES(1, "C string" + string); - EXPECT_N_WTF_STRING_COPIES(1, string + "C string"); - EXPECT_N_WTF_STRING_COPIES(1, "C string" + atomicString); - EXPECT_N_WTF_STRING_COPIES(1, atomicString + "C string"); - - EXPECT_N_WTF_STRING_COPIES(1, literal + string); - EXPECT_N_WTF_STRING_COPIES(1, string + literal); - EXPECT_N_WTF_STRING_COPIES(1, literal + atomicString); - EXPECT_N_WTF_STRING_COPIES(1, atomicString + literal); - - EXPECT_N_WTF_STRING_COPIES(2, "C string" + string + "C string" + string); - EXPECT_N_WTF_STRING_COPIES(2, "C string" + (string + "C string" + string)); - EXPECT_N_WTF_STRING_COPIES(2, ("C string" + string) + ("C string" + string)); - EXPECT_N_WTF_STRING_COPIES(2, string + "C string" + string + "C string"); - EXPECT_N_WTF_STRING_COPIES(2, string + ("C string" + string + "C string")); - EXPECT_N_WTF_STRING_COPIES(2, (string + "C string") + (string + "C string")); - - EXPECT_N_WTF_STRING_COPIES(2, literal + string + literal + string); - EXPECT_N_WTF_STRING_COPIES(2, literal + (string + literal + string)); - EXPECT_N_WTF_STRING_COPIES(2, (literal + string) + (literal + string)); - EXPECT_N_WTF_STRING_COPIES(2, string + literal + string + literal); - EXPECT_N_WTF_STRING_COPIES(2, string + (literal + string + literal)); - EXPECT_N_WTF_STRING_COPIES(2, (string + literal) + (string + literal)); - - EXPECT_N_WTF_STRING_COPIES(2, literal + string + "C string" + string); - EXPECT_N_WTF_STRING_COPIES(2, literal + (string + "C string" + string)); - EXPECT_N_WTF_STRING_COPIES(2, (literal + string) + ("C string" + string)); - EXPECT_N_WTF_STRING_COPIES(2, "C string" + string + literal + string); - EXPECT_N_WTF_STRING_COPIES(2, "C string" + (string + literal + string)); - EXPECT_N_WTF_STRING_COPIES(2, ("C string" + string) + (literal + string)); - - EXPECT_N_WTF_STRING_COPIES(2, literal + atomicString + "C string" + atomicString); - EXPECT_N_WTF_STRING_COPIES(2, literal + (atomicString + "C string" + atomicString)); - EXPECT_N_WTF_STRING_COPIES(2, (literal + atomicString) + ("C string" + atomicString)); - EXPECT_N_WTF_STRING_COPIES(2, "C string" + atomicString + literal + atomicString); - EXPECT_N_WTF_STRING_COPIES(2, "C string" + (atomicString + literal + atomicString)); - EXPECT_N_WTF_STRING_COPIES(2, ("C string" + atomicString) + (literal + atomicString)); - - EXPECT_N_WTF_STRING_COPIES(2, literal + atomicString + "C string" + string); - EXPECT_N_WTF_STRING_COPIES(2, literal + (atomicString + "C string" + string)); - EXPECT_N_WTF_STRING_COPIES(2, (literal + atomicString) + ("C string" + string)); - EXPECT_N_WTF_STRING_COPIES(2, "C string" + atomicString + literal + string); - EXPECT_N_WTF_STRING_COPIES(2, "C string" + (atomicString + literal + string)); - EXPECT_N_WTF_STRING_COPIES(2, ("C string" + atomicString) + (literal + string)); - - EXPECT_N_WTF_STRING_COPIES(2, literal + string + "C string" + atomicString); - EXPECT_N_WTF_STRING_COPIES(2, literal + (string + "C string" + atomicString)); - EXPECT_N_WTF_STRING_COPIES(2, (literal + string) + ("C string" + atomicString)); - EXPECT_N_WTF_STRING_COPIES(2, "C string" + string + literal + atomicString); - EXPECT_N_WTF_STRING_COPIES(2, "C string" + (string + literal + atomicString)); - EXPECT_N_WTF_STRING_COPIES(2, ("C string" + string) + (literal + atomicString)); - - EXPECT_N_WTF_STRING_COPIES(2, "C string" + atomicString + "C string" + atomicString); - EXPECT_N_WTF_STRING_COPIES(2, "C string" + (atomicString + "C string" + atomicString)); - EXPECT_N_WTF_STRING_COPIES(2, ("C string" + atomicString) + ("C string" + atomicString)); - EXPECT_N_WTF_STRING_COPIES(2, atomicString + "C string" + atomicString + "C string"); - EXPECT_N_WTF_STRING_COPIES(2, atomicString + ("C string" + atomicString + "C string")); - EXPECT_N_WTF_STRING_COPIES(2, (atomicString + "C string") + (atomicString + "C string")); - - EXPECT_N_WTF_STRING_COPIES(2, literal + atomicString + literal + atomicString); - EXPECT_N_WTF_STRING_COPIES(2, literal + (atomicString + literal + atomicString)); - EXPECT_N_WTF_STRING_COPIES(2, (literal + atomicString) + (literal + atomicString)); - EXPECT_N_WTF_STRING_COPIES(2, atomicString + literal + atomicString + literal); - EXPECT_N_WTF_STRING_COPIES(2, atomicString + (literal + atomicString + literal)); - EXPECT_N_WTF_STRING_COPIES(2, (atomicString + literal) + (atomicString + literal)); - - EXPECT_N_WTF_STRING_COPIES(2, "C string" + string + "C string" + atomicString); - EXPECT_N_WTF_STRING_COPIES(2, "C string" + (string + "C string" + atomicString)); - EXPECT_N_WTF_STRING_COPIES(2, ("C string" + string) + ("C string" + atomicString)); - EXPECT_N_WTF_STRING_COPIES(2, string + "C string" + atomicString + "C string"); - EXPECT_N_WTF_STRING_COPIES(2, string + ("C string" + atomicString + "C string")); - EXPECT_N_WTF_STRING_COPIES(2, (string + "C string") + (atomicString + "C string")); - - EXPECT_N_WTF_STRING_COPIES(2, literal + string + literal + atomicString); - EXPECT_N_WTF_STRING_COPIES(2, literal + (string + literal + atomicString)); - EXPECT_N_WTF_STRING_COPIES(2, (literal + string) + (literal + atomicString)); - EXPECT_N_WTF_STRING_COPIES(2, string + literal + atomicString + literal); - EXPECT_N_WTF_STRING_COPIES(2, string + (literal + atomicString + literal)); - EXPECT_N_WTF_STRING_COPIES(2, (string + literal) + (atomicString + literal)); - - EXPECT_N_WTF_STRING_COPIES(2, "C string" + atomicString + "C string" + string); - EXPECT_N_WTF_STRING_COPIES(2, "C string" + (atomicString + "C string" + string)); - EXPECT_N_WTF_STRING_COPIES(2, ("C string" + atomicString) + ("C string" + string)); - EXPECT_N_WTF_STRING_COPIES(2, atomicString + "C string" + string + "C string"); - EXPECT_N_WTF_STRING_COPIES(2, atomicString + ("C string" + string + "C string")); - EXPECT_N_WTF_STRING_COPIES(2, (atomicString + "C string") + (string + "C string")); - - EXPECT_N_WTF_STRING_COPIES(2, literal + atomicString + literal + string); - EXPECT_N_WTF_STRING_COPIES(2, literal + (atomicString + literal + string)); - EXPECT_N_WTF_STRING_COPIES(2, (literal + atomicString) + (literal + string)); - EXPECT_N_WTF_STRING_COPIES(2, atomicString + literal + string + literal); - EXPECT_N_WTF_STRING_COPIES(2, atomicString + (literal + string + literal)); - EXPECT_N_WTF_STRING_COPIES(2, (atomicString + literal) + (string + literal)); +#define EXPECT_N_WTF_STRING_COPIES(count, expr) \ + do { \ + wtfStringCopyCount = 0; \ + String __testString = expr; \ + (void)__testString; \ + EXPECT_EQ(count, wtfStringCopyCount) << #expr; \ + } while (false) + +TEST(WTF, DISABLED_StringOperators) { + String string("String"); + AtomicString atomicString("AtomicString"); + const char* literal = "ASCIILiteral"; + + EXPECT_EQ(0, wtfStringCopyCount); + + EXPECT_N_WTF_STRING_COPIES(2, string + string); + EXPECT_N_WTF_STRING_COPIES(2, string + atomicString); + EXPECT_N_WTF_STRING_COPIES(2, atomicString + string); + EXPECT_N_WTF_STRING_COPIES(2, atomicString + atomicString); + + EXPECT_N_WTF_STRING_COPIES(1, "C string" + string); + EXPECT_N_WTF_STRING_COPIES(1, string + "C string"); + EXPECT_N_WTF_STRING_COPIES(1, "C string" + atomicString); + EXPECT_N_WTF_STRING_COPIES(1, atomicString + "C string"); + + EXPECT_N_WTF_STRING_COPIES(1, literal + string); + EXPECT_N_WTF_STRING_COPIES(1, string + literal); + EXPECT_N_WTF_STRING_COPIES(1, literal + atomicString); + EXPECT_N_WTF_STRING_COPIES(1, atomicString + literal); + + EXPECT_N_WTF_STRING_COPIES(2, "C string" + string + "C string" + string); + EXPECT_N_WTF_STRING_COPIES(2, "C string" + (string + "C string" + string)); + EXPECT_N_WTF_STRING_COPIES(2, ("C string" + string) + ("C string" + string)); + EXPECT_N_WTF_STRING_COPIES(2, string + "C string" + string + "C string"); + EXPECT_N_WTF_STRING_COPIES(2, string + ("C string" + string + "C string")); + EXPECT_N_WTF_STRING_COPIES(2, (string + "C string") + (string + "C string")); + + EXPECT_N_WTF_STRING_COPIES(2, literal + string + literal + string); + EXPECT_N_WTF_STRING_COPIES(2, literal + (string + literal + string)); + EXPECT_N_WTF_STRING_COPIES(2, (literal + string) + (literal + string)); + EXPECT_N_WTF_STRING_COPIES(2, string + literal + string + literal); + EXPECT_N_WTF_STRING_COPIES(2, string + (literal + string + literal)); + EXPECT_N_WTF_STRING_COPIES(2, (string + literal) + (string + literal)); + + EXPECT_N_WTF_STRING_COPIES(2, literal + string + "C string" + string); + EXPECT_N_WTF_STRING_COPIES(2, literal + (string + "C string" + string)); + EXPECT_N_WTF_STRING_COPIES(2, (literal + string) + ("C string" + string)); + EXPECT_N_WTF_STRING_COPIES(2, "C string" + string + literal + string); + EXPECT_N_WTF_STRING_COPIES(2, "C string" + (string + literal + string)); + EXPECT_N_WTF_STRING_COPIES(2, ("C string" + string) + (literal + string)); + + EXPECT_N_WTF_STRING_COPIES( + 2, literal + atomicString + "C string" + atomicString); + EXPECT_N_WTF_STRING_COPIES( + 2, literal + (atomicString + "C string" + atomicString)); + EXPECT_N_WTF_STRING_COPIES( + 2, (literal + atomicString) + ("C string" + atomicString)); + EXPECT_N_WTF_STRING_COPIES( + 2, "C string" + atomicString + literal + atomicString); + EXPECT_N_WTF_STRING_COPIES( + 2, "C string" + (atomicString + literal + atomicString)); + EXPECT_N_WTF_STRING_COPIES( + 2, ("C string" + atomicString) + (literal + atomicString)); + + EXPECT_N_WTF_STRING_COPIES(2, literal + atomicString + "C string" + string); + EXPECT_N_WTF_STRING_COPIES(2, literal + (atomicString + "C string" + string)); + EXPECT_N_WTF_STRING_COPIES(2, + (literal + atomicString) + ("C string" + string)); + EXPECT_N_WTF_STRING_COPIES(2, "C string" + atomicString + literal + string); + EXPECT_N_WTF_STRING_COPIES(2, "C string" + (atomicString + literal + string)); + EXPECT_N_WTF_STRING_COPIES(2, + ("C string" + atomicString) + (literal + string)); + + EXPECT_N_WTF_STRING_COPIES(2, literal + string + "C string" + atomicString); + EXPECT_N_WTF_STRING_COPIES(2, literal + (string + "C string" + atomicString)); + EXPECT_N_WTF_STRING_COPIES(2, + (literal + string) + ("C string" + atomicString)); + EXPECT_N_WTF_STRING_COPIES(2, "C string" + string + literal + atomicString); + EXPECT_N_WTF_STRING_COPIES(2, "C string" + (string + literal + atomicString)); + EXPECT_N_WTF_STRING_COPIES(2, + ("C string" + string) + (literal + atomicString)); + + EXPECT_N_WTF_STRING_COPIES( + 2, "C string" + atomicString + "C string" + atomicString); + EXPECT_N_WTF_STRING_COPIES( + 2, "C string" + (atomicString + "C string" + atomicString)); + EXPECT_N_WTF_STRING_COPIES( + 2, ("C string" + atomicString) + ("C string" + atomicString)); + EXPECT_N_WTF_STRING_COPIES( + 2, atomicString + "C string" + atomicString + "C string"); + EXPECT_N_WTF_STRING_COPIES( + 2, atomicString + ("C string" + atomicString + "C string")); + EXPECT_N_WTF_STRING_COPIES( + 2, (atomicString + "C string") + (atomicString + "C string")); + + EXPECT_N_WTF_STRING_COPIES(2, + literal + atomicString + literal + atomicString); + EXPECT_N_WTF_STRING_COPIES(2, + literal + (atomicString + literal + atomicString)); + EXPECT_N_WTF_STRING_COPIES( + 2, (literal + atomicString) + (literal + atomicString)); + EXPECT_N_WTF_STRING_COPIES(2, + atomicString + literal + atomicString + literal); + EXPECT_N_WTF_STRING_COPIES(2, + atomicString + (literal + atomicString + literal)); + EXPECT_N_WTF_STRING_COPIES( + 2, (atomicString + literal) + (atomicString + literal)); + + EXPECT_N_WTF_STRING_COPIES(2, + "C string" + string + "C string" + atomicString); + EXPECT_N_WTF_STRING_COPIES(2, + "C string" + (string + "C string" + atomicString)); + EXPECT_N_WTF_STRING_COPIES( + 2, ("C string" + string) + ("C string" + atomicString)); + EXPECT_N_WTF_STRING_COPIES(2, + string + "C string" + atomicString + "C string"); + EXPECT_N_WTF_STRING_COPIES(2, + string + ("C string" + atomicString + "C string")); + EXPECT_N_WTF_STRING_COPIES( + 2, (string + "C string") + (atomicString + "C string")); + + EXPECT_N_WTF_STRING_COPIES(2, literal + string + literal + atomicString); + EXPECT_N_WTF_STRING_COPIES(2, literal + (string + literal + atomicString)); + EXPECT_N_WTF_STRING_COPIES(2, (literal + string) + (literal + atomicString)); + EXPECT_N_WTF_STRING_COPIES(2, string + literal + atomicString + literal); + EXPECT_N_WTF_STRING_COPIES(2, string + (literal + atomicString + literal)); + EXPECT_N_WTF_STRING_COPIES(2, (string + literal) + (atomicString + literal)); + + EXPECT_N_WTF_STRING_COPIES(2, + "C string" + atomicString + "C string" + string); + EXPECT_N_WTF_STRING_COPIES(2, + "C string" + (atomicString + "C string" + string)); + EXPECT_N_WTF_STRING_COPIES( + 2, ("C string" + atomicString) + ("C string" + string)); + EXPECT_N_WTF_STRING_COPIES(2, + atomicString + "C string" + string + "C string"); + EXPECT_N_WTF_STRING_COPIES(2, + atomicString + ("C string" + string + "C string")); + EXPECT_N_WTF_STRING_COPIES( + 2, (atomicString + "C string") + (string + "C string")); + + EXPECT_N_WTF_STRING_COPIES(2, literal + atomicString + literal + string); + EXPECT_N_WTF_STRING_COPIES(2, literal + (atomicString + literal + string)); + EXPECT_N_WTF_STRING_COPIES(2, (literal + atomicString) + (literal + string)); + EXPECT_N_WTF_STRING_COPIES(2, atomicString + literal + string + literal); + EXPECT_N_WTF_STRING_COPIES(2, atomicString + (literal + string + literal)); + EXPECT_N_WTF_STRING_COPIES(2, (atomicString + literal) + (string + literal)); } -} // namespace +} // namespace diff --git a/sky/engine/wtf/text/StringStatics.cpp b/sky/engine/wtf/text/StringStatics.cpp index bf6032f821e9b..d1caad63fc77e 100644 --- a/sky/engine/wtf/text/StringStatics.cpp +++ b/sky/engine/wtf/text/StringStatics.cpp @@ -33,40 +33,38 @@ namespace WTF { -StringImpl* StringImpl::empty() -{ - DEFINE_STATIC_LOCAL(StringImpl, emptyString, (ConstructEmptyString)); - WTF_ANNOTATE_BENIGN_RACE(&emptyString, "Benign race on StringImpl::emptyString reference counter"); - return &emptyString; +StringImpl* StringImpl::empty() { + DEFINE_STATIC_LOCAL(StringImpl, emptyString, (ConstructEmptyString)); + WTF_ANNOTATE_BENIGN_RACE( + &emptyString, "Benign race on StringImpl::emptyString reference counter"); + return &emptyString; } -WTF_EXPORT DEFINE_GLOBAL(AtomicString, nullAtom) -WTF_EXPORT DEFINE_GLOBAL(AtomicString, emptyAtom) -WTF_EXPORT DEFINE_GLOBAL(AtomicString, starAtom) +WTF_EXPORT DEFINE_GLOBAL(AtomicString, nullAtom) WTF_EXPORT + DEFINE_GLOBAL(AtomicString, emptyAtom) WTF_EXPORT + DEFINE_GLOBAL(AtomicString, starAtom) -NEVER_INLINE unsigned StringImpl::hashSlowCase() const -{ - if (is8Bit()) - setHash(StringHasher::computeHashAndMaskTop8Bits(characters8(), m_length)); - else - setHash(StringHasher::computeHashAndMaskTop8Bits(characters16(), m_length)); - return existingHash(); + NEVER_INLINE unsigned StringImpl::hashSlowCase() const { + if (is8Bit()) + setHash(StringHasher::computeHashAndMaskTop8Bits(characters8(), m_length)); + else + setHash(StringHasher::computeHashAndMaskTop8Bits(characters16(), m_length)); + return existingHash(); } -void AtomicString::init() -{ - ASSERT(isMainThread()); +void AtomicString::init() { + ASSERT(isMainThread()); - new (NotNull, (void*)&nullAtom) AtomicString; - new (NotNull, (void*)&emptyAtom) AtomicString(""); + new (NotNull, (void*)&nullAtom) AtomicString; + new (NotNull, (void*)&emptyAtom) AtomicString(""); } -void StringStatics::init() -{ - ASSERT(isMainThread()); +void StringStatics::init() { + ASSERT(isMainThread()); - // FIXME: These should be allocated at compile time. - new (NotNull, (void*)&starAtom) AtomicString("*", AtomicString::ConstructFromLiteral); + // FIXME: These should be allocated at compile time. + new (NotNull, (void*)&starAtom) + AtomicString("*", AtomicString::ConstructFromLiteral); } -} +} // namespace WTF diff --git a/sky/engine/wtf/text/StringStatics.h b/sky/engine/wtf/text/StringStatics.h index 9492a453efe33..ac8c7437a9215 100644 --- a/sky/engine/wtf/text/StringStatics.h +++ b/sky/engine/wtf/text/StringStatics.h @@ -36,13 +36,13 @@ namespace WTF { class StringStatics { -public: - WTF_EXPORT static void init(); + public: + WTF_EXPORT static void init(); -private: - StringStatics(); + private: + StringStatics(); }; -} +} // namespace WTF #endif // SKY_ENGINE_WTF_TEXT_STRINGSTATICS_H_ diff --git a/sky/engine/wtf/text/StringUTF8Adaptor.h b/sky/engine/wtf/text/StringUTF8Adaptor.h index 45090ca18127b..4d97930975776 100644 --- a/sky/engine/wtf/text/StringUTF8Adaptor.h +++ b/sky/engine/wtf/text/StringUTF8Adaptor.h @@ -41,45 +41,43 @@ namespace WTF { // separate buffer to hold the data if the String happens to be 8 bit and // contain only ASCII characters. class StringUTF8Adaptor { -public: - enum ShouldNormalize { - DoNotNormalize, - Normalize - }; + public: + enum ShouldNormalize { DoNotNormalize, Normalize }; - explicit StringUTF8Adaptor(const String& string, ShouldNormalize normalize = DoNotNormalize, UnencodableHandling handling = EntitiesForUnencodables) - : m_data(0) - , m_length(0) - { - if (string.isEmpty()) - return; - // Unfortunately, 8 bit WTFStrings are encoded in Latin-1 and GURL uses UTF-8 - // when processing 8 bit strings. If |relative| is entirely ASCII, we luck out - // and can avoid mallocing a new buffer to hold the UTF-8 data because UTF-8 - // and Latin-1 use the same code units for ASCII code points. - if (string.is8Bit() && string.containsOnlyASCII()) { - m_data = reinterpret_cast(string.characters8()); - m_length = string.length(); - } else { - if (normalize == Normalize) - m_utf8Buffer = UTF8Encoding().normalizeAndEncode(string, handling); - else - m_utf8Buffer = string.utf8(); - m_data = m_utf8Buffer.data(); - m_length = m_utf8Buffer.length(); - } + explicit StringUTF8Adaptor( + const String& string, + ShouldNormalize normalize = DoNotNormalize, + UnencodableHandling handling = EntitiesForUnencodables) + : m_data(0), m_length(0) { + if (string.isEmpty()) + return; + // Unfortunately, 8 bit WTFStrings are encoded in Latin-1 and GURL uses + // UTF-8 when processing 8 bit strings. If |relative| is entirely ASCII, we + // luck out and can avoid mallocing a new buffer to hold the UTF-8 data + // because UTF-8 and Latin-1 use the same code units for ASCII code points. + if (string.is8Bit() && string.containsOnlyASCII()) { + m_data = reinterpret_cast(string.characters8()); + m_length = string.length(); + } else { + if (normalize == Normalize) + m_utf8Buffer = UTF8Encoding().normalizeAndEncode(string, handling); + else + m_utf8Buffer = string.utf8(); + m_data = m_utf8Buffer.data(); + m_length = m_utf8Buffer.length(); } + } - const char* data() const { return m_data; } - size_t length() const { return m_length; } + const char* data() const { return m_data; } + size_t length() const { return m_length; } -private: - CString m_utf8Buffer; - const char* m_data; - size_t m_length; + private: + CString m_utf8Buffer; + const char* m_data; + size_t m_length; }; -} // namespace WTF +} // namespace WTF using WTF::StringUTF8Adaptor; diff --git a/sky/engine/wtf/text/StringView.h b/sky/engine/wtf/text/StringView.h index fa361019e11af..1f6da0ec0fc54 100644 --- a/sky/engine/wtf/text/StringView.h +++ b/sky/engine/wtf/text/StringView.h @@ -36,72 +36,57 @@ namespace WTF { class WTF_EXPORT StringView { -public: - StringView() - : m_offset(0) - , m_length(0) - { - } - - explicit StringView(PassRefPtr impl) - : m_impl(impl) - , m_offset(0) - , m_length(m_impl->length()) - { - } - - StringView(PassRefPtr impl, unsigned offset, unsigned length) - : m_impl(impl) - , m_offset(offset) - , m_length(length) - { - ASSERT_WITH_SECURITY_IMPLICATION(offset + length <= m_impl->length()); - } - - void narrow(unsigned offset, unsigned length) - { - ASSERT_WITH_SECURITY_IMPLICATION(offset + length <= m_length); - m_offset += offset; - m_length = length; - } - - bool isEmpty() const { return !m_length; } - unsigned length() const { return m_length; } - - bool is8Bit() const { return m_impl->is8Bit(); } - - const LChar* characters8() const - { - if (!m_impl) - return 0; - ASSERT(is8Bit()); - return m_impl->characters8() + m_offset; - } - - const UChar* characters16() const - { - if (!m_impl) - return 0; - ASSERT(!is8Bit()); - return m_impl->characters16() + m_offset; - } - - PassRefPtr toString() const - { - if (!m_impl) - return m_impl; - if (m_impl->is8Bit()) - return StringImpl::create(characters8(), m_length); - return StringImpl::create(characters16(), m_length); - } - -private: - RefPtr m_impl; - unsigned m_offset; - unsigned m_length; + public: + StringView() : m_offset(0), m_length(0) {} + + explicit StringView(PassRefPtr impl) + : m_impl(impl), m_offset(0), m_length(m_impl->length()) {} + + StringView(PassRefPtr impl, unsigned offset, unsigned length) + : m_impl(impl), m_offset(offset), m_length(length) { + ASSERT_WITH_SECURITY_IMPLICATION(offset + length <= m_impl->length()); + } + + void narrow(unsigned offset, unsigned length) { + ASSERT_WITH_SECURITY_IMPLICATION(offset + length <= m_length); + m_offset += offset; + m_length = length; + } + + bool isEmpty() const { return !m_length; } + unsigned length() const { return m_length; } + + bool is8Bit() const { return m_impl->is8Bit(); } + + const LChar* characters8() const { + if (!m_impl) + return 0; + ASSERT(is8Bit()); + return m_impl->characters8() + m_offset; + } + + const UChar* characters16() const { + if (!m_impl) + return 0; + ASSERT(!is8Bit()); + return m_impl->characters16() + m_offset; + } + + PassRefPtr toString() const { + if (!m_impl) + return m_impl; + if (m_impl->is8Bit()) + return StringImpl::create(characters8(), m_length); + return StringImpl::create(characters16(), m_length); + } + + private: + RefPtr m_impl; + unsigned m_offset; + unsigned m_length; }; -} +} // namespace WTF using WTF::StringView; diff --git a/sky/engine/wtf/text/TextCodec.cpp b/sky/engine/wtf/text/TextCodec.cpp index b34fd8092713a..d7a3b8497889a 100644 --- a/sky/engine/wtf/text/TextCodec.cpp +++ b/sky/engine/wtf/text/TextCodec.cpp @@ -30,27 +30,29 @@ namespace WTF { -TextCodec::~TextCodec() -{ -} +TextCodec::~TextCodec() {} -int TextCodec::getUnencodableReplacement(unsigned codePoint, UnencodableHandling handling, UnencodableReplacementArray replacement) -{ - switch (handling) { - case QuestionMarksForUnencodables: - replacement[0] = '?'; - replacement[1] = 0; - return 1; - case EntitiesForUnencodables: - snprintf(replacement, sizeof(UnencodableReplacementArray), "&#%u;", codePoint); - return static_cast(strlen(replacement)); - case URLEncodedEntitiesForUnencodables: - snprintf(replacement, sizeof(UnencodableReplacementArray), "%%26%%23%u%%3B", codePoint); - return static_cast(strlen(replacement)); - } - ASSERT_NOT_REACHED(); - replacement[0] = 0; - return 0; +int TextCodec::getUnencodableReplacement( + unsigned codePoint, + UnencodableHandling handling, + UnencodableReplacementArray replacement) { + switch (handling) { + case QuestionMarksForUnencodables: + replacement[0] = '?'; + replacement[1] = 0; + return 1; + case EntitiesForUnencodables: + snprintf(replacement, sizeof(UnencodableReplacementArray), "&#%u;", + codePoint); + return static_cast(strlen(replacement)); + case URLEncodedEntitiesForUnencodables: + snprintf(replacement, sizeof(UnencodableReplacementArray), + "%%26%%23%u%%3B", codePoint); + return static_cast(strlen(replacement)); + } + ASSERT_NOT_REACHED(); + replacement[0] = 0; + return 0; } -} // namespace WTF +} // namespace WTF diff --git a/sky/engine/wtf/text/TextCodec.h b/sky/engine/wtf/text/TextCodec.h index 19fc036839007..44b56c7e24c24 100644 --- a/sky/engine/wtf/text/TextCodec.h +++ b/sky/engine/wtf/text/TextCodec.h @@ -40,65 +40,77 @@ class TextEncoding; // Specifies what will happen when a character is encountered that is // not encodable in the character set. enum UnencodableHandling { - // Substitutes the replacement character "?". - QuestionMarksForUnencodables, + // Substitutes the replacement character "?". + QuestionMarksForUnencodables, - // Encodes the character as an XML entity. For example, U+06DE - // would be "۞" (0x6DE = 1758 in octal). - EntitiesForUnencodables, + // Encodes the character as an XML entity. For example, U+06DE + // would be "۞" (0x6DE = 1758 in octal). + EntitiesForUnencodables, - // Encodes the character as en entity as above, but escaped - // non-alphanumeric characters. This is used in URLs. - // For example, U+6DE would be "%26%231758%3B". - URLEncodedEntitiesForUnencodables + // Encodes the character as en entity as above, but escaped + // non-alphanumeric characters. This is used in URLs. + // For example, U+6DE would be "%26%231758%3B". + URLEncodedEntitiesForUnencodables }; typedef char UnencodableReplacementArray[32]; enum FlushBehavior { - // More bytes are coming, don't flush the codec. - DoNotFlush = 0, + // More bytes are coming, don't flush the codec. + DoNotFlush = 0, - // A fetch has hit EOF. Some codecs handle fetches differently, for compat reasons. - FetchEOF, + // A fetch has hit EOF. Some codecs handle fetches differently, for compat + // reasons. + FetchEOF, - // Do a full flush of the codec. - DataEOF + // Do a full flush of the codec. + DataEOF }; COMPILE_ASSERT(!DoNotFlush, DoNotFlush_is_falsy); COMPILE_ASSERT(FetchEOF, FetchEOF_is_truthy); COMPILE_ASSERT(DataEOF, DataEOF_is_truthy); - class TextCodec { - WTF_MAKE_NONCOPYABLE(TextCodec); WTF_MAKE_FAST_ALLOCATED; -public: - TextCodec() { } - virtual ~TextCodec(); - - String decode(const char* str, size_t length, FlushBehavior flush = DoNotFlush) - { - bool ignored; - return decode(str, length, flush, false, ignored); - } - - virtual String decode(const char*, size_t length, FlushBehavior, bool stopOnError, bool& sawError) = 0; - virtual CString encode(const UChar*, size_t length, UnencodableHandling) = 0; - virtual CString encode(const LChar*, size_t length, UnencodableHandling) = 0; - - // Fills a null-terminated string representation of the given - // unencodable character into the given replacement buffer. - // The length of the string (not including the null) will be returned. - static int getUnencodableReplacement(unsigned codePoint, UnencodableHandling, UnencodableReplacementArray); + WTF_MAKE_NONCOPYABLE(TextCodec); + WTF_MAKE_FAST_ALLOCATED; + + public: + TextCodec() {} + virtual ~TextCodec(); + + String decode(const char* str, + size_t length, + FlushBehavior flush = DoNotFlush) { + bool ignored; + return decode(str, length, flush, false, ignored); + } + + virtual String decode(const char*, + size_t length, + FlushBehavior, + bool stopOnError, + bool& sawError) = 0; + virtual CString encode(const UChar*, size_t length, UnencodableHandling) = 0; + virtual CString encode(const LChar*, size_t length, UnencodableHandling) = 0; + + // Fills a null-terminated string representation of the given + // unencodable character into the given replacement buffer. + // The length of the string (not including the null) will be returned. + static int getUnencodableReplacement(unsigned codePoint, + UnencodableHandling, + UnencodableReplacementArray); }; typedef void (*EncodingNameRegistrar)(const char* alias, const char* name); -typedef PassOwnPtr (*NewTextCodecFunction)(const TextEncoding&, const void* additionalData); -typedef void (*TextCodecRegistrar)(const char* name, NewTextCodecFunction, const void* additionalData); +typedef PassOwnPtr ( + *NewTextCodecFunction)(const TextEncoding&, const void* additionalData); +typedef void (*TextCodecRegistrar)(const char* name, + NewTextCodecFunction, + const void* additionalData); -} // namespace WTF +} // namespace WTF using WTF::TextCodec; diff --git a/sky/engine/wtf/text/TextCodecASCIIFastPath.h b/sky/engine/wtf/text/TextCodecASCIIFastPath.h index 3a2963f76bef0..2a33abfdd2c2a 100644 --- a/sky/engine/wtf/text/TextCodecASCIIFastPath.h +++ b/sky/engine/wtf/text/TextCodecASCIIFastPath.h @@ -31,50 +31,47 @@ namespace WTF { -template struct UCharByteFiller; -template<> struct UCharByteFiller<4> { - static void copy(LChar* destination, const uint8_t* source) - { - memcpy(destination, source, 4); - } +template +struct UCharByteFiller; +template <> +struct UCharByteFiller<4> { + static void copy(LChar* destination, const uint8_t* source) { + memcpy(destination, source, 4); + } - static void copy(UChar* destination, const uint8_t* source) - { - destination[0] = source[0]; - destination[1] = source[1]; - destination[2] = source[2]; - destination[3] = source[3]; - } + static void copy(UChar* destination, const uint8_t* source) { + destination[0] = source[0]; + destination[1] = source[1]; + destination[2] = source[2]; + destination[3] = source[3]; + } }; -template<> struct UCharByteFiller<8> { - static void copy(LChar* destination, const uint8_t* source) - { - memcpy(destination, source, 8); - } +template <> +struct UCharByteFiller<8> { + static void copy(LChar* destination, const uint8_t* source) { + memcpy(destination, source, 8); + } - static void copy(UChar* destination, const uint8_t* source) - { - destination[0] = source[0]; - destination[1] = source[1]; - destination[2] = source[2]; - destination[3] = source[3]; - destination[4] = source[4]; - destination[5] = source[5]; - destination[6] = source[6]; - destination[7] = source[7]; - } + static void copy(UChar* destination, const uint8_t* source) { + destination[0] = source[0]; + destination[1] = source[1]; + destination[2] = source[2]; + destination[3] = source[3]; + destination[4] = source[4]; + destination[5] = source[5]; + destination[6] = source[6]; + destination[7] = source[7]; + } }; -inline void copyASCIIMachineWord(LChar* destination, const uint8_t* source) -{ - UCharByteFiller::copy(destination, source); +inline void copyASCIIMachineWord(LChar* destination, const uint8_t* source) { + UCharByteFiller::copy(destination, source); } -inline void copyASCIIMachineWord(UChar* destination, const uint8_t* source) -{ - UCharByteFiller::copy(destination, source); +inline void copyASCIIMachineWord(UChar* destination, const uint8_t* source) { + UCharByteFiller::copy(destination, source); } -} // namespace WTF +} // namespace WTF #endif // SKY_ENGINE_WTF_TEXT_TEXTCODECASCIIFASTPATH_H_ diff --git a/sky/engine/wtf/text/TextCodecICU.cpp b/sky/engine/wtf/text/TextCodecICU.cpp index 435f855fd377a..637621e8cda44 100644 --- a/sky/engine/wtf/text/TextCodecICU.cpp +++ b/sky/engine/wtf/text/TextCodecICU.cpp @@ -42,504 +42,552 @@ namespace WTF { const size_t ConversionBufferSize = 16384; -ICUConverterWrapper::~ICUConverterWrapper() -{ - if (converter) - ucnv_close(converter); +ICUConverterWrapper::~ICUConverterWrapper() { + if (converter) + ucnv_close(converter); } -static UConverter*& cachedConverterICU() -{ - return wtfThreadData().cachedConverterICU().converter; +static UConverter*& cachedConverterICU() { + return wtfThreadData().cachedConverterICU().converter; } -PassOwnPtr TextCodecICU::create(const TextEncoding& encoding, const void*) -{ - return adoptPtr(new TextCodecICU(encoding)); +PassOwnPtr TextCodecICU::create(const TextEncoding& encoding, + const void*) { + return adoptPtr(new TextCodecICU(encoding)); } -void TextCodecICU::registerEncodingNames(EncodingNameRegistrar registrar) -{ - // We register Hebrew with logical ordering using a separate name. - // Otherwise, this would share the same canonical name as the - // visual ordering case, and then TextEncoding could not tell them - // apart; ICU treats these names as synonyms. - registrar("ISO-8859-8-I", "ISO-8859-8-I"); - - int32_t numEncodings = ucnv_countAvailable(); - for (int32_t i = 0; i < numEncodings; ++i) { - const char* name = ucnv_getAvailableName(i); - UErrorCode error = U_ZERO_ERROR; - // Try MIME before trying IANA to pick up commonly used names like - // 'EUC-JP' instead of horrendously long names like - // 'Extended_UNIX_Code_Packed_Format_for_Japanese'. - const char* standardName = ucnv_getStandardName(name, "MIME", &error); - if (!U_SUCCESS(error) || !standardName) { - error = U_ZERO_ERROR; - // Try IANA to pick up 'windows-12xx' and other names - // which are not preferred MIME names but are widely used. - standardName = ucnv_getStandardName(name, "IANA", &error); - if (!U_SUCCESS(error) || !standardName) - continue; - } - - // A number of these aliases are handled in Chrome's copy of ICU, but - // Chromium can be compiled with the system ICU. - - // 1. Treat GB2312 encoding as GBK (its more modern superset), to match other browsers. - // 2. On the Web, GB2312 is encoded as EUC-CN or HZ, while ICU provides a native encoding - // for encoding GB_2312-80 and several others. So, we need to override this behavior, too. - if (!strcmp(standardName, "GB2312") || !strcmp(standardName, "GB_2312-80")) - standardName = "GBK"; - // Similarly, EUC-KR encodings all map to an extended version, but - // per HTML5, the canonical name still should be EUC-KR. - else if (!strcmp(standardName, "EUC-KR") || !strcmp(standardName, "KSC_5601") || !strcmp(standardName, "cp1363")) - standardName = "EUC-KR"; - // And so on. - else if (!strcasecmp(standardName, "iso-8859-9")) // This name is returned in different case by ICU 3.2 and 3.6. - standardName = "windows-1254"; - else if (!strcmp(standardName, "TIS-620")) - standardName = "windows-874"; - - registrar(standardName, standardName); - - uint16_t numAliases = ucnv_countAliases(name, &error); - ASSERT(U_SUCCESS(error)); - if (U_SUCCESS(error)) - for (uint16_t j = 0; j < numAliases; ++j) { - error = U_ZERO_ERROR; - const char* alias = ucnv_getAlias(name, j, &error); - ASSERT(U_SUCCESS(error)); - if (U_SUCCESS(error) && alias != standardName) - registrar(alias, standardName); - } +void TextCodecICU::registerEncodingNames(EncodingNameRegistrar registrar) { + // We register Hebrew with logical ordering using a separate name. + // Otherwise, this would share the same canonical name as the + // visual ordering case, and then TextEncoding could not tell them + // apart; ICU treats these names as synonyms. + registrar("ISO-8859-8-I", "ISO-8859-8-I"); + + int32_t numEncodings = ucnv_countAvailable(); + for (int32_t i = 0; i < numEncodings; ++i) { + const char* name = ucnv_getAvailableName(i); + UErrorCode error = U_ZERO_ERROR; + // Try MIME before trying IANA to pick up commonly used names like + // 'EUC-JP' instead of horrendously long names like + // 'Extended_UNIX_Code_Packed_Format_for_Japanese'. + const char* standardName = ucnv_getStandardName(name, "MIME", &error); + if (!U_SUCCESS(error) || !standardName) { + error = U_ZERO_ERROR; + // Try IANA to pick up 'windows-12xx' and other names + // which are not preferred MIME names but are widely used. + standardName = ucnv_getStandardName(name, "IANA", &error); + if (!U_SUCCESS(error) || !standardName) + continue; } - // Additional alias for MacCyrillic not present in ICU. - registrar("maccyrillic", "x-mac-cyrillic"); - - // Additional aliases that historically were present in the encoding - // table in WebKit on Macintosh that don't seem to be present in ICU. - // Perhaps we can prove these are not used on the web and remove them. - // Or perhaps we can get them added to ICU. - registrar("x-mac-roman", "macintosh"); - registrar("x-mac-ukrainian", "x-mac-cyrillic"); - registrar("cn-big5", "Big5"); - registrar("x-x-big5", "Big5"); - registrar("cn-gb", "GBK"); - registrar("csgb231280", "GBK"); - registrar("x-euc-cn", "GBK"); - registrar("x-gbk", "GBK"); - registrar("csISO88598I", "ISO-8859-8-I"); - registrar("koi", "KOI8-R"); - registrar("logical", "ISO-8859-8-I"); - registrar("visual", "ISO-8859-8"); - registrar("winarabic", "windows-1256"); - registrar("winbaltic", "windows-1257"); - registrar("wincyrillic", "windows-1251"); - registrar("iso-8859-11", "windows-874"); - registrar("iso8859-11", "windows-874"); - registrar("dos-874", "windows-874"); - registrar("wingreek", "windows-1253"); - registrar("winhebrew", "windows-1255"); - registrar("winlatin2", "windows-1250"); - registrar("winturkish", "windows-1254"); - registrar("winvietnamese", "windows-1258"); - registrar("x-cp1250", "windows-1250"); - registrar("x-cp1251", "windows-1251"); - registrar("x-euc", "EUC-JP"); - registrar("x-windows-949", "EUC-KR"); - registrar("KSC5601", "EUC-KR"); - registrar("x-uhc", "EUC-KR"); - registrar("shift-jis", "Shift_JIS"); - - // Alternative spelling of ISO encoding names. - registrar("ISO8859-1", "ISO-8859-1"); - registrar("ISO8859-2", "ISO-8859-2"); - registrar("ISO8859-3", "ISO-8859-3"); - registrar("ISO8859-4", "ISO-8859-4"); - registrar("ISO8859-5", "ISO-8859-5"); - registrar("ISO8859-6", "ISO-8859-6"); - registrar("ISO8859-7", "ISO-8859-7"); - registrar("ISO8859-8", "ISO-8859-8"); - registrar("ISO8859-8-I", "ISO-8859-8-I"); - registrar("ISO8859-9", "ISO-8859-9"); - registrar("ISO8859-10", "ISO-8859-10"); - registrar("ISO8859-13", "ISO-8859-13"); - registrar("ISO8859-14", "ISO-8859-14"); - registrar("ISO8859-15", "ISO-8859-15"); - // No need to have an entry for ISO8859-16. ISO-8859-16 has just one label - // listed in WHATWG Encoding Living Standard (http://encoding.spec.whatwg.org/ ). - - // Additional aliases present in the WHATWG Encoding Standard - // and Firefox (24), but not in ICU 4.6. - registrar("csiso58gb231280", "GBK"); - registrar("csiso88596e", "ISO-8859-6"); - registrar("csiso88596i", "ISO-8859-6"); - registrar("csiso88598e", "ISO-8859-8"); - registrar("gb_2312", "GBK"); - registrar("iso88591", "windows-1252"); - registrar("iso88592", "ISO-8859-2"); - registrar("iso88593", "ISO-8859-3"); - registrar("iso88594", "ISO-8859-4"); - registrar("iso88595", "ISO-8859-5"); - registrar("iso88596", "ISO-8859-6"); - registrar("iso88597", "ISO-8859-7"); - registrar("iso88598", "ISO-8859-8"); - registrar("iso88599", "windows-1254"); - registrar("iso885910", "ISO-8859-10"); - registrar("iso885911", "windows-874"); - registrar("iso885913", "ISO-8859-13"); - registrar("iso885914", "ISO-8859-14"); - registrar("iso885915", "ISO-8859-15"); - registrar("iso_8859-1", "windows-1252"); - registrar("iso_8859-2", "ISO-8859-2"); - registrar("iso_8859-3", "ISO-8859-3"); - registrar("iso_8859-4", "ISO-8859-4"); - registrar("iso_8859-5", "ISO-8859-5"); - registrar("iso_8859-6", "ISO-8859-6"); - registrar("iso_8859-7", "ISO-8859-7"); - registrar("iso_8859-8", "ISO-8859-8"); - registrar("iso_8859-9", "windows-1254"); - registrar("iso_8859-15", "ISO-8859-15"); - registrar("koi8_r", "KOI8-R"); - registrar("x-cp1252", "windows-1252"); - registrar("x-cp1253", "windows-1253"); - registrar("x-cp1254", "windows-1254"); - registrar("x-cp1255", "windows-1255"); - registrar("x-cp1256", "windows-1256"); - registrar("x-cp1257", "windows-1257"); - registrar("x-cp1258", "windows-1258"); + // A number of these aliases are handled in Chrome's copy of ICU, but + // Chromium can be compiled with the system ICU. + + // 1. Treat GB2312 encoding as GBK (its more modern superset), to match + // other browsers. + // 2. On the Web, GB2312 is encoded as EUC-CN or HZ, while ICU provides a + // native encoding + // for encoding GB_2312-80 and several others. So, we need to override + // this behavior, too. + if (!strcmp(standardName, "GB2312") || !strcmp(standardName, "GB_2312-80")) + standardName = "GBK"; + // Similarly, EUC-KR encodings all map to an extended version, but + // per HTML5, the canonical name still should be EUC-KR. + else if (!strcmp(standardName, "EUC-KR") || + !strcmp(standardName, "KSC_5601") || + !strcmp(standardName, "cp1363")) + standardName = "EUC-KR"; + // And so on. + else if (!strcasecmp(standardName, "iso-8859-9")) // This name is returned + // in different case by + // ICU 3.2 and 3.6. + standardName = "windows-1254"; + else if (!strcmp(standardName, "TIS-620")) + standardName = "windows-874"; + + registrar(standardName, standardName); + + uint16_t numAliases = ucnv_countAliases(name, &error); + ASSERT(U_SUCCESS(error)); + if (U_SUCCESS(error)) + for (uint16_t j = 0; j < numAliases; ++j) { + error = U_ZERO_ERROR; + const char* alias = ucnv_getAlias(name, j, &error); + ASSERT(U_SUCCESS(error)); + if (U_SUCCESS(error) && alias != standardName) + registrar(alias, standardName); + } + } + + // Additional alias for MacCyrillic not present in ICU. + registrar("maccyrillic", "x-mac-cyrillic"); + + // Additional aliases that historically were present in the encoding + // table in WebKit on Macintosh that don't seem to be present in ICU. + // Perhaps we can prove these are not used on the web and remove them. + // Or perhaps we can get them added to ICU. + registrar("x-mac-roman", "macintosh"); + registrar("x-mac-ukrainian", "x-mac-cyrillic"); + registrar("cn-big5", "Big5"); + registrar("x-x-big5", "Big5"); + registrar("cn-gb", "GBK"); + registrar("csgb231280", "GBK"); + registrar("x-euc-cn", "GBK"); + registrar("x-gbk", "GBK"); + registrar("csISO88598I", "ISO-8859-8-I"); + registrar("koi", "KOI8-R"); + registrar("logical", "ISO-8859-8-I"); + registrar("visual", "ISO-8859-8"); + registrar("winarabic", "windows-1256"); + registrar("winbaltic", "windows-1257"); + registrar("wincyrillic", "windows-1251"); + registrar("iso-8859-11", "windows-874"); + registrar("iso8859-11", "windows-874"); + registrar("dos-874", "windows-874"); + registrar("wingreek", "windows-1253"); + registrar("winhebrew", "windows-1255"); + registrar("winlatin2", "windows-1250"); + registrar("winturkish", "windows-1254"); + registrar("winvietnamese", "windows-1258"); + registrar("x-cp1250", "windows-1250"); + registrar("x-cp1251", "windows-1251"); + registrar("x-euc", "EUC-JP"); + registrar("x-windows-949", "EUC-KR"); + registrar("KSC5601", "EUC-KR"); + registrar("x-uhc", "EUC-KR"); + registrar("shift-jis", "Shift_JIS"); + + // Alternative spelling of ISO encoding names. + registrar("ISO8859-1", "ISO-8859-1"); + registrar("ISO8859-2", "ISO-8859-2"); + registrar("ISO8859-3", "ISO-8859-3"); + registrar("ISO8859-4", "ISO-8859-4"); + registrar("ISO8859-5", "ISO-8859-5"); + registrar("ISO8859-6", "ISO-8859-6"); + registrar("ISO8859-7", "ISO-8859-7"); + registrar("ISO8859-8", "ISO-8859-8"); + registrar("ISO8859-8-I", "ISO-8859-8-I"); + registrar("ISO8859-9", "ISO-8859-9"); + registrar("ISO8859-10", "ISO-8859-10"); + registrar("ISO8859-13", "ISO-8859-13"); + registrar("ISO8859-14", "ISO-8859-14"); + registrar("ISO8859-15", "ISO-8859-15"); + // No need to have an entry for ISO8859-16. ISO-8859-16 has just one label + // listed in WHATWG Encoding Living Standard (http://encoding.spec.whatwg.org/ + // ). + + // Additional aliases present in the WHATWG Encoding Standard + // and Firefox (24), but not in ICU 4.6. + registrar("csiso58gb231280", "GBK"); + registrar("csiso88596e", "ISO-8859-6"); + registrar("csiso88596i", "ISO-8859-6"); + registrar("csiso88598e", "ISO-8859-8"); + registrar("gb_2312", "GBK"); + registrar("iso88591", "windows-1252"); + registrar("iso88592", "ISO-8859-2"); + registrar("iso88593", "ISO-8859-3"); + registrar("iso88594", "ISO-8859-4"); + registrar("iso88595", "ISO-8859-5"); + registrar("iso88596", "ISO-8859-6"); + registrar("iso88597", "ISO-8859-7"); + registrar("iso88598", "ISO-8859-8"); + registrar("iso88599", "windows-1254"); + registrar("iso885910", "ISO-8859-10"); + registrar("iso885911", "windows-874"); + registrar("iso885913", "ISO-8859-13"); + registrar("iso885914", "ISO-8859-14"); + registrar("iso885915", "ISO-8859-15"); + registrar("iso_8859-1", "windows-1252"); + registrar("iso_8859-2", "ISO-8859-2"); + registrar("iso_8859-3", "ISO-8859-3"); + registrar("iso_8859-4", "ISO-8859-4"); + registrar("iso_8859-5", "ISO-8859-5"); + registrar("iso_8859-6", "ISO-8859-6"); + registrar("iso_8859-7", "ISO-8859-7"); + registrar("iso_8859-8", "ISO-8859-8"); + registrar("iso_8859-9", "windows-1254"); + registrar("iso_8859-15", "ISO-8859-15"); + registrar("koi8_r", "KOI8-R"); + registrar("x-cp1252", "windows-1252"); + registrar("x-cp1253", "windows-1253"); + registrar("x-cp1254", "windows-1254"); + registrar("x-cp1255", "windows-1255"); + registrar("x-cp1256", "windows-1256"); + registrar("x-cp1257", "windows-1257"); + registrar("x-cp1258", "windows-1258"); } -void TextCodecICU::registerCodecs(TextCodecRegistrar registrar) -{ - // See comment above in registerEncodingNames. - registrar("ISO-8859-8-I", create, 0); - - int32_t numEncodings = ucnv_countAvailable(); - for (int32_t i = 0; i < numEncodings; ++i) { - const char* name = ucnv_getAvailableName(i); - UErrorCode error = U_ZERO_ERROR; - const char* standardName = ucnv_getStandardName(name, "MIME", &error); - if (!U_SUCCESS(error) || !standardName) { - error = U_ZERO_ERROR; - standardName = ucnv_getStandardName(name, "IANA", &error); - if (!U_SUCCESS(error) || !standardName) - continue; - } - registrar(standardName, create, 0); +void TextCodecICU::registerCodecs(TextCodecRegistrar registrar) { + // See comment above in registerEncodingNames. + registrar("ISO-8859-8-I", create, 0); + + int32_t numEncodings = ucnv_countAvailable(); + for (int32_t i = 0; i < numEncodings; ++i) { + const char* name = ucnv_getAvailableName(i); + UErrorCode error = U_ZERO_ERROR; + const char* standardName = ucnv_getStandardName(name, "MIME", &error); + if (!U_SUCCESS(error) || !standardName) { + error = U_ZERO_ERROR; + standardName = ucnv_getStandardName(name, "IANA", &error); + if (!U_SUCCESS(error) || !standardName) + continue; } + registrar(standardName, create, 0); + } } TextCodecICU::TextCodecICU(const TextEncoding& encoding) - : m_encoding(encoding) - , m_converterICU(0) - , m_needsGBKFallbacks(false) -{ -} + : m_encoding(encoding), m_converterICU(0), m_needsGBKFallbacks(false) {} -TextCodecICU::~TextCodecICU() -{ - releaseICUConverter(); +TextCodecICU::~TextCodecICU() { + releaseICUConverter(); } -void TextCodecICU::releaseICUConverter() const -{ - if (m_converterICU) { - UConverter*& cachedConverter = cachedConverterICU(); - if (cachedConverter) - ucnv_close(cachedConverter); - cachedConverter = m_converterICU; - m_converterICU = 0; - } +void TextCodecICU::releaseICUConverter() const { + if (m_converterICU) { + UConverter*& cachedConverter = cachedConverterICU(); + if (cachedConverter) + ucnv_close(cachedConverter); + cachedConverter = m_converterICU; + m_converterICU = 0; + } } -void TextCodecICU::createICUConverter() const -{ - ASSERT(!m_converterICU); +void TextCodecICU::createICUConverter() const { + ASSERT(!m_converterICU); - const char* name = m_encoding.name(); - m_needsGBKFallbacks = name[0] == 'G' && name[1] == 'B' && name[2] == 'K' && !name[3]; + const char* name = m_encoding.name(); + m_needsGBKFallbacks = + name[0] == 'G' && name[1] == 'B' && name[2] == 'K' && !name[3]; - UErrorCode err; + UErrorCode err; - UConverter*& cachedConverter = cachedConverterICU(); - if (cachedConverter) { - err = U_ZERO_ERROR; - const char* cachedName = ucnv_getName(cachedConverter, &err); - if (U_SUCCESS(err) && m_encoding == cachedName) { - m_converterICU = cachedConverter; - cachedConverter = 0; - return; - } + UConverter*& cachedConverter = cachedConverterICU(); + if (cachedConverter) { + err = U_ZERO_ERROR; + const char* cachedName = ucnv_getName(cachedConverter, &err); + if (U_SUCCESS(err) && m_encoding == cachedName) { + m_converterICU = cachedConverter; + cachedConverter = 0; + return; } + } - err = U_ZERO_ERROR; - m_converterICU = ucnv_open(m_encoding.name(), &err); + err = U_ZERO_ERROR; + m_converterICU = ucnv_open(m_encoding.name(), &err); #if !LOG_DISABLED - if (err == U_AMBIGUOUS_ALIAS_WARNING) - WTF_LOG_ERROR("ICU ambiguous alias warning for encoding: %s", m_encoding.name()); + if (err == U_AMBIGUOUS_ALIAS_WARNING) + WTF_LOG_ERROR("ICU ambiguous alias warning for encoding: %s", + m_encoding.name()); #endif - if (m_converterICU) - ucnv_setFallback(m_converterICU, TRUE); + if (m_converterICU) + ucnv_setFallback(m_converterICU, TRUE); } -int TextCodecICU::decodeToBuffer(UChar* target, UChar* targetLimit, const char*& source, const char* sourceLimit, int32_t* offsets, bool flush, UErrorCode& err) -{ - UChar* targetStart = target; - err = U_ZERO_ERROR; - ucnv_toUnicode(m_converterICU, &target, targetLimit, &source, sourceLimit, offsets, flush, &err); - return target - targetStart; +int TextCodecICU::decodeToBuffer(UChar* target, + UChar* targetLimit, + const char*& source, + const char* sourceLimit, + int32_t* offsets, + bool flush, + UErrorCode& err) { + UChar* targetStart = target; + err = U_ZERO_ERROR; + ucnv_toUnicode(m_converterICU, &target, targetLimit, &source, sourceLimit, + offsets, flush, &err); + return target - targetStart; } class ErrorCallbackSetter { -public: - ErrorCallbackSetter(UConverter* converter, bool stopOnError) - : m_converter(converter) - , m_shouldStopOnEncodingErrors(stopOnError) - { - if (m_shouldStopOnEncodingErrors) { - UErrorCode err = U_ZERO_ERROR; - ucnv_setToUCallBack(m_converter, UCNV_TO_U_CALLBACK_SUBSTITUTE, - UCNV_SUB_STOP_ON_ILLEGAL, &m_savedAction, - &m_savedContext, &err); - ASSERT(err == U_ZERO_ERROR); - } + public: + ErrorCallbackSetter(UConverter* converter, bool stopOnError) + : m_converter(converter), m_shouldStopOnEncodingErrors(stopOnError) { + if (m_shouldStopOnEncodingErrors) { + UErrorCode err = U_ZERO_ERROR; + ucnv_setToUCallBack(m_converter, UCNV_TO_U_CALLBACK_SUBSTITUTE, + UCNV_SUB_STOP_ON_ILLEGAL, &m_savedAction, + &m_savedContext, &err); + ASSERT(err == U_ZERO_ERROR); } - ~ErrorCallbackSetter() - { - if (m_shouldStopOnEncodingErrors) { - UErrorCode err = U_ZERO_ERROR; - const void* oldContext; - UConverterToUCallback oldAction; - ucnv_setToUCallBack(m_converter, m_savedAction, - m_savedContext, &oldAction, - &oldContext, &err); - ASSERT(oldAction == UCNV_TO_U_CALLBACK_SUBSTITUTE); - ASSERT(!strcmp(static_cast(oldContext), UCNV_SUB_STOP_ON_ILLEGAL)); - ASSERT(err == U_ZERO_ERROR); - } + } + ~ErrorCallbackSetter() { + if (m_shouldStopOnEncodingErrors) { + UErrorCode err = U_ZERO_ERROR; + const void* oldContext; + UConverterToUCallback oldAction; + ucnv_setToUCallBack(m_converter, m_savedAction, m_savedContext, + &oldAction, &oldContext, &err); + ASSERT(oldAction == UCNV_TO_U_CALLBACK_SUBSTITUTE); + ASSERT(!strcmp(static_cast(oldContext), + UCNV_SUB_STOP_ON_ILLEGAL)); + ASSERT(err == U_ZERO_ERROR); } + } -private: - UConverter* m_converter; - bool m_shouldStopOnEncodingErrors; - const void* m_savedContext; - UConverterToUCallback m_savedAction; + private: + UConverter* m_converter; + bool m_shouldStopOnEncodingErrors; + const void* m_savedContext; + UConverterToUCallback m_savedAction; }; -String TextCodecICU::decode(const char* bytes, size_t length, FlushBehavior flush, bool stopOnError, bool& sawError) -{ - // Get a converter for the passed-in encoding. +String TextCodecICU::decode(const char* bytes, + size_t length, + FlushBehavior flush, + bool stopOnError, + bool& sawError) { + // Get a converter for the passed-in encoding. + if (!m_converterICU) { + createICUConverter(); + ASSERT(m_converterICU); if (!m_converterICU) { - createICUConverter(); - ASSERT(m_converterICU); - if (!m_converterICU) { - WTF_LOG_ERROR("error creating ICU encoder even though encoding was in table"); - return String(); - } + WTF_LOG_ERROR( + "error creating ICU encoder even though encoding was in table"); + return String(); } + } - ErrorCallbackSetter callbackSetter(m_converterICU, stopOnError); - - StringBuilder result; + ErrorCallbackSetter callbackSetter(m_converterICU, stopOnError); - UChar buffer[ConversionBufferSize]; - UChar* bufferLimit = buffer + ConversionBufferSize; - const char* source = reinterpret_cast(bytes); - const char* sourceLimit = source + length; - int32_t* offsets = NULL; - UErrorCode err = U_ZERO_ERROR; - - do { - int ucharsDecoded = decodeToBuffer(buffer, bufferLimit, source, sourceLimit, offsets, flush != DoNotFlush, err); - result.append(buffer, ucharsDecoded); - } while (err == U_BUFFER_OVERFLOW_ERROR); - - if (U_FAILURE(err)) { - // flush the converter so it can be reused, and not be bothered by this error. - do { - decodeToBuffer(buffer, bufferLimit, source, sourceLimit, offsets, true, err); - } while (source < sourceLimit); - sawError = true; - } + StringBuilder result; - String resultString = result.toString(); + UChar buffer[ConversionBufferSize]; + UChar* bufferLimit = buffer + ConversionBufferSize; + const char* source = reinterpret_cast(bytes); + const char* sourceLimit = source + length; + int32_t* offsets = NULL; + UErrorCode err = U_ZERO_ERROR; - // - // Simplified Chinese pages use the code A3A0 to mean "full-width space", but ICU decodes it as U+E5E5. - if (!strcmp(m_encoding.name(), "GBK") || !strcasecmp(m_encoding.name(), "gb18030")) - resultString.replace(0xE5E5, ideographicSpace); + do { + int ucharsDecoded = decodeToBuffer(buffer, bufferLimit, source, sourceLimit, + offsets, flush != DoNotFlush, err); + result.append(buffer, ucharsDecoded); + } while (err == U_BUFFER_OVERFLOW_ERROR); - return resultString; + if (U_FAILURE(err)) { + // flush the converter so it can be reused, and not be bothered by this + // error. + do { + decodeToBuffer(buffer, bufferLimit, source, sourceLimit, offsets, true, + err); + } while (source < sourceLimit); + sawError = true; + } + + String resultString = result.toString(); + + // + // Simplified Chinese pages use the code A3A0 to mean "full-width space", but + // ICU decodes it as U+E5E5. + if (!strcmp(m_encoding.name(), "GBK") || + !strcasecmp(m_encoding.name(), "gb18030")) + resultString.replace(0xE5E5, ideographicSpace); + + return resultString; } -// We need to apply these fallbacks ourselves as they are not currently supported by ICU and -// they were provided by the old TEC encoding path. Needed to fix . -static UChar fallbackForGBK(UChar32 character) -{ - switch (character) { +// We need to apply these fallbacks ourselves as they are not currently +// supported by ICU and they were provided by the old TEC encoding path. Needed +// to fix . +static UChar fallbackForGBK(UChar32 character) { + switch (character) { case 0x01F9: - return 0xE7C8; + return 0xE7C8; case 0x1E3F: - return 0xE7C7; + return 0xE7C7; case 0x22EF: - return 0x2026; + return 0x2026; case 0x301C: - return 0xFF5E; - } - return 0; + return 0xFF5E; + } + return 0; } // Invalid character handler when writing escaped entities for unrepresentable // characters. See the declaration of TextCodec::encode for more. -static void urlEscapedEntityCallback(const void* context, UConverterFromUnicodeArgs* fromUArgs, const UChar* codeUnits, int32_t length, - UChar32 codePoint, UConverterCallbackReason reason, UErrorCode* err) -{ - if (reason == UCNV_UNASSIGNED) { - *err = U_ZERO_ERROR; - - UnencodableReplacementArray entity; - int entityLen = TextCodec::getUnencodableReplacement(codePoint, URLEncodedEntitiesForUnencodables, entity); - ucnv_cbFromUWriteBytes(fromUArgs, entity, entityLen, 0, err); - } else - UCNV_FROM_U_CALLBACK_ESCAPE(context, fromUArgs, codeUnits, length, codePoint, reason, err); +static void urlEscapedEntityCallback(const void* context, + UConverterFromUnicodeArgs* fromUArgs, + const UChar* codeUnits, + int32_t length, + UChar32 codePoint, + UConverterCallbackReason reason, + UErrorCode* err) { + if (reason == UCNV_UNASSIGNED) { + *err = U_ZERO_ERROR; + + UnencodableReplacementArray entity; + int entityLen = TextCodec::getUnencodableReplacement( + codePoint, URLEncodedEntitiesForUnencodables, entity); + ucnv_cbFromUWriteBytes(fromUArgs, entity, entityLen, 0, err); + } else + UCNV_FROM_U_CALLBACK_ESCAPE(context, fromUArgs, codeUnits, length, + codePoint, reason, err); } // Substitutes special GBK characters, escaping all other unassigned entities. -static void gbkCallbackEscape(const void* context, UConverterFromUnicodeArgs* fromUArgs, const UChar* codeUnits, int32_t length, - UChar32 codePoint, UConverterCallbackReason reason, UErrorCode* err) -{ - UChar outChar; - if (reason == UCNV_UNASSIGNED && (outChar = fallbackForGBK(codePoint))) { - const UChar* source = &outChar; - *err = U_ZERO_ERROR; - ucnv_cbFromUWriteUChars(fromUArgs, &source, source + 1, 0, err); - return; - } - UCNV_FROM_U_CALLBACK_ESCAPE(context, fromUArgs, codeUnits, length, codePoint, reason, err); +static void gbkCallbackEscape(const void* context, + UConverterFromUnicodeArgs* fromUArgs, + const UChar* codeUnits, + int32_t length, + UChar32 codePoint, + UConverterCallbackReason reason, + UErrorCode* err) { + UChar outChar; + if (reason == UCNV_UNASSIGNED && (outChar = fallbackForGBK(codePoint))) { + const UChar* source = &outChar; + *err = U_ZERO_ERROR; + ucnv_cbFromUWriteUChars(fromUArgs, &source, source + 1, 0, err); + return; + } + UCNV_FROM_U_CALLBACK_ESCAPE(context, fromUArgs, codeUnits, length, codePoint, + reason, err); } // Combines both gbkUrlEscapedEntityCallback and GBK character substitution. -static void gbkUrlEscapedEntityCallack(const void* context, UConverterFromUnicodeArgs* fromUArgs, const UChar* codeUnits, int32_t length, - UChar32 codePoint, UConverterCallbackReason reason, UErrorCode* err) -{ - if (reason == UCNV_UNASSIGNED) { - if (UChar outChar = fallbackForGBK(codePoint)) { - const UChar* source = &outChar; - *err = U_ZERO_ERROR; - ucnv_cbFromUWriteUChars(fromUArgs, &source, source + 1, 0, err); - return; - } - urlEscapedEntityCallback(context, fromUArgs, codeUnits, length, codePoint, reason, err); - return; +static void gbkUrlEscapedEntityCallack(const void* context, + UConverterFromUnicodeArgs* fromUArgs, + const UChar* codeUnits, + int32_t length, + UChar32 codePoint, + UConverterCallbackReason reason, + UErrorCode* err) { + if (reason == UCNV_UNASSIGNED) { + if (UChar outChar = fallbackForGBK(codePoint)) { + const UChar* source = &outChar; + *err = U_ZERO_ERROR; + ucnv_cbFromUWriteUChars(fromUArgs, &source, source + 1, 0, err); + return; } - UCNV_FROM_U_CALLBACK_ESCAPE(context, fromUArgs, codeUnits, length, codePoint, reason, err); + urlEscapedEntityCallback(context, fromUArgs, codeUnits, length, codePoint, + reason, err); + return; + } + UCNV_FROM_U_CALLBACK_ESCAPE(context, fromUArgs, codeUnits, length, codePoint, + reason, err); } -static void gbkCallbackSubstitute(const void* context, UConverterFromUnicodeArgs* fromUArgs, const UChar* codeUnits, int32_t length, - UChar32 codePoint, UConverterCallbackReason reason, UErrorCode* err) -{ - UChar outChar; - if (reason == UCNV_UNASSIGNED && (outChar = fallbackForGBK(codePoint))) { - const UChar* source = &outChar; - *err = U_ZERO_ERROR; - ucnv_cbFromUWriteUChars(fromUArgs, &source, source + 1, 0, err); - return; - } - UCNV_FROM_U_CALLBACK_SUBSTITUTE(context, fromUArgs, codeUnits, length, codePoint, reason, err); +static void gbkCallbackSubstitute(const void* context, + UConverterFromUnicodeArgs* fromUArgs, + const UChar* codeUnits, + int32_t length, + UChar32 codePoint, + UConverterCallbackReason reason, + UErrorCode* err) { + UChar outChar; + if (reason == UCNV_UNASSIGNED && (outChar = fallbackForGBK(codePoint))) { + const UChar* source = &outChar; + *err = U_ZERO_ERROR; + ucnv_cbFromUWriteUChars(fromUArgs, &source, source + 1, 0, err); + return; + } + UCNV_FROM_U_CALLBACK_SUBSTITUTE(context, fromUArgs, codeUnits, length, + codePoint, reason, err); } class TextCodecInput { -public: - TextCodecInput(const TextEncoding& encoding, const UChar* characters, size_t length) - : m_begin(characters) - , m_end(characters + length) - { } - - TextCodecInput(const TextEncoding& encoding, const LChar* characters, size_t length) - { - m_buffer.reserveInitialCapacity(length); - for (size_t i = 0; i < length; ++i) - m_buffer.append(characters[i]); - m_begin = m_buffer.data(); - m_end = m_begin + m_buffer.size(); - } - - const UChar* begin() const { return m_begin; } - const UChar* end() const { return m_end; } - -private: - const UChar* m_begin; - const UChar* m_end; - Vector m_buffer; + public: + TextCodecInput(const TextEncoding& encoding, + const UChar* characters, + size_t length) + : m_begin(characters), m_end(characters + length) {} + + TextCodecInput(const TextEncoding& encoding, + const LChar* characters, + size_t length) { + m_buffer.reserveInitialCapacity(length); + for (size_t i = 0; i < length; ++i) + m_buffer.append(characters[i]); + m_begin = m_buffer.data(); + m_end = m_begin + m_buffer.size(); + } + + const UChar* begin() const { return m_begin; } + const UChar* end() const { return m_end; } + + private: + const UChar* m_begin; + const UChar* m_end; + Vector m_buffer; }; -CString TextCodecICU::encodeInternal(const TextCodecInput& input, UnencodableHandling handling) -{ - const UChar* source = input.begin(); - const UChar* end = input.end(); - - UErrorCode err = U_ZERO_ERROR; - - switch (handling) { - case QuestionMarksForUnencodables: - ucnv_setSubstChars(m_converterICU, "?", 1, &err); - ucnv_setFromUCallBack(m_converterICU, m_needsGBKFallbacks ? gbkCallbackSubstitute : UCNV_FROM_U_CALLBACK_SUBSTITUTE, 0, 0, 0, &err); - break; - case EntitiesForUnencodables: - ucnv_setFromUCallBack(m_converterICU, m_needsGBKFallbacks ? gbkCallbackEscape : UCNV_FROM_U_CALLBACK_ESCAPE, UCNV_ESCAPE_XML_DEC, 0, 0, &err); - break; - case URLEncodedEntitiesForUnencodables: - ucnv_setFromUCallBack(m_converterICU, m_needsGBKFallbacks ? gbkUrlEscapedEntityCallack : urlEscapedEntityCallback, 0, 0, 0, &err); - break; - } - - ASSERT(U_SUCCESS(err)); - if (U_FAILURE(err)) - return CString(); - - Vector result; - size_t size = 0; - do { - char buffer[ConversionBufferSize]; - char* target = buffer; - char* targetLimit = target + ConversionBufferSize; - err = U_ZERO_ERROR; - ucnv_fromUnicode(m_converterICU, &target, targetLimit, &source, end, 0, true, &err); - size_t count = target - buffer; - result.grow(size + count); - memcpy(result.data() + size, buffer, count); - size += count; - } while (err == U_BUFFER_OVERFLOW_ERROR); - - return CString(result.data(), size); +CString TextCodecICU::encodeInternal(const TextCodecInput& input, + UnencodableHandling handling) { + const UChar* source = input.begin(); + const UChar* end = input.end(); + + UErrorCode err = U_ZERO_ERROR; + + switch (handling) { + case QuestionMarksForUnencodables: + ucnv_setSubstChars(m_converterICU, "?", 1, &err); + ucnv_setFromUCallBack(m_converterICU, + m_needsGBKFallbacks + ? gbkCallbackSubstitute + : UCNV_FROM_U_CALLBACK_SUBSTITUTE, + 0, 0, 0, &err); + break; + case EntitiesForUnencodables: + ucnv_setFromUCallBack( + m_converterICU, + m_needsGBKFallbacks ? gbkCallbackEscape : UCNV_FROM_U_CALLBACK_ESCAPE, + UCNV_ESCAPE_XML_DEC, 0, 0, &err); + break; + case URLEncodedEntitiesForUnencodables: + ucnv_setFromUCallBack(m_converterICU, + m_needsGBKFallbacks ? gbkUrlEscapedEntityCallack + : urlEscapedEntityCallback, + 0, 0, 0, &err); + break; + } + + ASSERT(U_SUCCESS(err)); + if (U_FAILURE(err)) + return CString(); + + Vector result; + size_t size = 0; + do { + char buffer[ConversionBufferSize]; + char* target = buffer; + char* targetLimit = target + ConversionBufferSize; + err = U_ZERO_ERROR; + ucnv_fromUnicode(m_converterICU, &target, targetLimit, &source, end, 0, + true, &err); + size_t count = target - buffer; + result.grow(size + count); + memcpy(result.data() + size, buffer, count); + size += count; + } while (err == U_BUFFER_OVERFLOW_ERROR); + + return CString(result.data(), size); } -template -CString TextCodecICU::encodeCommon(const CharType* characters, size_t length, UnencodableHandling handling) -{ - if (!length) - return ""; +template +CString TextCodecICU::encodeCommon(const CharType* characters, + size_t length, + UnencodableHandling handling) { + if (!length) + return ""; - if (!m_converterICU) - createICUConverter(); - if (!m_converterICU) - return CString(); + if (!m_converterICU) + createICUConverter(); + if (!m_converterICU) + return CString(); - TextCodecInput input(m_encoding, characters, length); - return encodeInternal(input, handling); + TextCodecInput input(m_encoding, characters, length); + return encodeInternal(input, handling); } -CString TextCodecICU::encode(const UChar* characters, size_t length, UnencodableHandling handling) -{ - return encodeCommon(characters, length, handling); +CString TextCodecICU::encode(const UChar* characters, + size_t length, + UnencodableHandling handling) { + return encodeCommon(characters, length, handling); } -CString TextCodecICU::encode(const LChar* characters, size_t length, UnencodableHandling handling) -{ - return encodeCommon(characters, length, handling); +CString TextCodecICU::encode(const LChar* characters, + size_t length, + UnencodableHandling handling) { + return encodeCommon(characters, length, handling); } -} // namespace WTF +} // namespace WTF diff --git a/sky/engine/wtf/text/TextCodecICU.h b/sky/engine/wtf/text/TextCodecICU.h index 9d5c17dc6012b..cb5b7835dad3f 100644 --- a/sky/engine/wtf/text/TextCodecICU.h +++ b/sky/engine/wtf/text/TextCodecICU.h @@ -38,46 +38,63 @@ namespace WTF { class TextCodecInput; class TextCodecICU final : public TextCodec { -public: - static void registerEncodingNames(EncodingNameRegistrar); - static void registerCodecs(TextCodecRegistrar); - - virtual ~TextCodecICU(); - -private: - TextCodecICU(const TextEncoding&); - static PassOwnPtr create(const TextEncoding&, const void*); - - virtual String decode(const char*, size_t length, FlushBehavior, bool stopOnError, bool& sawError) override; - virtual CString encode(const UChar*, size_t length, UnencodableHandling) override; - virtual CString encode(const LChar*, size_t length, UnencodableHandling) override; - - template - CString encodeCommon(const CharType*, size_t length, UnencodableHandling); - CString encodeInternal(const TextCodecInput&, UnencodableHandling); - - void createICUConverter() const; - void releaseICUConverter() const; - bool needsGBKFallbacks() const { return m_needsGBKFallbacks; } - void setNeedsGBKFallbacks(bool needsFallbacks) { m_needsGBKFallbacks = needsFallbacks; } - - int decodeToBuffer(UChar* buffer, UChar* bufferLimit, const char*& source, - const char* sourceLimit, int32_t* offsets, bool flush, UErrorCode&); - - TextEncoding m_encoding; - mutable UConverter* m_converterICU; - mutable bool m_needsGBKFallbacks; + public: + static void registerEncodingNames(EncodingNameRegistrar); + static void registerCodecs(TextCodecRegistrar); + + virtual ~TextCodecICU(); + + private: + TextCodecICU(const TextEncoding&); + static PassOwnPtr create(const TextEncoding&, const void*); + + virtual String decode(const char*, + size_t length, + FlushBehavior, + bool stopOnError, + bool& sawError) override; + virtual CString encode(const UChar*, + size_t length, + UnencodableHandling) override; + virtual CString encode(const LChar*, + size_t length, + UnencodableHandling) override; + + template + CString encodeCommon(const CharType*, size_t length, UnencodableHandling); + CString encodeInternal(const TextCodecInput&, UnencodableHandling); + + void createICUConverter() const; + void releaseICUConverter() const; + bool needsGBKFallbacks() const { return m_needsGBKFallbacks; } + void setNeedsGBKFallbacks(bool needsFallbacks) { + m_needsGBKFallbacks = needsFallbacks; + } + + int decodeToBuffer(UChar* buffer, + UChar* bufferLimit, + const char*& source, + const char* sourceLimit, + int32_t* offsets, + bool flush, + UErrorCode&); + + TextEncoding m_encoding; + mutable UConverter* m_converterICU; + mutable bool m_needsGBKFallbacks; }; struct ICUConverterWrapper { - WTF_MAKE_NONCOPYABLE(ICUConverterWrapper); WTF_MAKE_FAST_ALLOCATED; -public: - ICUConverterWrapper() : converter(0) { } - ~ICUConverterWrapper(); + WTF_MAKE_NONCOPYABLE(ICUConverterWrapper); + WTF_MAKE_FAST_ALLOCATED; + + public: + ICUConverterWrapper() : converter(0) {} + ~ICUConverterWrapper(); - UConverter* converter; + UConverter* converter; }; -} // namespace WTF +} // namespace WTF #endif // SKY_ENGINE_WTF_TEXT_TEXTCODECICU_H_ diff --git a/sky/engine/wtf/text/TextCodecLatin1.cpp b/sky/engine/wtf/text/TextCodecLatin1.cpp index f1e98b64c5580..60ce95704054a 100644 --- a/sky/engine/wtf/text/TextCodecLatin1.cpp +++ b/sky/engine/wtf/text/TextCodecLatin1.cpp @@ -36,245 +36,254 @@ using namespace WTF; namespace WTF { static const UChar table[256] = { - 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, // 00-07 - 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, // 08-0F - 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, // 10-17 - 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, // 18-1F - 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, // 20-27 - 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, // 28-2F - 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, // 30-37 - 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, // 38-3F - 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, // 40-47 - 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, // 48-4F - 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, // 50-57 - 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, // 58-5F - 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, // 60-67 - 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, // 68-6F - 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, // 70-77 - 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, // 78-7F - 0x20AC, 0x0081, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, // 80-87 - 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008D, 0x017D, 0x008F, // 88-8F - 0x0090, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, // 90-97 - 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x009D, 0x017E, 0x0178, // 98-9F - 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, // A0-A7 - 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, // A8-AF - 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, // B0-B7 - 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, // B8-BF - 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, // C0-C7 - 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, // C8-CF - 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, // D0-D7 - 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, // D8-DF - 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, // E0-E7 - 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, // E8-EF - 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, // F0-F7 - 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF // F8-FF + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, // 00-07 + 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, // 08-0F + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, // 10-17 + 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, // 18-1F + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, // 20-27 + 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, // 28-2F + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, // 30-37 + 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, // 38-3F + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, // 40-47 + 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, // 48-4F + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, // 50-57 + 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, // 58-5F + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, // 60-67 + 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, // 68-6F + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, // 70-77 + 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, // 78-7F + 0x20AC, 0x0081, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, // 80-87 + 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008D, 0x017D, 0x008F, // 88-8F + 0x0090, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, // 90-97 + 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x009D, 0x017E, 0x0178, // 98-9F + 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, // A0-A7 + 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, // A8-AF + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, // B0-B7 + 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, // B8-BF + 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, // C0-C7 + 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, // C8-CF + 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, // D0-D7 + 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, // D8-DF + 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, // E0-E7 + 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, // E8-EF + 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, // F0-F7 + 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF // F8-FF }; -void TextCodecLatin1::registerEncodingNames(EncodingNameRegistrar registrar) -{ - registrar("windows-1252", "windows-1252"); - registrar("ISO-8859-1", "ISO-8859-1"); - registrar("US-ASCII", "US-ASCII"); - - registrar("WinLatin1", "windows-1252"); - registrar("ibm-1252", "windows-1252"); - registrar("ibm-1252_P100-2000", "windows-1252"); - - registrar("CP819", "ISO-8859-1"); - registrar("IBM819", "ISO-8859-1"); - registrar("csISOLatin1", "ISO-8859-1"); - registrar("iso-ir-100", "ISO-8859-1"); - registrar("iso_8859-1:1987", "ISO-8859-1"); - registrar("l1", "ISO-8859-1"); - registrar("latin1", "ISO-8859-1"); - - registrar("ANSI_X3.4-1968", "US-ASCII"); - registrar("ANSI_X3.4-1986", "US-ASCII"); - registrar("ASCII", "US-ASCII"); - registrar("IBM367", "US-ASCII"); - registrar("ISO646-US", "US-ASCII"); - registrar("ISO_646.irv:1991", "US-ASCII"); - registrar("cp367", "US-ASCII"); - registrar("csASCII", "US-ASCII"); - registrar("ibm-367_P100-1995", "US-ASCII"); - registrar("iso-ir-6", "US-ASCII"); - registrar("iso-ir-6-us", "US-ASCII"); - registrar("us", "US-ASCII"); - registrar("x-ansi", "US-ASCII"); +void TextCodecLatin1::registerEncodingNames(EncodingNameRegistrar registrar) { + registrar("windows-1252", "windows-1252"); + registrar("ISO-8859-1", "ISO-8859-1"); + registrar("US-ASCII", "US-ASCII"); + + registrar("WinLatin1", "windows-1252"); + registrar("ibm-1252", "windows-1252"); + registrar("ibm-1252_P100-2000", "windows-1252"); + + registrar("CP819", "ISO-8859-1"); + registrar("IBM819", "ISO-8859-1"); + registrar("csISOLatin1", "ISO-8859-1"); + registrar("iso-ir-100", "ISO-8859-1"); + registrar("iso_8859-1:1987", "ISO-8859-1"); + registrar("l1", "ISO-8859-1"); + registrar("latin1", "ISO-8859-1"); + + registrar("ANSI_X3.4-1968", "US-ASCII"); + registrar("ANSI_X3.4-1986", "US-ASCII"); + registrar("ASCII", "US-ASCII"); + registrar("IBM367", "US-ASCII"); + registrar("ISO646-US", "US-ASCII"); + registrar("ISO_646.irv:1991", "US-ASCII"); + registrar("cp367", "US-ASCII"); + registrar("csASCII", "US-ASCII"); + registrar("ibm-367_P100-1995", "US-ASCII"); + registrar("iso-ir-6", "US-ASCII"); + registrar("iso-ir-6-us", "US-ASCII"); + registrar("us", "US-ASCII"); + registrar("x-ansi", "US-ASCII"); } -static PassOwnPtr newStreamingTextDecoderWindowsLatin1(const TextEncoding&, const void*) -{ - return adoptPtr(new TextCodecLatin1); +static PassOwnPtr newStreamingTextDecoderWindowsLatin1( + const TextEncoding&, + const void*) { + return adoptPtr(new TextCodecLatin1); } -void TextCodecLatin1::registerCodecs(TextCodecRegistrar registrar) -{ - registrar("windows-1252", newStreamingTextDecoderWindowsLatin1, 0); +void TextCodecLatin1::registerCodecs(TextCodecRegistrar registrar) { + registrar("windows-1252", newStreamingTextDecoderWindowsLatin1, 0); - // ASCII and Latin-1 both decode as Windows Latin-1 although they retain unique identities. - registrar("ISO-8859-1", newStreamingTextDecoderWindowsLatin1, 0); - registrar("US-ASCII", newStreamingTextDecoderWindowsLatin1, 0); + // ASCII and Latin-1 both decode as Windows Latin-1 although they retain + // unique identities. + registrar("ISO-8859-1", newStreamingTextDecoderWindowsLatin1, 0); + registrar("US-ASCII", newStreamingTextDecoderWindowsLatin1, 0); } -String TextCodecLatin1::decode(const char* bytes, size_t length, FlushBehavior, bool, bool&) -{ - LChar* characters; - if (!length) - return emptyString(); - String result = String::createUninitialized(length, characters); - - const uint8_t* source = reinterpret_cast(bytes); - const uint8_t* end = reinterpret_cast(bytes + length); - const uint8_t* alignedEnd = alignToMachineWord(end); - LChar* destination = characters; - - while (source < end) { - if (isASCII(*source)) { - // Fast path for ASCII. Most Latin-1 text will be ASCII. - if (isAlignedToMachineWord(source)) { - while (source < alignedEnd) { - MachineWord chunk = *reinterpret_cast_ptr(source); - - if (!isAllASCII(chunk)) - goto useLookupTable; - - copyASCIIMachineWord(destination, source); - source += sizeof(MachineWord); - destination += sizeof(MachineWord); - } - - if (source == end) - break; - } - *destination = *source; - } else { -useLookupTable: - if (table[*source] > 0xff) - goto upConvertTo16Bit; - - *destination = table[*source]; +String TextCodecLatin1::decode(const char* bytes, + size_t length, + FlushBehavior, + bool, + bool&) { + LChar* characters; + if (!length) + return emptyString(); + String result = String::createUninitialized(length, characters); + + const uint8_t* source = reinterpret_cast(bytes); + const uint8_t* end = reinterpret_cast(bytes + length); + const uint8_t* alignedEnd = alignToMachineWord(end); + LChar* destination = characters; + + while (source < end) { + if (isASCII(*source)) { + // Fast path for ASCII. Most Latin-1 text will be ASCII. + if (isAlignedToMachineWord(source)) { + while (source < alignedEnd) { + MachineWord chunk = *reinterpret_cast_ptr(source); + + if (!isAllASCII(chunk)) + goto useLookupTable; + + copyASCIIMachineWord(destination, source); + source += sizeof(MachineWord); + destination += sizeof(MachineWord); } - ++source; - ++destination; + if (source == end) + break; + } + *destination = *source; + } else { + useLookupTable: + if (table[*source] > 0xff) + goto upConvertTo16Bit; + + *destination = table[*source]; } - return result; + ++source; + ++destination; + } + + return result; upConvertTo16Bit: - UChar* characters16; - String result16 = String::createUninitialized(length, characters16); + UChar* characters16; + String result16 = String::createUninitialized(length, characters16); - UChar* destination16 = characters16; + UChar* destination16 = characters16; - // Zero extend and copy already processed 8 bit data - LChar* ptr8 = characters; - LChar* endPtr8 = destination; + // Zero extend and copy already processed 8 bit data + LChar* ptr8 = characters; + LChar* endPtr8 = destination; - while (ptr8 < endPtr8) - *destination16++ = *ptr8++; + while (ptr8 < endPtr8) + *destination16++ = *ptr8++; - // Handle the character that triggered the 16 bit path - *destination16 = table[*source]; - ++source; - ++destination16; + // Handle the character that triggered the 16 bit path + *destination16 = table[*source]; + ++source; + ++destination16; + + while (source < end) { + if (isASCII(*source)) { + // Fast path for ASCII. Most Latin-1 text will be ASCII. + if (isAlignedToMachineWord(source)) { + while (source < alignedEnd) { + MachineWord chunk = *reinterpret_cast_ptr(source); + + if (!isAllASCII(chunk)) + goto useLookupTable16; - while (source < end) { - if (isASCII(*source)) { - // Fast path for ASCII. Most Latin-1 text will be ASCII. - if (isAlignedToMachineWord(source)) { - while (source < alignedEnd) { - MachineWord chunk = *reinterpret_cast_ptr(source); - - if (!isAllASCII(chunk)) - goto useLookupTable16; - - copyASCIIMachineWord(destination16, source); - source += sizeof(MachineWord); - destination16 += sizeof(MachineWord); - } - - if (source == end) - break; - } - *destination16 = *source; - } else { -useLookupTable16: - *destination16 = table[*source]; + copyASCIIMachineWord(destination16, source); + source += sizeof(MachineWord); + destination16 += sizeof(MachineWord); } - ++source; - ++destination16; + if (source == end) + break; + } + *destination16 = *source; + } else { + useLookupTable16: + *destination16 = table[*source]; } - return result16; + ++source; + ++destination16; + } + + return result16; } -template -static CString encodeComplexWindowsLatin1(const CharType* characters, size_t length, UnencodableHandling handling) -{ - Vector result(length); - char* bytes = result.data(); - - size_t resultLength = 0; - for (size_t i = 0; i < length; ) { - UChar32 c; - U16_NEXT(characters, i, length, c); - unsigned char b = c; - // Do an efficient check to detect characters other than 00-7F and A0-FF. - if (b != c || (c & 0xE0) == 0x80) { - // Look for a way to encode this with Windows Latin-1. - for (b = 0x80; b < 0xA0; ++b) - if (table[b] == c) - goto gotByte; - // No way to encode this character with Windows Latin-1. - UnencodableReplacementArray replacement; - int replacementLength = TextCodec::getUnencodableReplacement(c, handling, replacement); - result.grow(resultLength + replacementLength + length - i); - bytes = result.data(); - memcpy(bytes + resultLength, replacement, replacementLength); - resultLength += replacementLength; - continue; - } - gotByte: - bytes[resultLength++] = b; +template +static CString encodeComplexWindowsLatin1(const CharType* characters, + size_t length, + UnencodableHandling handling) { + Vector result(length); + char* bytes = result.data(); + + size_t resultLength = 0; + for (size_t i = 0; i < length;) { + UChar32 c; + U16_NEXT(characters, i, length, c); + unsigned char b = c; + // Do an efficient check to detect characters other than 00-7F and A0-FF. + if (b != c || (c & 0xE0) == 0x80) { + // Look for a way to encode this with Windows Latin-1. + for (b = 0x80; b < 0xA0; ++b) + if (table[b] == c) + goto gotByte; + // No way to encode this character with Windows Latin-1. + UnencodableReplacementArray replacement; + int replacementLength = + TextCodec::getUnencodableReplacement(c, handling, replacement); + result.grow(resultLength + replacementLength + length - i); + bytes = result.data(); + memcpy(bytes + resultLength, replacement, replacementLength); + resultLength += replacementLength; + continue; } + gotByte: + bytes[resultLength++] = b; + } - return CString(bytes, resultLength); + return CString(bytes, resultLength); } -template -CString TextCodecLatin1::encodeCommon(const CharType* characters, size_t length, UnencodableHandling handling) -{ - { - char* bytes; - CString string = CString::newUninitialized(length, bytes); - - // Convert the string a fast way and simultaneously do an efficient check to see if it's all ASCII. - UChar ored = 0; - for (size_t i = 0; i < length; ++i) { - UChar c = characters[i]; - bytes[i] = c; - ored |= c; - } - - if (!(ored & 0xFF80)) - return string; +template +CString TextCodecLatin1::encodeCommon(const CharType* characters, + size_t length, + UnencodableHandling handling) { + { + char* bytes; + CString string = CString::newUninitialized(length, bytes); + + // Convert the string a fast way and simultaneously do an efficient check to + // see if it's all ASCII. + UChar ored = 0; + for (size_t i = 0; i < length; ++i) { + UChar c = characters[i]; + bytes[i] = c; + ored |= c; } - // If it wasn't all ASCII, call the function that handles more-complex cases. - return encodeComplexWindowsLatin1(characters, length, handling); + if (!(ored & 0xFF80)) + return string; + } + + // If it wasn't all ASCII, call the function that handles more-complex cases. + return encodeComplexWindowsLatin1(characters, length, handling); } -CString TextCodecLatin1::encode(const UChar* characters, size_t length, UnencodableHandling handling) -{ - return encodeCommon(characters, length, handling); +CString TextCodecLatin1::encode(const UChar* characters, + size_t length, + UnencodableHandling handling) { + return encodeCommon(characters, length, handling); } -CString TextCodecLatin1::encode(const LChar* characters, size_t length, UnencodableHandling handling) -{ - return encodeCommon(characters, length, handling); +CString TextCodecLatin1::encode(const LChar* characters, + size_t length, + UnencodableHandling handling) { + return encodeCommon(characters, length, handling); } -} // namespace WTF +} // namespace WTF diff --git a/sky/engine/wtf/text/TextCodecLatin1.h b/sky/engine/wtf/text/TextCodecLatin1.h index 3f5d40a722bc7..fede4d12541b1 100644 --- a/sky/engine/wtf/text/TextCodecLatin1.h +++ b/sky/engine/wtf/text/TextCodecLatin1.h @@ -31,19 +31,27 @@ namespace WTF { class TextCodecLatin1 final : public TextCodec { -public: - static void registerEncodingNames(EncodingNameRegistrar); - static void registerCodecs(TextCodecRegistrar); - -private: - virtual String decode(const char*, size_t length, FlushBehavior, bool stopOnError, bool& sawError) override; - virtual CString encode(const UChar*, size_t length, UnencodableHandling) override; - virtual CString encode(const LChar*, size_t length, UnencodableHandling) override; - - template - CString encodeCommon(const CharType*, size_t length, UnencodableHandling); + public: + static void registerEncodingNames(EncodingNameRegistrar); + static void registerCodecs(TextCodecRegistrar); + + private: + virtual String decode(const char*, + size_t length, + FlushBehavior, + bool stopOnError, + bool& sawError) override; + virtual CString encode(const UChar*, + size_t length, + UnencodableHandling) override; + virtual CString encode(const LChar*, + size_t length, + UnencodableHandling) override; + + template + CString encodeCommon(const CharType*, size_t length, UnencodableHandling); }; -} // namespace WTF +} // namespace WTF #endif // SKY_ENGINE_WTF_TEXT_TEXTCODECLATIN1_H_ diff --git a/sky/engine/wtf/text/TextCodecReplacement.cpp b/sky/engine/wtf/text/TextCodecReplacement.cpp index 19f6722cb73a0..dc3b2a3df94bb 100644 --- a/sky/engine/wtf/text/TextCodecReplacement.cpp +++ b/sky/engine/wtf/text/TextCodecReplacement.cpp @@ -10,44 +10,44 @@ namespace WTF { -TextCodecReplacement::TextCodecReplacement() - : m_sentEOF(false) -{ +TextCodecReplacement::TextCodecReplacement() : m_sentEOF(false) {} + +void TextCodecReplacement::registerEncodingNames( + EncodingNameRegistrar registrar) { + // The 'replacement' label itself should not be referenceable by + // resources or script - it's a specification convenience - but much of + // the wtf/text API asserts that an encoding name is a label for itself. + // This is handled in TextEncoding by marking it as not valid. + registrar("replacement", "replacement"); + + registrar("csiso2022kr", "replacement"); + registrar("hz-gb-2312", "replacement"); + registrar("iso-2022-cn", "replacement"); + registrar("iso-2022-cn-ext", "replacement"); + registrar("iso-2022-kr", "replacement"); } -void TextCodecReplacement::registerEncodingNames(EncodingNameRegistrar registrar) -{ - // The 'replacement' label itself should not be referenceable by - // resources or script - it's a specification convenience - but much of - // the wtf/text API asserts that an encoding name is a label for itself. - // This is handled in TextEncoding by marking it as not valid. - registrar("replacement", "replacement"); - - registrar("csiso2022kr", "replacement"); - registrar("hz-gb-2312", "replacement"); - registrar("iso-2022-cn", "replacement"); - registrar("iso-2022-cn-ext", "replacement"); - registrar("iso-2022-kr", "replacement"); +static PassOwnPtr newStreamingTextDecoderReplacement( + const TextEncoding&, + const void*) { + return adoptPtr(new TextCodecReplacement); } -static PassOwnPtr newStreamingTextDecoderReplacement(const TextEncoding&, const void*) -{ - return adoptPtr(new TextCodecReplacement); +void TextCodecReplacement::registerCodecs(TextCodecRegistrar registrar) { + registrar("replacement", newStreamingTextDecoderReplacement, 0); } -void TextCodecReplacement::registerCodecs(TextCodecRegistrar registrar) -{ - registrar("replacement", newStreamingTextDecoderReplacement, 0); +String TextCodecReplacement::decode(const char*, + size_t, + FlushBehavior, + bool, + bool& sawError) { + sawError = true; + if (m_sentEOF) + return String(); + + m_sentEOF = true; + return String(&Unicode::replacementCharacter, 1); } -String TextCodecReplacement::decode(const char*, size_t, FlushBehavior, bool, bool& sawError) -{ - sawError = true; - if (m_sentEOF) - return String(); - - m_sentEOF = true; - return String(&Unicode::replacementCharacter, 1); -} - -} // namespace WTF +} // namespace WTF diff --git a/sky/engine/wtf/text/TextCodecReplacement.h b/sky/engine/wtf/text/TextCodecReplacement.h index d67de69a48b1a..aa0620d46dfca 100644 --- a/sky/engine/wtf/text/TextCodecReplacement.h +++ b/sky/engine/wtf/text/TextCodecReplacement.h @@ -11,18 +11,22 @@ namespace WTF { class TextCodecReplacement final : public TextCodecUTF8 { -public: - TextCodecReplacement(); + public: + TextCodecReplacement(); - static void registerEncodingNames(EncodingNameRegistrar); - static void registerCodecs(TextCodecRegistrar); + static void registerEncodingNames(EncodingNameRegistrar); + static void registerCodecs(TextCodecRegistrar); -private: - virtual String decode(const char*, size_t length, FlushBehavior, bool stopOnError, bool& sawError) override; + private: + virtual String decode(const char*, + size_t length, + FlushBehavior, + bool stopOnError, + bool& sawError) override; - bool m_sentEOF; + bool m_sentEOF; }; -} // namespace WTF +} // namespace WTF #endif // SKY_ENGINE_WTF_TEXT_TEXTCODECREPLACEMENT_H_ diff --git a/sky/engine/wtf/text/TextCodecReplacementTest.cpp b/sky/engine/wtf/text/TextCodecReplacementTest.cpp index a1aaf2f1a9e83..f4b5b7f3f1fcd 100644 --- a/sky/engine/wtf/text/TextCodecReplacementTest.cpp +++ b/sky/engine/wtf/text/TextCodecReplacementTest.cpp @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. - #include "flutter/sky/engine/wtf/text/TextCodecReplacement.h" #include @@ -20,44 +19,43 @@ namespace { // Just one example, others are listed in the codec implementation. const char* replacementAlias = "iso-2022-kr"; -TEST(TextCodecReplacement, Aliases) -{ - // "replacement" is not a valid alias for itself - EXPECT_FALSE(TextEncoding("replacement").isValid()); - EXPECT_FALSE(TextEncoding("rEpLaCeMeNt").isValid()); +TEST(TextCodecReplacement, Aliases) { + // "replacement" is not a valid alias for itself + EXPECT_FALSE(TextEncoding("replacement").isValid()); + EXPECT_FALSE(TextEncoding("rEpLaCeMeNt").isValid()); - EXPECT_TRUE(TextEncoding(replacementAlias).isValid()); - EXPECT_STREQ("replacement", TextEncoding(replacementAlias).name()); + EXPECT_TRUE(TextEncoding(replacementAlias).isValid()); + EXPECT_STREQ("replacement", TextEncoding(replacementAlias).name()); } -TEST(TextCodecReplacement, DecodesToFFFD) -{ - TextEncoding encoding(replacementAlias); - OwnPtr codec(newTextCodec(encoding)); +TEST(TextCodecReplacement, DecodesToFFFD) { + TextEncoding encoding(replacementAlias); + OwnPtr codec(newTextCodec(encoding)); - bool sawError = false; - const char testCase[] = "hello world"; - size_t testCaseSize = sizeof(testCase) - 1; + bool sawError = false; + const char testCase[] = "hello world"; + size_t testCaseSize = sizeof(testCase) - 1; - const String result = codec->decode(testCase, testCaseSize, DataEOF, false, sawError); - EXPECT_TRUE(sawError); - ASSERT_EQ(1u, result.length()); - EXPECT_EQ(0xFFFDU, result[0]); + const String result = + codec->decode(testCase, testCaseSize, DataEOF, false, sawError); + EXPECT_TRUE(sawError); + ASSERT_EQ(1u, result.length()); + EXPECT_EQ(0xFFFDU, result[0]); } -TEST(TextCodecReplacement, EncodesToUTF8) -{ - TextEncoding encoding(replacementAlias); - OwnPtr codec(newTextCodec(encoding)); +TEST(TextCodecReplacement, EncodesToUTF8) { + TextEncoding encoding(replacementAlias); + OwnPtr codec(newTextCodec(encoding)); - // "Kanji" in Chinese characters. - const UChar testCase[] = { 0x6F22, 0x5B57 }; - size_t testCaseSize = WTF_ARRAY_LENGTH(testCase); - CString result = codec->encode(testCase, testCaseSize, QuestionMarksForUnencodables); + // "Kanji" in Chinese characters. + const UChar testCase[] = {0x6F22, 0x5B57}; + size_t testCaseSize = WTF_ARRAY_LENGTH(testCase); + CString result = + codec->encode(testCase, testCaseSize, QuestionMarksForUnencodables); - EXPECT_STREQ("\xE6\xBC\xA2\xE5\xAD\x97", result.data()); + EXPECT_STREQ("\xE6\xBC\xA2\xE5\xAD\x97", result.data()); } -} // namespace +} // namespace -} // namespace WTF +} // namespace WTF diff --git a/sky/engine/wtf/text/TextCodecUTF16.cpp b/sky/engine/wtf/text/TextCodecUTF16.cpp index e1115f3030ec5..2d6e690f46a5a 100644 --- a/sky/engine/wtf/text/TextCodecUTF16.cpp +++ b/sky/engine/wtf/text/TextCodecUTF16.cpp @@ -35,155 +35,160 @@ using namespace std; namespace WTF { -void TextCodecUTF16::registerEncodingNames(EncodingNameRegistrar registrar) -{ - registrar("UTF-16LE", "UTF-16LE"); - registrar("UTF-16BE", "UTF-16BE"); - - registrar("ISO-10646-UCS-2", "UTF-16LE"); - registrar("UCS-2", "UTF-16LE"); - registrar("UTF-16", "UTF-16LE"); - registrar("Unicode", "UTF-16LE"); - registrar("csUnicode", "UTF-16LE"); - registrar("unicodeFEFF", "UTF-16LE"); - - registrar("unicodeFFFE", "UTF-16BE"); +void TextCodecUTF16::registerEncodingNames(EncodingNameRegistrar registrar) { + registrar("UTF-16LE", "UTF-16LE"); + registrar("UTF-16BE", "UTF-16BE"); + + registrar("ISO-10646-UCS-2", "UTF-16LE"); + registrar("UCS-2", "UTF-16LE"); + registrar("UTF-16", "UTF-16LE"); + registrar("Unicode", "UTF-16LE"); + registrar("csUnicode", "UTF-16LE"); + registrar("unicodeFEFF", "UTF-16LE"); + + registrar("unicodeFFFE", "UTF-16BE"); } -static PassOwnPtr newStreamingTextDecoderUTF16LE(const TextEncoding&, const void*) -{ - return adoptPtr(new TextCodecUTF16(true)); +static PassOwnPtr newStreamingTextDecoderUTF16LE(const TextEncoding&, + const void*) { + return adoptPtr(new TextCodecUTF16(true)); } -static PassOwnPtr newStreamingTextDecoderUTF16BE(const TextEncoding&, const void*) -{ - return adoptPtr(new TextCodecUTF16(false)); +static PassOwnPtr newStreamingTextDecoderUTF16BE(const TextEncoding&, + const void*) { + return adoptPtr(new TextCodecUTF16(false)); } -void TextCodecUTF16::registerCodecs(TextCodecRegistrar registrar) -{ - registrar("UTF-16LE", newStreamingTextDecoderUTF16LE, 0); - registrar("UTF-16BE", newStreamingTextDecoderUTF16BE, 0); +void TextCodecUTF16::registerCodecs(TextCodecRegistrar registrar) { + registrar("UTF-16LE", newStreamingTextDecoderUTF16LE, 0); + registrar("UTF-16BE", newStreamingTextDecoderUTF16BE, 0); } -String TextCodecUTF16::decode(const char* bytes, size_t length, FlushBehavior flush, bool, bool& sawError) -{ - // For compatibility reasons, ignore flush from fetch EOF. - const bool reallyFlush = flush != DoNotFlush && flush != FetchEOF; - - if (!length) { - if (!reallyFlush || !m_haveBufferedByte) - return String(); - sawError = true; - return String(&Unicode::replacementCharacter, 1); - } - - // FIXME: This should generate an error if there is an unpaired surrogate. - - const unsigned char* p = reinterpret_cast(bytes); - size_t numBytes = length + m_haveBufferedByte; - size_t numCharsIn = numBytes / 2; - size_t numCharsOut = ((numBytes & 1) && reallyFlush) ? numCharsIn + 1 : numCharsIn; - - StringBuffer buffer(numCharsOut); - UChar* q = buffer.characters(); - - if (m_haveBufferedByte) { - UChar c; - if (m_littleEndian) - c = m_bufferedByte | (p[0] << 8); - else - c = (m_bufferedByte << 8) | p[0]; - *q++ = c; - m_haveBufferedByte = false; - p += 1; - numCharsIn -= 1; +String TextCodecUTF16::decode(const char* bytes, + size_t length, + FlushBehavior flush, + bool, + bool& sawError) { + // For compatibility reasons, ignore flush from fetch EOF. + const bool reallyFlush = flush != DoNotFlush && flush != FetchEOF; + + if (!length) { + if (!reallyFlush || !m_haveBufferedByte) + return String(); + sawError = true; + return String(&Unicode::replacementCharacter, 1); + } + + // FIXME: This should generate an error if there is an unpaired surrogate. + + const unsigned char* p = reinterpret_cast(bytes); + size_t numBytes = length + m_haveBufferedByte; + size_t numCharsIn = numBytes / 2; + size_t numCharsOut = + ((numBytes & 1) && reallyFlush) ? numCharsIn + 1 : numCharsIn; + + StringBuffer buffer(numCharsOut); + UChar* q = buffer.characters(); + + if (m_haveBufferedByte) { + UChar c; + if (m_littleEndian) + c = m_bufferedByte | (p[0] << 8); + else + c = (m_bufferedByte << 8) | p[0]; + *q++ = c; + m_haveBufferedByte = false; + p += 1; + numCharsIn -= 1; + } + + if (m_littleEndian) { + for (size_t i = 0; i < numCharsIn; ++i) { + UChar c = p[0] | (p[1] << 8); + p += 2; + *q++ = c; } - - if (m_littleEndian) { - for (size_t i = 0; i < numCharsIn; ++i) { - UChar c = p[0] | (p[1] << 8); - p += 2; - *q++ = c; - } - } else { - for (size_t i = 0; i < numCharsIn; ++i) { - UChar c = (p[0] << 8) | p[1]; - p += 2; - *q++ = c; - } + } else { + for (size_t i = 0; i < numCharsIn; ++i) { + UChar c = (p[0] << 8) | p[1]; + p += 2; + *q++ = c; } + } - if (numBytes & 1) { - ASSERT(!m_haveBufferedByte); + if (numBytes & 1) { + ASSERT(!m_haveBufferedByte); - if (reallyFlush) { - sawError = true; - *q++ = Unicode::replacementCharacter; - } else { - m_haveBufferedByte = true; - m_bufferedByte = p[0]; - } + if (reallyFlush) { + sawError = true; + *q++ = Unicode::replacementCharacter; + } else { + m_haveBufferedByte = true; + m_bufferedByte = p[0]; } + } - buffer.shrink(q - buffer.characters()); + buffer.shrink(q - buffer.characters()); - return String::adopt(buffer); + return String::adopt(buffer); } -CString TextCodecUTF16::encode(const UChar* characters, size_t length, UnencodableHandling) -{ - // We need to be sure we can double the length without overflowing. - // Since the passed-in length is the length of an actual existing - // character buffer, each character is two bytes, and we know - // the buffer doesn't occupy the entire address space, we can - // assert here that doubling the length does not overflow size_t - // and there's no need for a runtime check. - ASSERT(length <= numeric_limits::max() / 2); - - char* bytes; - CString result = CString::newUninitialized(length * 2, bytes); - - // FIXME: CString is not a reasonable data structure for encoded UTF-16, which will have - // null characters inside it. Perhaps the result of encode should not be a CString. - if (m_littleEndian) { - for (size_t i = 0; i < length; ++i) { - UChar c = characters[i]; - bytes[i * 2] = c; - bytes[i * 2 + 1] = c >> 8; - } - } else { - for (size_t i = 0; i < length; ++i) { - UChar c = characters[i]; - bytes[i * 2] = c >> 8; - bytes[i * 2 + 1] = c; - } +CString TextCodecUTF16::encode(const UChar* characters, + size_t length, + UnencodableHandling) { + // We need to be sure we can double the length without overflowing. + // Since the passed-in length is the length of an actual existing + // character buffer, each character is two bytes, and we know + // the buffer doesn't occupy the entire address space, we can + // assert here that doubling the length does not overflow size_t + // and there's no need for a runtime check. + ASSERT(length <= numeric_limits::max() / 2); + + char* bytes; + CString result = CString::newUninitialized(length * 2, bytes); + + // FIXME: CString is not a reasonable data structure for encoded UTF-16, which + // will have null characters inside it. Perhaps the result of encode should + // not be a CString. + if (m_littleEndian) { + for (size_t i = 0; i < length; ++i) { + UChar c = characters[i]; + bytes[i * 2] = c; + bytes[i * 2 + 1] = c >> 8; + } + } else { + for (size_t i = 0; i < length; ++i) { + UChar c = characters[i]; + bytes[i * 2] = c >> 8; + bytes[i * 2 + 1] = c; } + } - return result; + return result; } -CString TextCodecUTF16::encode(const LChar* characters, size_t length, UnencodableHandling) -{ - // In the LChar case, we do actually need to perform this check in release. :) - RELEASE_ASSERT(length <= numeric_limits::max() / 2); +CString TextCodecUTF16::encode(const LChar* characters, + size_t length, + UnencodableHandling) { + // In the LChar case, we do actually need to perform this check in release. :) + RELEASE_ASSERT(length <= numeric_limits::max() / 2); - char* bytes; - CString result = CString::newUninitialized(length * 2, bytes); + char* bytes; + CString result = CString::newUninitialized(length * 2, bytes); - if (m_littleEndian) { - for (size_t i = 0; i < length; ++i) { - bytes[i * 2] = characters[i]; - bytes[i * 2 + 1] = 0; - } - } else { - for (size_t i = 0; i < length; ++i) { - bytes[i * 2] = 0; - bytes[i * 2 + 1] = characters[i]; - } + if (m_littleEndian) { + for (size_t i = 0; i < length; ++i) { + bytes[i * 2] = characters[i]; + bytes[i * 2 + 1] = 0; + } + } else { + for (size_t i = 0; i < length; ++i) { + bytes[i * 2] = 0; + bytes[i * 2 + 1] = characters[i]; } + } - return result; + return result; } -} // namespace WTF +} // namespace WTF diff --git a/sky/engine/wtf/text/TextCodecUTF16.h b/sky/engine/wtf/text/TextCodecUTF16.h index 9a4d78d57f773..66f05fb4bdfef 100644 --- a/sky/engine/wtf/text/TextCodecUTF16.h +++ b/sky/engine/wtf/text/TextCodecUTF16.h @@ -30,23 +30,32 @@ namespace WTF { - class TextCodecUTF16 final : public TextCodec { - public: - static void registerEncodingNames(EncodingNameRegistrar); - static void registerCodecs(TextCodecRegistrar); - - TextCodecUTF16(bool littleEndian) : m_littleEndian(littleEndian), m_haveBufferedByte(false) { } - - virtual String decode(const char*, size_t length, FlushBehavior, bool stopOnError, bool& sawError) override; - virtual CString encode(const UChar*, size_t length, UnencodableHandling) override; - virtual CString encode(const LChar*, size_t length, UnencodableHandling) override; - - private: - bool m_littleEndian; - bool m_haveBufferedByte; - unsigned char m_bufferedByte; - }; - -} // namespace WTF +class TextCodecUTF16 final : public TextCodec { + public: + static void registerEncodingNames(EncodingNameRegistrar); + static void registerCodecs(TextCodecRegistrar); + + TextCodecUTF16(bool littleEndian) + : m_littleEndian(littleEndian), m_haveBufferedByte(false) {} + + virtual String decode(const char*, + size_t length, + FlushBehavior, + bool stopOnError, + bool& sawError) override; + virtual CString encode(const UChar*, + size_t length, + UnencodableHandling) override; + virtual CString encode(const LChar*, + size_t length, + UnencodableHandling) override; + + private: + bool m_littleEndian; + bool m_haveBufferedByte; + unsigned char m_bufferedByte; +}; + +} // namespace WTF #endif // SKY_ENGINE_WTF_TEXT_TEXTCODECUTF16_H_ diff --git a/sky/engine/wtf/text/TextCodecUTF8.cpp b/sky/engine/wtf/text/TextCodecUTF8.cpp index 7df5fba2490ba..faae30ba4a162 100644 --- a/sky/engine/wtf/text/TextCodecUTF8.cpp +++ b/sky/engine/wtf/text/TextCodecUTF8.cpp @@ -38,425 +38,443 @@ namespace WTF { const int nonCharacter = -1; -PassOwnPtr TextCodecUTF8::create(const TextEncoding&, const void*) -{ - return adoptPtr(new TextCodecUTF8); +PassOwnPtr TextCodecUTF8::create(const TextEncoding&, const void*) { + return adoptPtr(new TextCodecUTF8); } -void TextCodecUTF8::registerEncodingNames(EncodingNameRegistrar registrar) -{ - registrar("UTF-8", "UTF-8"); - - // Additional aliases that originally were present in the encoding - // table in WebKit on Macintosh, and subsequently added by - // TextCodecICU. Perhaps we can prove some are not used on the web - // and remove them. - registrar("unicode11utf8", "UTF-8"); - registrar("unicode20utf8", "UTF-8"); - registrar("utf8", "UTF-8"); - registrar("x-unicode20utf8", "UTF-8"); - - // Additional aliases present in the WHATWG Encoding Standard (http://encoding.spec.whatwg.org/) - // and Firefox (24), but not in ICU 4.6. - registrar("unicode-1-1-utf-8", "UTF-8"); +void TextCodecUTF8::registerEncodingNames(EncodingNameRegistrar registrar) { + registrar("UTF-8", "UTF-8"); + + // Additional aliases that originally were present in the encoding + // table in WebKit on Macintosh, and subsequently added by + // TextCodecICU. Perhaps we can prove some are not used on the web + // and remove them. + registrar("unicode11utf8", "UTF-8"); + registrar("unicode20utf8", "UTF-8"); + registrar("utf8", "UTF-8"); + registrar("x-unicode20utf8", "UTF-8"); + + // Additional aliases present in the WHATWG Encoding Standard + // (http://encoding.spec.whatwg.org/) and Firefox (24), but not in ICU 4.6. + registrar("unicode-1-1-utf-8", "UTF-8"); } -void TextCodecUTF8::registerCodecs(TextCodecRegistrar registrar) -{ - registrar("UTF-8", create, 0); +void TextCodecUTF8::registerCodecs(TextCodecRegistrar registrar) { + registrar("UTF-8", create, 0); } -static inline int nonASCIISequenceLength(uint8_t firstByte) -{ - static const uint8_t lengths[256] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; - return lengths[firstByte]; +static inline int nonASCIISequenceLength(uint8_t firstByte) { + static const uint8_t lengths[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + return lengths[firstByte]; } -static inline int decodeNonASCIISequence(const uint8_t* sequence, unsigned length) -{ - ASSERT(!isASCII(sequence[0])); - if (length == 2) { - ASSERT(sequence[0] <= 0xDF); - if (sequence[0] < 0xC2) - return nonCharacter; - if (sequence[1] < 0x80 || sequence[1] > 0xBF) - return nonCharacter; - return ((sequence[0] << 6) + sequence[1]) - 0x00003080; - } - if (length == 3) { - ASSERT(sequence[0] >= 0xE0 && sequence[0] <= 0xEF); - switch (sequence[0]) { - case 0xE0: - if (sequence[1] < 0xA0 || sequence[1] > 0xBF) - return nonCharacter; - break; - case 0xED: - if (sequence[1] < 0x80 || sequence[1] > 0x9F) - return nonCharacter; - break; - default: - if (sequence[1] < 0x80 || sequence[1] > 0xBF) - return nonCharacter; - } - if (sequence[2] < 0x80 || sequence[2] > 0xBF) - return nonCharacter; - return ((sequence[0] << 12) + (sequence[1] << 6) + sequence[2]) - 0x000E2080; - } - ASSERT(length == 4); - ASSERT(sequence[0] >= 0xF0 && sequence[0] <= 0xF4); +static inline int decodeNonASCIISequence(const uint8_t* sequence, + unsigned length) { + ASSERT(!isASCII(sequence[0])); + if (length == 2) { + ASSERT(sequence[0] <= 0xDF); + if (sequence[0] < 0xC2) + return nonCharacter; + if (sequence[1] < 0x80 || sequence[1] > 0xBF) + return nonCharacter; + return ((sequence[0] << 6) + sequence[1]) - 0x00003080; + } + if (length == 3) { + ASSERT(sequence[0] >= 0xE0 && sequence[0] <= 0xEF); switch (sequence[0]) { - case 0xF0: - if (sequence[1] < 0x90 || sequence[1] > 0xBF) - return nonCharacter; + case 0xE0: + if (sequence[1] < 0xA0 || sequence[1] > 0xBF) + return nonCharacter; break; - case 0xF4: - if (sequence[1] < 0x80 || sequence[1] > 0x8F) - return nonCharacter; + case 0xED: + if (sequence[1] < 0x80 || sequence[1] > 0x9F) + return nonCharacter; break; - default: + default: if (sequence[1] < 0x80 || sequence[1] > 0xBF) - return nonCharacter; + return nonCharacter; } if (sequence[2] < 0x80 || sequence[2] > 0xBF) + return nonCharacter; + return ((sequence[0] << 12) + (sequence[1] << 6) + sequence[2]) - + 0x000E2080; + } + ASSERT(length == 4); + ASSERT(sequence[0] >= 0xF0 && sequence[0] <= 0xF4); + switch (sequence[0]) { + case 0xF0: + if (sequence[1] < 0x90 || sequence[1] > 0xBF) + return nonCharacter; + break; + case 0xF4: + if (sequence[1] < 0x80 || sequence[1] > 0x8F) return nonCharacter; - if (sequence[3] < 0x80 || sequence[3] > 0xBF) + break; + default: + if (sequence[1] < 0x80 || sequence[1] > 0xBF) return nonCharacter; - return ((sequence[0] << 18) + (sequence[1] << 12) + (sequence[2] << 6) + sequence[3]) - 0x03C82080; + } + if (sequence[2] < 0x80 || sequence[2] > 0xBF) + return nonCharacter; + if (sequence[3] < 0x80 || sequence[3] > 0xBF) + return nonCharacter; + return ((sequence[0] << 18) + (sequence[1] << 12) + (sequence[2] << 6) + + sequence[3]) - + 0x03C82080; } -static inline UChar* appendCharacter(UChar* destination, int character) -{ - ASSERT(character != nonCharacter); - ASSERT(!U_IS_SURROGATE(character)); - if (U_IS_BMP(character)) - *destination++ = character; - else { - *destination++ = U16_LEAD(character); - *destination++ = U16_TRAIL(character); - } - return destination; +static inline UChar* appendCharacter(UChar* destination, int character) { + ASSERT(character != nonCharacter); + ASSERT(!U_IS_SURROGATE(character)); + if (U_IS_BMP(character)) + *destination++ = character; + else { + *destination++ = U16_LEAD(character); + *destination++ = U16_TRAIL(character); + } + return destination; } -void TextCodecUTF8::consumePartialSequenceByte() -{ - --m_partialSequenceSize; - memmove(m_partialSequence, m_partialSequence + 1, m_partialSequenceSize); +void TextCodecUTF8::consumePartialSequenceByte() { + --m_partialSequenceSize; + memmove(m_partialSequence, m_partialSequence + 1, m_partialSequenceSize); } -void TextCodecUTF8::handleError(UChar*& destination, bool stopOnError, bool& sawError) -{ - sawError = true; - if (stopOnError) - return; - // Each error generates a replacement character and consumes one byte. - *destination++ = replacementCharacter; - consumePartialSequenceByte(); +void TextCodecUTF8::handleError(UChar*& destination, + bool stopOnError, + bool& sawError) { + sawError = true; + if (stopOnError) + return; + // Each error generates a replacement character and consumes one byte. + *destination++ = replacementCharacter; + consumePartialSequenceByte(); } template <> -bool TextCodecUTF8::handlePartialSequence(LChar*& destination, const uint8_t*& source, const uint8_t* end, bool flush, bool, bool&) -{ - ASSERT(m_partialSequenceSize); - do { - if (isASCII(m_partialSequence[0])) { - *destination++ = m_partialSequence[0]; - consumePartialSequenceByte(); - continue; - } - int count = nonASCIISequenceLength(m_partialSequence[0]); - if (!count) - return true; - - if (count > m_partialSequenceSize) { - if (count - m_partialSequenceSize > end - source) { - if (!flush) { - // The new data is not enough to complete the sequence, so - // add it to the existing partial sequence. - memcpy(m_partialSequence + m_partialSequenceSize, source, end - source); - m_partialSequenceSize += end - source; - return false; - } - // An incomplete partial sequence at the end is an error, but it will create - // a 16 bit string due to the replacementCharacter. Let the 16 bit path handle - // the error. - return true; - } - memcpy(m_partialSequence + m_partialSequenceSize, source, count - m_partialSequenceSize); - source += count - m_partialSequenceSize; - m_partialSequenceSize = count; +bool TextCodecUTF8::handlePartialSequence(LChar*& destination, + const uint8_t*& source, + const uint8_t* end, + bool flush, + bool, + bool&) { + ASSERT(m_partialSequenceSize); + do { + if (isASCII(m_partialSequence[0])) { + *destination++ = m_partialSequence[0]; + consumePartialSequenceByte(); + continue; + } + int count = nonASCIISequenceLength(m_partialSequence[0]); + if (!count) + return true; + + if (count > m_partialSequenceSize) { + if (count - m_partialSequenceSize > end - source) { + if (!flush) { + // The new data is not enough to complete the sequence, so + // add it to the existing partial sequence. + memcpy(m_partialSequence + m_partialSequenceSize, source, + end - source); + m_partialSequenceSize += end - source; + return false; } - int character = decodeNonASCIISequence(m_partialSequence, count); - if ((character == nonCharacter) || (character > 0xff)) - return true; + // An incomplete partial sequence at the end is an error, but it will + // create a 16 bit string due to the replacementCharacter. Let the 16 + // bit path handle the error. + return true; + } + memcpy(m_partialSequence + m_partialSequenceSize, source, + count - m_partialSequenceSize); + source += count - m_partialSequenceSize; + m_partialSequenceSize = count; + } + int character = decodeNonASCIISequence(m_partialSequence, count); + if ((character == nonCharacter) || (character > 0xff)) + return true; - m_partialSequenceSize -= count; - *destination++ = character; - } while (m_partialSequenceSize); + m_partialSequenceSize -= count; + *destination++ = character; + } while (m_partialSequenceSize); - return false; + return false; } template <> -bool TextCodecUTF8::handlePartialSequence(UChar*& destination, const uint8_t*& source, const uint8_t* end, bool flush, bool stopOnError, bool& sawError) -{ - ASSERT(m_partialSequenceSize); - do { - if (isASCII(m_partialSequence[0])) { - *destination++ = m_partialSequence[0]; - consumePartialSequenceByte(); - continue; - } - int count = nonASCIISequenceLength(m_partialSequence[0]); - if (!count) { - handleError(destination, stopOnError, sawError); - if (stopOnError) - return false; - continue; - } - if (count > m_partialSequenceSize) { - if (count - m_partialSequenceSize > end - source) { - if (!flush) { - // The new data is not enough to complete the sequence, so - // add it to the existing partial sequence. - memcpy(m_partialSequence + m_partialSequenceSize, source, end - source); - m_partialSequenceSize += end - source; - return false; - } - // An incomplete partial sequence at the end is an error. - handleError(destination, stopOnError, sawError); - if (stopOnError) - return false; - continue; - } - memcpy(m_partialSequence + m_partialSequenceSize, source, count - m_partialSequenceSize); - source += count - m_partialSequenceSize; - m_partialSequenceSize = count; - } - int character = decodeNonASCIISequence(m_partialSequence, count); - if (character == nonCharacter) { - handleError(destination, stopOnError, sawError); - if (stopOnError) - return false; - continue; +bool TextCodecUTF8::handlePartialSequence(UChar*& destination, + const uint8_t*& source, + const uint8_t* end, + bool flush, + bool stopOnError, + bool& sawError) { + ASSERT(m_partialSequenceSize); + do { + if (isASCII(m_partialSequence[0])) { + *destination++ = m_partialSequence[0]; + consumePartialSequenceByte(); + continue; + } + int count = nonASCIISequenceLength(m_partialSequence[0]); + if (!count) { + handleError(destination, stopOnError, sawError); + if (stopOnError) + return false; + continue; + } + if (count > m_partialSequenceSize) { + if (count - m_partialSequenceSize > end - source) { + if (!flush) { + // The new data is not enough to complete the sequence, so + // add it to the existing partial sequence. + memcpy(m_partialSequence + m_partialSequenceSize, source, + end - source); + m_partialSequenceSize += end - source; + return false; } + // An incomplete partial sequence at the end is an error. + handleError(destination, stopOnError, sawError); + if (stopOnError) + return false; + continue; + } + memcpy(m_partialSequence + m_partialSequenceSize, source, + count - m_partialSequenceSize); + source += count - m_partialSequenceSize; + m_partialSequenceSize = count; + } + int character = decodeNonASCIISequence(m_partialSequence, count); + if (character == nonCharacter) { + handleError(destination, stopOnError, sawError); + if (stopOnError) + return false; + continue; + } - m_partialSequenceSize -= count; - destination = appendCharacter(destination, character); - } while (m_partialSequenceSize); + m_partialSequenceSize -= count; + destination = appendCharacter(destination, character); + } while (m_partialSequenceSize); - return false; + return false; } -String TextCodecUTF8::decode(const char* bytes, size_t length, FlushBehavior flush, bool stopOnError, bool& sawError) -{ - // Each input byte might turn into a character. - // That includes all bytes in the partial-sequence buffer because - // each byte in an invalid sequence will turn into a replacement character. - StringBuffer buffer(m_partialSequenceSize + length); - - const uint8_t* source = reinterpret_cast(bytes); - const uint8_t* end = source + length; - const uint8_t* alignedEnd = alignToMachineWord(end); - LChar* destination = buffer.characters(); - - do { - if (m_partialSequenceSize) { - // Explicitly copy destination and source pointers to avoid taking pointers to the - // local variables, which may harm code generation by disabling some optimizations - // in some compilers. - LChar* destinationForHandlePartialSequence = destination; - const uint8_t* sourceForHandlePartialSequence = source; - if (handlePartialSequence(destinationForHandlePartialSequence, sourceForHandlePartialSequence, end, flush, stopOnError, sawError)) { - source = sourceForHandlePartialSequence; - goto upConvertTo16Bit; - } - destination = destinationForHandlePartialSequence; - source = sourceForHandlePartialSequence; - if (m_partialSequenceSize) - break; - } +String TextCodecUTF8::decode(const char* bytes, + size_t length, + FlushBehavior flush, + bool stopOnError, + bool& sawError) { + // Each input byte might turn into a character. + // That includes all bytes in the partial-sequence buffer because + // each byte in an invalid sequence will turn into a replacement character. + StringBuffer buffer(m_partialSequenceSize + length); + + const uint8_t* source = reinterpret_cast(bytes); + const uint8_t* end = source + length; + const uint8_t* alignedEnd = alignToMachineWord(end); + LChar* destination = buffer.characters(); + + do { + if (m_partialSequenceSize) { + // Explicitly copy destination and source pointers to avoid taking + // pointers to the local variables, which may harm code generation by + // disabling some optimizations in some compilers. + LChar* destinationForHandlePartialSequence = destination; + const uint8_t* sourceForHandlePartialSequence = source; + if (handlePartialSequence(destinationForHandlePartialSequence, + sourceForHandlePartialSequence, end, flush, + stopOnError, sawError)) { + source = sourceForHandlePartialSequence; + goto upConvertTo16Bit; + } + destination = destinationForHandlePartialSequence; + source = sourceForHandlePartialSequence; + if (m_partialSequenceSize) + break; + } - while (source < end) { - if (isASCII(*source)) { - // Fast path for ASCII. Most UTF-8 text will be ASCII. - if (isAlignedToMachineWord(source)) { - while (source < alignedEnd) { - MachineWord chunk = *reinterpret_cast_ptr(source); - if (!isAllASCII(chunk)) - break; - copyASCIIMachineWord(destination, source); - source += sizeof(MachineWord); - destination += sizeof(MachineWord); - } - if (source == end) - break; - if (!isASCII(*source)) - continue; - } - *destination++ = *source++; - continue; - } - int count = nonASCIISequenceLength(*source); - int character; - if (!count) - character = nonCharacter; - else { - if (count > end - source) { - ASSERT_WITH_SECURITY_IMPLICATION(end - source < static_cast(sizeof(m_partialSequence))); - ASSERT(!m_partialSequenceSize); - m_partialSequenceSize = end - source; - memcpy(m_partialSequence, source, m_partialSequenceSize); - source = end; - break; - } - character = decodeNonASCIISequence(source, count); - } - if (character == nonCharacter) { - sawError = true; - if (stopOnError) - break; - - goto upConvertTo16Bit; - } - if (character > 0xff) - goto upConvertTo16Bit; - - source += count; - *destination++ = character; + while (source < end) { + if (isASCII(*source)) { + // Fast path for ASCII. Most UTF-8 text will be ASCII. + if (isAlignedToMachineWord(source)) { + while (source < alignedEnd) { + MachineWord chunk = + *reinterpret_cast_ptr(source); + if (!isAllASCII(chunk)) + break; + copyASCIIMachineWord(destination, source); + source += sizeof(MachineWord); + destination += sizeof(MachineWord); + } + if (source == end) + break; + if (!isASCII(*source)) + continue; } - } while (flush && m_partialSequenceSize); + *destination++ = *source++; + continue; + } + int count = nonASCIISequenceLength(*source); + int character; + if (!count) + character = nonCharacter; + else { + if (count > end - source) { + ASSERT_WITH_SECURITY_IMPLICATION( + end - source < static_cast(sizeof(m_partialSequence))); + ASSERT(!m_partialSequenceSize); + m_partialSequenceSize = end - source; + memcpy(m_partialSequence, source, m_partialSequenceSize); + source = end; + break; + } + character = decodeNonASCIISequence(source, count); + } + if (character == nonCharacter) { + sawError = true; + if (stopOnError) + break; + + goto upConvertTo16Bit; + } + if (character > 0xff) + goto upConvertTo16Bit; + + source += count; + *destination++ = character; + } + } while (flush && m_partialSequenceSize); - buffer.shrink(destination - buffer.characters()); + buffer.shrink(destination - buffer.characters()); - return String::adopt(buffer); + return String::adopt(buffer); upConvertTo16Bit: - StringBuffer buffer16(m_partialSequenceSize + length); - - UChar* destination16 = buffer16.characters(); - - // Copy the already converted characters - for (LChar* converted8 = buffer.characters(); converted8 < destination;) - *destination16++ = *converted8++; - - do { - if (m_partialSequenceSize) { - // Explicitly copy destination and source pointers to avoid taking pointers to the - // local variables, which may harm code generation by disabling some optimizations - // in some compilers. - UChar* destinationForHandlePartialSequence = destination16; - const uint8_t* sourceForHandlePartialSequence = source; - handlePartialSequence(destinationForHandlePartialSequence, sourceForHandlePartialSequence, end, flush, stopOnError, sawError); - destination16 = destinationForHandlePartialSequence; - source = sourceForHandlePartialSequence; - if (m_partialSequenceSize) - break; - } + StringBuffer buffer16(m_partialSequenceSize + length); + + UChar* destination16 = buffer16.characters(); + + // Copy the already converted characters + for (LChar* converted8 = buffer.characters(); converted8 < destination;) + *destination16++ = *converted8++; + + do { + if (m_partialSequenceSize) { + // Explicitly copy destination and source pointers to avoid taking + // pointers to the local variables, which may harm code generation by + // disabling some optimizations in some compilers. + UChar* destinationForHandlePartialSequence = destination16; + const uint8_t* sourceForHandlePartialSequence = source; + handlePartialSequence(destinationForHandlePartialSequence, + sourceForHandlePartialSequence, end, flush, + stopOnError, sawError); + destination16 = destinationForHandlePartialSequence; + source = sourceForHandlePartialSequence; + if (m_partialSequenceSize) + break; + } - while (source < end) { - if (isASCII(*source)) { - // Fast path for ASCII. Most UTF-8 text will be ASCII. - if (isAlignedToMachineWord(source)) { - while (source < alignedEnd) { - MachineWord chunk = *reinterpret_cast_ptr(source); - if (!isAllASCII(chunk)) - break; - copyASCIIMachineWord(destination16, source); - source += sizeof(MachineWord); - destination16 += sizeof(MachineWord); - } - if (source == end) - break; - if (!isASCII(*source)) - continue; - } - *destination16++ = *source++; - continue; - } - int count = nonASCIISequenceLength(*source); - int character; - if (!count) - character = nonCharacter; - else { - if (count > end - source) { - ASSERT_WITH_SECURITY_IMPLICATION(end - source < static_cast(sizeof(m_partialSequence))); - ASSERT(!m_partialSequenceSize); - m_partialSequenceSize = end - source; - memcpy(m_partialSequence, source, m_partialSequenceSize); - source = end; - break; - } - character = decodeNonASCIISequence(source, count); - } - if (character == nonCharacter) { - sawError = true; - if (stopOnError) - break; - // Each error generates a replacement character and consumes one byte. - *destination16++ = replacementCharacter; - ++source; - continue; - } - source += count; - destination16 = appendCharacter(destination16, character); + while (source < end) { + if (isASCII(*source)) { + // Fast path for ASCII. Most UTF-8 text will be ASCII. + if (isAlignedToMachineWord(source)) { + while (source < alignedEnd) { + MachineWord chunk = + *reinterpret_cast_ptr(source); + if (!isAllASCII(chunk)) + break; + copyASCIIMachineWord(destination16, source); + source += sizeof(MachineWord); + destination16 += sizeof(MachineWord); + } + if (source == end) + break; + if (!isASCII(*source)) + continue; + } + *destination16++ = *source++; + continue; + } + int count = nonASCIISequenceLength(*source); + int character; + if (!count) + character = nonCharacter; + else { + if (count > end - source) { + ASSERT_WITH_SECURITY_IMPLICATION( + end - source < static_cast(sizeof(m_partialSequence))); + ASSERT(!m_partialSequenceSize); + m_partialSequenceSize = end - source; + memcpy(m_partialSequence, source, m_partialSequenceSize); + source = end; + break; } - } while (flush && m_partialSequenceSize); + character = decodeNonASCIISequence(source, count); + } + if (character == nonCharacter) { + sawError = true; + if (stopOnError) + break; + // Each error generates a replacement character and consumes one byte. + *destination16++ = replacementCharacter; + ++source; + continue; + } + source += count; + destination16 = appendCharacter(destination16, character); + } + } while (flush && m_partialSequenceSize); - buffer16.shrink(destination16 - buffer16.characters()); + buffer16.shrink(destination16 - buffer16.characters()); - return String::adopt(buffer16); + return String::adopt(buffer16); } -template -CString TextCodecUTF8::encodeCommon(const CharType* characters, size_t length) -{ - // The maximum number of UTF-8 bytes needed per UTF-16 code unit is 3. - // BMP characters take only one UTF-16 code unit and can take up to 3 bytes (3x). - // Non-BMP characters take two UTF-16 code units and can take up to 4 bytes (2x). - if (length > numeric_limits::max() / 3) - CRASH(); - Vector bytes(length * 3); - - size_t i = 0; - size_t bytesWritten = 0; - while (i < length) { - UChar32 character; - U16_NEXT(characters, i, length, character); - // U16_NEXT will simply emit a surrogate code point if an unmatched surrogate - // is encountered; we must convert it to a U+FFFD (REPLACEMENT CHARACTER) here. - if (0xD800 <= character && character <= 0xDFFF) - character = replacementCharacter; - U8_APPEND_UNSAFE(bytes.data(), bytesWritten, character); - } - - return CString(reinterpret_cast(bytes.data()), bytesWritten); +template +CString TextCodecUTF8::encodeCommon(const CharType* characters, size_t length) { + // The maximum number of UTF-8 bytes needed per UTF-16 code unit is 3. + // BMP characters take only one UTF-16 code unit and can take up to 3 bytes + // (3x). Non-BMP characters take two UTF-16 code units and can take up to 4 + // bytes (2x). + if (length > numeric_limits::max() / 3) + CRASH(); + Vector bytes(length * 3); + + size_t i = 0; + size_t bytesWritten = 0; + while (i < length) { + UChar32 character; + U16_NEXT(characters, i, length, character); + // U16_NEXT will simply emit a surrogate code point if an unmatched + // surrogate is encountered; we must convert it to a U+FFFD (REPLACEMENT + // CHARACTER) here. + if (0xD800 <= character && character <= 0xDFFF) + character = replacementCharacter; + U8_APPEND_UNSAFE(bytes.data(), bytesWritten, character); + } + + return CString(reinterpret_cast(bytes.data()), bytesWritten); } -CString TextCodecUTF8::encode(const UChar* characters, size_t length, UnencodableHandling) -{ - return encodeCommon(characters, length); +CString TextCodecUTF8::encode(const UChar* characters, + size_t length, + UnencodableHandling) { + return encodeCommon(characters, length); } -CString TextCodecUTF8::encode(const LChar* characters, size_t length, UnencodableHandling) -{ - return encodeCommon(characters, length); +CString TextCodecUTF8::encode(const LChar* characters, + size_t length, + UnencodableHandling) { + return encodeCommon(characters, length); } -} // namespace WTF +} // namespace WTF diff --git a/sky/engine/wtf/text/TextCodecUTF8.h b/sky/engine/wtf/text/TextCodecUTF8.h index 4062cf7710f4e..8ebee98fc2581 100644 --- a/sky/engine/wtf/text/TextCodecUTF8.h +++ b/sky/engine/wtf/text/TextCodecUTF8.h @@ -31,33 +31,45 @@ namespace WTF { class TextCodecUTF8 : public TextCodec { -public: - static void registerEncodingNames(EncodingNameRegistrar); - static void registerCodecs(TextCodecRegistrar); + public: + static void registerEncodingNames(EncodingNameRegistrar); + static void registerCodecs(TextCodecRegistrar); -protected: - TextCodecUTF8() : m_partialSequenceSize(0) { } + protected: + TextCodecUTF8() : m_partialSequenceSize(0) {} -private: - static PassOwnPtr create(const TextEncoding&, const void*); + private: + static PassOwnPtr create(const TextEncoding&, const void*); - virtual String decode(const char*, size_t length, FlushBehavior, bool stopOnError, bool& sawError) override; - virtual CString encode(const UChar*, size_t length, UnencodableHandling) override; - virtual CString encode(const LChar*, size_t length, UnencodableHandling) override; + virtual String decode(const char*, + size_t length, + FlushBehavior, + bool stopOnError, + bool& sawError) override; + virtual CString encode(const UChar*, + size_t length, + UnencodableHandling) override; + virtual CString encode(const LChar*, + size_t length, + UnencodableHandling) override; - template - CString encodeCommon(const CharType* characters, size_t length); + template + CString encodeCommon(const CharType* characters, size_t length); - template - bool handlePartialSequence(CharType*& destination, const uint8_t*& source, const uint8_t* end, bool flush, bool stopOnError, bool& sawError); - void handleError(UChar*& destination, bool stopOnError, bool& sawError); - void consumePartialSequenceByte(); - - int m_partialSequenceSize; - uint8_t m_partialSequence[U8_MAX_LENGTH]; + template + bool handlePartialSequence(CharType*& destination, + const uint8_t*& source, + const uint8_t* end, + bool flush, + bool stopOnError, + bool& sawError); + void handleError(UChar*& destination, bool stopOnError, bool& sawError); + void consumePartialSequenceByte(); + int m_partialSequenceSize; + uint8_t m_partialSequence[U8_MAX_LENGTH]; }; -} // namespace WTF +} // namespace WTF #endif // SKY_ENGINE_WTF_TEXT_TEXTCODECUTF8_H_ diff --git a/sky/engine/wtf/text/TextCodecUTF8Test.cpp b/sky/engine/wtf/text/TextCodecUTF8Test.cpp index ddc334ce3f934..80f9be30478fd 100644 --- a/sky/engine/wtf/text/TextCodecUTF8Test.cpp +++ b/sky/engine/wtf/text/TextCodecUTF8Test.cpp @@ -28,7 +28,6 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #include "flutter/sky/engine/wtf/text/TextCodecUTF8.h" #include @@ -42,52 +41,51 @@ namespace WTF { namespace { -TEST(TextCodecUTF8, DecodeAscii) -{ - TextEncoding encoding("UTF-8"); - OwnPtr codec(newTextCodec(encoding)); +TEST(TextCodecUTF8, DecodeAscii) { + TextEncoding encoding("UTF-8"); + OwnPtr codec(newTextCodec(encoding)); - const char testCase[] = "HelloWorld"; - size_t testCaseSize = sizeof(testCase) - 1; + const char testCase[] = "HelloWorld"; + size_t testCaseSize = sizeof(testCase) - 1; - bool sawError = false; - const String& result = codec->decode(testCase, testCaseSize, DataEOF, false, sawError); - EXPECT_FALSE(sawError); - ASSERT_EQ(testCaseSize, result.length()); - for (size_t i = 0; i < testCaseSize; ++i) { - EXPECT_EQ(testCase[i], result[i]); - } + bool sawError = false; + const String& result = + codec->decode(testCase, testCaseSize, DataEOF, false, sawError); + EXPECT_FALSE(sawError); + ASSERT_EQ(testCaseSize, result.length()); + for (size_t i = 0; i < testCaseSize; ++i) { + EXPECT_EQ(testCase[i], result[i]); + } } -TEST(TextCodecUTF8, DecodeChineseCharacters) -{ - TextEncoding encoding("UTF-8"); - OwnPtr codec(newTextCodec(encoding)); +TEST(TextCodecUTF8, DecodeChineseCharacters) { + TextEncoding encoding("UTF-8"); + OwnPtr codec(newTextCodec(encoding)); - // "Kanji" in Chinese characters. - const char testCase[] = "\xe6\xbc\xa2\xe5\xad\x97"; - size_t testCaseSize = sizeof(testCase) - 1; + // "Kanji" in Chinese characters. + const char testCase[] = "\xe6\xbc\xa2\xe5\xad\x97"; + size_t testCaseSize = sizeof(testCase) - 1; - bool sawError = false; - const String& result = codec->decode(testCase, testCaseSize, DataEOF, false, sawError); - EXPECT_FALSE(sawError); - ASSERT_EQ(2u, result.length()); - EXPECT_EQ(0x6f22U, result[0]); - EXPECT_EQ(0x5b57U, result[1]); + bool sawError = false; + const String& result = + codec->decode(testCase, testCaseSize, DataEOF, false, sawError); + EXPECT_FALSE(sawError); + ASSERT_EQ(2u, result.length()); + EXPECT_EQ(0x6f22U, result[0]); + EXPECT_EQ(0x5b57U, result[1]); } -TEST(TextCodecUTF8, Decode0xFF) -{ - TextEncoding encoding("UTF-8"); - OwnPtr codec(newTextCodec(encoding)); +TEST(TextCodecUTF8, Decode0xFF) { + TextEncoding encoding("UTF-8"); + OwnPtr codec(newTextCodec(encoding)); - bool sawError = false; - const String& result = codec->decode("\xff", 1, DataEOF, false, sawError); - EXPECT_TRUE(sawError); - ASSERT_EQ(1u, result.length()); - EXPECT_EQ(0xFFFDU, result[0]); + bool sawError = false; + const String& result = codec->decode("\xff", 1, DataEOF, false, sawError); + EXPECT_TRUE(sawError); + ASSERT_EQ(1u, result.length()); + EXPECT_EQ(0xFFFDU, result[0]); } -} // namespace +} // namespace -} // namespace WTF +} // namespace WTF diff --git a/sky/engine/wtf/text/TextCodecUserDefined.cpp b/sky/engine/wtf/text/TextCodecUserDefined.cpp index 0ee4797250aff..aaa29385d6d86 100644 --- a/sky/engine/wtf/text/TextCodecUserDefined.cpp +++ b/sky/engine/wtf/text/TextCodecUserDefined.cpp @@ -33,90 +33,99 @@ namespace WTF { -void TextCodecUserDefined::registerEncodingNames(EncodingNameRegistrar registrar) -{ - registrar("x-user-defined", "x-user-defined"); +void TextCodecUserDefined::registerEncodingNames( + EncodingNameRegistrar registrar) { + registrar("x-user-defined", "x-user-defined"); } -static PassOwnPtr newStreamingTextDecoderUserDefined(const TextEncoding&, const void*) -{ - return adoptPtr(new TextCodecUserDefined); +static PassOwnPtr newStreamingTextDecoderUserDefined( + const TextEncoding&, + const void*) { + return adoptPtr(new TextCodecUserDefined); } -void TextCodecUserDefined::registerCodecs(TextCodecRegistrar registrar) -{ - registrar("x-user-defined", newStreamingTextDecoderUserDefined, 0); +void TextCodecUserDefined::registerCodecs(TextCodecRegistrar registrar) { + registrar("x-user-defined", newStreamingTextDecoderUserDefined, 0); } -String TextCodecUserDefined::decode(const char* bytes, size_t length, FlushBehavior, bool, bool&) -{ - StringBuilder result; - result.reserveCapacity(length); +String TextCodecUserDefined::decode(const char* bytes, + size_t length, + FlushBehavior, + bool, + bool&) { + StringBuilder result; + result.reserveCapacity(length); - for (size_t i = 0; i < length; ++i) { - signed char c = bytes[i]; - result.append(static_cast(c & 0xF7FF)); - } + for (size_t i = 0; i < length; ++i) { + signed char c = bytes[i]; + result.append(static_cast(c & 0xF7FF)); + } - return result.toString(); + return result.toString(); } -template -static CString encodeComplexUserDefined(const CharType* characters, size_t length, UnencodableHandling handling) -{ - Vector result(length); - char* bytes = result.data(); - - size_t resultLength = 0; - for (size_t i = 0; i < length; ) { - UChar32 c; - U16_NEXT(characters, i, length, c); - signed char signedByte = c; - if ((signedByte & 0xF7FF) == c) - bytes[resultLength++] = signedByte; - else { - // No way to encode this character with x-user-defined. - UnencodableReplacementArray replacement; - int replacementLength = TextCodec::getUnencodableReplacement(c, handling, replacement); - result.grow(resultLength + replacementLength + length - i); - bytes = result.data(); - memcpy(bytes + resultLength, replacement, replacementLength); - resultLength += replacementLength; - } +template +static CString encodeComplexUserDefined(const CharType* characters, + size_t length, + UnencodableHandling handling) { + Vector result(length); + char* bytes = result.data(); + + size_t resultLength = 0; + for (size_t i = 0; i < length;) { + UChar32 c; + U16_NEXT(characters, i, length, c); + signed char signedByte = c; + if ((signedByte & 0xF7FF) == c) + bytes[resultLength++] = signedByte; + else { + // No way to encode this character with x-user-defined. + UnencodableReplacementArray replacement; + int replacementLength = + TextCodec::getUnencodableReplacement(c, handling, replacement); + result.grow(resultLength + replacementLength + length - i); + bytes = result.data(); + memcpy(bytes + resultLength, replacement, replacementLength); + resultLength += replacementLength; } + } - return CString(bytes, resultLength); + return CString(bytes, resultLength); } -template -CString TextCodecUserDefined::encodeCommon(const CharType* characters, size_t length, UnencodableHandling handling) -{ - char* bytes; - CString result = CString::newUninitialized(length, bytes); - - // Convert the string a fast way and simultaneously do an efficient check to see if it's all ASCII. - UChar ored = 0; - for (size_t i = 0; i < length; ++i) { - UChar c = characters[i]; - bytes[i] = c; - ored |= c; - } - - if (!(ored & 0xFF80)) - return result; - - // If it wasn't all ASCII, call the function that handles more-complex cases. - return encodeComplexUserDefined(characters, length, handling); +template +CString TextCodecUserDefined::encodeCommon(const CharType* characters, + size_t length, + UnencodableHandling handling) { + char* bytes; + CString result = CString::newUninitialized(length, bytes); + + // Convert the string a fast way and simultaneously do an efficient check to + // see if it's all ASCII. + UChar ored = 0; + for (size_t i = 0; i < length; ++i) { + UChar c = characters[i]; + bytes[i] = c; + ored |= c; + } + + if (!(ored & 0xFF80)) + return result; + + // If it wasn't all ASCII, call the function that handles more-complex cases. + return encodeComplexUserDefined(characters, length, handling); } -CString TextCodecUserDefined::encode(const UChar* characters, size_t length, UnencodableHandling handling) -{ - return encodeCommon(characters, length, handling); +CString TextCodecUserDefined::encode(const UChar* characters, + size_t length, + UnencodableHandling handling) { + return encodeCommon(characters, length, handling); } -CString TextCodecUserDefined::encode(const LChar* characters, size_t length, UnencodableHandling handling) -{ - return encodeCommon(characters, length, handling); +CString TextCodecUserDefined::encode(const LChar* characters, + size_t length, + UnencodableHandling handling) { + return encodeCommon(characters, length, handling); } -} // namespace WTF +} // namespace WTF diff --git a/sky/engine/wtf/text/TextCodecUserDefined.h b/sky/engine/wtf/text/TextCodecUserDefined.h index 0572857b50836..b67e30aa864b4 100644 --- a/sky/engine/wtf/text/TextCodecUserDefined.h +++ b/sky/engine/wtf/text/TextCodecUserDefined.h @@ -30,20 +30,28 @@ namespace WTF { - class TextCodecUserDefined final : public TextCodec { - public: - static void registerEncodingNames(EncodingNameRegistrar); - static void registerCodecs(TextCodecRegistrar); - - private: - virtual String decode(const char*, size_t length, FlushBehavior, bool stopOnError, bool& sawError) override; - virtual CString encode(const UChar*, size_t length, UnencodableHandling) override; - virtual CString encode(const LChar*, size_t length, UnencodableHandling) override; - - template - CString encodeCommon(const CharType*, size_t length, UnencodableHandling); - }; - -} // namespace WTF +class TextCodecUserDefined final : public TextCodec { + public: + static void registerEncodingNames(EncodingNameRegistrar); + static void registerCodecs(TextCodecRegistrar); + + private: + virtual String decode(const char*, + size_t length, + FlushBehavior, + bool stopOnError, + bool& sawError) override; + virtual CString encode(const UChar*, + size_t length, + UnencodableHandling) override; + virtual CString encode(const LChar*, + size_t length, + UnencodableHandling) override; + + template + CString encodeCommon(const CharType*, size_t length, UnencodableHandling); +}; + +} // namespace WTF #endif // SKY_ENGINE_WTF_TEXT_TEXTCODECUSERDEFINED_H_ diff --git a/sky/engine/wtf/text/TextEncoding.cpp b/sky/engine/wtf/text/TextEncoding.cpp index d1820d16d344f..44ccc36e39da1 100644 --- a/sky/engine/wtf/text/TextEncoding.cpp +++ b/sky/engine/wtf/text/TextEncoding.cpp @@ -36,127 +36,131 @@ namespace WTF { -static const TextEncoding& UTF7Encoding() -{ - static TextEncoding globalUTF7Encoding("UTF-7"); - return globalUTF7Encoding; +static const TextEncoding& UTF7Encoding() { + static TextEncoding globalUTF7Encoding("UTF-7"); + return globalUTF7Encoding; } TextEncoding::TextEncoding(const char* name) - : m_name(atomicCanonicalTextEncodingName(name)) -{ - // Aliases are valid, but not "replacement" itself. - if (m_name && isReplacementEncoding(name)) - m_name = 0; + : m_name(atomicCanonicalTextEncodingName(name)) { + // Aliases are valid, but not "replacement" itself. + if (m_name && isReplacementEncoding(name)) + m_name = 0; } TextEncoding::TextEncoding(const String& name) - : m_name(atomicCanonicalTextEncodingName(name)) -{ - // Aliases are valid, but not "replacement" itself. - if (m_name && isReplacementEncoding(name)) - m_name = 0; -} - -String TextEncoding::decode(const char* data, size_t length, bool stopOnError, bool& sawError) const -{ - if (!m_name) - return String(); - - return newTextCodec(*this)->decode(data, length, DataEOF, stopOnError, sawError); -} - -CString TextEncoding::encode(const String& string, UnencodableHandling handling) const -{ - if (!m_name) - return CString(); - - if (string.isEmpty()) - return ""; - - OwnPtr textCodec = newTextCodec(*this); - CString encodedString; - if (string.is8Bit()) - encodedString = textCodec->encode(string.characters8(), string.length(), handling); - else - encodedString = textCodec->encode(string.characters16(), string.length(), handling); - return encodedString; -} - -CString TextEncoding::normalizeAndEncode(const String& string, UnencodableHandling handling) const -{ - if (!m_name) - return CString(); - - if (string.isEmpty()) - return ""; - - // Text exclusively containing Latin-1 characters (U+0000..U+00FF) is left - // unaffected by NFC. This is effectively the same as saying that all - // Latin-1 text is already normalized to NFC. - // Source: http://unicode.org/reports/tr15/ - if (string.is8Bit()) - return newTextCodec(*this)->encode(string.characters8(), string.length(), handling); - - const UChar* source = string.characters16(); - size_t length = string.length(); - - Vector normalizedCharacters; - - UErrorCode err = U_ZERO_ERROR; - if (unorm_quickCheck(source, length, UNORM_NFC, &err) != UNORM_YES) { - // First try using the length of the original string, since normalization to NFC rarely increases length. - normalizedCharacters.grow(length); - int32_t normalizedLength = unorm_normalize(source, length, UNORM_NFC, 0, normalizedCharacters.data(), length, &err); - if (err == U_BUFFER_OVERFLOW_ERROR) { - err = U_ZERO_ERROR; - normalizedCharacters.resize(normalizedLength); - normalizedLength = unorm_normalize(source, length, UNORM_NFC, 0, normalizedCharacters.data(), normalizedLength, &err); - } - ASSERT(U_SUCCESS(err)); - - source = normalizedCharacters.data(); - length = normalizedLength; + : m_name(atomicCanonicalTextEncodingName(name)) { + // Aliases are valid, but not "replacement" itself. + if (m_name && isReplacementEncoding(name)) + m_name = 0; +} + +String TextEncoding::decode(const char* data, + size_t length, + bool stopOnError, + bool& sawError) const { + if (!m_name) + return String(); + + return newTextCodec(*this)->decode(data, length, DataEOF, stopOnError, + sawError); +} + +CString TextEncoding::encode(const String& string, + UnencodableHandling handling) const { + if (!m_name) + return CString(); + + if (string.isEmpty()) + return ""; + + OwnPtr textCodec = newTextCodec(*this); + CString encodedString; + if (string.is8Bit()) + encodedString = + textCodec->encode(string.characters8(), string.length(), handling); + else + encodedString = + textCodec->encode(string.characters16(), string.length(), handling); + return encodedString; +} + +CString TextEncoding::normalizeAndEncode(const String& string, + UnencodableHandling handling) const { + if (!m_name) + return CString(); + + if (string.isEmpty()) + return ""; + + // Text exclusively containing Latin-1 characters (U+0000..U+00FF) is left + // unaffected by NFC. This is effectively the same as saying that all + // Latin-1 text is already normalized to NFC. + // Source: http://unicode.org/reports/tr15/ + if (string.is8Bit()) + return newTextCodec(*this)->encode(string.characters8(), string.length(), + handling); + + const UChar* source = string.characters16(); + size_t length = string.length(); + + Vector normalizedCharacters; + + UErrorCode err = U_ZERO_ERROR; + if (unorm_quickCheck(source, length, UNORM_NFC, &err) != UNORM_YES) { + // First try using the length of the original string, since normalization to + // NFC rarely increases length. + normalizedCharacters.grow(length); + int32_t normalizedLength = + unorm_normalize(source, length, UNORM_NFC, 0, + normalizedCharacters.data(), length, &err); + if (err == U_BUFFER_OVERFLOW_ERROR) { + err = U_ZERO_ERROR; + normalizedCharacters.resize(normalizedLength); + normalizedLength = + unorm_normalize(source, length, UNORM_NFC, 0, + normalizedCharacters.data(), normalizedLength, &err); } + ASSERT(U_SUCCESS(err)); - return newTextCodec(*this)->encode(source, length, handling); + source = normalizedCharacters.data(); + length = normalizedLength; + } + + return newTextCodec(*this)->encode(source, length, handling); } -bool TextEncoding::usesVisualOrdering() const -{ - if (noExtendedTextEncodingNameUsed()) - return false; +bool TextEncoding::usesVisualOrdering() const { + if (noExtendedTextEncodingNameUsed()) + return false; - static const char* const a = atomicCanonicalTextEncodingName("ISO-8859-8"); - return m_name == a; + static const char* const a = atomicCanonicalTextEncodingName("ISO-8859-8"); + return m_name == a; } -bool TextEncoding::isNonByteBasedEncoding() const -{ - if (noExtendedTextEncodingNameUsed()) { - return *this == UTF16LittleEndianEncoding() - || *this == UTF16BigEndianEncoding(); - } +bool TextEncoding::isNonByteBasedEncoding() const { + if (noExtendedTextEncodingNameUsed()) { + return *this == UTF16LittleEndianEncoding() || + *this == UTF16BigEndianEncoding(); + } - return *this == UTF16LittleEndianEncoding() - || *this == UTF16BigEndianEncoding() - || *this == UTF32BigEndianEncoding() - || *this == UTF32LittleEndianEncoding(); + return *this == UTF16LittleEndianEncoding() || + *this == UTF16BigEndianEncoding() || + *this == UTF32BigEndianEncoding() || + *this == UTF32LittleEndianEncoding(); } -bool TextEncoding::isUTF7Encoding() const -{ - if (noExtendedTextEncodingNameUsed()) - return false; +bool TextEncoding::isUTF7Encoding() const { + if (noExtendedTextEncodingNameUsed()) + return false; - return *this == UTF7Encoding(); + return *this == UTF7Encoding(); } -const TextEncoding& TextEncoding::closestByteBasedEquivalent() const -{ - if (isNonByteBasedEncoding()) - return UTF8Encoding(); - return *this; +const TextEncoding& TextEncoding::closestByteBasedEquivalent() const { + if (isNonByteBasedEncoding()) + return UTF8Encoding(); + return *this; } // HTML5 specifies that UTF-8 be used in form submission when a form is @@ -164,60 +168,51 @@ const TextEncoding& TextEncoding::closestByteBasedEquivalent() const // byte-based encoding and can contain 0x00. By extension, the same // should be done for UTF-32. In case of UTF-7, it is a byte-based encoding, // but it's fraught with problems and we'd rather steer clear of it. -const TextEncoding& TextEncoding::encodingForFormSubmission() const -{ - if (isNonByteBasedEncoding() || isUTF7Encoding()) - return UTF8Encoding(); - return *this; +const TextEncoding& TextEncoding::encodingForFormSubmission() const { + if (isNonByteBasedEncoding() || isUTF7Encoding()) + return UTF8Encoding(); + return *this; } -const TextEncoding& ASCIIEncoding() -{ - static TextEncoding globalASCIIEncoding("ASCII"); - return globalASCIIEncoding; +const TextEncoding& ASCIIEncoding() { + static TextEncoding globalASCIIEncoding("ASCII"); + return globalASCIIEncoding; } -const TextEncoding& Latin1Encoding() -{ - static TextEncoding globalLatin1Encoding("latin1"); - return globalLatin1Encoding; +const TextEncoding& Latin1Encoding() { + static TextEncoding globalLatin1Encoding("latin1"); + return globalLatin1Encoding; } -const TextEncoding& UTF16BigEndianEncoding() -{ - static TextEncoding globalUTF16BigEndianEncoding("UTF-16BE"); - return globalUTF16BigEndianEncoding; +const TextEncoding& UTF16BigEndianEncoding() { + static TextEncoding globalUTF16BigEndianEncoding("UTF-16BE"); + return globalUTF16BigEndianEncoding; } -const TextEncoding& UTF16LittleEndianEncoding() -{ - static TextEncoding globalUTF16LittleEndianEncoding("UTF-16LE"); - return globalUTF16LittleEndianEncoding; +const TextEncoding& UTF16LittleEndianEncoding() { + static TextEncoding globalUTF16LittleEndianEncoding("UTF-16LE"); + return globalUTF16LittleEndianEncoding; } -const TextEncoding& UTF32BigEndianEncoding() -{ - static TextEncoding globalUTF32BigEndianEncoding("UTF-32BE"); - return globalUTF32BigEndianEncoding; +const TextEncoding& UTF32BigEndianEncoding() { + static TextEncoding globalUTF32BigEndianEncoding("UTF-32BE"); + return globalUTF32BigEndianEncoding; } -const TextEncoding& UTF32LittleEndianEncoding() -{ - static TextEncoding globalUTF32LittleEndianEncoding("UTF-32LE"); - return globalUTF32LittleEndianEncoding; +const TextEncoding& UTF32LittleEndianEncoding() { + static TextEncoding globalUTF32LittleEndianEncoding("UTF-32LE"); + return globalUTF32LittleEndianEncoding; } -const TextEncoding& UTF8Encoding() -{ - static TextEncoding globalUTF8Encoding("UTF-8"); - ASSERT(globalUTF8Encoding.isValid()); - return globalUTF8Encoding; +const TextEncoding& UTF8Encoding() { + static TextEncoding globalUTF8Encoding("UTF-8"); + ASSERT(globalUTF8Encoding.isValid()); + return globalUTF8Encoding; } -const TextEncoding& WindowsLatin1Encoding() -{ - static TextEncoding globalWindowsLatin1Encoding("WinLatin1"); - return globalWindowsLatin1Encoding; +const TextEncoding& WindowsLatin1Encoding() { + static TextEncoding globalWindowsLatin1Encoding("WinLatin1"); + return globalWindowsLatin1Encoding; } -} // namespace WTF +} // namespace WTF diff --git a/sky/engine/wtf/text/TextEncoding.h b/sky/engine/wtf/text/TextEncoding.h index 2410f468ce804..a74f01b8fce17 100644 --- a/sky/engine/wtf/text/TextEncoding.h +++ b/sky/engine/wtf/text/TextEncoding.h @@ -34,39 +34,45 @@ namespace WTF { class WTF_EXPORT TextEncoding { -public: - TextEncoding() : m_name(0) { } - TextEncoding(const char* name); - TextEncoding(const String& name); + public: + TextEncoding() : m_name(0) {} + TextEncoding(const char* name); + TextEncoding(const String& name); - bool isValid() const { return m_name; } - const char* name() const { return m_name; } - bool usesVisualOrdering() const; - const TextEncoding& closestByteBasedEquivalent() const; - const TextEncoding& encodingForFormSubmission() const; + bool isValid() const { return m_name; } + const char* name() const { return m_name; } + bool usesVisualOrdering() const; + const TextEncoding& closestByteBasedEquivalent() const; + const TextEncoding& encodingForFormSubmission() const; - String decode(const char* str, size_t length) const - { - bool ignored; - return decode(str, length, false, ignored); - } - String decode(const char*, size_t length, bool stopOnError, bool& sawError) const; + String decode(const char* str, size_t length) const { + bool ignored; + return decode(str, length, false, ignored); + } + String decode(const char*, + size_t length, + bool stopOnError, + bool& sawError) const; - // Encodes the string, but does *not* normalize first. - CString encode(const String&, UnencodableHandling) const; + // Encodes the string, but does *not* normalize first. + CString encode(const String&, UnencodableHandling) const; - // Applies Unicode NFC normalization, then encodes the normalized string. - CString normalizeAndEncode(const String&, UnencodableHandling) const; + // Applies Unicode NFC normalization, then encodes the normalized string. + CString normalizeAndEncode(const String&, UnencodableHandling) const; -private: - bool isNonByteBasedEncoding() const; - bool isUTF7Encoding() const; + private: + bool isNonByteBasedEncoding() const; + bool isUTF7Encoding() const; - const char* m_name; + const char* m_name; }; -inline bool operator==(const TextEncoding& a, const TextEncoding& b) { return a.name() == b.name(); } -inline bool operator!=(const TextEncoding& a, const TextEncoding& b) { return a.name() != b.name(); } +inline bool operator==(const TextEncoding& a, const TextEncoding& b) { + return a.name() == b.name(); +} +inline bool operator!=(const TextEncoding& a, const TextEncoding& b) { + return a.name() != b.name(); +} WTF_EXPORT const TextEncoding& ASCIIEncoding(); WTF_EXPORT const TextEncoding& Latin1Encoding(); @@ -77,7 +83,7 @@ WTF_EXPORT const TextEncoding& UTF32LittleEndianEncoding(); WTF_EXPORT const TextEncoding& UTF8Encoding(); WTF_EXPORT const TextEncoding& WindowsLatin1Encoding(); -} // namespace WTF +} // namespace WTF using WTF::ASCIIEncoding; using WTF::Latin1Encoding; diff --git a/sky/engine/wtf/text/TextEncodingRegistry.cpp b/sky/engine/wtf/text/TextEncodingRegistry.cpp index 699ba4b3fa946..d8dcd4086b79f 100644 --- a/sky/engine/wtf/text/TextEncodingRegistry.cpp +++ b/sky/engine/wtf/text/TextEncodingRegistry.cpp @@ -48,269 +48,264 @@ const size_t maxEncodingNameLength = 63; // Hash for all-ASCII strings that does case folding. struct TextEncodingNameHash { - static bool equal(const char* s1, const char* s2) - { - char c1; - char c2; - do { + static bool equal(const char* s1, const char* s2) { + char c1; + char c2; + do { #if defined(_MSC_FULL_VER) && _MSC_FULL_VER == 170051106 - // Workaround for a bug in the VS2012 Update 1 optimizer, remove once the fix is released. - // https://connect.microsoft.com/VisualStudio/feedback/details/777533/vs2012-c-optimizing-bug-when-using-inline-and-char-return-type-x86-target-only - c1 = toASCIILower(*s1++); - c2 = toASCIILower(*s2++); - if (c1 != c2) - return false; + // Workaround for a bug in the VS2012 Update 1 optimizer, remove once the + // fix is released. + // https://connect.microsoft.com/VisualStudio/feedback/details/777533/vs2012-c-optimizing-bug-when-using-inline-and-char-return-type-x86-target-only + c1 = toASCIILower(*s1++); + c2 = toASCIILower(*s2++); + if (c1 != c2) + return false; #else - c1 = *s1++; - c2 = *s2++; - if (toASCIILower(c1) != toASCIILower(c2)) - return false; + c1 = *s1++; + c2 = *s2++; + if (toASCIILower(c1) != toASCIILower(c2)) + return false; #endif - } while (c1 && c2); - return !c1 && !c2; + } while (c1 && c2); + return !c1 && !c2; + } + + // This algorithm is the one-at-a-time hash from: + // http://burtleburtle.net/bob/hash/hashfaq.html + // http://burtleburtle.net/bob/hash/doobs.html + static unsigned hash(const char* s) { + unsigned h = WTF::stringHashingStartValue; + for (;;) { + char c = *s++; + if (!c) { + h += (h << 3); + h ^= (h >> 11); + h += (h << 15); + return h; + } + h += toASCIILower(c); + h += (h << 10); + h ^= (h >> 6); } + } - // This algorithm is the one-at-a-time hash from: - // http://burtleburtle.net/bob/hash/hashfaq.html - // http://burtleburtle.net/bob/hash/doobs.html - static unsigned hash(const char* s) - { - unsigned h = WTF::stringHashingStartValue; - for (;;) { - char c = *s++; - if (!c) { - h += (h << 3); - h ^= (h >> 11); - h += (h << 15); - return h; - } - h += toASCIILower(c); - h += (h << 10); - h ^= (h >> 6); - } - } - - static const bool safeToCompareToEmptyOrDeleted = false; + static const bool safeToCompareToEmptyOrDeleted = false; }; struct TextCodecFactory { - NewTextCodecFunction function; - const void* additionalData; - TextCodecFactory(NewTextCodecFunction f = 0, const void* d = 0) : function(f), additionalData(d) { } + NewTextCodecFunction function; + const void* additionalData; + TextCodecFactory(NewTextCodecFunction f = 0, const void* d = 0) + : function(f), additionalData(d) {} }; -typedef HashMap TextEncodingNameMap; +typedef HashMap + TextEncodingNameMap; typedef HashMap TextCodecMap; -static Mutex& encodingRegistryMutex() -{ - // We don't have to use AtomicallyInitializedStatic here because - // this function is called on the main thread for any page before - // it is used in worker threads. - DEFINE_STATIC_LOCAL(Mutex, mutex, ()); - return mutex; +static Mutex& encodingRegistryMutex() { + // We don't have to use AtomicallyInitializedStatic here because + // this function is called on the main thread for any page before + // it is used in worker threads. + DEFINE_STATIC_LOCAL(Mutex, mutex, ()); + return mutex; } static TextEncodingNameMap* textEncodingNameMap; static TextCodecMap* textCodecMap; static bool didExtendTextCodecMaps; -static const char textEncodingNameBlacklist[][6] = { "UTF-7" }; +static const char textEncodingNameBlacklist[][6] = {"UTF-7"}; #if ERROR_DISABLED -static inline void checkExistingName(const char*, const char*) { } +static inline void checkExistingName(const char*, const char*) {} #else -static void checkExistingName(const char* alias, const char* atomicName) -{ - const char* oldAtomicName = textEncodingNameMap->get(alias); - if (!oldAtomicName) - return; - if (oldAtomicName == atomicName) - return; - // Keep the warning silent about one case where we know this will happen. - if (strcmp(alias, "ISO-8859-8-I") == 0 - && strcmp(oldAtomicName, "ISO-8859-8-I") == 0 - && strcasecmp(atomicName, "iso-8859-8") == 0) - return; - WTF_LOG_ERROR("alias %s maps to %s already, but someone is trying to make it map to %s", alias, oldAtomicName, atomicName); +static void checkExistingName(const char* alias, const char* atomicName) { + const char* oldAtomicName = textEncodingNameMap->get(alias); + if (!oldAtomicName) + return; + if (oldAtomicName == atomicName) + return; + // Keep the warning silent about one case where we know this will happen. + if (strcmp(alias, "ISO-8859-8-I") == 0 && + strcmp(oldAtomicName, "ISO-8859-8-I") == 0 && + strcasecmp(atomicName, "iso-8859-8") == 0) + return; + WTF_LOG_ERROR( + "alias %s maps to %s already, but someone is trying to make it map to %s", + alias, oldAtomicName, atomicName); } #endif -static bool isUndesiredAlias(const char* alias) -{ - // Reject aliases with version numbers that are supported by some back-ends (such as "ISO_2022,locale=ja,version=0" in ICU). - for (const char* p = alias; *p; ++p) { - if (*p == ',') - return true; - } - // 8859_1 is known to (at least) ICU, but other browsers don't support this name - and having it caused a compatibility - // problem, see bug 43554. - if (0 == strcmp(alias, "8859_1")) - return true; - return false; +static bool isUndesiredAlias(const char* alias) { + // Reject aliases with version numbers that are supported by some back-ends + // (such as "ISO_2022,locale=ja,version=0" in ICU). + for (const char* p = alias; *p; ++p) { + if (*p == ',') + return true; + } + // 8859_1 is known to (at least) ICU, but other browsers don't support this + // name - and having it caused a compatibility problem, see bug 43554. + if (0 == strcmp(alias, "8859_1")) + return true; + return false; } -static void addToTextEncodingNameMap(const char* alias, const char* name) -{ - ASSERT(strlen(alias) <= maxEncodingNameLength); - if (isUndesiredAlias(alias)) - return; - const char* atomicName = textEncodingNameMap->get(name); - ASSERT(strcmp(alias, name) == 0 || atomicName); - if (!atomicName) - atomicName = name; - checkExistingName(alias, atomicName); - textEncodingNameMap->add(alias, atomicName); +static void addToTextEncodingNameMap(const char* alias, const char* name) { + ASSERT(strlen(alias) <= maxEncodingNameLength); + if (isUndesiredAlias(alias)) + return; + const char* atomicName = textEncodingNameMap->get(name); + ASSERT(strcmp(alias, name) == 0 || atomicName); + if (!atomicName) + atomicName = name; + checkExistingName(alias, atomicName); + textEncodingNameMap->add(alias, atomicName); } -static void addToTextCodecMap(const char* name, NewTextCodecFunction function, const void* additionalData) -{ - const char* atomicName = textEncodingNameMap->get(name); - ASSERT(atomicName); - textCodecMap->add(atomicName, TextCodecFactory(function, additionalData)); +static void addToTextCodecMap(const char* name, + NewTextCodecFunction function, + const void* additionalData) { + const char* atomicName = textEncodingNameMap->get(name); + ASSERT(atomicName); + textCodecMap->add(atomicName, TextCodecFactory(function, additionalData)); } -static void pruneBlacklistedCodecs() -{ - for (size_t i = 0; i < WTF_ARRAY_LENGTH(textEncodingNameBlacklist); ++i) { - const char* atomicName = textEncodingNameMap->get(textEncodingNameBlacklist[i]); - if (!atomicName) - continue; +static void pruneBlacklistedCodecs() { + for (size_t i = 0; i < WTF_ARRAY_LENGTH(textEncodingNameBlacklist); ++i) { + const char* atomicName = + textEncodingNameMap->get(textEncodingNameBlacklist[i]); + if (!atomicName) + continue; - Vector names; - TextEncodingNameMap::const_iterator it = textEncodingNameMap->begin(); - TextEncodingNameMap::const_iterator end = textEncodingNameMap->end(); - for (; it != end; ++it) { - if (it->value == atomicName) - names.append(it->key); - } + Vector names; + TextEncodingNameMap::const_iterator it = textEncodingNameMap->begin(); + TextEncodingNameMap::const_iterator end = textEncodingNameMap->end(); + for (; it != end; ++it) { + if (it->value == atomicName) + names.append(it->key); + } - textEncodingNameMap->removeAll(names); + textEncodingNameMap->removeAll(names); - textCodecMap->remove(atomicName); - } + textCodecMap->remove(atomicName); + } } -static void buildBaseTextCodecMaps() -{ - ASSERT(isMainThread()); - ASSERT(!textCodecMap); - ASSERT(!textEncodingNameMap); +static void buildBaseTextCodecMaps() { + ASSERT(isMainThread()); + ASSERT(!textCodecMap); + ASSERT(!textEncodingNameMap); - textCodecMap = new TextCodecMap; - textEncodingNameMap = new TextEncodingNameMap; + textCodecMap = new TextCodecMap; + textEncodingNameMap = new TextEncodingNameMap; - TextCodecLatin1::registerEncodingNames(addToTextEncodingNameMap); - TextCodecLatin1::registerCodecs(addToTextCodecMap); + TextCodecLatin1::registerEncodingNames(addToTextEncodingNameMap); + TextCodecLatin1::registerCodecs(addToTextCodecMap); - TextCodecUTF8::registerEncodingNames(addToTextEncodingNameMap); - TextCodecUTF8::registerCodecs(addToTextCodecMap); + TextCodecUTF8::registerEncodingNames(addToTextEncodingNameMap); + TextCodecUTF8::registerCodecs(addToTextCodecMap); - TextCodecUTF16::registerEncodingNames(addToTextEncodingNameMap); - TextCodecUTF16::registerCodecs(addToTextCodecMap); + TextCodecUTF16::registerEncodingNames(addToTextEncodingNameMap); + TextCodecUTF16::registerCodecs(addToTextCodecMap); - TextCodecUserDefined::registerEncodingNames(addToTextEncodingNameMap); - TextCodecUserDefined::registerCodecs(addToTextCodecMap); + TextCodecUserDefined::registerEncodingNames(addToTextEncodingNameMap); + TextCodecUserDefined::registerCodecs(addToTextCodecMap); } -bool isReplacementEncoding(const char* alias) -{ - return alias && !strcasecmp(alias, "replacement"); +bool isReplacementEncoding(const char* alias) { + return alias && !strcasecmp(alias, "replacement"); } -bool isReplacementEncoding(const String& alias) -{ - return alias == "replacement"; +bool isReplacementEncoding(const String& alias) { + return alias == "replacement"; } -static void extendTextCodecMaps() -{ - TextCodecReplacement::registerEncodingNames(addToTextEncodingNameMap); - TextCodecReplacement::registerCodecs(addToTextCodecMap); +static void extendTextCodecMaps() { + TextCodecReplacement::registerEncodingNames(addToTextEncodingNameMap); + TextCodecReplacement::registerCodecs(addToTextCodecMap); - TextCodecICU::registerEncodingNames(addToTextEncodingNameMap); - TextCodecICU::registerCodecs(addToTextCodecMap); + TextCodecICU::registerEncodingNames(addToTextEncodingNameMap); + TextCodecICU::registerCodecs(addToTextCodecMap); - pruneBlacklistedCodecs(); + pruneBlacklistedCodecs(); } -PassOwnPtr newTextCodec(const TextEncoding& encoding) -{ - MutexLocker lock(encodingRegistryMutex()); +PassOwnPtr newTextCodec(const TextEncoding& encoding) { + MutexLocker lock(encodingRegistryMutex()); - ASSERT(textCodecMap); - TextCodecFactory factory = textCodecMap->get(encoding.name()); - ASSERT(factory.function); - return factory.function(encoding, factory.additionalData); + ASSERT(textCodecMap); + TextCodecFactory factory = textCodecMap->get(encoding.name()); + ASSERT(factory.function); + return factory.function(encoding, factory.additionalData); } -const char* atomicCanonicalTextEncodingName(const char* name) -{ - if (!name || !name[0]) - return 0; - if (!textEncodingNameMap) - buildBaseTextCodecMaps(); - - MutexLocker lock(encodingRegistryMutex()); - - if (const char* atomicName = textEncodingNameMap->get(name)) - return atomicName; - if (didExtendTextCodecMaps) - return 0; - extendTextCodecMaps(); - didExtendTextCodecMaps = true; - return textEncodingNameMap->get(name); +const char* atomicCanonicalTextEncodingName(const char* name) { + if (!name || !name[0]) + return 0; + if (!textEncodingNameMap) + buildBaseTextCodecMaps(); + + MutexLocker lock(encodingRegistryMutex()); + + if (const char* atomicName = textEncodingNameMap->get(name)) + return atomicName; + if (didExtendTextCodecMaps) + return 0; + extendTextCodecMaps(); + didExtendTextCodecMaps = true; + return textEncodingNameMap->get(name); } template -const char* atomicCanonicalTextEncodingName(const CharacterType* characters, size_t length) -{ - char buffer[maxEncodingNameLength + 1]; - size_t j = 0; - for (size_t i = 0; i < length; ++i) { - CharacterType c = characters[i]; - if (j == maxEncodingNameLength) - return 0; - buffer[j++] = c; - } - buffer[j] = 0; - return atomicCanonicalTextEncodingName(buffer); +const char* atomicCanonicalTextEncodingName(const CharacterType* characters, + size_t length) { + char buffer[maxEncodingNameLength + 1]; + size_t j = 0; + for (size_t i = 0; i < length; ++i) { + CharacterType c = characters[i]; + if (j == maxEncodingNameLength) + return 0; + buffer[j++] = c; + } + buffer[j] = 0; + return atomicCanonicalTextEncodingName(buffer); } -const char* atomicCanonicalTextEncodingName(const String& alias) -{ - if (!alias.length()) - return 0; +const char* atomicCanonicalTextEncodingName(const String& alias) { + if (!alias.length()) + return 0; - if (alias.is8Bit()) - return atomicCanonicalTextEncodingName(alias.characters8(), alias.length()); + if (alias.is8Bit()) + return atomicCanonicalTextEncodingName(alias.characters8(), + alias.length()); - return atomicCanonicalTextEncodingName(alias.characters16(), alias.length()); + return atomicCanonicalTextEncodingName(alias.characters16(), + alias.length()); } -bool noExtendedTextEncodingNameUsed() -{ - // If the calling thread did not use extended encoding names, it is fine for it to use a stale false value. - return !didExtendTextCodecMaps; +bool noExtendedTextEncodingNameUsed() { + // If the calling thread did not use extended encoding names, it is fine for + // it to use a stale false value. + return !didExtendTextCodecMaps; } #ifndef NDEBUG -void dumpTextEncodingNameMap() -{ - unsigned size = textEncodingNameMap->size(); - fprintf(stderr, "Dumping %u entries in WTF::TextEncodingNameMap...\n", size); +void dumpTextEncodingNameMap() { + unsigned size = textEncodingNameMap->size(); + fprintf(stderr, "Dumping %u entries in WTF::TextEncodingNameMap...\n", size); - MutexLocker lock(encodingRegistryMutex()); + MutexLocker lock(encodingRegistryMutex()); - TextEncodingNameMap::const_iterator it = textEncodingNameMap->begin(); - TextEncodingNameMap::const_iterator end = textEncodingNameMap->end(); - for (; it != end; ++it) - fprintf(stderr, "'%s' => '%s'\n", it->key, it->value); + TextEncodingNameMap::const_iterator it = textEncodingNameMap->begin(); + TextEncodingNameMap::const_iterator end = textEncodingNameMap->end(); + for (; it != end; ++it) + fprintf(stderr, "'%s' => '%s'\n", it->key, it->value); } #endif -} // namespace WTF +} // namespace WTF diff --git a/sky/engine/wtf/text/TextEncodingRegistry.h b/sky/engine/wtf/text/TextEncodingRegistry.h index 1b31de8ade98b..9a9ee91bb1111 100644 --- a/sky/engine/wtf/text/TextEncodingRegistry.h +++ b/sky/engine/wtf/text/TextEncodingRegistry.h @@ -53,10 +53,10 @@ bool isReplacementEncoding(const String& alias); void dumpTextEncodingNameMap(); #endif -} // namespace WTF +} // namespace WTF -using WTF::newTextCodec; using WTF::atomicCanonicalTextEncodingName; +using WTF::newTextCodec; using WTF::noExtendedTextEncodingNameUsed; #ifndef NDEBUG using WTF::dumpTextEncodingNameMap; diff --git a/sky/engine/wtf/text/TextPosition.cpp b/sky/engine/wtf/text/TextPosition.cpp index f943c890be37e..b2476710affdf 100644 --- a/sky/engine/wtf/text/TextPosition.cpp +++ b/sky/engine/wtf/text/TextPosition.cpp @@ -10,16 +10,16 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "flutter/sky/engine/wtf/text/TextPosition.h" @@ -29,37 +29,42 @@ namespace WTF { -PassOwnPtr > lineEndings(const String& text) -{ - OwnPtr > result(adoptPtr(new Vector())); +PassOwnPtr> lineEndings(const String& text) { + OwnPtr> result(adoptPtr(new Vector())); - unsigned start = 0; - while (start < text.length()) { - size_t lineEnd = text.find('\n', start); - if (lineEnd == kNotFound) - break; + unsigned start = 0; + while (start < text.length()) { + size_t lineEnd = text.find('\n', start); + if (lineEnd == kNotFound) + break; - result->append(static_cast(lineEnd)); - start = lineEnd + 1; - } - result->append(text.length()); + result->append(static_cast(lineEnd)); + start = lineEnd + 1; + } + result->append(text.length()); - return result.release(); + return result.release(); } -OrdinalNumber TextPosition::toOffset(const Vector& lineEndings) -{ - unsigned lineStartOffset = m_line != OrdinalNumber::first() ? lineEndings.at(m_line.zeroBasedInt() - 1) + 1 : 0; - return OrdinalNumber::fromZeroBasedInt(lineStartOffset + m_column.zeroBasedInt()); +OrdinalNumber TextPosition::toOffset(const Vector& lineEndings) { + unsigned lineStartOffset = m_line != OrdinalNumber::first() + ? lineEndings.at(m_line.zeroBasedInt() - 1) + 1 + : 0; + return OrdinalNumber::fromZeroBasedInt(lineStartOffset + + m_column.zeroBasedInt()); } -TextPosition TextPosition::fromOffsetAndLineEndings(unsigned offset, const Vector& lineEndings) -{ - const unsigned* foundLineEnding = std::lower_bound(lineEndings.begin(), lineEndings.end(), offset); - int lineIndex = foundLineEnding - &lineEndings.at(0); - unsigned lineStartOffset = lineIndex > 0 ? lineEndings.at(lineIndex - 1) + 1 : 0; - int column = offset - lineStartOffset; - return TextPosition(OrdinalNumber::fromZeroBasedInt(lineIndex), OrdinalNumber::fromZeroBasedInt(column)); +TextPosition TextPosition::fromOffsetAndLineEndings( + unsigned offset, + const Vector& lineEndings) { + const unsigned* foundLineEnding = + std::lower_bound(lineEndings.begin(), lineEndings.end(), offset); + int lineIndex = foundLineEnding - &lineEndings.at(0); + unsigned lineStartOffset = + lineIndex > 0 ? lineEndings.at(lineIndex - 1) + 1 : 0; + int column = offset - lineStartOffset; + return TextPosition(OrdinalNumber::fromZeroBasedInt(lineIndex), + OrdinalNumber::fromZeroBasedInt(column)); } -} // namespace WTF +} // namespace WTF diff --git a/sky/engine/wtf/text/TextPosition.h b/sky/engine/wtf/text/TextPosition.h index 68c1a3e8c1489..cc284c2295a72 100644 --- a/sky/engine/wtf/text/TextPosition.h +++ b/sky/engine/wtf/text/TextPosition.h @@ -10,16 +10,16 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef SKY_ENGINE_WTF_TEXT_TEXTPOSITION_H_ @@ -32,60 +32,74 @@ namespace WTF { -// An abstract number of element in a sequence. The sequence has a first element. -// This type should be used instead of integer because 2 contradicting traditions can -// call a first element '0' or '1' which makes integer type ambiguous. +// An abstract number of element in a sequence. The sequence has a first +// element. This type should be used instead of integer because 2 contradicting +// traditions can call a first element '0' or '1' which makes integer type +// ambiguous. class OrdinalNumber { -public: - static OrdinalNumber fromZeroBasedInt(int zeroBasedInt) { return OrdinalNumber(zeroBasedInt); } - static OrdinalNumber fromOneBasedInt(int oneBasedInt) { return OrdinalNumber(oneBasedInt - 1); } - OrdinalNumber() : m_zeroBasedValue(0) { } - - int zeroBasedInt() const { return m_zeroBasedValue; } - int oneBasedInt() const { return m_zeroBasedValue + 1; } - - bool operator==(OrdinalNumber other) { return m_zeroBasedValue == other.m_zeroBasedValue; } - bool operator!=(OrdinalNumber other) { return !((*this) == other); } - - static OrdinalNumber first() { return OrdinalNumber(0); } - static OrdinalNumber beforeFirst() { return OrdinalNumber(-1); } - -private: - OrdinalNumber(int zeroBasedInt) : m_zeroBasedValue(zeroBasedInt) { } - int m_zeroBasedValue; + public: + static OrdinalNumber fromZeroBasedInt(int zeroBasedInt) { + return OrdinalNumber(zeroBasedInt); + } + static OrdinalNumber fromOneBasedInt(int oneBasedInt) { + return OrdinalNumber(oneBasedInt - 1); + } + OrdinalNumber() : m_zeroBasedValue(0) {} + + int zeroBasedInt() const { return m_zeroBasedValue; } + int oneBasedInt() const { return m_zeroBasedValue + 1; } + + bool operator==(OrdinalNumber other) { + return m_zeroBasedValue == other.m_zeroBasedValue; + } + bool operator!=(OrdinalNumber other) { return !((*this) == other); } + + static OrdinalNumber first() { return OrdinalNumber(0); } + static OrdinalNumber beforeFirst() { return OrdinalNumber(-1); } + + private: + OrdinalNumber(int zeroBasedInt) : m_zeroBasedValue(zeroBasedInt) {} + int m_zeroBasedValue; }; - -// TextPosition structure specifies coordinates within an text resource. It is used mostly -// for saving script source position. +// TextPosition structure specifies coordinates within an text resource. It is +// used mostly for saving script source position. class TextPosition { -public: - TextPosition(OrdinalNumber line, OrdinalNumber column) - : m_line(line) - , m_column(column) - { - } - TextPosition() { } - bool operator==(const TextPosition& other) { return m_line == other.m_line && m_column == other.m_column; } - bool operator!=(const TextPosition& other) { return !((*this) == other); } - WTF_EXPORT OrdinalNumber toOffset(const Vector&); - - // A 'minimum' value of position, used as a default value. - static TextPosition minimumPosition() { return TextPosition(OrdinalNumber::first(), OrdinalNumber::first()); } - - // A value with line value less than a minimum; used as an impossible position. - static TextPosition belowRangePosition() { return TextPosition(OrdinalNumber::beforeFirst(), OrdinalNumber::beforeFirst()); } - - // A value corresponding to a position with given offset within text having the specified line ending offsets. - WTF_EXPORT static TextPosition fromOffsetAndLineEndings(unsigned, const Vector&); - - OrdinalNumber m_line; - OrdinalNumber m_column; + public: + TextPosition(OrdinalNumber line, OrdinalNumber column) + : m_line(line), m_column(column) {} + TextPosition() {} + bool operator==(const TextPosition& other) { + return m_line == other.m_line && m_column == other.m_column; + } + bool operator!=(const TextPosition& other) { return !((*this) == other); } + WTF_EXPORT OrdinalNumber toOffset(const Vector&); + + // A 'minimum' value of position, used as a default value. + static TextPosition minimumPosition() { + return TextPosition(OrdinalNumber::first(), OrdinalNumber::first()); + } + + // A value with line value less than a minimum; used as an impossible + // position. + static TextPosition belowRangePosition() { + return TextPosition(OrdinalNumber::beforeFirst(), + OrdinalNumber::beforeFirst()); + } + + // A value corresponding to a position with given offset within text having + // the specified line ending offsets. + WTF_EXPORT static TextPosition fromOffsetAndLineEndings( + unsigned, + const Vector&); + + OrdinalNumber m_line; + OrdinalNumber m_column; }; -WTF_EXPORT PassOwnPtr > lineEndings(const String&); +WTF_EXPORT PassOwnPtr> lineEndings(const String&); -} // namespace WTF +} // namespace WTF using WTF::OrdinalNumber; diff --git a/sky/engine/wtf/text/WTFString.cpp b/sky/engine/wtf/text/WTFString.cpp index 8767074edf636..fe461cc3da440 100644 --- a/sky/engine/wtf/text/WTFString.cpp +++ b/sky/engine/wtf/text/WTFString.cpp @@ -1,6 +1,7 @@ /* * (C) 1999 Lars Knoll (knoll@kde.org) - * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2010, 2012 Apple Inc. All rights reserved. + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2010, 2012 Apple Inc. + * All rights reserved. * Copyright (C) 2007-2009 Torch Mobile, Inc. * * This library is free software; you can redistribute it and/or @@ -44,1180 +45,1197 @@ using namespace std; // Construct a string with UTF-16 data. String::String(const UChar* characters, unsigned length) - : m_impl(characters ? StringImpl::create(characters, length) : nullptr) -{ -} + : m_impl(characters ? StringImpl::create(characters, length) : nullptr) {} // Construct a string with UTF-16 data, from a null-terminated source. -String::String(const UChar* str) -{ - if (!str) - return; - m_impl = StringImpl::create(str, lengthOfNullTerminatedString(str)); +String::String(const UChar* str) { + if (!str) + return; + m_impl = StringImpl::create(str, lengthOfNullTerminatedString(str)); } // Construct a string with latin1 data. String::String(const LChar* characters, unsigned length) - : m_impl(characters ? StringImpl::create(characters, length) : nullptr) -{ -} + : m_impl(characters ? StringImpl::create(characters, length) : nullptr) {} String::String(const char* characters, unsigned length) - : m_impl(characters ? StringImpl::create(reinterpret_cast(characters), length) : nullptr) -{ -} + : m_impl(characters ? StringImpl::create( + reinterpret_cast(characters), + length) + : nullptr) {} // Construct a string with latin1 data, from a null-terminated source. String::String(const LChar* characters) - : m_impl(characters ? StringImpl::create(characters) : nullptr) -{ -} + : m_impl(characters ? StringImpl::create(characters) : nullptr) {} String::String(const char* characters) - : m_impl(characters ? StringImpl::create(reinterpret_cast(characters)) : nullptr) -{ -} - -void String::append(const String& string) -{ - if (string.isEmpty()) - return; - if (!m_impl) { - m_impl = string.m_impl; - return; - } - - // FIXME: This is extremely inefficient. So much so that we might want to take this - // out of String's API. We can make it better by optimizing the case where exactly - // one String is pointing at this StringImpl, but even then it's going to require a - // call into the allocator every single time. - - if (m_impl->is8Bit() && string.m_impl->is8Bit()) { - LChar* data; - RELEASE_ASSERT(string.length() <= numeric_limits::max() - m_impl->length()); - RefPtr newImpl = StringImpl::createUninitialized(m_impl->length() + string.length(), data); - memcpy(data, m_impl->characters8(), m_impl->length() * sizeof(LChar)); - memcpy(data + m_impl->length(), string.characters8(), string.length() * sizeof(LChar)); - m_impl = newImpl.release(); - return; - } - - UChar* data; - RELEASE_ASSERT(string.length() <= numeric_limits::max() - m_impl->length()); - RefPtr newImpl = StringImpl::createUninitialized(m_impl->length() + string.length(), data); - - if (m_impl->is8Bit()) - StringImpl::copyChars(data, m_impl->characters8(), m_impl->length()); - else - StringImpl::copyChars(data, m_impl->characters16(), m_impl->length()); - - if (string.impl()->is8Bit()) - StringImpl::copyChars(data + m_impl->length(), string.impl()->characters8(), string.impl()->length()); - else - StringImpl::copyChars(data + m_impl->length(), string.impl()->characters16(), string.impl()->length()); - - m_impl = newImpl.release(); -} - -template -inline void String::appendInternal(CharacterType c) -{ - // FIXME: This is extremely inefficient. So much so that we might want to take this - // out of String's API. We can make it better by optimizing the case where exactly - // one String is pointing at this StringImpl, but even then it's going to require a - // call into the allocator every single time. - if (!m_impl) { - m_impl = StringImpl::create(&c, 1); - return; - } - - UChar* data; // FIXME: We should be able to create an 8 bit string via this code path. - RELEASE_ASSERT(m_impl->length() < numeric_limits::max()); - RefPtr newImpl = StringImpl::createUninitialized(m_impl->length() + 1, data); - if (m_impl->is8Bit()) - StringImpl::copyChars(data, m_impl->characters8(), m_impl->length()); - else - StringImpl::copyChars(data, m_impl->characters16(), m_impl->length()); - data[m_impl->length()] = c; + : m_impl(characters ? StringImpl::create( + reinterpret_cast(characters)) + : nullptr) {} + +void String::append(const String& string) { + if (string.isEmpty()) + return; + if (!m_impl) { + m_impl = string.m_impl; + return; + } + + // FIXME: This is extremely inefficient. So much so that we might want to take + // this out of String's API. We can make it better by optimizing the case + // where exactly one String is pointing at this StringImpl, but even then it's + // going to require a call into the allocator every single time. + + if (m_impl->is8Bit() && string.m_impl->is8Bit()) { + LChar* data; + RELEASE_ASSERT(string.length() <= + numeric_limits::max() - m_impl->length()); + RefPtr newImpl = StringImpl::createUninitialized( + m_impl->length() + string.length(), data); + memcpy(data, m_impl->characters8(), m_impl->length() * sizeof(LChar)); + memcpy(data + m_impl->length(), string.characters8(), + string.length() * sizeof(LChar)); m_impl = newImpl.release(); -} - -void String::append(LChar c) -{ - appendInternal(c); -} + return; + } -void String::append(UChar c) -{ - appendInternal(c); -} + UChar* data; + RELEASE_ASSERT(string.length() <= + numeric_limits::max() - m_impl->length()); + RefPtr newImpl = + StringImpl::createUninitialized(m_impl->length() + string.length(), data); -int codePointCompare(const String& a, const String& b) -{ - return codePointCompare(a.impl(), b.impl()); -} + if (m_impl->is8Bit()) + StringImpl::copyChars(data, m_impl->characters8(), m_impl->length()); + else + StringImpl::copyChars(data, m_impl->characters16(), m_impl->length()); -void String::insert(const String& string, unsigned position) -{ - if (string.isEmpty()) { - if (string.isNull()) - return; - if (isNull()) - m_impl = string.impl(); - return; - } + if (string.impl()->is8Bit()) + StringImpl::copyChars(data + m_impl->length(), string.impl()->characters8(), + string.impl()->length()); + else + StringImpl::copyChars(data + m_impl->length(), + string.impl()->characters16(), + string.impl()->length()); - if (string.is8Bit()) - insert(string.impl()->characters8(), string.length(), position); - else - insert(string.impl()->characters16(), string.length(), position); + m_impl = newImpl.release(); } -void String::append(const LChar* charactersToAppend, unsigned lengthToAppend) -{ - if (!m_impl) { - if (!charactersToAppend) - return; - m_impl = StringImpl::create(charactersToAppend, lengthToAppend); - return; - } - - if (!lengthToAppend) - return; - - ASSERT(charactersToAppend); - - unsigned strLength = m_impl->length(); - - if (m_impl->is8Bit()) { - RELEASE_ASSERT(lengthToAppend <= numeric_limits::max() - strLength); - LChar* data; - RefPtr newImpl = StringImpl::createUninitialized(strLength + lengthToAppend, data); - StringImpl::copyChars(data, m_impl->characters8(), strLength); - StringImpl::copyChars(data + strLength, charactersToAppend, lengthToAppend); - m_impl = newImpl.release(); - return; - } - - RELEASE_ASSERT(lengthToAppend <= numeric_limits::max() - strLength); - UChar* data; - RefPtr newImpl = StringImpl::createUninitialized(length() + lengthToAppend, data); - StringImpl::copyChars(data, m_impl->characters16(), strLength); +template +inline void String::appendInternal(CharacterType c) { + // FIXME: This is extremely inefficient. So much so that we might want to take + // this out of String's API. We can make it better by optimizing the case + // where exactly one String is pointing at this StringImpl, but even then it's + // going to require a call into the allocator every single time. + if (!m_impl) { + m_impl = StringImpl::create(&c, 1); + return; + } + + UChar* data; // FIXME: We should be able to create an 8 bit string via this + // code path. + RELEASE_ASSERT(m_impl->length() < numeric_limits::max()); + RefPtr newImpl = + StringImpl::createUninitialized(m_impl->length() + 1, data); + if (m_impl->is8Bit()) + StringImpl::copyChars(data, m_impl->characters8(), m_impl->length()); + else + StringImpl::copyChars(data, m_impl->characters16(), m_impl->length()); + data[m_impl->length()] = c; + m_impl = newImpl.release(); +} + +void String::append(LChar c) { + appendInternal(c); +} + +void String::append(UChar c) { + appendInternal(c); +} + +int codePointCompare(const String& a, const String& b) { + return codePointCompare(a.impl(), b.impl()); +} + +void String::insert(const String& string, unsigned position) { + if (string.isEmpty()) { + if (string.isNull()) + return; + if (isNull()) + m_impl = string.impl(); + return; + } + + if (string.is8Bit()) + insert(string.impl()->characters8(), string.length(), position); + else + insert(string.impl()->characters16(), string.length(), position); +} + +void String::append(const LChar* charactersToAppend, unsigned lengthToAppend) { + if (!m_impl) { + if (!charactersToAppend) + return; + m_impl = StringImpl::create(charactersToAppend, lengthToAppend); + return; + } + + if (!lengthToAppend) + return; + + ASSERT(charactersToAppend); + + unsigned strLength = m_impl->length(); + + if (m_impl->is8Bit()) { + RELEASE_ASSERT(lengthToAppend <= + numeric_limits::max() - strLength); + LChar* data; + RefPtr newImpl = + StringImpl::createUninitialized(strLength + lengthToAppend, data); + StringImpl::copyChars(data, m_impl->characters8(), strLength); StringImpl::copyChars(data + strLength, charactersToAppend, lengthToAppend); m_impl = newImpl.release(); + return; + } + + RELEASE_ASSERT(lengthToAppend <= numeric_limits::max() - strLength); + UChar* data; + RefPtr newImpl = + StringImpl::createUninitialized(length() + lengthToAppend, data); + StringImpl::copyChars(data, m_impl->characters16(), strLength); + StringImpl::copyChars(data + strLength, charactersToAppend, lengthToAppend); + m_impl = newImpl.release(); +} + +void String::append(const UChar* charactersToAppend, unsigned lengthToAppend) { + if (!m_impl) { + if (!charactersToAppend) + return; + m_impl = StringImpl::create(charactersToAppend, lengthToAppend); + return; + } + + if (!lengthToAppend) + return; + + unsigned strLength = m_impl->length(); + + ASSERT(charactersToAppend); + RELEASE_ASSERT(lengthToAppend <= numeric_limits::max() - strLength); + UChar* data; + RefPtr newImpl = + StringImpl::createUninitialized(strLength + lengthToAppend, data); + if (m_impl->is8Bit()) + StringImpl::copyChars(data, characters8(), strLength); + else + StringImpl::copyChars(data, characters16(), strLength); + StringImpl::copyChars(data + strLength, charactersToAppend, lengthToAppend); + m_impl = newImpl.release(); } -void String::append(const UChar* charactersToAppend, unsigned lengthToAppend) -{ - if (!m_impl) { - if (!charactersToAppend) - return; - m_impl = StringImpl::create(charactersToAppend, lengthToAppend); - return; - } - - if (!lengthToAppend) - return; - - unsigned strLength = m_impl->length(); - - ASSERT(charactersToAppend); - RELEASE_ASSERT(lengthToAppend <= numeric_limits::max() - strLength); +template +PassRefPtr insertInternal(PassRefPtr impl, + const CharType* charactersToInsert, + unsigned lengthToInsert, + unsigned position) { + if (!lengthToInsert) + return impl; + + ASSERT(charactersToInsert); + UChar* data; // FIXME: We should be able to create an 8 bit string here. + RELEASE_ASSERT(lengthToInsert <= + numeric_limits::max() - impl->length()); + RefPtr newImpl = + StringImpl::createUninitialized(impl->length() + lengthToInsert, data); + + if (impl->is8Bit()) + StringImpl::copyChars(data, impl->characters8(), position); + else + StringImpl::copyChars(data, impl->characters16(), position); + + StringImpl::copyChars(data + position, charactersToInsert, lengthToInsert); + + if (impl->is8Bit()) + StringImpl::copyChars(data + position + lengthToInsert, + impl->characters8() + position, + impl->length() - position); + else + StringImpl::copyChars(data + position + lengthToInsert, + impl->characters16() + position, + impl->length() - position); + + return newImpl.release(); +} + +void String::insert(const UChar* charactersToInsert, + unsigned lengthToInsert, + unsigned position) { + if (position >= length()) { + append(charactersToInsert, lengthToInsert); + return; + } + ASSERT(m_impl); + m_impl = insertInternal(m_impl.release(), charactersToInsert, lengthToInsert, + position); +} + +void String::insert(const LChar* charactersToInsert, + unsigned lengthToInsert, + unsigned position) { + if (position >= length()) { + append(charactersToInsert, lengthToInsert); + return; + } + ASSERT(m_impl); + m_impl = insertInternal(m_impl.release(), charactersToInsert, lengthToInsert, + position); +} + +UChar32 String::characterStartingAt(unsigned i) const { + if (!m_impl || i >= m_impl->length()) + return 0; + return m_impl->characterStartingAt(i); +} + +void String::ensure16Bit() { + unsigned length = this->length(); + if (!length || !is8Bit()) + return; + m_impl = make16BitFrom8BitSource(m_impl->characters8(), length).impl(); +} + +void String::truncate(unsigned position) { + if (position >= length()) + return; + if (m_impl->is8Bit()) { + LChar* data; + RefPtr newImpl = + StringImpl::createUninitialized(position, data); + memcpy(data, m_impl->characters8(), position * sizeof(LChar)); + m_impl = newImpl.release(); + } else { UChar* data; - RefPtr newImpl = StringImpl::createUninitialized(strLength + lengthToAppend, data); - if (m_impl->is8Bit()) - StringImpl::copyChars(data, characters8(), strLength); - else - StringImpl::copyChars(data, characters16(), strLength); - StringImpl::copyChars(data + strLength, charactersToAppend, lengthToAppend); + RefPtr newImpl = + StringImpl::createUninitialized(position, data); + memcpy(data, m_impl->characters16(), position * sizeof(UChar)); m_impl = newImpl.release(); -} - -template -PassRefPtr insertInternal(PassRefPtr impl, const CharType* charactersToInsert, unsigned lengthToInsert, unsigned position) -{ - if (!lengthToInsert) - return impl; - - ASSERT(charactersToInsert); - UChar* data; // FIXME: We should be able to create an 8 bit string here. - RELEASE_ASSERT(lengthToInsert <= numeric_limits::max() - impl->length()); - RefPtr newImpl = StringImpl::createUninitialized(impl->length() + lengthToInsert, data); - - if (impl->is8Bit()) - StringImpl::copyChars(data, impl->characters8(), position); - else - StringImpl::copyChars(data, impl->characters16(), position); - - StringImpl::copyChars(data + position, charactersToInsert, lengthToInsert); - - if (impl->is8Bit()) - StringImpl::copyChars(data + position + lengthToInsert, impl->characters8() + position, impl->length() - position); - else - StringImpl::copyChars(data + position + lengthToInsert, impl->characters16() + position, impl->length() - position); - - return newImpl.release(); -} - -void String::insert(const UChar* charactersToInsert, unsigned lengthToInsert, unsigned position) -{ - if (position >= length()) { - append(charactersToInsert, lengthToInsert); - return; - } - ASSERT(m_impl); - m_impl = insertInternal(m_impl.release(), charactersToInsert, lengthToInsert, position); -} - -void String::insert(const LChar* charactersToInsert, unsigned lengthToInsert, unsigned position) -{ - if (position >= length()) { - append(charactersToInsert, lengthToInsert); - return; - } - ASSERT(m_impl); - m_impl = insertInternal(m_impl.release(), charactersToInsert, lengthToInsert, position); -} - -UChar32 String::characterStartingAt(unsigned i) const -{ - if (!m_impl || i >= m_impl->length()) - return 0; - return m_impl->characterStartingAt(i); -} - -void String::ensure16Bit() -{ - unsigned length = this->length(); - if (!length || !is8Bit()) - return; - m_impl = make16BitFrom8BitSource(m_impl->characters8(), length).impl(); -} - -void String::truncate(unsigned position) -{ - if (position >= length()) - return; - if (m_impl->is8Bit()) { - LChar* data; - RefPtr newImpl = StringImpl::createUninitialized(position, data); - memcpy(data, m_impl->characters8(), position * sizeof(LChar)); - m_impl = newImpl.release(); - } else { - UChar* data; - RefPtr newImpl = StringImpl::createUninitialized(position, data); - memcpy(data, m_impl->characters16(), position * sizeof(UChar)); - m_impl = newImpl.release(); - } + } } template -inline void String::removeInternal(const CharacterType* characters, unsigned position, int lengthToRemove) -{ - CharacterType* data; - RefPtr newImpl = StringImpl::createUninitialized(length() - lengthToRemove, data); - memcpy(data, characters, position * sizeof(CharacterType)); - memcpy(data + position, characters + position + lengthToRemove, - (length() - lengthToRemove - position) * sizeof(CharacterType)); +inline void String::removeInternal(const CharacterType* characters, + unsigned position, + int lengthToRemove) { + CharacterType* data; + RefPtr newImpl = + StringImpl::createUninitialized(length() - lengthToRemove, data); + memcpy(data, characters, position * sizeof(CharacterType)); + memcpy(data + position, characters + position + lengthToRemove, + (length() - lengthToRemove - position) * sizeof(CharacterType)); - m_impl = newImpl.release(); + m_impl = newImpl.release(); } -void String::remove(unsigned position, int lengthToRemove) -{ - if (lengthToRemove <= 0) - return; - if (position >= length()) - return; - if (static_cast(lengthToRemove) > length() - position) - lengthToRemove = length() - position; +void String::remove(unsigned position, int lengthToRemove) { + if (lengthToRemove <= 0) + return; + if (position >= length()) + return; + if (static_cast(lengthToRemove) > length() - position) + lengthToRemove = length() - position; - if (is8Bit()) { - removeInternal(characters8(), position, lengthToRemove); + if (is8Bit()) { + removeInternal(characters8(), position, lengthToRemove); - return; - } + return; + } - removeInternal(characters16(), position, lengthToRemove); + removeInternal(characters16(), position, lengthToRemove); } -String String::substring(unsigned pos, unsigned len) const -{ - if (!m_impl) - return String(); - return m_impl->substring(pos, len); +String String::substring(unsigned pos, unsigned len) const { + if (!m_impl) + return String(); + return m_impl->substring(pos, len); } -String String::lower() const -{ - if (!m_impl) - return String(); - return m_impl->lower(); +String String::lower() const { + if (!m_impl) + return String(); + return m_impl->lower(); } -String String::upper() const -{ - if (!m_impl) - return String(); - return m_impl->upper(); +String String::upper() const { + if (!m_impl) + return String(); + return m_impl->upper(); } -String String::lower(const AtomicString& localeIdentifier) const -{ - if (!m_impl) - return String(); - return m_impl->lower(localeIdentifier); +String String::lower(const AtomicString& localeIdentifier) const { + if (!m_impl) + return String(); + return m_impl->lower(localeIdentifier); } -String String::upper(const AtomicString& localeIdentifier) const -{ - if (!m_impl) - return String(); - return m_impl->upper(localeIdentifier); +String String::upper(const AtomicString& localeIdentifier) const { + if (!m_impl) + return String(); + return m_impl->upper(localeIdentifier); } -String String::stripWhiteSpace() const -{ - if (!m_impl) - return String(); - return m_impl->stripWhiteSpace(); +String String::stripWhiteSpace() const { + if (!m_impl) + return String(); + return m_impl->stripWhiteSpace(); } -String String::stripWhiteSpace(IsWhiteSpaceFunctionPtr isWhiteSpace) const -{ - if (!m_impl) - return String(); - return m_impl->stripWhiteSpace(isWhiteSpace); +String String::stripWhiteSpace(IsWhiteSpaceFunctionPtr isWhiteSpace) const { + if (!m_impl) + return String(); + return m_impl->stripWhiteSpace(isWhiteSpace); } -String String::simplifyWhiteSpace(StripBehavior stripBehavior) const -{ - if (!m_impl) - return String(); - return m_impl->simplifyWhiteSpace(stripBehavior); +String String::simplifyWhiteSpace(StripBehavior stripBehavior) const { + if (!m_impl) + return String(); + return m_impl->simplifyWhiteSpace(stripBehavior); } -String String::simplifyWhiteSpace(IsWhiteSpaceFunctionPtr isWhiteSpace, StripBehavior stripBehavior) const -{ - if (!m_impl) - return String(); - return m_impl->simplifyWhiteSpace(isWhiteSpace, stripBehavior); +String String::simplifyWhiteSpace(IsWhiteSpaceFunctionPtr isWhiteSpace, + StripBehavior stripBehavior) const { + if (!m_impl) + return String(); + return m_impl->simplifyWhiteSpace(isWhiteSpace, stripBehavior); } -String String::removeCharacters(CharacterMatchFunctionPtr findMatch) const -{ - if (!m_impl) - return String(); - return m_impl->removeCharacters(findMatch); +String String::removeCharacters(CharacterMatchFunctionPtr findMatch) const { + if (!m_impl) + return String(); + return m_impl->removeCharacters(findMatch); } -String String::foldCase() const -{ - if (!m_impl) - return String(); - return m_impl->foldCase(); +String String::foldCase() const { + if (!m_impl) + return String(); + return m_impl->foldCase(); } -bool String::percentage(int& result) const -{ - if (!m_impl || !m_impl->length()) - return false; +bool String::percentage(int& result) const { + if (!m_impl || !m_impl->length()) + return false; - if ((*m_impl)[m_impl->length() - 1] != '%') - return false; + if ((*m_impl)[m_impl->length() - 1] != '%') + return false; - if (m_impl->is8Bit()) - result = charactersToIntStrict(m_impl->characters8(), m_impl->length() - 1); - else - result = charactersToIntStrict(m_impl->characters16(), m_impl->length() - 1); + if (m_impl->is8Bit()) + result = charactersToIntStrict(m_impl->characters8(), m_impl->length() - 1); + else + result = + charactersToIntStrict(m_impl->characters16(), m_impl->length() - 1); - return true; + return true; } -Vector String::charactersWithNullTermination() const -{ - if (!m_impl) - return Vector(); +Vector String::charactersWithNullTermination() const { + if (!m_impl) + return Vector(); - Vector result; - result.reserveInitialCapacity(length() + 1); - appendTo(result); - result.append(0); - return result; + Vector result; + result.reserveInitialCapacity(length() + 1); + appendTo(result); + result.append(0); + return result; } -unsigned String::copyTo(UChar* buffer, unsigned pos, unsigned maxLength) const -{ - unsigned length = this->length(); - RELEASE_ASSERT(pos <= length); - unsigned numCharacters = std::min(length - pos, maxLength); - if (!numCharacters) - return 0; - if (is8Bit()) - StringImpl::copyChars(buffer, characters8() + pos, numCharacters); - else - StringImpl::copyChars(buffer, characters16() + pos, numCharacters); - return numCharacters; +unsigned String::copyTo(UChar* buffer, unsigned pos, unsigned maxLength) const { + unsigned length = this->length(); + RELEASE_ASSERT(pos <= length); + unsigned numCharacters = std::min(length - pos, maxLength); + if (!numCharacters) + return 0; + if (is8Bit()) + StringImpl::copyChars(buffer, characters8() + pos, numCharacters); + else + StringImpl::copyChars(buffer, characters16() + pos, numCharacters); + return numCharacters; } -String String::format(const char *format, ...) -{ - va_list args; - va_start(args, format); +String String::format(const char* format, ...) { + va_list args; + va_start(args, format); - Vector buffer; + Vector buffer; - // Do the format once to get the length. - char ch; - int result = vsnprintf(&ch, 1, format, args); - // We need to call va_end() and then va_start() again here, as the - // contents of args is undefined after the call to vsnprintf - // according to http://man.cx/snprintf(3) - // - // Not calling va_end/va_start here happens to work on lots of - // systems, but fails e.g. on 64bit Linux. - va_end(args); - va_start(args, format); + // Do the format once to get the length. + char ch; + int result = vsnprintf(&ch, 1, format, args); + // We need to call va_end() and then va_start() again here, as the + // contents of args is undefined after the call to vsnprintf + // according to http://man.cx/snprintf(3) + // + // Not calling va_end/va_start here happens to work on lots of + // systems, but fails e.g. on 64bit Linux. + va_end(args); + va_start(args, format); - if (result == 0) - return String(""); - if (result < 0) - return String(); - unsigned len = result; - buffer.grow(len + 1); + if (result == 0) + return String(""); + if (result < 0) + return String(); + unsigned len = result; + buffer.grow(len + 1); - // Now do the formatting again, guaranteed to fit. - vsnprintf(buffer.data(), buffer.size(), format, args); + // Now do the formatting again, guaranteed to fit. + vsnprintf(buffer.data(), buffer.size(), format, args); - va_end(args); + va_end(args); - return StringImpl::create(reinterpret_cast(buffer.data()), len); + return StringImpl::create(reinterpret_cast(buffer.data()), len); } -String String::number(int number) -{ - return numberToStringSigned(number); +String String::number(int number) { + return numberToStringSigned(number); } -String String::number(unsigned number) -{ - return numberToStringUnsigned(number); +String String::number(unsigned number) { + return numberToStringUnsigned(number); } -String String::number(long number) -{ - return numberToStringSigned(number); +String String::number(long number) { + return numberToStringSigned(number); } -String String::number(unsigned long number) -{ - return numberToStringUnsigned(number); +String String::number(unsigned long number) { + return numberToStringUnsigned(number); } -String String::number(long long number) -{ - return numberToStringSigned(number); +String String::number(long long number) { + return numberToStringSigned(number); } -String String::number(unsigned long long number) -{ - return numberToStringUnsigned(number); +String String::number(unsigned long long number) { + return numberToStringUnsigned(number); } -String String::number(double number, unsigned precision, TrailingZerosTruncatingPolicy trailingZerosTruncatingPolicy) -{ - NumberToStringBuffer buffer; - return String(numberToFixedPrecisionString(number, precision, buffer, trailingZerosTruncatingPolicy == TruncateTrailingZeros)); +String String::number( + double number, + unsigned precision, + TrailingZerosTruncatingPolicy trailingZerosTruncatingPolicy) { + NumberToStringBuffer buffer; + return String(numberToFixedPrecisionString( + number, precision, buffer, + trailingZerosTruncatingPolicy == TruncateTrailingZeros)); } -String String::numberToStringECMAScript(double number) -{ - NumberToStringBuffer buffer; - return String(numberToString(number, buffer)); +String String::numberToStringECMAScript(double number) { + NumberToStringBuffer buffer; + return String(numberToString(number, buffer)); } -String String::numberToStringFixedWidth(double number, unsigned decimalPlaces) -{ - NumberToStringBuffer buffer; - return String(numberToFixedWidthString(number, decimalPlaces, buffer)); +String String::numberToStringFixedWidth(double number, unsigned decimalPlaces) { + NumberToStringBuffer buffer; + return String(numberToFixedWidthString(number, decimalPlaces, buffer)); } -int String::toIntStrict(bool* ok, int base) const -{ - if (!m_impl) { - if (ok) - *ok = false; - return 0; - } - return m_impl->toIntStrict(ok, base); +int String::toIntStrict(bool* ok, int base) const { + if (!m_impl) { + if (ok) + *ok = false; + return 0; + } + return m_impl->toIntStrict(ok, base); } -unsigned String::toUIntStrict(bool* ok, int base) const -{ - if (!m_impl) { - if (ok) - *ok = false; - return 0; - } - return m_impl->toUIntStrict(ok, base); +unsigned String::toUIntStrict(bool* ok, int base) const { + if (!m_impl) { + if (ok) + *ok = false; + return 0; + } + return m_impl->toUIntStrict(ok, base); } -int64_t String::toInt64Strict(bool* ok, int base) const -{ - if (!m_impl) { - if (ok) - *ok = false; - return 0; - } - return m_impl->toInt64Strict(ok, base); +int64_t String::toInt64Strict(bool* ok, int base) const { + if (!m_impl) { + if (ok) + *ok = false; + return 0; + } + return m_impl->toInt64Strict(ok, base); } -uint64_t String::toUInt64Strict(bool* ok, int base) const -{ - if (!m_impl) { - if (ok) - *ok = false; - return 0; - } - return m_impl->toUInt64Strict(ok, base); +uint64_t String::toUInt64Strict(bool* ok, int base) const { + if (!m_impl) { + if (ok) + *ok = false; + return 0; + } + return m_impl->toUInt64Strict(ok, base); } -intptr_t String::toIntPtrStrict(bool* ok, int base) const -{ - if (!m_impl) { - if (ok) - *ok = false; - return 0; - } - return m_impl->toIntPtrStrict(ok, base); +intptr_t String::toIntPtrStrict(bool* ok, int base) const { + if (!m_impl) { + if (ok) + *ok = false; + return 0; + } + return m_impl->toIntPtrStrict(ok, base); } -int String::toInt(bool* ok) const -{ - if (!m_impl) { - if (ok) - *ok = false; - return 0; - } - return m_impl->toInt(ok); +int String::toInt(bool* ok) const { + if (!m_impl) { + if (ok) + *ok = false; + return 0; + } + return m_impl->toInt(ok); } -unsigned String::toUInt(bool* ok) const -{ - if (!m_impl) { - if (ok) - *ok = false; - return 0; - } - return m_impl->toUInt(ok); +unsigned String::toUInt(bool* ok) const { + if (!m_impl) { + if (ok) + *ok = false; + return 0; + } + return m_impl->toUInt(ok); } -int64_t String::toInt64(bool* ok) const -{ - if (!m_impl) { - if (ok) - *ok = false; - return 0; - } - return m_impl->toInt64(ok); +int64_t String::toInt64(bool* ok) const { + if (!m_impl) { + if (ok) + *ok = false; + return 0; + } + return m_impl->toInt64(ok); } -uint64_t String::toUInt64(bool* ok) const -{ - if (!m_impl) { - if (ok) - *ok = false; - return 0; - } - return m_impl->toUInt64(ok); +uint64_t String::toUInt64(bool* ok) const { + if (!m_impl) { + if (ok) + *ok = false; + return 0; + } + return m_impl->toUInt64(ok); } -intptr_t String::toIntPtr(bool* ok) const -{ - if (!m_impl) { - if (ok) - *ok = false; - return 0; - } - return m_impl->toIntPtr(ok); +intptr_t String::toIntPtr(bool* ok) const { + if (!m_impl) { + if (ok) + *ok = false; + return 0; + } + return m_impl->toIntPtr(ok); } -double String::toDouble(bool* ok) const -{ - if (!m_impl) { - if (ok) - *ok = false; - return 0.0; - } - return m_impl->toDouble(ok); +double String::toDouble(bool* ok) const { + if (!m_impl) { + if (ok) + *ok = false; + return 0.0; + } + return m_impl->toDouble(ok); } -float String::toFloat(bool* ok) const -{ - if (!m_impl) { - if (ok) - *ok = false; - return 0.0f; - } - return m_impl->toFloat(ok); -} - -String String::isolatedCopy() const -{ - if (!m_impl) - return String(); - return m_impl->isolatedCopy(); -} - -bool String::isSafeToSendToAnotherThread() const -{ - if (!impl()) - return true; - if (impl()->isStatic()) - return true; - // AtomicStrings are not safe to send between threads as ~StringImpl() - // will try to remove them from the wrong AtomicStringTable. - if (impl()->isAtomic()) - return false; - if (impl()->hasOneRef()) - return true; - return false; +float String::toFloat(bool* ok) const { + if (!m_impl) { + if (ok) + *ok = false; + return 0.0f; + } + return m_impl->toFloat(ok); } -void String::split(const String& separator, bool allowEmptyEntries, Vector& result) const -{ - result.clear(); - - unsigned startPos = 0; - size_t endPos; - while ((endPos = find(separator, startPos)) != kNotFound) { - if (allowEmptyEntries || startPos != endPos) - result.append(substring(startPos, endPos - startPos)); - startPos = endPos + separator.length(); - } - if (allowEmptyEntries || startPos != length()) - result.append(substring(startPos)); +String String::isolatedCopy() const { + if (!m_impl) + return String(); + return m_impl->isolatedCopy(); } -void String::split(UChar separator, bool allowEmptyEntries, Vector& result) const -{ - result.clear(); +bool String::isSafeToSendToAnotherThread() const { + if (!impl()) + return true; + if (impl()->isStatic()) + return true; + // AtomicStrings are not safe to send between threads as ~StringImpl() + // will try to remove them from the wrong AtomicStringTable. + if (impl()->isAtomic()) + return false; + if (impl()->hasOneRef()) + return true; + return false; +} + +void String::split(const String& separator, + bool allowEmptyEntries, + Vector& result) const { + result.clear(); + + unsigned startPos = 0; + size_t endPos; + while ((endPos = find(separator, startPos)) != kNotFound) { + if (allowEmptyEntries || startPos != endPos) + result.append(substring(startPos, endPos - startPos)); + startPos = endPos + separator.length(); + } + if (allowEmptyEntries || startPos != length()) + result.append(substring(startPos)); +} + +void String::split(UChar separator, + bool allowEmptyEntries, + Vector& result) const { + result.clear(); + + unsigned startPos = 0; + size_t endPos; + while ((endPos = find(separator, startPos)) != kNotFound) { + if (allowEmptyEntries || startPos != endPos) + result.append(substring(startPos, endPos - startPos)); + startPos = endPos + 1; + } + if (allowEmptyEntries || startPos != length()) + result.append(substring(startPos)); +} + +CString String::ascii() const { + // Printable ASCII characters 32..127 and the null character are + // preserved, characters outside of this range are converted to '?'. + + unsigned length = this->length(); + if (!length) { + char* characterBuffer; + return CString::newUninitialized(length, characterBuffer); + } - unsigned startPos = 0; - size_t endPos; - while ((endPos = find(separator, startPos)) != kNotFound) { - if (allowEmptyEntries || startPos != endPos) - result.append(substring(startPos, endPos - startPos)); - startPos = endPos + 1; - } - if (allowEmptyEntries || startPos != length()) - result.append(substring(startPos)); -} + if (this->is8Bit()) { + const LChar* characters = this->characters8(); -CString String::ascii() const -{ - // Printable ASCII characters 32..127 and the null character are - // preserved, characters outside of this range are converted to '?'. + char* characterBuffer; + CString result = CString::newUninitialized(length, characterBuffer); - unsigned length = this->length(); - if (!length) { - char* characterBuffer; - return CString::newUninitialized(length, characterBuffer); + for (unsigned i = 0; i < length; ++i) { + LChar ch = characters[i]; + characterBuffer[i] = ch && (ch < 0x20 || ch > 0x7f) ? '?' : ch; } - if (this->is8Bit()) { - const LChar* characters = this->characters8(); + return result; + } - char* characterBuffer; - CString result = CString::newUninitialized(length, characterBuffer); + const UChar* characters = this->characters16(); - for (unsigned i = 0; i < length; ++i) { - LChar ch = characters[i]; - characterBuffer[i] = ch && (ch < 0x20 || ch > 0x7f) ? '?' : ch; - } + char* characterBuffer; + CString result = CString::newUninitialized(length, characterBuffer); - return result; - } + for (unsigned i = 0; i < length; ++i) { + UChar ch = characters[i]; + characterBuffer[i] = ch && (ch < 0x20 || ch > 0x7f) ? '?' : ch; + } - const UChar* characters = this->characters16(); + return result; +} - char* characterBuffer; - CString result = CString::newUninitialized(length, characterBuffer); +CString String::latin1() const { + // Basic Latin1 (ISO) encoding - Unicode characters 0..255 are + // preserved, characters outside of this range are converted to '?'. - for (unsigned i = 0; i < length; ++i) { - UChar ch = characters[i]; - characterBuffer[i] = ch && (ch < 0x20 || ch > 0x7f) ? '?' : ch; - } + unsigned length = this->length(); - return result; -} + if (!length) + return CString("", 0); -CString String::latin1() const -{ - // Basic Latin1 (ISO) encoding - Unicode characters 0..255 are - // preserved, characters outside of this range are converted to '?'. + if (is8Bit()) + return CString(reinterpret_cast(this->characters8()), length); - unsigned length = this->length(); + const UChar* characters = this->characters16(); - if (!length) - return CString("", 0); + char* characterBuffer; + CString result = CString::newUninitialized(length, characterBuffer); - if (is8Bit()) - return CString(reinterpret_cast(this->characters8()), length); + for (unsigned i = 0; i < length; ++i) { + UChar ch = characters[i]; + characterBuffer[i] = ch > 0xff ? '?' : ch; + } - const UChar* characters = this->characters16(); + return result; +} - char* characterBuffer; - CString result = CString::newUninitialized(length, characterBuffer); +// Helper to write a three-byte UTF-8 code point to the buffer, caller must +// check room is available. +static inline void putUTF8Triple(char*& buffer, UChar ch) { + ASSERT(ch >= 0x0800); + *buffer++ = static_cast(((ch >> 12) & 0x0F) | 0xE0); + *buffer++ = static_cast(((ch >> 6) & 0x3F) | 0x80); + *buffer++ = static_cast((ch & 0x3F) | 0x80); +} - for (unsigned i = 0; i < length; ++i) { - UChar ch = characters[i]; - characterBuffer[i] = ch > 0xff ? '?' : ch; - } +CString String::utf8(UTF8ConversionMode mode) const { + unsigned length = this->length(); - return result; -} + if (!length) + return CString("", 0); -// Helper to write a three-byte UTF-8 code point to the buffer, caller must check room is available. -static inline void putUTF8Triple(char*& buffer, UChar ch) -{ - ASSERT(ch >= 0x0800); - *buffer++ = static_cast(((ch >> 12) & 0x0F) | 0xE0); - *buffer++ = static_cast(((ch >> 6) & 0x3F) | 0x80); - *buffer++ = static_cast((ch & 0x3F) | 0x80); -} - -CString String::utf8(UTF8ConversionMode mode) const -{ - unsigned length = this->length(); - - if (!length) - return CString("", 0); - - // Allocate a buffer big enough to hold all the characters - // (an individual UTF-16 UChar can only expand to 3 UTF-8 bytes). - // Optimization ideas, if we find this function is hot: - // * We could speculatively create a CStringBuffer to contain 'length' - // characters, and resize if necessary (i.e. if the buffer contains - // non-ascii characters). (Alternatively, scan the buffer first for - // ascii characters, so we know this will be sufficient). - // * We could allocate a CStringBuffer with an appropriate size to - // have a good chance of being able to write the string into the - // buffer without reallocing (say, 1.5 x length). - if (length > numeric_limits::max() / 3) - return CString(); - Vector bufferVector(length * 3); + // Allocate a buffer big enough to hold all the characters + // (an individual UTF-16 UChar can only expand to 3 UTF-8 bytes). + // Optimization ideas, if we find this function is hot: + // * We could speculatively create a CStringBuffer to contain 'length' + // characters, and resize if necessary (i.e. if the buffer contains + // non-ascii characters). (Alternatively, scan the buffer first for + // ascii characters, so we know this will be sufficient). + // * We could allocate a CStringBuffer with an appropriate size to + // have a good chance of being able to write the string into the + // buffer without reallocing (say, 1.5 x length). + if (length > numeric_limits::max() / 3) + return CString(); + Vector bufferVector(length * 3); - char* buffer = bufferVector.data(); + char* buffer = bufferVector.data(); - if (is8Bit()) { - const LChar* characters = this->characters8(); + if (is8Bit()) { + const LChar* characters = this->characters8(); - ConversionResult result = convertLatin1ToUTF8(&characters, characters + length, &buffer, buffer + bufferVector.size()); - ASSERT_UNUSED(result, result != targetExhausted); // (length * 3) should be sufficient for any conversion - } else { - const UChar* characters = this->characters16(); - - if (mode == StrictUTF8ConversionReplacingUnpairedSurrogatesWithFFFD) { - const UChar* charactersEnd = characters + length; - char* bufferEnd = buffer + bufferVector.size(); - while (characters < charactersEnd) { - // Use strict conversion to detect unpaired surrogates. - ConversionResult result = convertUTF16ToUTF8(&characters, charactersEnd, &buffer, bufferEnd, true); - ASSERT(result != targetExhausted); - // Conversion fails when there is an unpaired surrogate. - // Put replacement character (U+FFFD) instead of the unpaired surrogate. - if (result != conversionOK) { - ASSERT((0xD800 <= *characters && *characters <= 0xDFFF)); - // There should be room left, since one UChar hasn't been converted. - ASSERT((buffer + 3) <= bufferEnd); - putUTF8Triple(buffer, replacementCharacter); - ++characters; - } - } - } else { - bool strict = mode == StrictUTF8Conversion; - ConversionResult result = convertUTF16ToUTF8(&characters, characters + length, &buffer, buffer + bufferVector.size(), strict); - ASSERT(result != targetExhausted); // (length * 3) should be sufficient for any conversion - - // Only produced from strict conversion. - if (result == sourceIllegal) { - ASSERT(strict); - return CString(); - } - - // Check for an unconverted high surrogate. - if (result == sourceExhausted) { - if (strict) - return CString(); - // This should be one unpaired high surrogate. Treat it the same - // was as an unpaired high surrogate would have been handled in - // the middle of a string with non-strict conversion - which is - // to say, simply encode it to UTF-8. - ASSERT((characters + 1) == (this->characters16() + length)); - ASSERT((*characters >= 0xD800) && (*characters <= 0xDBFF)); - // There should be room left, since one UChar hasn't been converted. - ASSERT((buffer + 3) <= (buffer + bufferVector.size())); - putUTF8Triple(buffer, *characters); - } + ConversionResult result = + convertLatin1ToUTF8(&characters, characters + length, &buffer, + buffer + bufferVector.size()); + ASSERT_UNUSED(result, result != targetExhausted); // (length * 3) should be + // sufficient for any + // conversion + } else { + const UChar* characters = this->characters16(); + + if (mode == StrictUTF8ConversionReplacingUnpairedSurrogatesWithFFFD) { + const UChar* charactersEnd = characters + length; + char* bufferEnd = buffer + bufferVector.size(); + while (characters < charactersEnd) { + // Use strict conversion to detect unpaired surrogates. + ConversionResult result = convertUTF16ToUTF8(&characters, charactersEnd, + &buffer, bufferEnd, true); + ASSERT(result != targetExhausted); + // Conversion fails when there is an unpaired surrogate. + // Put replacement character (U+FFFD) instead of the unpaired surrogate. + if (result != conversionOK) { + ASSERT((0xD800 <= *characters && *characters <= 0xDFFF)); + // There should be room left, since one UChar hasn't been converted. + ASSERT((buffer + 3) <= bufferEnd); + putUTF8Triple(buffer, replacementCharacter); + ++characters; } + } + } else { + bool strict = mode == StrictUTF8Conversion; + ConversionResult result = + convertUTF16ToUTF8(&characters, characters + length, &buffer, + buffer + bufferVector.size(), strict); + ASSERT(result != targetExhausted); // (length * 3) should be sufficient + // for any conversion + + // Only produced from strict conversion. + if (result == sourceIllegal) { + ASSERT(strict); + return CString(); + } + + // Check for an unconverted high surrogate. + if (result == sourceExhausted) { + if (strict) + return CString(); + // This should be one unpaired high surrogate. Treat it the same + // was as an unpaired high surrogate would have been handled in + // the middle of a string with non-strict conversion - which is + // to say, simply encode it to UTF-8. + ASSERT((characters + 1) == (this->characters16() + length)); + ASSERT((*characters >= 0xD800) && (*characters <= 0xDBFF)); + // There should be room left, since one UChar hasn't been converted. + ASSERT((buffer + 3) <= (buffer + bufferVector.size())); + putUTF8Triple(buffer, *characters); + } } + } - return CString(bufferVector.data(), buffer - bufferVector.data()); + return CString(bufferVector.data(), buffer - bufferVector.data()); } -std::string String::toUTF8() const -{ - return utf8().toStdString(); +std::string String::toUTF8() const { + return utf8().toStdString(); } -String String::make8BitFrom16BitSource(const UChar* source, size_t length) -{ - if (!length) - return emptyString(); +String String::make8BitFrom16BitSource(const UChar* source, size_t length) { + if (!length) + return emptyString(); - LChar* destination; - String result = String::createUninitialized(length, destination); + LChar* destination; + String result = String::createUninitialized(length, destination); - copyLCharsFromUCharSource(destination, source, length); + copyLCharsFromUCharSource(destination, source, length); - return result; + return result; } -String String::make16BitFrom8BitSource(const LChar* source, size_t length) -{ - if (!length) - return emptyString(); +String String::make16BitFrom8BitSource(const LChar* source, size_t length) { + if (!length) + return emptyString(); - UChar* destination; - String result = String::createUninitialized(length, destination); + UChar* destination; + String result = String::createUninitialized(length, destination); - StringImpl::copyChars(destination, source, length); + StringImpl::copyChars(destination, source, length); - return result; + return result; } -String String::fromUTF8(const LChar* stringStart, size_t length) -{ - RELEASE_ASSERT(length <= numeric_limits::max()); +String String::fromUTF8(const LChar* stringStart, size_t length) { + RELEASE_ASSERT(length <= numeric_limits::max()); - if (!stringStart) - return String(); + if (!stringStart) + return String(); - if (!length) - return emptyString(); + if (!length) + return emptyString(); - if (charactersAreAllASCII(stringStart, length)) - return StringImpl::create(stringStart, length); + if (charactersAreAllASCII(stringStart, length)) + return StringImpl::create(stringStart, length); - Vector buffer(length); - UChar* bufferStart = buffer.data(); + Vector buffer(length); + UChar* bufferStart = buffer.data(); - UChar* bufferCurrent = bufferStart; - const char* stringCurrent = reinterpret_cast(stringStart); - if (convertUTF8ToUTF16(&stringCurrent, reinterpret_cast(stringStart + length), &bufferCurrent, bufferCurrent + buffer.size()) != conversionOK) - return String(); + UChar* bufferCurrent = bufferStart; + const char* stringCurrent = reinterpret_cast(stringStart); + if (convertUTF8ToUTF16( + &stringCurrent, reinterpret_cast(stringStart + length), + &bufferCurrent, bufferCurrent + buffer.size()) != conversionOK) + return String(); - unsigned utf16Length = bufferCurrent - bufferStart; - ASSERT(utf16Length < length); - return StringImpl::create(bufferStart, utf16Length); + unsigned utf16Length = bufferCurrent - bufferStart; + ASSERT(utf16Length < length); + return StringImpl::create(bufferStart, utf16Length); } -String String::fromUTF8(const LChar* string) -{ - if (!string) - return String(); - return fromUTF8(string, strlen(reinterpret_cast(string))); +String String::fromUTF8(const LChar* string) { + if (!string) + return String(); + return fromUTF8(string, strlen(reinterpret_cast(string))); } -String String::fromUTF8(const CString& s) -{ - return fromUTF8(s.data()); +String String::fromUTF8(const CString& s) { + return fromUTF8(s.data()); } -String String::fromUTF8WithLatin1Fallback(const LChar* string, size_t size) -{ - String utf8 = fromUTF8(string, size); - if (!utf8) - return String(string, size); - return utf8; +String String::fromUTF8WithLatin1Fallback(const LChar* string, size_t size) { + String utf8 = fromUTF8(string, size); + if (!utf8) + return String(string, size); + return utf8; } // String Operations -static bool isCharacterAllowedInBase(UChar c, int base) -{ - if (c > 0x7F) - return false; - if (isASCIIDigit(c)) - return c - '0' < base; - if (isASCIIAlpha(c)) { - if (base > 36) - base = 36; - return (c >= 'a' && c < 'a' + base - 10) - || (c >= 'A' && c < 'A' + base - 10); - } +static bool isCharacterAllowedInBase(UChar c, int base) { + if (c > 0x7F) return false; + if (isASCIIDigit(c)) + return c - '0' < base; + if (isASCIIAlpha(c)) { + if (base > 36) + base = 36; + return (c >= 'a' && c < 'a' + base - 10) || + (c >= 'A' && c < 'A' + base - 10); + } + return false; } template -static inline IntegralType toIntegralType(const CharType* data, size_t length, bool* ok, int base) -{ - static const IntegralType integralMax = numeric_limits::max(); - static const bool isSigned = numeric_limits::is_signed; - const IntegralType maxMultiplier = integralMax / base; - - IntegralType value = 0; - bool isOk = false; - bool isNegative = false; - - if (!data) - goto bye; - - // skip leading whitespace - while (length && isSpaceOrNewline(*data)) { - --length; - ++data; - } +static inline IntegralType toIntegralType(const CharType* data, + size_t length, + bool* ok, + int base) { + static const IntegralType integralMax = numeric_limits::max(); + static const bool isSigned = numeric_limits::is_signed; + const IntegralType maxMultiplier = integralMax / base; + + IntegralType value = 0; + bool isOk = false; + bool isNegative = false; + + if (!data) + goto bye; + + // skip leading whitespace + while (length && isSpaceOrNewline(*data)) { + --length; + ++data; + } + + if (isSigned && length && *data == '-') { + --length; + ++data; + isNegative = true; + } else if (length && *data == '+') { + --length; + ++data; + } + + if (!length || !isCharacterAllowedInBase(*data, base)) + goto bye; + + while (length && isCharacterAllowedInBase(*data, base)) { + --length; + IntegralType digitValue; + CharType c = *data; + if (isASCIIDigit(c)) + digitValue = c - '0'; + else if (c >= 'a') + digitValue = c - 'a' + 10; + else + digitValue = c - 'A' + 10; - if (isSigned && length && *data == '-') { - --length; - ++data; - isNegative = true; - } else if (length && *data == '+') { - --length; - ++data; - } + if (value > maxMultiplier || + (value == maxMultiplier && + digitValue > (integralMax % base) + isNegative)) + goto bye; - if (!length || !isCharacterAllowedInBase(*data, base)) - goto bye; - - while (length && isCharacterAllowedInBase(*data, base)) { - --length; - IntegralType digitValue; - CharType c = *data; - if (isASCIIDigit(c)) - digitValue = c - '0'; - else if (c >= 'a') - digitValue = c - 'a' + 10; - else - digitValue = c - 'A' + 10; - - if (value > maxMultiplier || (value == maxMultiplier && digitValue > (integralMax % base) + isNegative)) - goto bye; - - value = base * value + digitValue; - ++data; - } + value = base * value + digitValue; + ++data; + } - if (isNegative) - value = -value; + if (isNegative) + value = -value; - // skip trailing space - while (length && isSpaceOrNewline(*data)) { - --length; - ++data; - } + // skip trailing space + while (length && isSpaceOrNewline(*data)) { + --length; + ++data; + } - if (!length) - isOk = true; + if (!length) + isOk = true; bye: - if (ok) - *ok = isOk; - return isOk ? value : 0; + if (ok) + *ok = isOk; + return isOk ? value : 0; } template -static unsigned lengthOfCharactersAsInteger(const CharType* data, size_t length) -{ - size_t i = 0; - - // Allow leading spaces. - for (; i != length; ++i) { - if (!isSpaceOrNewline(data[i])) - break; - } +static unsigned lengthOfCharactersAsInteger(const CharType* data, + size_t length) { + size_t i = 0; - // Allow sign. - if (i != length && (data[i] == '+' || data[i] == '-')) - ++i; + // Allow leading spaces. + for (; i != length; ++i) { + if (!isSpaceOrNewline(data[i])) + break; + } - // Allow digits. - for (; i != length; ++i) { - if (!isASCIIDigit(data[i])) - break; - } + // Allow sign. + if (i != length && (data[i] == '+' || data[i] == '-')) + ++i; - return i; + // Allow digits. + for (; i != length; ++i) { + if (!isASCIIDigit(data[i])) + break; + } + + return i; } -int charactersToIntStrict(const LChar* data, size_t length, bool* ok, int base) -{ - return toIntegralType(data, length, ok, base); +int charactersToIntStrict(const LChar* data, + size_t length, + bool* ok, + int base) { + return toIntegralType(data, length, ok, base); } -int charactersToIntStrict(const UChar* data, size_t length, bool* ok, int base) -{ - return toIntegralType(data, length, ok, base); +int charactersToIntStrict(const UChar* data, + size_t length, + bool* ok, + int base) { + return toIntegralType(data, length, ok, base); } -unsigned charactersToUIntStrict(const LChar* data, size_t length, bool* ok, int base) -{ - return toIntegralType(data, length, ok, base); +unsigned charactersToUIntStrict(const LChar* data, + size_t length, + bool* ok, + int base) { + return toIntegralType(data, length, ok, base); } -unsigned charactersToUIntStrict(const UChar* data, size_t length, bool* ok, int base) -{ - return toIntegralType(data, length, ok, base); +unsigned charactersToUIntStrict(const UChar* data, + size_t length, + bool* ok, + int base) { + return toIntegralType(data, length, ok, base); } -int64_t charactersToInt64Strict(const LChar* data, size_t length, bool* ok, int base) -{ - return toIntegralType(data, length, ok, base); +int64_t charactersToInt64Strict(const LChar* data, + size_t length, + bool* ok, + int base) { + return toIntegralType(data, length, ok, base); } -int64_t charactersToInt64Strict(const UChar* data, size_t length, bool* ok, int base) -{ - return toIntegralType(data, length, ok, base); +int64_t charactersToInt64Strict(const UChar* data, + size_t length, + bool* ok, + int base) { + return toIntegralType(data, length, ok, base); } -uint64_t charactersToUInt64Strict(const LChar* data, size_t length, bool* ok, int base) -{ - return toIntegralType(data, length, ok, base); +uint64_t charactersToUInt64Strict(const LChar* data, + size_t length, + bool* ok, + int base) { + return toIntegralType(data, length, ok, base); } -uint64_t charactersToUInt64Strict(const UChar* data, size_t length, bool* ok, int base) -{ - return toIntegralType(data, length, ok, base); +uint64_t charactersToUInt64Strict(const UChar* data, + size_t length, + bool* ok, + int base) { + return toIntegralType(data, length, ok, base); } -intptr_t charactersToIntPtrStrict(const LChar* data, size_t length, bool* ok, int base) -{ - return toIntegralType(data, length, ok, base); +intptr_t charactersToIntPtrStrict(const LChar* data, + size_t length, + bool* ok, + int base) { + return toIntegralType(data, length, ok, base); } -intptr_t charactersToIntPtrStrict(const UChar* data, size_t length, bool* ok, int base) -{ - return toIntegralType(data, length, ok, base); +intptr_t charactersToIntPtrStrict(const UChar* data, + size_t length, + bool* ok, + int base) { + return toIntegralType(data, length, ok, base); } -int charactersToInt(const LChar* data, size_t length, bool* ok) -{ - return toIntegralType(data, lengthOfCharactersAsInteger(data, length), ok, 10); +int charactersToInt(const LChar* data, size_t length, bool* ok) { + return toIntegralType( + data, lengthOfCharactersAsInteger(data, length), ok, 10); } -int charactersToInt(const UChar* data, size_t length, bool* ok) -{ - return toIntegralType(data, lengthOfCharactersAsInteger(data, length), ok, 10); +int charactersToInt(const UChar* data, size_t length, bool* ok) { + return toIntegralType( + data, lengthOfCharactersAsInteger(data, length), ok, 10); } -unsigned charactersToUInt(const LChar* data, size_t length, bool* ok) -{ - return toIntegralType(data, lengthOfCharactersAsInteger(data, length), ok, 10); +unsigned charactersToUInt(const LChar* data, size_t length, bool* ok) { + return toIntegralType( + data, lengthOfCharactersAsInteger(data, length), ok, 10); } -unsigned charactersToUInt(const UChar* data, size_t length, bool* ok) -{ - return toIntegralType(data, lengthOfCharactersAsInteger(data, length), ok, 10); +unsigned charactersToUInt(const UChar* data, size_t length, bool* ok) { + return toIntegralType( + data, lengthOfCharactersAsInteger(data, length), ok, 10); } -int64_t charactersToInt64(const LChar* data, size_t length, bool* ok) -{ - return toIntegralType(data, lengthOfCharactersAsInteger(data, length), ok, 10); +int64_t charactersToInt64(const LChar* data, size_t length, bool* ok) { + return toIntegralType( + data, lengthOfCharactersAsInteger(data, length), ok, 10); } -int64_t charactersToInt64(const UChar* data, size_t length, bool* ok) -{ - return toIntegralType(data, lengthOfCharactersAsInteger(data, length), ok, 10); +int64_t charactersToInt64(const UChar* data, size_t length, bool* ok) { + return toIntegralType( + data, lengthOfCharactersAsInteger(data, length), ok, 10); } -uint64_t charactersToUInt64(const LChar* data, size_t length, bool* ok) -{ - return toIntegralType(data, lengthOfCharactersAsInteger(data, length), ok, 10); +uint64_t charactersToUInt64(const LChar* data, size_t length, bool* ok) { + return toIntegralType( + data, lengthOfCharactersAsInteger(data, length), ok, 10); } -uint64_t charactersToUInt64(const UChar* data, size_t length, bool* ok) -{ - return toIntegralType(data, lengthOfCharactersAsInteger(data, length), ok, 10); +uint64_t charactersToUInt64(const UChar* data, size_t length, bool* ok) { + return toIntegralType( + data, lengthOfCharactersAsInteger(data, length), ok, 10); } -intptr_t charactersToIntPtr(const LChar* data, size_t length, bool* ok) -{ - return toIntegralType(data, lengthOfCharactersAsInteger(data, length), ok, 10); +intptr_t charactersToIntPtr(const LChar* data, size_t length, bool* ok) { + return toIntegralType( + data, lengthOfCharactersAsInteger(data, length), ok, 10); } -intptr_t charactersToIntPtr(const UChar* data, size_t length, bool* ok) -{ - return toIntegralType(data, lengthOfCharactersAsInteger(data, length), ok, 10); +intptr_t charactersToIntPtr(const UChar* data, size_t length, bool* ok) { + return toIntegralType( + data, lengthOfCharactersAsInteger(data, length), ok, 10); } enum TrailingJunkPolicy { DisallowTrailingJunk, AllowTrailingJunk }; template -static inline double toDoubleType(const CharType* data, size_t length, bool* ok, size_t& parsedLength) -{ - size_t leadingSpacesLength = 0; - while (leadingSpacesLength < length && isASCIISpace(data[leadingSpacesLength])) - ++leadingSpacesLength; - - double number = parseDouble(data + leadingSpacesLength, length - leadingSpacesLength, parsedLength); - if (!parsedLength) { - if (ok) - *ok = false; - return 0.0; - } - - parsedLength += leadingSpacesLength; +static inline double toDoubleType(const CharType* data, + size_t length, + bool* ok, + size_t& parsedLength) { + size_t leadingSpacesLength = 0; + while (leadingSpacesLength < length && + isASCIISpace(data[leadingSpacesLength])) + ++leadingSpacesLength; + + double number = parseDouble(data + leadingSpacesLength, + length - leadingSpacesLength, parsedLength); + if (!parsedLength) { if (ok) - *ok = policy == AllowTrailingJunk || parsedLength == length; - return number; + *ok = false; + return 0.0; + } + + parsedLength += leadingSpacesLength; + if (ok) + *ok = policy == AllowTrailingJunk || parsedLength == length; + return number; } -double charactersToDouble(const LChar* data, size_t length, bool* ok) -{ - size_t parsedLength; - return toDoubleType(data, length, ok, parsedLength); +double charactersToDouble(const LChar* data, size_t length, bool* ok) { + size_t parsedLength; + return toDoubleType(data, length, ok, + parsedLength); } -double charactersToDouble(const UChar* data, size_t length, bool* ok) -{ - size_t parsedLength; - return toDoubleType(data, length, ok, parsedLength); +double charactersToDouble(const UChar* data, size_t length, bool* ok) { + size_t parsedLength; + return toDoubleType(data, length, ok, + parsedLength); } -float charactersToFloat(const LChar* data, size_t length, bool* ok) -{ - // FIXME: This will return ok even when the string fits into a double but not a float. - size_t parsedLength; - return static_cast(toDoubleType(data, length, ok, parsedLength)); +float charactersToFloat(const LChar* data, size_t length, bool* ok) { + // FIXME: This will return ok even when the string fits into a double but not + // a float. + size_t parsedLength; + return static_cast(toDoubleType( + data, length, ok, parsedLength)); } -float charactersToFloat(const UChar* data, size_t length, bool* ok) -{ - // FIXME: This will return ok even when the string fits into a double but not a float. - size_t parsedLength; - return static_cast(toDoubleType(data, length, ok, parsedLength)); +float charactersToFloat(const UChar* data, size_t length, bool* ok) { + // FIXME: This will return ok even when the string fits into a double but not + // a float. + size_t parsedLength; + return static_cast(toDoubleType( + data, length, ok, parsedLength)); } -float charactersToFloat(const LChar* data, size_t length, size_t& parsedLength) -{ - // FIXME: This will return ok even when the string fits into a double but not a float. - return static_cast(toDoubleType(data, length, 0, parsedLength)); +float charactersToFloat(const LChar* data, + size_t length, + size_t& parsedLength) { + // FIXME: This will return ok even when the string fits into a double but not + // a float. + return static_cast( + toDoubleType(data, length, 0, parsedLength)); } -float charactersToFloat(const UChar* data, size_t length, size_t& parsedLength) -{ - // FIXME: This will return ok even when the string fits into a double but not a float. - return static_cast(toDoubleType(data, length, 0, parsedLength)); +float charactersToFloat(const UChar* data, + size_t length, + size_t& parsedLength) { + // FIXME: This will return ok even when the string fits into a double but not + // a float. + return static_cast( + toDoubleType(data, length, 0, parsedLength)); } -const String& emptyString() -{ - DEFINE_STATIC_LOCAL(String, emptyString, (StringImpl::empty())); - return emptyString; +const String& emptyString() { + DEFINE_STATIC_LOCAL(String, emptyString, (StringImpl::empty())); + return emptyString; } -} // namespace WTF +} // namespace WTF #ifndef NDEBUG // For use in the debugger @@ -1225,42 +1243,38 @@ String* string(const char*); Vector asciiDebug(StringImpl* impl); Vector asciiDebug(String& string); -void String::show() const -{ - dataLogF("%s\n", asciiDebug(impl()).data()); -} - -String* string(const char* s) -{ - // leaks memory! - return new String(s); -} - -Vector asciiDebug(StringImpl* impl) -{ - if (!impl) - return asciiDebug(String("[null]").impl()); - - Vector buffer; - for (unsigned i = 0; i < impl->length(); ++i) { - UChar ch = (*impl)[i]; - if (isASCIIPrintable(ch)) { - if (ch == '\\') - buffer.append(ch); - buffer.append(ch); - } else { - buffer.append('\\'); - buffer.append('u'); - appendUnsignedAsHexFixedSize(ch, buffer, 4); - } +void String::show() const { + dataLogF("%s\n", asciiDebug(impl()).data()); +} + +String* string(const char* s) { + // leaks memory! + return new String(s); +} + +Vector asciiDebug(StringImpl* impl) { + if (!impl) + return asciiDebug(String("[null]").impl()); + + Vector buffer; + for (unsigned i = 0; i < impl->length(); ++i) { + UChar ch = (*impl)[i]; + if (isASCIIPrintable(ch)) { + if (ch == '\\') + buffer.append(ch); + buffer.append(ch); + } else { + buffer.append('\\'); + buffer.append('u'); + appendUnsignedAsHexFixedSize(ch, buffer, 4); } - buffer.append('\0'); - return buffer; + } + buffer.append('\0'); + return buffer; } -Vector asciiDebug(String& string) -{ - return asciiDebug(string.impl()); +Vector asciiDebug(String& string) { + return asciiDebug(string.impl()); } #endif diff --git a/sky/engine/wtf/text/WTFString.h b/sky/engine/wtf/text/WTFString.h index 4c96f856d645c..47d083dec29f2 100644 --- a/sky/engine/wtf/text/WTFString.h +++ b/sky/engine/wtf/text/WTFString.h @@ -1,6 +1,7 @@ /* * (C) 1999 Lars Knoll (knoll@kde.org) - * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012, 2013 Apple Inc. + * All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -39,31 +40,86 @@ struct StringHash; // Declarations of string operations -WTF_EXPORT int charactersToIntStrict(const LChar*, size_t, bool* ok = 0, int base = 10); -WTF_EXPORT int charactersToIntStrict(const UChar*, size_t, bool* ok = 0, int base = 10); -WTF_EXPORT unsigned charactersToUIntStrict(const LChar*, size_t, bool* ok = 0, int base = 10); -WTF_EXPORT unsigned charactersToUIntStrict(const UChar*, size_t, bool* ok = 0, int base = 10); -WTF_EXPORT int64_t charactersToInt64Strict(const LChar*, size_t, bool* ok = 0, int base = 10); -WTF_EXPORT int64_t charactersToInt64Strict(const UChar*, size_t, bool* ok = 0, int base = 10); -WTF_EXPORT uint64_t charactersToUInt64Strict(const LChar*, size_t, bool* ok = 0, int base = 10); -WTF_EXPORT uint64_t charactersToUInt64Strict(const UChar*, size_t, bool* ok = 0, int base = 10); -WTF_EXPORT intptr_t charactersToIntPtrStrict(const LChar*, size_t, bool* ok = 0, int base = 10); -WTF_EXPORT intptr_t charactersToIntPtrStrict(const UChar*, size_t, bool* ok = 0, int base = 10); - -WTF_EXPORT int charactersToInt(const LChar*, size_t, bool* ok = 0); // ignores trailing garbage -WTF_EXPORT int charactersToInt(const UChar*, size_t, bool* ok = 0); // ignores trailing garbage -WTF_EXPORT unsigned charactersToUInt(const LChar*, size_t, bool* ok = 0); // ignores trailing garbage -WTF_EXPORT unsigned charactersToUInt(const UChar*, size_t, bool* ok = 0); // ignores trailing garbage -WTF_EXPORT int64_t charactersToInt64(const LChar*, size_t, bool* ok = 0); // ignores trailing garbage -WTF_EXPORT int64_t charactersToInt64(const UChar*, size_t, bool* ok = 0); // ignores trailing garbage -WTF_EXPORT uint64_t charactersToUInt64(const LChar*, size_t, bool* ok = 0); // ignores trailing garbage -WTF_EXPORT uint64_t charactersToUInt64(const UChar*, size_t, bool* ok = 0); // ignores trailing garbage -WTF_EXPORT intptr_t charactersToIntPtr(const LChar*, size_t, bool* ok = 0); // ignores trailing garbage -WTF_EXPORT intptr_t charactersToIntPtr(const UChar*, size_t, bool* ok = 0); // ignores trailing garbage - -// FIXME: Like the strict functions above, these give false for "ok" when there is trailing garbage. -// Like the non-strict functions above, these return the value when there is trailing garbage. -// It would be better if these were more consistent with the above functions instead. +WTF_EXPORT int charactersToIntStrict(const LChar*, + size_t, + bool* ok = 0, + int base = 10); +WTF_EXPORT int charactersToIntStrict(const UChar*, + size_t, + bool* ok = 0, + int base = 10); +WTF_EXPORT unsigned charactersToUIntStrict(const LChar*, + size_t, + bool* ok = 0, + int base = 10); +WTF_EXPORT unsigned charactersToUIntStrict(const UChar*, + size_t, + bool* ok = 0, + int base = 10); +WTF_EXPORT int64_t charactersToInt64Strict(const LChar*, + size_t, + bool* ok = 0, + int base = 10); +WTF_EXPORT int64_t charactersToInt64Strict(const UChar*, + size_t, + bool* ok = 0, + int base = 10); +WTF_EXPORT uint64_t charactersToUInt64Strict(const LChar*, + size_t, + bool* ok = 0, + int base = 10); +WTF_EXPORT uint64_t charactersToUInt64Strict(const UChar*, + size_t, + bool* ok = 0, + int base = 10); +WTF_EXPORT intptr_t charactersToIntPtrStrict(const LChar*, + size_t, + bool* ok = 0, + int base = 10); +WTF_EXPORT intptr_t charactersToIntPtrStrict(const UChar*, + size_t, + bool* ok = 0, + int base = 10); + +WTF_EXPORT int charactersToInt(const LChar*, + size_t, + bool* ok = 0); // ignores trailing garbage +WTF_EXPORT int charactersToInt(const UChar*, + size_t, + bool* ok = 0); // ignores trailing garbage +WTF_EXPORT unsigned charactersToUInt(const LChar*, + size_t, + bool* ok = 0); // ignores trailing garbage +WTF_EXPORT unsigned charactersToUInt(const UChar*, + size_t, + bool* ok = 0); // ignores trailing garbage +WTF_EXPORT int64_t charactersToInt64(const LChar*, + size_t, + bool* ok = 0); // ignores trailing garbage +WTF_EXPORT int64_t charactersToInt64(const UChar*, + size_t, + bool* ok = 0); // ignores trailing garbage +WTF_EXPORT uint64_t +charactersToUInt64(const LChar*, + size_t, + bool* ok = 0); // ignores trailing garbage +WTF_EXPORT uint64_t +charactersToUInt64(const UChar*, + size_t, + bool* ok = 0); // ignores trailing garbage +WTF_EXPORT intptr_t +charactersToIntPtr(const LChar*, + size_t, + bool* ok = 0); // ignores trailing garbage +WTF_EXPORT intptr_t +charactersToIntPtr(const UChar*, + size_t, + bool* ok = 0); // ignores trailing garbage + +// FIXME: Like the strict functions above, these give false for "ok" when there +// is trailing garbage. Like the non-strict functions above, these return the +// value when there is trailing garbage. It would be better if these were more +// consistent with the above functions instead. WTF_EXPORT double charactersToDouble(const LChar*, size_t, bool* ok = 0); WTF_EXPORT double charactersToDouble(const UChar*, size_t, bool* ok = 0); WTF_EXPORT float charactersToFloat(const LChar*, size_t, bool* ok = 0); @@ -71,573 +127,701 @@ WTF_EXPORT float charactersToFloat(const UChar*, size_t, bool* ok = 0); WTF_EXPORT float charactersToFloat(const LChar*, size_t, size_t& parsedLength); WTF_EXPORT float charactersToFloat(const UChar*, size_t, size_t& parsedLength); -enum TrailingZerosTruncatingPolicy { - KeepTrailingZeros, - TruncateTrailingZeros -}; +enum TrailingZerosTruncatingPolicy { KeepTrailingZeros, TruncateTrailingZeros }; enum UTF8ConversionMode { - LenientUTF8Conversion, - StrictUTF8Conversion, - StrictUTF8ConversionReplacingUnpairedSurrogatesWithFFFD + LenientUTF8Conversion, + StrictUTF8Conversion, + StrictUTF8ConversionReplacingUnpairedSurrogatesWithFFFD }; -template +template bool isAllSpecialCharacters(const CharacterType*, size_t); // You can find documentation about this class in this doc: // https://docs.google.com/document/d/1kOCUlJdh2WJMJGDf-WoEQhmnjKLaOYRbiHz5TiGJl14/edit?usp=sharing class WTF_EXPORT String { -public: - // Construct a null string, distinguishable from an empty string. - String() { } - - // Construct a string with UTF-16 data. - String(const UChar* characters, unsigned length); - - // Construct a string by copying the contents of a vector. - // This method will never create a null string. Vectors with size() == 0 - // will return the empty string. - // NOTE: This is different from String(vector.data(), vector.size()) - // which will sometimes return a null string when vector.data() is null - // which can only occur for vectors without inline capacity. - // See: https://bugs.webkit.org/show_bug.cgi?id=109792 - template - explicit String(const Vector&); - - // Construct a string with UTF-16 data, from a null-terminated source. - String(const UChar*); - - // Construct a string with latin1 data. - String(const LChar* characters, unsigned length); - String(const char* characters, unsigned length); - - // Construct a string with latin1 data, from a null-terminated source. - String(const LChar* characters); - String(const char* characters); - - // Construct a string referencing an existing StringImpl. - String(StringImpl* impl) : m_impl(impl) { } - String(PassRefPtr impl) : m_impl(impl) { } - - void swap(String& o) { m_impl.swap(o.m_impl); } - - template - static String adopt(StringBuffer& buffer) - { - if (!buffer.length()) - return StringImpl::empty(); - return String(buffer.release()); - } - - bool isNull() const { return !m_impl; } - bool isEmpty() const { return !m_impl || !m_impl->length(); } - - StringImpl* impl() const { return m_impl.get(); } - PassRefPtr releaseImpl() { return m_impl.release(); } - - unsigned length() const - { - if (!m_impl) - return 0; - return m_impl->length(); - } - - const LChar* characters8() const - { - if (!m_impl) - return 0; - ASSERT(m_impl->is8Bit()); - return m_impl->characters8(); - } - - const UChar* characters16() const - { - if (!m_impl) - return 0; - ASSERT(!m_impl->is8Bit()); - return m_impl->characters16(); - } - - // Return characters8() or characters16() depending on CharacterType. - template - inline const CharacterType* getCharacters() const; - - bool is8Bit() const { return m_impl->is8Bit(); } - - unsigned sizeInBytes() const - { - if (!m_impl) - return 0; - return m_impl->length() * (is8Bit() ? sizeof(LChar) : sizeof(UChar)); - } - - CString ascii() const; - CString latin1() const; - CString utf8(UTF8ConversionMode = LenientUTF8Conversion) const; - - // We should replace CString with std::string. - std::string toUTF8() const; - - UChar operator[](unsigned index) const - { - if (!m_impl || index >= m_impl->length()) - return 0; - return (*m_impl)[index]; - } - - static String number(int); - static String number(unsigned); - static String number(long); - static String number(unsigned long); - static String number(long long); - static String number(unsigned long long); - - static String number(double, unsigned precision = 6, TrailingZerosTruncatingPolicy = TruncateTrailingZeros); - - // Number to String conversion following the ECMAScript definition. - static String numberToStringECMAScript(double); - static String numberToStringFixedWidth(double, unsigned decimalPlaces); - - // Find a single character or string, also with match function & latin1 forms. - size_t find(UChar c, unsigned start = 0) const - { return m_impl ? m_impl->find(c, start) : kNotFound; } - - size_t find(const String& str) const - { return m_impl ? m_impl->find(str.impl()) : kNotFound; } - size_t find(const String& str, unsigned start) const - { return m_impl ? m_impl->find(str.impl(), start) : kNotFound; } - - size_t find(CharacterMatchFunctionPtr matchFunction, unsigned start = 0) const - { return m_impl ? m_impl->find(matchFunction, start) : kNotFound; } - size_t find(const LChar* str, unsigned start = 0) const - { return m_impl ? m_impl->find(str, start) : kNotFound; } - - size_t findNextLineStart(unsigned start = 0) const - { return m_impl ? m_impl->findNextLineStart(start) : kNotFound; } - - // Find the last instance of a single character or string. - size_t reverseFind(UChar c, unsigned start = UINT_MAX) const - { return m_impl ? m_impl->reverseFind(c, start) : kNotFound; } - size_t reverseFind(const String& str, unsigned start = UINT_MAX) const - { return m_impl ? m_impl->reverseFind(str.impl(), start) : kNotFound; } - - // Case insensitive string matching. - size_t findIgnoringCase(const LChar* str, unsigned start = 0) const - { return m_impl ? m_impl->findIgnoringCase(str, start) : kNotFound; } - size_t findIgnoringCase(const String& str, unsigned start = 0) const - { return m_impl ? m_impl->findIgnoringCase(str.impl(), start) : kNotFound; } - size_t reverseFindIgnoringCase(const String& str, unsigned start = UINT_MAX) const - { return m_impl ? m_impl->reverseFindIgnoringCase(str.impl(), start) : kNotFound; } - - // Wrappers for find & reverseFind adding dynamic sensitivity check. - size_t find(const LChar* str, unsigned start, bool caseSensitive) const - { return caseSensitive ? find(str, start) : findIgnoringCase(str, start); } - size_t find(const String& str, unsigned start, bool caseSensitive) const - { return caseSensitive ? find(str, start) : findIgnoringCase(str, start); } - size_t reverseFind(const String& str, unsigned start, bool caseSensitive) const - { return caseSensitive ? reverseFind(str, start) : reverseFindIgnoringCase(str, start); } - - Vector charactersWithNullTermination() const; - unsigned copyTo(UChar* buffer, unsigned pos, unsigned maxLength) const; - - template - void appendTo(Vector&, unsigned pos = 0, unsigned len = UINT_MAX) const; - - template - void appendTo(BufferType&, unsigned pos = 0, unsigned len = UINT_MAX) const; - - template - void prependTo(Vector&, unsigned pos = 0, unsigned len = UINT_MAX) const; - - UChar32 characterStartingAt(unsigned) const; - - bool contains(UChar c) const { return find(c) != kNotFound; } - bool contains(const LChar* str, bool caseSensitive = true) const { return find(str, 0, caseSensitive) != kNotFound; } - bool contains(const String& str, bool caseSensitive = true) const { return find(str, 0, caseSensitive) != kNotFound; } - - bool startsWith(const String& s, bool caseSensitive = true) const - { return m_impl ? m_impl->startsWith(s.impl(), caseSensitive) : s.isEmpty(); } - bool startsWith(UChar character) const - { return m_impl ? m_impl->startsWith(character) : false; } - template - bool startsWith(const char (&prefix)[matchLength], bool caseSensitive = true) const - { return m_impl ? m_impl->startsWith(prefix, caseSensitive) : !matchLength; } - - bool endsWith(const String& s, bool caseSensitive = true) const - { return m_impl ? m_impl->endsWith(s.impl(), caseSensitive) : s.isEmpty(); } - bool endsWith(UChar character) const - { return m_impl ? m_impl->endsWith(character) : false; } - template - bool endsWith(const char (&prefix)[matchLength], bool caseSensitive = true) const - { return m_impl ? m_impl->endsWith(prefix, caseSensitive) : !matchLength; } - - void append(const String&); - void append(LChar); - void append(char c) { append(static_cast(c)); } - void append(UChar); - void append(const LChar*, unsigned length); - void append(const char* charactersToAppend, unsigned length) { append(reinterpret_cast(charactersToAppend), length); } - void append(const UChar*, unsigned length); - void insert(const String&, unsigned pos); - void insert(const LChar*, unsigned length, unsigned pos); - void insert(const UChar*, unsigned length, unsigned pos); - - String& replace(UChar a, UChar b) { if (m_impl) m_impl = m_impl->replace(a, b); return *this; } - String& replace(UChar a, const String& b) { if (m_impl) m_impl = m_impl->replace(a, b.impl()); return *this; } - String& replace(const String& a, const String& b) { if (m_impl) m_impl = m_impl->replace(a.impl(), b.impl()); return *this; } - String& replace(unsigned index, unsigned len, const String& b) { if (m_impl) m_impl = m_impl->replace(index, len, b.impl()); return *this; } - - template - ALWAYS_INLINE String& replaceWithLiteral(UChar a, const char (&characters)[charactersCount]) - { - if (m_impl) - m_impl = m_impl->replace(a, characters, charactersCount - 1); - - return *this; - } - - void fill(UChar c) { if (m_impl) m_impl = m_impl->fill(c); } - - void ensure16Bit(); - - void truncate(unsigned len); - void remove(unsigned pos, int len = 1); - - String substring(unsigned pos, unsigned len = UINT_MAX) const; - String left(unsigned len) const { return substring(0, len); } - String right(unsigned len) const { return substring(length() - len, len); } - - StringView createView() const { return StringView(impl()); } - StringView createView(unsigned offset, unsigned length) const { return StringView(impl(), offset, length); } - - // Returns a lowercase/uppercase version of the string - String lower() const; - String upper() const; - - String lower(const AtomicString& localeIdentifier) const; - String upper(const AtomicString& localeIdentifier) const; - - String stripWhiteSpace() const; - String stripWhiteSpace(IsWhiteSpaceFunctionPtr) const; - String simplifyWhiteSpace(StripBehavior stripBehavior = StripExtraWhiteSpace) const; - String simplifyWhiteSpace(IsWhiteSpaceFunctionPtr, StripBehavior stripBehavior = StripExtraWhiteSpace) const; - - String removeCharacters(CharacterMatchFunctionPtr) const; - template bool isAllSpecialCharacters() const; - - // Return the string with case folded for case insensitive comparison. - String foldCase() const; - - static String format(const char *, ...) WTF_ATTRIBUTE_PRINTF(1, 2); - - // Returns an uninitialized string. The characters needs to be written - // into the buffer returned in data before the returned string is used. - // Failure to do this will have unpredictable results. - static String createUninitialized(unsigned length, UChar*& data) { return StringImpl::createUninitialized(length, data); } - static String createUninitialized(unsigned length, LChar*& data) { return StringImpl::createUninitialized(length, data); } - - void split(const String& separator, bool allowEmptyEntries, Vector& result) const; - void split(const String& separator, Vector& result) const - { - split(separator, false, result); - } - void split(UChar separator, bool allowEmptyEntries, Vector& result) const; - void split(UChar separator, Vector& result) const - { - split(separator, false, result); - } - - int toIntStrict(bool* ok = 0, int base = 10) const; - unsigned toUIntStrict(bool* ok = 0, int base = 10) const; - int64_t toInt64Strict(bool* ok = 0, int base = 10) const; - uint64_t toUInt64Strict(bool* ok = 0, int base = 10) const; - intptr_t toIntPtrStrict(bool* ok = 0, int base = 10) const; - - int toInt(bool* ok = 0) const; - unsigned toUInt(bool* ok = 0) const; - int64_t toInt64(bool* ok = 0) const; - uint64_t toUInt64(bool* ok = 0) const; - intptr_t toIntPtr(bool* ok = 0) const; - - // FIXME: Like the strict functions above, these give false for "ok" when there is trailing garbage. - // Like the non-strict functions above, these return the value when there is trailing garbage. - // It would be better if these were more consistent with the above functions instead. - double toDouble(bool* ok = 0) const; - float toFloat(bool* ok = 0) const; - - bool percentage(int& percentage) const; - - String isolatedCopy() const; - bool isSafeToSendToAnotherThread() const; + public: + // Construct a null string, distinguishable from an empty string. + String() {} + + // Construct a string with UTF-16 data. + String(const UChar* characters, unsigned length); + + // Construct a string by copying the contents of a vector. + // This method will never create a null string. Vectors with size() == 0 + // will return the empty string. + // NOTE: This is different from String(vector.data(), vector.size()) + // which will sometimes return a null string when vector.data() is null + // which can only occur for vectors without inline capacity. + // See: https://bugs.webkit.org/show_bug.cgi?id=109792 + template + explicit String(const Vector&); + + // Construct a string with UTF-16 data, from a null-terminated source. + String(const UChar*); + + // Construct a string with latin1 data. + String(const LChar* characters, unsigned length); + String(const char* characters, unsigned length); + + // Construct a string with latin1 data, from a null-terminated source. + String(const LChar* characters); + String(const char* characters); + + // Construct a string referencing an existing StringImpl. + String(StringImpl* impl) : m_impl(impl) {} + String(PassRefPtr impl) : m_impl(impl) {} + + void swap(String& o) { m_impl.swap(o.m_impl); } + + template + static String adopt(StringBuffer& buffer) { + if (!buffer.length()) + return StringImpl::empty(); + return String(buffer.release()); + } + + bool isNull() const { return !m_impl; } + bool isEmpty() const { return !m_impl || !m_impl->length(); } + + StringImpl* impl() const { return m_impl.get(); } + PassRefPtr releaseImpl() { return m_impl.release(); } + + unsigned length() const { + if (!m_impl) + return 0; + return m_impl->length(); + } + + const LChar* characters8() const { + if (!m_impl) + return 0; + ASSERT(m_impl->is8Bit()); + return m_impl->characters8(); + } + + const UChar* characters16() const { + if (!m_impl) + return 0; + ASSERT(!m_impl->is8Bit()); + return m_impl->characters16(); + } + + // Return characters8() or characters16() depending on CharacterType. + template + inline const CharacterType* getCharacters() const; + + bool is8Bit() const { return m_impl->is8Bit(); } + + unsigned sizeInBytes() const { + if (!m_impl) + return 0; + return m_impl->length() * (is8Bit() ? sizeof(LChar) : sizeof(UChar)); + } + + CString ascii() const; + CString latin1() const; + CString utf8(UTF8ConversionMode = LenientUTF8Conversion) const; + + // We should replace CString with std::string. + std::string toUTF8() const; + + UChar operator[](unsigned index) const { + if (!m_impl || index >= m_impl->length()) + return 0; + return (*m_impl)[index]; + } + + static String number(int); + static String number(unsigned); + static String number(long); + static String number(unsigned long); + static String number(long long); + static String number(unsigned long long); + + static String number(double, + unsigned precision = 6, + TrailingZerosTruncatingPolicy = TruncateTrailingZeros); + + // Number to String conversion following the ECMAScript definition. + static String numberToStringECMAScript(double); + static String numberToStringFixedWidth(double, unsigned decimalPlaces); + + // Find a single character or string, also with match function & latin1 forms. + size_t find(UChar c, unsigned start = 0) const { + return m_impl ? m_impl->find(c, start) : kNotFound; + } + + size_t find(const String& str) const { + return m_impl ? m_impl->find(str.impl()) : kNotFound; + } + size_t find(const String& str, unsigned start) const { + return m_impl ? m_impl->find(str.impl(), start) : kNotFound; + } + + size_t find(CharacterMatchFunctionPtr matchFunction, + unsigned start = 0) const { + return m_impl ? m_impl->find(matchFunction, start) : kNotFound; + } + size_t find(const LChar* str, unsigned start = 0) const { + return m_impl ? m_impl->find(str, start) : kNotFound; + } + + size_t findNextLineStart(unsigned start = 0) const { + return m_impl ? m_impl->findNextLineStart(start) : kNotFound; + } + + // Find the last instance of a single character or string. + size_t reverseFind(UChar c, unsigned start = UINT_MAX) const { + return m_impl ? m_impl->reverseFind(c, start) : kNotFound; + } + size_t reverseFind(const String& str, unsigned start = UINT_MAX) const { + return m_impl ? m_impl->reverseFind(str.impl(), start) : kNotFound; + } + + // Case insensitive string matching. + size_t findIgnoringCase(const LChar* str, unsigned start = 0) const { + return m_impl ? m_impl->findIgnoringCase(str, start) : kNotFound; + } + size_t findIgnoringCase(const String& str, unsigned start = 0) const { + return m_impl ? m_impl->findIgnoringCase(str.impl(), start) : kNotFound; + } + size_t reverseFindIgnoringCase(const String& str, + unsigned start = UINT_MAX) const { + return m_impl ? m_impl->reverseFindIgnoringCase(str.impl(), start) + : kNotFound; + } + + // Wrappers for find & reverseFind adding dynamic sensitivity check. + size_t find(const LChar* str, unsigned start, bool caseSensitive) const { + return caseSensitive ? find(str, start) : findIgnoringCase(str, start); + } + size_t find(const String& str, unsigned start, bool caseSensitive) const { + return caseSensitive ? find(str, start) : findIgnoringCase(str, start); + } + size_t reverseFind(const String& str, + unsigned start, + bool caseSensitive) const { + return caseSensitive ? reverseFind(str, start) + : reverseFindIgnoringCase(str, start); + } + + Vector charactersWithNullTermination() const; + unsigned copyTo(UChar* buffer, unsigned pos, unsigned maxLength) const; + + template + void appendTo(Vector&, + unsigned pos = 0, + unsigned len = UINT_MAX) const; + + template + void appendTo(BufferType&, unsigned pos = 0, unsigned len = UINT_MAX) const; + + template + void prependTo(Vector&, + unsigned pos = 0, + unsigned len = UINT_MAX) const; + + UChar32 characterStartingAt(unsigned) const; + + bool contains(UChar c) const { return find(c) != kNotFound; } + bool contains(const LChar* str, bool caseSensitive = true) const { + return find(str, 0, caseSensitive) != kNotFound; + } + bool contains(const String& str, bool caseSensitive = true) const { + return find(str, 0, caseSensitive) != kNotFound; + } + + bool startsWith(const String& s, bool caseSensitive = true) const { + return m_impl ? m_impl->startsWith(s.impl(), caseSensitive) : s.isEmpty(); + } + bool startsWith(UChar character) const { + return m_impl ? m_impl->startsWith(character) : false; + } + template + bool startsWith(const char (&prefix)[matchLength], + bool caseSensitive = true) const { + return m_impl ? m_impl->startsWith(prefix, caseSensitive) + : !matchLength; + } + + bool endsWith(const String& s, bool caseSensitive = true) const { + return m_impl ? m_impl->endsWith(s.impl(), caseSensitive) : s.isEmpty(); + } + bool endsWith(UChar character) const { + return m_impl ? m_impl->endsWith(character) : false; + } + template + bool endsWith(const char (&prefix)[matchLength], + bool caseSensitive = true) const { + return m_impl ? m_impl->endsWith(prefix, caseSensitive) + : !matchLength; + } + + void append(const String&); + void append(LChar); + void append(char c) { append(static_cast(c)); } + void append(UChar); + void append(const LChar*, unsigned length); + void append(const char* charactersToAppend, unsigned length) { + append(reinterpret_cast(charactersToAppend), length); + } + void append(const UChar*, unsigned length); + void insert(const String&, unsigned pos); + void insert(const LChar*, unsigned length, unsigned pos); + void insert(const UChar*, unsigned length, unsigned pos); + + String& replace(UChar a, UChar b) { + if (m_impl) + m_impl = m_impl->replace(a, b); + return *this; + } + String& replace(UChar a, const String& b) { + if (m_impl) + m_impl = m_impl->replace(a, b.impl()); + return *this; + } + String& replace(const String& a, const String& b) { + if (m_impl) + m_impl = m_impl->replace(a.impl(), b.impl()); + return *this; + } + String& replace(unsigned index, unsigned len, const String& b) { + if (m_impl) + m_impl = m_impl->replace(index, len, b.impl()); + return *this; + } + + template + ALWAYS_INLINE String& replaceWithLiteral( + UChar a, + const char (&characters)[charactersCount]) { + if (m_impl) + m_impl = m_impl->replace(a, characters, charactersCount - 1); + + return *this; + } + + void fill(UChar c) { + if (m_impl) + m_impl = m_impl->fill(c); + } + + void ensure16Bit(); + + void truncate(unsigned len); + void remove(unsigned pos, int len = 1); + + String substring(unsigned pos, unsigned len = UINT_MAX) const; + String left(unsigned len) const { return substring(0, len); } + String right(unsigned len) const { return substring(length() - len, len); } + + StringView createView() const { return StringView(impl()); } + StringView createView(unsigned offset, unsigned length) const { + return StringView(impl(), offset, length); + } + + // Returns a lowercase/uppercase version of the string + String lower() const; + String upper() const; + + String lower(const AtomicString& localeIdentifier) const; + String upper(const AtomicString& localeIdentifier) const; + + String stripWhiteSpace() const; + String stripWhiteSpace(IsWhiteSpaceFunctionPtr) const; + String simplifyWhiteSpace( + StripBehavior stripBehavior = StripExtraWhiteSpace) const; + String simplifyWhiteSpace( + IsWhiteSpaceFunctionPtr, + StripBehavior stripBehavior = StripExtraWhiteSpace) const; + + String removeCharacters(CharacterMatchFunctionPtr) const; + template + bool isAllSpecialCharacters() const; + + // Return the string with case folded for case insensitive comparison. + String foldCase() const; + + static String format(const char*, ...) WTF_ATTRIBUTE_PRINTF(1, 2); + + // Returns an uninitialized string. The characters needs to be written + // into the buffer returned in data before the returned string is used. + // Failure to do this will have unpredictable results. + static String createUninitialized(unsigned length, UChar*& data) { + return StringImpl::createUninitialized(length, data); + } + static String createUninitialized(unsigned length, LChar*& data) { + return StringImpl::createUninitialized(length, data); + } + + void split(const String& separator, + bool allowEmptyEntries, + Vector& result) const; + void split(const String& separator, Vector& result) const { + split(separator, false, result); + } + void split(UChar separator, + bool allowEmptyEntries, + Vector& result) const; + void split(UChar separator, Vector& result) const { + split(separator, false, result); + } + + int toIntStrict(bool* ok = 0, int base = 10) const; + unsigned toUIntStrict(bool* ok = 0, int base = 10) const; + int64_t toInt64Strict(bool* ok = 0, int base = 10) const; + uint64_t toUInt64Strict(bool* ok = 0, int base = 10) const; + intptr_t toIntPtrStrict(bool* ok = 0, int base = 10) const; + + int toInt(bool* ok = 0) const; + unsigned toUInt(bool* ok = 0) const; + int64_t toInt64(bool* ok = 0) const; + uint64_t toUInt64(bool* ok = 0) const; + intptr_t toIntPtr(bool* ok = 0) const; + + // FIXME: Like the strict functions above, these give false for "ok" when + // there is trailing garbage. Like the non-strict functions above, these + // return the value when there is trailing garbage. It would be better if + // these were more consistent with the above functions instead. + double toDouble(bool* ok = 0) const; + float toFloat(bool* ok = 0) const; + + bool percentage(int& percentage) const; + + String isolatedCopy() const; + bool isSafeToSendToAnotherThread() const; #if USE(CF) - String(CFStringRef); - RetainPtr createCFString() const; + String(CFStringRef); + RetainPtr createCFString() const; #endif - static String make8BitFrom16BitSource(const UChar*, size_t); - template - static String make8BitFrom16BitSource(const Vector& buffer) - { - return make8BitFrom16BitSource(buffer.data(), buffer.size()); - } - - static String make16BitFrom8BitSource(const LChar*, size_t); - - // String::fromUTF8 will return a null string if - // the input data contains invalid UTF-8 sequences. - static String fromUTF8(const LChar*, size_t); - static String fromUTF8(const LChar*); - static String fromUTF8(const char* s, size_t length) { return fromUTF8(reinterpret_cast(s), length); }; - static String fromUTF8(const char* s) { return fromUTF8(reinterpret_cast(s)); }; - static String fromUTF8(const CString&); - static String fromUTF8(const std::string& s) { return fromUTF8(s.data(), s.size()); } - - // Tries to convert the passed in string to UTF-8, but will fall back to Latin-1 if the string is not valid UTF-8. - static String fromUTF8WithLatin1Fallback(const LChar*, size_t); - static String fromUTF8WithLatin1Fallback(const char* s, size_t length) { return fromUTF8WithLatin1Fallback(reinterpret_cast(s), length); }; - - bool containsOnlyASCII() const; - bool containsOnlyLatin1() const; - bool containsOnlyWhitespace() const { return !m_impl || m_impl->containsOnlyWhitespace(); } - - // Hash table deleted values, which are only constructed and never copied or destroyed. - String(WTF::HashTableDeletedValueType) : m_impl(WTF::HashTableDeletedValue) { } - bool isHashTableDeletedValue() const { return m_impl.isHashTableDeletedValue(); } + static String make8BitFrom16BitSource(const UChar*, size_t); + template + static String make8BitFrom16BitSource( + const Vector& buffer) { + return make8BitFrom16BitSource(buffer.data(), buffer.size()); + } + + static String make16BitFrom8BitSource(const LChar*, size_t); + + // String::fromUTF8 will return a null string if + // the input data contains invalid UTF-8 sequences. + static String fromUTF8(const LChar*, size_t); + static String fromUTF8(const LChar*); + static String fromUTF8(const char* s, size_t length) { + return fromUTF8(reinterpret_cast(s), length); + }; + static String fromUTF8(const char* s) { + return fromUTF8(reinterpret_cast(s)); + }; + static String fromUTF8(const CString&); + static String fromUTF8(const std::string& s) { + return fromUTF8(s.data(), s.size()); + } + + // Tries to convert the passed in string to UTF-8, but will fall back to + // Latin-1 if the string is not valid UTF-8. + static String fromUTF8WithLatin1Fallback(const LChar*, size_t); + static String fromUTF8WithLatin1Fallback(const char* s, size_t length) { + return fromUTF8WithLatin1Fallback(reinterpret_cast(s), + length); + }; + + bool containsOnlyASCII() const; + bool containsOnlyLatin1() const; + bool containsOnlyWhitespace() const { + return !m_impl || m_impl->containsOnlyWhitespace(); + } + + // Hash table deleted values, which are only constructed and never copied or + // destroyed. + String(WTF::HashTableDeletedValueType) : m_impl(WTF::HashTableDeletedValue) {} + bool isHashTableDeletedValue() const { + return m_impl.isHashTableDeletedValue(); + } #ifndef NDEBUG - void show() const; + void show() const; #endif - // Workaround for a compiler bug. Use operator[] instead. - UChar characterAt(unsigned index) const - { - if (!m_impl || index >= m_impl->length()) - return 0; - return (*m_impl)[index]; - } + // Workaround for a compiler bug. Use operator[] instead. + UChar characterAt(unsigned index) const { + if (!m_impl || index >= m_impl->length()) + return 0; + return (*m_impl)[index]; + } -private: - typedef struct ImplicitConversionFromWTFStringToBoolDisallowed* (String::*UnspecifiedBoolType); - operator UnspecifiedBoolType() const; + private: + typedef struct ImplicitConversionFromWTFStringToBoolDisallowed*( + String::*UnspecifiedBoolType); + operator UnspecifiedBoolType() const; - template - void removeInternal(const CharacterType*, unsigned, int); + template + void removeInternal(const CharacterType*, unsigned, int); - template - void appendInternal(CharacterType); + template + void appendInternal(CharacterType); - RefPtr m_impl; + RefPtr m_impl; }; -inline bool operator==(const String& a, const String& b) { return equal(a.impl(), b.impl()); } -inline bool operator==(const String& a, const LChar* b) { return equal(a.impl(), b); } -inline bool operator==(const String& a, const char* b) { return equal(a.impl(), reinterpret_cast(b)); } -inline bool operator==(const LChar* a, const String& b) { return equal(a, b.impl()); } -inline bool operator==(const char* a, const String& b) { return equal(reinterpret_cast(a), b.impl()); } -template -inline bool operator==(const Vector& a, const String& b) { return equal(b.impl(), a.data(), a.size()); } -template -inline bool operator==(const String& a, const Vector& b) { return b == a; } - +inline bool operator==(const String& a, const String& b) { + return equal(a.impl(), b.impl()); +} +inline bool operator==(const String& a, const LChar* b) { + return equal(a.impl(), b); +} +inline bool operator==(const String& a, const char* b) { + return equal(a.impl(), reinterpret_cast(b)); +} +inline bool operator==(const LChar* a, const String& b) { + return equal(a, b.impl()); +} +inline bool operator==(const char* a, const String& b) { + return equal(reinterpret_cast(a), b.impl()); +} +template +inline bool operator==(const Vector& a, const String& b) { + return equal(b.impl(), a.data(), a.size()); +} +template +inline bool operator==(const String& a, const Vector& b) { + return b == a; +} -inline bool operator!=(const String& a, const String& b) { return !equal(a.impl(), b.impl()); } -inline bool operator!=(const String& a, const LChar* b) { return !equal(a.impl(), b); } -inline bool operator!=(const String& a, const char* b) { return !equal(a.impl(), reinterpret_cast(b)); } -inline bool operator!=(const LChar* a, const String& b) { return !equal(a, b.impl()); } -inline bool operator!=(const char* a, const String& b) { return !equal(reinterpret_cast(a), b.impl()); } -template -inline bool operator!=(const Vector& a, const String& b) { return !(a == b); } -template -inline bool operator!=(const String& a, const Vector& b) { return b != a; } +inline bool operator!=(const String& a, const String& b) { + return !equal(a.impl(), b.impl()); +} +inline bool operator!=(const String& a, const LChar* b) { + return !equal(a.impl(), b); +} +inline bool operator!=(const String& a, const char* b) { + return !equal(a.impl(), reinterpret_cast(b)); +} +inline bool operator!=(const LChar* a, const String& b) { + return !equal(a, b.impl()); +} +inline bool operator!=(const char* a, const String& b) { + return !equal(reinterpret_cast(a), b.impl()); +} +template +inline bool operator!=(const Vector& a, const String& b) { + return !(a == b); +} +template +inline bool operator!=(const String& a, const Vector& b) { + return b != a; +} -inline bool equalIgnoringCase(const String& a, const String& b) { return equalIgnoringCase(a.impl(), b.impl()); } -inline bool equalIgnoringCase(const String& a, const LChar* b) { return equalIgnoringCase(a.impl(), b); } -inline bool equalIgnoringCase(const String& a, const char* b) { return equalIgnoringCase(a.impl(), reinterpret_cast(b)); } -inline bool equalIgnoringCase(const LChar* a, const String& b) { return equalIgnoringCase(a, b.impl()); } -inline bool equalIgnoringCase(const char* a, const String& b) { return equalIgnoringCase(reinterpret_cast(a), b.impl()); } +inline bool equalIgnoringCase(const String& a, const String& b) { + return equalIgnoringCase(a.impl(), b.impl()); +} +inline bool equalIgnoringCase(const String& a, const LChar* b) { + return equalIgnoringCase(a.impl(), b); +} +inline bool equalIgnoringCase(const String& a, const char* b) { + return equalIgnoringCase(a.impl(), reinterpret_cast(b)); +} +inline bool equalIgnoringCase(const LChar* a, const String& b) { + return equalIgnoringCase(a, b.impl()); +} +inline bool equalIgnoringCase(const char* a, const String& b) { + return equalIgnoringCase(reinterpret_cast(a), b.impl()); +} -inline bool equalIgnoringNullity(const String& a, const String& b) { return equalIgnoringNullity(a.impl(), b.impl()); } +inline bool equalIgnoringNullity(const String& a, const String& b) { + return equalIgnoringNullity(a.impl(), b.impl()); +} -template -inline bool equalIgnoringNullity(const Vector& a, const String& b) { return equalIgnoringNullity(a, b.impl()); } +template +inline bool equalIgnoringNullity(const Vector& a, + const String& b) { + return equalIgnoringNullity(a, b.impl()); +} -inline bool operator!(const String& str) { return str.isNull(); } +inline bool operator!(const String& str) { + return str.isNull(); +} -inline void swap(String& a, String& b) { a.swap(b); } +inline void swap(String& a, String& b) { + a.swap(b); +} // Definitions of string operations -template +template String::String(const Vector& vector) - : m_impl(vector.size() ? StringImpl::create(vector.data(), vector.size()) : StringImpl::empty()) -{ -} + : m_impl(vector.size() ? StringImpl::create(vector.data(), vector.size()) + : StringImpl::empty()) {} -template<> -inline const LChar* String::getCharacters() const -{ - ASSERT(is8Bit()); - return characters8(); +template <> +inline const LChar* String::getCharacters() const { + ASSERT(is8Bit()); + return characters8(); } -template<> -inline const UChar* String::getCharacters() const -{ - ASSERT(!is8Bit()); - return characters16(); +template <> +inline const UChar* String::getCharacters() const { + ASSERT(!is8Bit()); + return characters16(); } -inline bool String::containsOnlyLatin1() const -{ - if (isEmpty()) - return true; +inline bool String::containsOnlyLatin1() const { + if (isEmpty()) + return true; - if (is8Bit()) - return true; + if (is8Bit()) + return true; - const UChar* characters = characters16(); - UChar ored = 0; - for (size_t i = 0; i < m_impl->length(); ++i) - ored |= characters[i]; - return !(ored & 0xFF00); + const UChar* characters = characters16(); + UChar ored = 0; + for (size_t i = 0; i < m_impl->length(); ++i) + ored |= characters[i]; + return !(ored & 0xFF00); } -inline bool String::containsOnlyASCII() const -{ - if (isEmpty()) - return true; +inline bool String::containsOnlyASCII() const { + if (isEmpty()) + return true; - if (is8Bit()) - return charactersAreAllASCII(characters8(), m_impl->length()); + if (is8Bit()) + return charactersAreAllASCII(characters8(), m_impl->length()); - return charactersAreAllASCII(characters16(), m_impl->length()); + return charactersAreAllASCII(characters16(), m_impl->length()); } WTF_EXPORT int codePointCompare(const String&, const String&); -inline bool codePointCompareLessThan(const String& a, const String& b) -{ - return codePointCompare(a.impl(), b.impl()) < 0; -} - -template -inline void append(Vector& vector, const String& string) -{ - unsigned length = string.length(); - if (!length) - return; - if (string.is8Bit()) { - const LChar* characters8 = string.characters8(); - vector.reserveCapacity(vector.size() + length); - for (size_t i = 0; i < length; ++i) - vector.uncheckedAppend(characters8[i]); - } else { - vector.append(string.characters16(), length); - } -} - -template -inline void appendNumber(Vector& vector, unsigned char number) -{ - int numberLength = number > 99 ? 3 : (number > 9 ? 2 : 1); - size_t vectorSize = vector.size(); - vector.grow(vectorSize + numberLength); - - switch (numberLength) { +inline bool codePointCompareLessThan(const String& a, const String& b) { + return codePointCompare(a.impl(), b.impl()) < 0; +} + +template +inline void append(Vector& vector, + const String& string) { + unsigned length = string.length(); + if (!length) + return; + if (string.is8Bit()) { + const LChar* characters8 = string.characters8(); + vector.reserveCapacity(vector.size() + length); + for (size_t i = 0; i < length; ++i) + vector.uncheckedAppend(characters8[i]); + } else { + vector.append(string.characters16(), length); + } +} + +template +inline void appendNumber(Vector& vector, unsigned char number) { + int numberLength = number > 99 ? 3 : (number > 9 ? 2 : 1); + size_t vectorSize = vector.size(); + vector.grow(vectorSize + numberLength); + + switch (numberLength) { case 3: - vector[vectorSize + 2] = number % 10 + '0'; - number /= 10; + vector[vectorSize + 2] = number % 10 + '0'; + number /= 10; case 2: - vector[vectorSize + 1] = number % 10 + '0'; - number /= 10; + vector[vectorSize + 1] = number % 10 + '0'; + number /= 10; case 1: - vector[vectorSize] = number % 10 + '0'; - } + vector[vectorSize] = number % 10 + '0'; + } +} + +template +inline bool isAllSpecialCharacters(const CharacterType* characters, + size_t length) { + for (size_t i = 0; i < length; ++i) { + if (!isSpecialCharacter(characters[i])) + return false; + } + return true; } -template -inline bool isAllSpecialCharacters(const CharacterType* characters, size_t length) -{ - for (size_t i = 0; i < length; ++i) { - if (!isSpecialCharacter(characters[i])) - return false; - } +template +inline bool String::isAllSpecialCharacters() const { + size_t len = length(); + + if (!len) return true; + + if (is8Bit()) + return WTF::isAllSpecialCharacters(characters8(), + len); + return WTF::isAllSpecialCharacters(characters16(), + len); +} + +template +inline void String::appendTo(Vector& result, + unsigned pos, + unsigned len) const { + unsigned numberOfCharactersToCopy = std::min(len, length() - pos); + if (!numberOfCharactersToCopy) + return; + result.reserveCapacity(result.size() + numberOfCharactersToCopy); + if (is8Bit()) { + const LChar* characters8 = m_impl->characters8(); + for (size_t i = 0; i < numberOfCharactersToCopy; ++i) + result.uncheckedAppend(characters8[pos + i]); + } else { + const UChar* characters16 = m_impl->characters16(); + result.append(characters16 + pos, numberOfCharactersToCopy); + } } -template -inline bool String::isAllSpecialCharacters() const -{ - size_t len = length(); - - if (!len) - return true; - - if (is8Bit()) - return WTF::isAllSpecialCharacters(characters8(), len); - return WTF::isAllSpecialCharacters(characters16(), len); -} - -template -inline void String::appendTo(Vector& result, unsigned pos, unsigned len) const -{ - unsigned numberOfCharactersToCopy = std::min(len, length() - pos); - if (!numberOfCharactersToCopy) - return; - result.reserveCapacity(result.size() + numberOfCharactersToCopy); - if (is8Bit()) { - const LChar* characters8 = m_impl->characters8(); - for (size_t i = 0; i < numberOfCharactersToCopy; ++i) - result.uncheckedAppend(characters8[pos + i]); - } else { - const UChar* characters16 = m_impl->characters16(); - result.append(characters16 + pos, numberOfCharactersToCopy); - } -} - -template -inline void String::appendTo(BufferType& result, unsigned pos, unsigned len) const -{ - unsigned numberOfCharactersToCopy = std::min(len, length() - pos); - if (!numberOfCharactersToCopy) - return; - if (is8Bit()) - result.append(m_impl->characters8() + pos, numberOfCharactersToCopy); - else - result.append(m_impl->characters16() + pos, numberOfCharactersToCopy); -} - -template -inline void String::prependTo(Vector& result, unsigned pos, unsigned len) const -{ - unsigned numberOfCharactersToCopy = std::min(len, length() - pos); - if (!numberOfCharactersToCopy) - return; - if (is8Bit()) { - size_t oldSize = result.size(); - result.resize(oldSize + numberOfCharactersToCopy); - memmove(result.data() + numberOfCharactersToCopy, result.data(), oldSize * sizeof(UChar)); - StringImpl::copyChars(result.data(), m_impl->characters8() + pos, numberOfCharactersToCopy); - } else { - result.prepend(m_impl->characters16() + pos, numberOfCharactersToCopy); - } +template +inline void String::appendTo(BufferType& result, + unsigned pos, + unsigned len) const { + unsigned numberOfCharactersToCopy = std::min(len, length() - pos); + if (!numberOfCharactersToCopy) + return; + if (is8Bit()) + result.append(m_impl->characters8() + pos, numberOfCharactersToCopy); + else + result.append(m_impl->characters16() + pos, numberOfCharactersToCopy); +} + +template +inline void String::prependTo(Vector& result, + unsigned pos, + unsigned len) const { + unsigned numberOfCharactersToCopy = std::min(len, length() - pos); + if (!numberOfCharactersToCopy) + return; + if (is8Bit()) { + size_t oldSize = result.size(); + result.resize(oldSize + numberOfCharactersToCopy); + memmove(result.data() + numberOfCharactersToCopy, result.data(), + oldSize * sizeof(UChar)); + StringImpl::copyChars(result.data(), m_impl->characters8() + pos, + numberOfCharactersToCopy); + } else { + result.prepend(m_impl->characters16() + pos, numberOfCharactersToCopy); + } } // StringHash is the default hash for String -template struct DefaultHash; -template<> struct DefaultHash { - typedef StringHash Hash; +template +struct DefaultHash; +template <> +struct DefaultHash { + typedef StringHash Hash; }; // Shared global empty string. WTF_EXPORT const String& emptyString(); WTF_EXPORT extern const String& xmlnsWithColon; -} // namespace WTF +} // namespace WTF WTF_ALLOW_MOVE_AND_INIT_WITH_MEM_FUNCTIONS(String); @@ -646,22 +830,22 @@ using WTF::KeepTrailingZeros; using WTF::StrictUTF8Conversion; using WTF::StrictUTF8ConversionReplacingUnpairedSurrogatesWithFFFD; using WTF::String; -using WTF::emptyString; using WTF::append; using WTF::appendNumber; using WTF::charactersAreAllASCII; -using WTF::charactersToIntStrict; -using WTF::charactersToUIntStrict; +using WTF::charactersToDouble; +using WTF::charactersToFloat; +using WTF::charactersToInt; +using WTF::charactersToInt64; using WTF::charactersToInt64Strict; -using WTF::charactersToUInt64Strict; +using WTF::charactersToIntPtr; using WTF::charactersToIntPtrStrict; -using WTF::charactersToInt; +using WTF::charactersToIntStrict; using WTF::charactersToUInt; -using WTF::charactersToInt64; using WTF::charactersToUInt64; -using WTF::charactersToIntPtr; -using WTF::charactersToDouble; -using WTF::charactersToFloat; +using WTF::charactersToUInt64Strict; +using WTF::charactersToUIntStrict; +using WTF::emptyString; using WTF::equal; using WTF::equalIgnoringCase; using WTF::find; diff --git a/sky/engine/wtf/text/WTFStringTest.cpp b/sky/engine/wtf/text/WTFStringTest.cpp index c3dce671d20ae..fab38a17efd43 100644 --- a/sky/engine/wtf/text/WTFStringTest.cpp +++ b/sky/engine/wtf/text/WTFStringTest.cpp @@ -23,7 +23,6 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ - #include #include #include "flutter/sky/engine/wtf/MathExtras.h" @@ -32,286 +31,327 @@ namespace { -TEST(WTF, StringCreationFromLiteral) -{ - String stringFromLiteral("Explicit construction syntax"); - ASSERT_EQ(strlen("Explicit construction syntax"), stringFromLiteral.length()); - ASSERT_TRUE(stringFromLiteral == "Explicit construction syntax"); - ASSERT_TRUE(stringFromLiteral.is8Bit()); - ASSERT_TRUE(String("Explicit construction syntax") == stringFromLiteral); +TEST(WTF, StringCreationFromLiteral) { + String stringFromLiteral("Explicit construction syntax"); + ASSERT_EQ(strlen("Explicit construction syntax"), stringFromLiteral.length()); + ASSERT_TRUE(stringFromLiteral == "Explicit construction syntax"); + ASSERT_TRUE(stringFromLiteral.is8Bit()); + ASSERT_TRUE(String("Explicit construction syntax") == stringFromLiteral); } -TEST(WTF, StringASCII) -{ - CString output; +TEST(WTF, StringASCII) { + CString output; - // Null String. - output = String().ascii(); - ASSERT_STREQ("", output.data()); + // Null String. + output = String().ascii(); + ASSERT_STREQ("", output.data()); - // Empty String. - output = emptyString().ascii(); - ASSERT_STREQ("", output.data()); + // Empty String. + output = emptyString().ascii(); + ASSERT_STREQ("", output.data()); - // Regular String. - output = String("foobar").ascii(); - ASSERT_STREQ("foobar", output.data()); + // Regular String. + output = String("foobar").ascii(); + ASSERT_STREQ("foobar", output.data()); } -static void testNumberToStringECMAScript(double number, const char* reference) -{ - CString numberString = String::numberToStringECMAScript(number).latin1(); - ASSERT_STREQ(reference, numberString.data()); +static void testNumberToStringECMAScript(double number, const char* reference) { + CString numberString = String::numberToStringECMAScript(number).latin1(); + ASSERT_STREQ(reference, numberString.data()); } -TEST(WTF, StringNumberToStringECMAScriptBoundaries) -{ - typedef std::numeric_limits Limits; +TEST(WTF, StringNumberToStringECMAScriptBoundaries) { + typedef std::numeric_limits Limits; - // Infinity. - testNumberToStringECMAScript(Limits::infinity(), "Infinity"); - testNumberToStringECMAScript(-Limits::infinity(), "-Infinity"); + // Infinity. + testNumberToStringECMAScript(Limits::infinity(), "Infinity"); + testNumberToStringECMAScript(-Limits::infinity(), "-Infinity"); - // NaN. - testNumberToStringECMAScript(-Limits::quiet_NaN(), "NaN"); + // NaN. + testNumberToStringECMAScript(-Limits::quiet_NaN(), "NaN"); - // Zeros. - testNumberToStringECMAScript(0, "0"); - testNumberToStringECMAScript(-0, "0"); + // Zeros. + testNumberToStringECMAScript(0, "0"); + testNumberToStringECMAScript(-0, "0"); - // Min-Max. - testNumberToStringECMAScript(Limits::min(), "2.2250738585072014e-308"); - testNumberToStringECMAScript(Limits::max(), "1.7976931348623157e+308"); + // Min-Max. + testNumberToStringECMAScript(Limits::min(), "2.2250738585072014e-308"); + testNumberToStringECMAScript(Limits::max(), "1.7976931348623157e+308"); } -TEST(WTF, StringNumberToStringECMAScriptRegularNumbers) -{ - // Pi. - testNumberToStringECMAScript(piDouble, "3.141592653589793"); - testNumberToStringECMAScript(piFloat, "3.1415927410125732"); - testNumberToStringECMAScript(piOverTwoDouble, "1.5707963267948966"); - testNumberToStringECMAScript(piOverTwoFloat, "1.5707963705062866"); - testNumberToStringECMAScript(piOverFourDouble, "0.7853981633974483"); - testNumberToStringECMAScript(piOverFourFloat, "0.7853981852531433"); - - // e. - const double e = 2.71828182845904523536028747135266249775724709369995; - testNumberToStringECMAScript(e, "2.718281828459045"); - - // c, speed of light in m/s. - const double c = 299792458; - testNumberToStringECMAScript(c, "299792458"); - - // Golen ratio. - const double phi = 1.6180339887498948482; - testNumberToStringECMAScript(phi, "1.618033988749895"); +TEST(WTF, StringNumberToStringECMAScriptRegularNumbers) { + // Pi. + testNumberToStringECMAScript(piDouble, "3.141592653589793"); + testNumberToStringECMAScript(piFloat, "3.1415927410125732"); + testNumberToStringECMAScript(piOverTwoDouble, "1.5707963267948966"); + testNumberToStringECMAScript(piOverTwoFloat, "1.5707963705062866"); + testNumberToStringECMAScript(piOverFourDouble, "0.7853981633974483"); + testNumberToStringECMAScript(piOverFourFloat, "0.7853981852531433"); + + // e. + const double e = 2.71828182845904523536028747135266249775724709369995; + testNumberToStringECMAScript(e, "2.718281828459045"); + + // c, speed of light in m/s. + const double c = 299792458; + testNumberToStringECMAScript(c, "299792458"); + + // Golen ratio. + const double phi = 1.6180339887498948482; + testNumberToStringECMAScript(phi, "1.618033988749895"); } -TEST(WTF, StringReplaceWithLiteral) -{ - // Cases for 8Bit source. - String testString = "1224"; - ASSERT_TRUE(testString.is8Bit()); - testString.replaceWithLiteral('2', ""); - ASSERT_STREQ("14", testString.utf8().data()); - - testString = "1224"; - ASSERT_TRUE(testString.is8Bit()); - testString.replaceWithLiteral('2', "3"); - ASSERT_STREQ("1334", testString.utf8().data()); - - testString = "1224"; - ASSERT_TRUE(testString.is8Bit()); - testString.replaceWithLiteral('2', "555"); - ASSERT_STREQ("15555554", testString.utf8().data()); - - testString = "1224"; - ASSERT_TRUE(testString.is8Bit()); - testString.replaceWithLiteral('3', "NotFound"); - ASSERT_STREQ("1224", testString.utf8().data()); - - // Cases for 16Bit source. - testString = String::fromUTF8("résumé"); - ASSERT_FALSE(testString.is8Bit()); - testString.replaceWithLiteral(UChar(0x00E9 /*U+00E9 is 'é'*/), "e"); - ASSERT_STREQ("resume", testString.utf8().data()); - - testString = String::fromUTF8("résumé"); - ASSERT_FALSE(testString.is8Bit()); - testString.replaceWithLiteral(UChar(0x00E9 /*U+00E9 is 'é'*/), ""); - ASSERT_STREQ("rsum", testString.utf8().data()); - - testString = String::fromUTF8("résumé"); - ASSERT_FALSE(testString.is8Bit()); - testString.replaceWithLiteral('3', "NotFound"); - ASSERT_STREQ("résumé", testString.utf8().data()); +TEST(WTF, StringReplaceWithLiteral) { + // Cases for 8Bit source. + String testString = "1224"; + ASSERT_TRUE(testString.is8Bit()); + testString.replaceWithLiteral('2', ""); + ASSERT_STREQ("14", testString.utf8().data()); + + testString = "1224"; + ASSERT_TRUE(testString.is8Bit()); + testString.replaceWithLiteral('2', "3"); + ASSERT_STREQ("1334", testString.utf8().data()); + + testString = "1224"; + ASSERT_TRUE(testString.is8Bit()); + testString.replaceWithLiteral('2', "555"); + ASSERT_STREQ("15555554", testString.utf8().data()); + + testString = "1224"; + ASSERT_TRUE(testString.is8Bit()); + testString.replaceWithLiteral('3', "NotFound"); + ASSERT_STREQ("1224", testString.utf8().data()); + + // Cases for 16Bit source. + testString = String::fromUTF8("résumé"); + ASSERT_FALSE(testString.is8Bit()); + testString.replaceWithLiteral(UChar(0x00E9 /*U+00E9 is 'é'*/), "e"); + ASSERT_STREQ("resume", testString.utf8().data()); + + testString = String::fromUTF8("résumé"); + ASSERT_FALSE(testString.is8Bit()); + testString.replaceWithLiteral(UChar(0x00E9 /*U+00E9 is 'é'*/), ""); + ASSERT_STREQ("rsum", testString.utf8().data()); + + testString = String::fromUTF8("résumé"); + ASSERT_FALSE(testString.is8Bit()); + testString.replaceWithLiteral('3', "NotFound"); + ASSERT_STREQ("résumé", testString.utf8().data()); } -TEST(WTF, StringComparisonOfSameStringVectors) -{ - Vector stringVector; - stringVector.append("one"); - stringVector.append("two"); +TEST(WTF, StringComparisonOfSameStringVectors) { + Vector stringVector; + stringVector.append("one"); + stringVector.append("two"); - Vector sameStringVector; - sameStringVector.append("one"); - sameStringVector.append("two"); + Vector sameStringVector; + sameStringVector.append("one"); + sameStringVector.append("two"); - ASSERT_EQ(stringVector, sameStringVector); + ASSERT_EQ(stringVector, sameStringVector); } -TEST(WTF, SimplifyWhiteSpace) -{ - String extraSpaces(" Hello world "); - ASSERT_EQ(String("Hello world"), extraSpaces.simplifyWhiteSpace()); - ASSERT_EQ(String(" Hello world "), extraSpaces.simplifyWhiteSpace(WTF::DoNotStripWhiteSpace)); - - String extraSpacesAndNewlines(" \nHello\n world\n "); - ASSERT_EQ(String("Hello world"), extraSpacesAndNewlines.simplifyWhiteSpace()); - ASSERT_EQ(String(" Hello world "), extraSpacesAndNewlines.simplifyWhiteSpace(WTF::DoNotStripWhiteSpace)); - - String extraSpacesAndTabs(" \nHello\t world\t "); - ASSERT_EQ(String("Hello world"), extraSpacesAndTabs.simplifyWhiteSpace()); - ASSERT_EQ(String(" Hello world "), extraSpacesAndTabs.simplifyWhiteSpace(WTF::DoNotStripWhiteSpace)); +TEST(WTF, SimplifyWhiteSpace) { + String extraSpaces(" Hello world "); + ASSERT_EQ(String("Hello world"), extraSpaces.simplifyWhiteSpace()); + ASSERT_EQ(String(" Hello world "), + extraSpaces.simplifyWhiteSpace(WTF::DoNotStripWhiteSpace)); + + String extraSpacesAndNewlines(" \nHello\n world\n "); + ASSERT_EQ(String("Hello world"), extraSpacesAndNewlines.simplifyWhiteSpace()); + ASSERT_EQ( + String(" Hello world "), + extraSpacesAndNewlines.simplifyWhiteSpace(WTF::DoNotStripWhiteSpace)); + + String extraSpacesAndTabs(" \nHello\t world\t "); + ASSERT_EQ(String("Hello world"), extraSpacesAndTabs.simplifyWhiteSpace()); + ASSERT_EQ(String(" Hello world "), + extraSpacesAndTabs.simplifyWhiteSpace(WTF::DoNotStripWhiteSpace)); } struct CaseFoldingTestData { - const char* sourceDescription; - const char* source; - const char** localeList; - size_t localeListLength; - const char* expected; + const char* sourceDescription; + const char* source; + const char** localeList; + size_t localeListLength; + const char* expected; }; // \xC4\xB0 = U+0130 (capital dotted I) // \xC4\xB1 = U+0131 (lowercase dotless I) const char* turkicInput = "Isi\xC4\xB0 \xC4\xB0s\xC4\xB1I"; -const char* greekInput = "\xCE\x9F\xCE\x94\xCE\x8C\xCE\xA3 \xCE\x9F\xCE\xB4\xCF\x8C\xCF\x82 \xCE\xA3\xCE\xBF \xCE\xA3\xCE\x9F o\xCE\xA3 \xCE\x9F\xCE\xA3 \xCF\x83 \xE1\xBC\x95\xCE\xBE"; -const char* lithuanianInput = "I \xC3\x8F J J\xCC\x88 \xC4\xAE \xC4\xAE\xCC\x88 \xC3\x8C \xC3\x8D \xC4\xA8 xi\xCC\x87\xCC\x88 xj\xCC\x87\xCC\x88 x\xC4\xAF\xCC\x87\xCC\x88 xi\xCC\x87\xCC\x80 xi\xCC\x87\xCC\x81 xi\xCC\x87\xCC\x83 XI X\xC3\x8F XJ XJ\xCC\x88 X\xC4\xAE X\xC4\xAE\xCC\x88"; - +const char* greekInput = + "\xCE\x9F\xCE\x94\xCE\x8C\xCE\xA3 \xCE\x9F\xCE\xB4\xCF\x8C\xCF\x82 " + "\xCE\xA3\xCE\xBF \xCE\xA3\xCE\x9F o\xCE\xA3 \xCE\x9F\xCE\xA3 \xCF\x83 " + "\xE1\xBC\x95\xCE\xBE"; +const char* lithuanianInput = + "I \xC3\x8F J J\xCC\x88 \xC4\xAE \xC4\xAE\xCC\x88 \xC3\x8C \xC3\x8D " + "\xC4\xA8 xi\xCC\x87\xCC\x88 xj\xCC\x87\xCC\x88 x\xC4\xAF\xCC\x87\xCC\x88 " + "xi\xCC\x87\xCC\x80 xi\xCC\x87\xCC\x81 xi\xCC\x87\xCC\x83 XI X\xC3\x8F XJ " + "XJ\xCC\x88 X\xC4\xAE X\xC4\xAE\xCC\x88"; const char* turkicLocales[] = { "tr", "tr-TR", "tr_TR", "tr@foo=bar", "tr-US", "TR", "tr-tr", "tR", - "az", "az-AZ", "az_AZ", "az@foo=bar", "az-US", "Az", "AZ-AZ", }; + "az", "az-AZ", "az_AZ", "az@foo=bar", "az-US", "Az", "AZ-AZ", +}; const char* nonTurkicLocales[] = { "en", "en-US", "en_US", "en@foo=bar", "EN", "En", - "ja", "el", "fil", "fi", "lt", }; + "ja", "el", "fil", "fi", "lt", +}; const char* greekLocales[] = { "el", "el-GR", "el_GR", "el@foo=bar", "el-US", "EL", "el-gr", "eL", }; const char* nonGreekLocales[] = { "en", "en-US", "en_US", "en@foo=bar", "EN", "En", - "ja", "tr", "az", "fil", "fi", "lt", }; + "ja", "tr", "az", "fil", "fi", "lt", +}; const char* lithuanianLocales[] = { "lt", "lt-LT", "lt_LT", "lt@foo=bar", "lt-US", "LT", "lt-lt", "lT", }; -// Should not have "tr" or "az" because "lt" and 'tr/az' rules conflict with each other. +// Should not have "tr" or "az" because "lt" and 'tr/az' rules conflict with +// each other. const char* nonLithuanianLocales[] = { - "en", "en-US", "en_US", "en@foo=bar", "EN", "En", "ja", "fil", "fi", "el", }; - -TEST(WTF, StringToUpperLocale) -{ - CaseFoldingTestData testDataList[] = { - { - "Turkic input", - turkicInput, - turkicLocales, - sizeof(turkicLocales) / sizeof(const char*), - "IS\xC4\xB0\xC4\xB0 \xC4\xB0SII", - }, { - "Turkic input", - turkicInput, - nonTurkicLocales, - sizeof(nonTurkicLocales) / sizeof(const char*), - "ISI\xC4\xB0 \xC4\xB0SII", - }, { - "Greek input", - greekInput, - greekLocales, - sizeof(greekLocales) / sizeof(const char*), - "\xCE\x9F\xCE\x94\xCE\x9F\xCE\xA3 \xCE\x9F\xCE\x94\xCE\x9F\xCE\xA3 \xCE\xA3\xCE\x9F \xCE\xA3\xCE\x9F \x4F\xCE\xA3 \xCE\x9F\xCE\xA3 \xCE\xA3 \xCE\x95\xCE\x9E", - }, { - "Greek input", - greekInput, - nonGreekLocales, - sizeof(nonGreekLocales) / sizeof(const char*), - "\xCE\x9F\xCE\x94\xCE\x8C\xCE\xA3 \xCE\x9F\xCE\x94\xCE\x8C\xCE\xA3 \xCE\xA3\xCE\x9F \xCE\xA3\xCE\x9F \x4F\xCE\xA3 \xCE\x9F\xCE\xA3 \xCE\xA3 \xE1\xBC\x9D\xCE\x9E", - }, { - "Lithuanian input", - lithuanianInput, - lithuanianLocales, - sizeof(lithuanianLocales) / sizeof(const char*), - "I \xC3\x8F J J\xCC\x88 \xC4\xAE \xC4\xAE\xCC\x88 \xC3\x8C \xC3\x8D \xC4\xA8 XI\xCC\x88 XJ\xCC\x88 X\xC4\xAE\xCC\x88 XI\xCC\x80 XI\xCC\x81 XI\xCC\x83 XI X\xC3\x8F XJ XJ\xCC\x88 X\xC4\xAE X\xC4\xAE\xCC\x88", - }, { - "Lithuanian input", - lithuanianInput, - nonLithuanianLocales, - sizeof(nonLithuanianLocales) / sizeof(const char*), - "I \xC3\x8F J J\xCC\x88 \xC4\xAE \xC4\xAE\xCC\x88 \xC3\x8C \xC3\x8D \xC4\xA8 XI\xCC\x87\xCC\x88 XJ\xCC\x87\xCC\x88 X\xC4\xAE\xCC\x87\xCC\x88 XI\xCC\x87\xCC\x80 XI\xCC\x87\xCC\x81 XI\xCC\x87\xCC\x83 XI X\xC3\x8F XJ XJ\xCC\x88 X\xC4\xAE X\xC4\xAE\xCC\x88", - }, - }; - - for (size_t i = 0; i < sizeof(testDataList) / sizeof(testDataList[0]); ++i) { - const char* expected = testDataList[i].expected; - String source = String::fromUTF8(testDataList[i].source); - for (size_t j = 0; j < testDataList[i].localeListLength; ++j) { - const char* locale = testDataList[i].localeList[j]; - EXPECT_STREQ(expected, source.upper(locale).utf8().data()) << testDataList[i].sourceDescription << "; locale=" << locale; - } + "en", "en-US", "en_US", "en@foo=bar", "EN", "En", "ja", "fil", "fi", "el", +}; + +TEST(WTF, StringToUpperLocale) { + CaseFoldingTestData testDataList[] = { + { + "Turkic input", + turkicInput, + turkicLocales, + sizeof(turkicLocales) / sizeof(const char*), + "IS\xC4\xB0\xC4\xB0 \xC4\xB0SII", + }, + { + "Turkic input", + turkicInput, + nonTurkicLocales, + sizeof(nonTurkicLocales) / sizeof(const char*), + "ISI\xC4\xB0 \xC4\xB0SII", + }, + { + "Greek input", + greekInput, + greekLocales, + sizeof(greekLocales) / sizeof(const char*), + "\xCE\x9F\xCE\x94\xCE\x9F\xCE\xA3 \xCE\x9F\xCE\x94\xCE\x9F\xCE\xA3 " + "\xCE\xA3\xCE\x9F \xCE\xA3\xCE\x9F \x4F\xCE\xA3 \xCE\x9F\xCE\xA3 " + "\xCE\xA3 \xCE\x95\xCE\x9E", + }, + { + "Greek input", + greekInput, + nonGreekLocales, + sizeof(nonGreekLocales) / sizeof(const char*), + "\xCE\x9F\xCE\x94\xCE\x8C\xCE\xA3 \xCE\x9F\xCE\x94\xCE\x8C\xCE\xA3 " + "\xCE\xA3\xCE\x9F \xCE\xA3\xCE\x9F \x4F\xCE\xA3 \xCE\x9F\xCE\xA3 " + "\xCE\xA3 \xE1\xBC\x9D\xCE\x9E", + }, + { + "Lithuanian input", + lithuanianInput, + lithuanianLocales, + sizeof(lithuanianLocales) / sizeof(const char*), + "I \xC3\x8F J J\xCC\x88 \xC4\xAE \xC4\xAE\xCC\x88 \xC3\x8C \xC3\x8D " + "\xC4\xA8 XI\xCC\x88 XJ\xCC\x88 X\xC4\xAE\xCC\x88 XI\xCC\x80 " + "XI\xCC\x81 XI\xCC\x83 XI X\xC3\x8F XJ XJ\xCC\x88 X\xC4\xAE " + "X\xC4\xAE\xCC\x88", + }, + { + "Lithuanian input", + lithuanianInput, + nonLithuanianLocales, + sizeof(nonLithuanianLocales) / sizeof(const char*), + "I \xC3\x8F J J\xCC\x88 \xC4\xAE \xC4\xAE\xCC\x88 \xC3\x8C \xC3\x8D " + "\xC4\xA8 XI\xCC\x87\xCC\x88 XJ\xCC\x87\xCC\x88 " + "X\xC4\xAE\xCC\x87\xCC\x88 XI\xCC\x87\xCC\x80 XI\xCC\x87\xCC\x81 " + "XI\xCC\x87\xCC\x83 XI X\xC3\x8F XJ XJ\xCC\x88 X\xC4\xAE " + "X\xC4\xAE\xCC\x88", + }, + }; + + for (size_t i = 0; i < sizeof(testDataList) / sizeof(testDataList[0]); ++i) { + const char* expected = testDataList[i].expected; + String source = String::fromUTF8(testDataList[i].source); + for (size_t j = 0; j < testDataList[i].localeListLength; ++j) { + const char* locale = testDataList[i].localeList[j]; + EXPECT_STREQ(expected, source.upper(locale).utf8().data()) + << testDataList[i].sourceDescription << "; locale=" << locale; } + } } -TEST(WTF, StringToLowerLocale) -{ - CaseFoldingTestData testDataList[] = { - { - "Turkic input", - turkicInput, - turkicLocales, - sizeof(turkicLocales) / sizeof(const char*), - "\xC4\xB1sii is\xC4\xB1\xC4\xB1", - }, { - "Turkic input", - turkicInput, - nonTurkicLocales, - sizeof(nonTurkicLocales) / sizeof(const char*), - // U+0130 is lowercased to U+0069 followed by U+0307 - "isii\xCC\x87 i\xCC\x87s\xC4\xB1i", - }, { - "Greek input", - greekInput, - greekLocales, - sizeof(greekLocales) / sizeof(const char*), - "\xCE\xBF\xCE\xB4\xCF\x8C\xCF\x82 \xCE\xBF\xCE\xB4\xCF\x8C\xCF\x82 \xCF\x83\xCE\xBF \xCF\x83\xCE\xBF \x6F\xCF\x82 \xCE\xBF\xCF\x82 \xCF\x83 \xE1\xBC\x95\xCE\xBE", - }, { - "Greek input", - greekInput, - nonGreekLocales, - sizeof(greekLocales) / sizeof(const char*), - "\xCE\xBF\xCE\xB4\xCF\x8C\xCF\x82 \xCE\xBF\xCE\xB4\xCF\x8C\xCF\x82 \xCF\x83\xCE\xBF \xCF\x83\xCE\xBF \x6F\xCF\x82 \xCE\xBF\xCF\x82 \xCF\x83 \xE1\xBC\x95\xCE\xBE", - }, { - "Lithuanian input", - lithuanianInput, - lithuanianLocales, - sizeof(lithuanianLocales) / sizeof(const char*), - "i \xC3\xAF j j\xCC\x87\xCC\x88 \xC4\xAF \xC4\xAF\xCC\x87\xCC\x88 i\xCC\x87\xCC\x80 i\xCC\x87\xCC\x81 i\xCC\x87\xCC\x83 xi\xCC\x87\xCC\x88 xj\xCC\x87\xCC\x88 x\xC4\xAF\xCC\x87\xCC\x88 xi\xCC\x87\xCC\x80 xi\xCC\x87\xCC\x81 xi\xCC\x87\xCC\x83 xi x\xC3\xAF xj xj\xCC\x87\xCC\x88 x\xC4\xAF x\xC4\xAF\xCC\x87\xCC\x88", - }, { - "Lithuanian input", - lithuanianInput, - nonLithuanianLocales, - sizeof(nonLithuanianLocales) / sizeof(const char*), - "\x69 \xC3\xAF \x6A \x6A\xCC\x88 \xC4\xAF \xC4\xAF\xCC\x88 \xC3\xAC \xC3\xAD \xC4\xA9 \x78\x69\xCC\x87\xCC\x88 \x78\x6A\xCC\x87\xCC\x88 \x78\xC4\xAF\xCC\x87\xCC\x88 \x78\x69\xCC\x87\xCC\x80 \x78\x69\xCC\x87\xCC\x81 \x78\x69\xCC\x87\xCC\x83 \x78\x69 \x78\xC3\xAF \x78\x6A \x78\x6A\xCC\x88 \x78\xC4\xAF \x78\xC4\xAF\xCC\x88", - }, - }; - - for (size_t i = 0; i < sizeof(testDataList) / sizeof(testDataList[0]); ++i) { - const char* expected = testDataList[i].expected; - String source = String::fromUTF8(testDataList[i].source); - for (size_t j = 0; j < testDataList[i].localeListLength; ++j) { - const char* locale = testDataList[i].localeList[j]; - EXPECT_STREQ(expected, source.lower(locale).utf8().data()) << testDataList[i].sourceDescription << "; locale=" << locale; - } +TEST(WTF, StringToLowerLocale) { + CaseFoldingTestData testDataList[] = { + { + "Turkic input", + turkicInput, + turkicLocales, + sizeof(turkicLocales) / sizeof(const char*), + "\xC4\xB1sii is\xC4\xB1\xC4\xB1", + }, + { + "Turkic input", + turkicInput, + nonTurkicLocales, + sizeof(nonTurkicLocales) / sizeof(const char*), + // U+0130 is lowercased to U+0069 followed by U+0307 + "isii\xCC\x87 i\xCC\x87s\xC4\xB1i", + }, + { + "Greek input", + greekInput, + greekLocales, + sizeof(greekLocales) / sizeof(const char*), + "\xCE\xBF\xCE\xB4\xCF\x8C\xCF\x82 \xCE\xBF\xCE\xB4\xCF\x8C\xCF\x82 " + "\xCF\x83\xCE\xBF \xCF\x83\xCE\xBF \x6F\xCF\x82 \xCE\xBF\xCF\x82 " + "\xCF\x83 \xE1\xBC\x95\xCE\xBE", + }, + { + "Greek input", + greekInput, + nonGreekLocales, + sizeof(greekLocales) / sizeof(const char*), + "\xCE\xBF\xCE\xB4\xCF\x8C\xCF\x82 \xCE\xBF\xCE\xB4\xCF\x8C\xCF\x82 " + "\xCF\x83\xCE\xBF \xCF\x83\xCE\xBF \x6F\xCF\x82 \xCE\xBF\xCF\x82 " + "\xCF\x83 \xE1\xBC\x95\xCE\xBE", + }, + { + "Lithuanian input", + lithuanianInput, + lithuanianLocales, + sizeof(lithuanianLocales) / sizeof(const char*), + "i \xC3\xAF j j\xCC\x87\xCC\x88 \xC4\xAF \xC4\xAF\xCC\x87\xCC\x88 " + "i\xCC\x87\xCC\x80 i\xCC\x87\xCC\x81 i\xCC\x87\xCC\x83 " + "xi\xCC\x87\xCC\x88 xj\xCC\x87\xCC\x88 x\xC4\xAF\xCC\x87\xCC\x88 " + "xi\xCC\x87\xCC\x80 xi\xCC\x87\xCC\x81 xi\xCC\x87\xCC\x83 xi " + "x\xC3\xAF xj xj\xCC\x87\xCC\x88 x\xC4\xAF x\xC4\xAF\xCC\x87\xCC\x88", + }, + { + "Lithuanian input", + lithuanianInput, + nonLithuanianLocales, + sizeof(nonLithuanianLocales) / sizeof(const char*), + "\x69 \xC3\xAF \x6A \x6A\xCC\x88 \xC4\xAF \xC4\xAF\xCC\x88 \xC3\xAC " + "\xC3\xAD \xC4\xA9 \x78\x69\xCC\x87\xCC\x88 \x78\x6A\xCC\x87\xCC\x88 " + "\x78\xC4\xAF\xCC\x87\xCC\x88 \x78\x69\xCC\x87\xCC\x80 " + "\x78\x69\xCC\x87\xCC\x81 \x78\x69\xCC\x87\xCC\x83 \x78\x69 " + "\x78\xC3\xAF \x78\x6A \x78\x6A\xCC\x88 \x78\xC4\xAF " + "\x78\xC4\xAF\xCC\x88", + }, + }; + + for (size_t i = 0; i < sizeof(testDataList) / sizeof(testDataList[0]); ++i) { + const char* expected = testDataList[i].expected; + String source = String::fromUTF8(testDataList[i].source); + for (size_t j = 0; j < testDataList[i].localeListLength; ++j) { + const char* locale = testDataList[i].localeList[j]; + EXPECT_STREQ(expected, source.lower(locale).utf8().data()) + << testDataList[i].sourceDescription << "; locale=" << locale; } + } } -} // namespace +} // namespace diff --git a/sky/engine/wtf/unicode/CharacterNames.h b/sky/engine/wtf/unicode/CharacterNames.h index e17f0b097f245..c93ed2f1cf10c 100644 --- a/sky/engine/wtf/unicode/CharacterNames.h +++ b/sky/engine/wtf/unicode/CharacterNames.h @@ -90,11 +90,12 @@ const UChar zeroWidthNonJoiner = 0x200C; const UChar zeroWidthSpace = 0x200B; const UChar zeroWidthNoBreakSpace = 0xFEFF; -} // namespace Unicode -} // namespace WTF +} // namespace Unicode +} // namespace WTF -using WTF::Unicode::aegeanWordSeparatorLine; +using WTF::Unicode::HiraganaLetterSmallA; using WTF::Unicode::aegeanWordSeparatorDot; +using WTF::Unicode::aegeanWordSeparatorLine; using WTF::Unicode::blackCircle; using WTF::Unicode::blackSquare; using WTF::Unicode::blackUpPointingTriangle; @@ -107,7 +108,6 @@ using WTF::Unicode::ethiopicWordspace; using WTF::Unicode::fisheye; using WTF::Unicode::hebrewPunctuationGeresh; using WTF::Unicode::hebrewPunctuationGershayim; -using WTF::Unicode::HiraganaLetterSmallA; using WTF::Unicode::horizontalEllipsis; using WTF::Unicode::hyphen; using WTF::Unicode::hyphenMinus; @@ -133,8 +133,8 @@ using WTF::Unicode::rightToLeftOverride; using WTF::Unicode::sesameDot; using WTF::Unicode::softHyphen; using WTF::Unicode::space; -using WTF::Unicode::tibetanMarkIntersyllabicTsheg; using WTF::Unicode::tibetanMarkDelimiterTshegBstar; +using WTF::Unicode::tibetanMarkIntersyllabicTsheg; using WTF::Unicode::ugariticWordDivider; using WTF::Unicode::whiteBullet; using WTF::Unicode::whiteCircle; @@ -142,8 +142,8 @@ using WTF::Unicode::whiteSesameDot; using WTF::Unicode::whiteUpPointingTriangle; using WTF::Unicode::yenSign; using WTF::Unicode::zeroWidthJoiner; +using WTF::Unicode::zeroWidthNoBreakSpace; using WTF::Unicode::zeroWidthNonJoiner; using WTF::Unicode::zeroWidthSpace; -using WTF::Unicode::zeroWidthNoBreakSpace; #endif // SKY_ENGINE_WTF_UNICODE_CHARACTERNAMES_H_ diff --git a/sky/engine/wtf/unicode/Collator.h b/sky/engine/wtf/unicode/Collator.h index 6f8c4ffcb2849..273ec9735de83 100644 --- a/sky/engine/wtf/unicode/Collator.h +++ b/sky/engine/wtf/unicode/Collator.h @@ -39,27 +39,31 @@ struct UCollator; namespace WTF { - class WTF_EXPORT Collator { - WTF_MAKE_NONCOPYABLE(Collator); WTF_MAKE_FAST_ALLOCATED; - public: - enum Result { Equal = 0, Greater = 1, Less = -1 }; +class WTF_EXPORT Collator { + WTF_MAKE_NONCOPYABLE(Collator); + WTF_MAKE_FAST_ALLOCATED; - Collator(const char* locale); // Parsing is lenient; e.g. language identifiers (such as "en-US") are accepted, too. - ~Collator(); - void setOrderLowerFirst(bool); + public: + enum Result { Equal = 0, Greater = 1, Less = -1 }; - static PassOwnPtr userDefault(); + Collator(const char* locale); // Parsing is lenient; e.g. language + // identifiers (such as "en-US") are accepted, + // too. + ~Collator(); + void setOrderLowerFirst(bool); - Result collate(const ::UChar*, size_t, const ::UChar*, size_t) const; + static PassOwnPtr userDefault(); - private: - void createCollator() const; - void releaseCollator(); - mutable UCollator* m_collator; - char* m_locale; - bool m_lowerFirst; - }; -} + Result collate(const ::UChar*, size_t, const ::UChar*, size_t) const; + + private: + void createCollator() const; + void releaseCollator(); + mutable UCollator* m_collator; + char* m_locale; + bool m_lowerFirst; +}; +} // namespace WTF using WTF::Collator; diff --git a/sky/engine/wtf/unicode/UTF8.cpp b/sky/engine/wtf/unicode/UTF8.cpp index 46c16a4db6383..32c1a207997e5 100644 --- a/sky/engine/wtf/unicode/UTF8.cpp +++ b/sky/engine/wtf/unicode/UTF8.cpp @@ -33,88 +33,85 @@ namespace WTF { namespace Unicode { -inline int inlineUTF8SequenceLengthNonASCII(char b0) -{ - if ((b0 & 0xC0) != 0xC0) - return 0; - if ((b0 & 0xE0) == 0xC0) - return 2; - if ((b0 & 0xF0) == 0xE0) - return 3; - if ((b0 & 0xF8) == 0xF0) - return 4; +inline int inlineUTF8SequenceLengthNonASCII(char b0) { + if ((b0 & 0xC0) != 0xC0) return 0; + if ((b0 & 0xE0) == 0xC0) + return 2; + if ((b0 & 0xF0) == 0xE0) + return 3; + if ((b0 & 0xF8) == 0xF0) + return 4; + return 0; } -inline int inlineUTF8SequenceLength(char b0) -{ - return isASCII(b0) ? 1 : inlineUTF8SequenceLengthNonASCII(b0); +inline int inlineUTF8SequenceLength(char b0) { + return isASCII(b0) ? 1 : inlineUTF8SequenceLengthNonASCII(b0); } -int UTF8SequenceLength(char b0) -{ - return isASCII(b0) ? 1 : inlineUTF8SequenceLengthNonASCII(b0); +int UTF8SequenceLength(char b0) { + return isASCII(b0) ? 1 : inlineUTF8SequenceLengthNonASCII(b0); } -int decodeUTF8Sequence(const char* sequence) -{ - // Handle 0-byte sequences (never valid). - const unsigned char b0 = sequence[0]; - const int length = inlineUTF8SequenceLength(b0); - if (length == 0) - return -1; - - // Handle 1-byte sequences (plain ASCII). - const unsigned char b1 = sequence[1]; - if (length == 1) { - if (b1) - return -1; - return b0; - } - - // Handle 2-byte sequences. - if ((b1 & 0xC0) != 0x80) - return -1; - const unsigned char b2 = sequence[2]; - if (length == 2) { - if (b2) - return -1; - const int c = ((b0 & 0x1F) << 6) | (b1 & 0x3F); - if (c < 0x80) - return -1; - return c; - } - - // Handle 3-byte sequences. - if ((b2 & 0xC0) != 0x80) - return -1; - const unsigned char b3 = sequence[3]; - if (length == 3) { - if (b3) - return -1; - const int c = ((b0 & 0xF) << 12) | ((b1 & 0x3F) << 6) | (b2 & 0x3F); - if (c < 0x800) - return -1; - // UTF-16 surrogates should never appear in UTF-8 data. - if (c >= 0xD800 && c <= 0xDFFF) - return -1; - return c; - } +int decodeUTF8Sequence(const char* sequence) { + // Handle 0-byte sequences (never valid). + const unsigned char b0 = sequence[0]; + const int length = inlineUTF8SequenceLength(b0); + if (length == 0) + return -1; - // Handle 4-byte sequences. - if ((b3 & 0xC0) != 0x80) - return -1; - const unsigned char b4 = sequence[4]; - if (length == 4) { - if (b4) - return -1; - const int c = ((b0 & 0x7) << 18) | ((b1 & 0x3F) << 12) | ((b2 & 0x3F) << 6) | (b3 & 0x3F); - if (c < 0x10000 || c > 0x10FFFF) - return -1; - return c; - } + // Handle 1-byte sequences (plain ASCII). + const unsigned char b1 = sequence[1]; + if (length == 1) { + if (b1) + return -1; + return b0; + } + // Handle 2-byte sequences. + if ((b1 & 0xC0) != 0x80) return -1; + const unsigned char b2 = sequence[2]; + if (length == 2) { + if (b2) + return -1; + const int c = ((b0 & 0x1F) << 6) | (b1 & 0x3F); + if (c < 0x80) + return -1; + return c; + } + + // Handle 3-byte sequences. + if ((b2 & 0xC0) != 0x80) + return -1; + const unsigned char b3 = sequence[3]; + if (length == 3) { + if (b3) + return -1; + const int c = ((b0 & 0xF) << 12) | ((b1 & 0x3F) << 6) | (b2 & 0x3F); + if (c < 0x800) + return -1; + // UTF-16 surrogates should never appear in UTF-8 data. + if (c >= 0xD800 && c <= 0xDFFF) + return -1; + return c; + } + + // Handle 4-byte sequences. + if ((b3 & 0xC0) != 0x80) + return -1; + const unsigned char b4 = sequence[4]; + if (length == 4) { + if (b4) + return -1; + const int c = ((b0 & 0x7) << 18) | ((b1 & 0x3F) << 12) | + ((b2 & 0x3F) << 6) | (b3 & 0x3F); + if (c < 0x10000 || c > 0x10FFFF) + return -1; + return c; + } + + return -1; } // Once the bits are split out into bytes of UTF-8, this is a mask OR-ed @@ -122,349 +119,411 @@ int decodeUTF8Sequence(const char* sequence) // as many entries in this table as there are UTF-8 sequence types. // (I.e., one byte sequence, two byte... etc.). Remember that sequencs // for *legal* UTF-8 will be 4 or fewer bytes total. -static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; - -ConversionResult convertLatin1ToUTF8( - const LChar** sourceStart, const LChar* sourceEnd, - char** targetStart, char* targetEnd) -{ - ConversionResult result = conversionOK; - const LChar* source = *sourceStart; - char* target = *targetStart; - while (source < sourceEnd) { - UChar32 ch; - unsigned short bytesToWrite = 0; - const UChar32 byteMask = 0xBF; - const UChar32 byteMark = 0x80; - const LChar* oldSource = source; // In case we have to back up because of target overflow. - ch = static_cast(*source++); - - // Figure out how many bytes the result will require - if (ch < (UChar32)0x80) - bytesToWrite = 1; - else - bytesToWrite = 2; - - target += bytesToWrite; - if (target > targetEnd) { - source = oldSource; // Back up source pointer! - target -= bytesToWrite; - result = targetExhausted; - break; - } - switch (bytesToWrite) { // note: everything falls through. - case 2: - *--target = (char)((ch | byteMark) & byteMask); - ch >>= 6; - case 1: - *--target = (char)(ch | firstByteMark[bytesToWrite]); - } - target += bytesToWrite; +static const unsigned char firstByteMark[7] = {0x00, 0x00, 0xC0, 0xE0, + 0xF0, 0xF8, 0xFC}; + +ConversionResult convertLatin1ToUTF8(const LChar** sourceStart, + const LChar* sourceEnd, + char** targetStart, + char* targetEnd) { + ConversionResult result = conversionOK; + const LChar* source = *sourceStart; + char* target = *targetStart; + while (source < sourceEnd) { + UChar32 ch; + unsigned short bytesToWrite = 0; + const UChar32 byteMask = 0xBF; + const UChar32 byteMark = 0x80; + const LChar* oldSource = + source; // In case we have to back up because of target overflow. + ch = static_cast(*source++); + + // Figure out how many bytes the result will require + if (ch < (UChar32)0x80) + bytesToWrite = 1; + else + bytesToWrite = 2; + + target += bytesToWrite; + if (target > targetEnd) { + source = oldSource; // Back up source pointer! + target -= bytesToWrite; + result = targetExhausted; + break; } - *sourceStart = source; - *targetStart = target; - return result; + switch (bytesToWrite) { // note: everything falls through. + case 2: + *--target = (char)((ch | byteMark) & byteMask); + ch >>= 6; + case 1: + *--target = (char)(ch | firstByteMark[bytesToWrite]); + } + target += bytesToWrite; + } + *sourceStart = source; + *targetStart = target; + return result; } -ConversionResult convertUTF16ToUTF8( - const UChar** sourceStart, const UChar* sourceEnd, - char** targetStart, char* targetEnd, bool strict) -{ - ConversionResult result = conversionOK; - const UChar* source = *sourceStart; - char* target = *targetStart; - while (source < sourceEnd) { - UChar32 ch; - unsigned short bytesToWrite = 0; - const UChar32 byteMask = 0xBF; - const UChar32 byteMark = 0x80; - const UChar* oldSource = source; // In case we have to back up because of target overflow. - ch = static_cast(*source++); - // If we have a surrogate pair, convert to UChar32 first. - if (ch >= 0xD800 && ch <= 0xDBFF) { - // If the 16 bits following the high surrogate are in the source buffer... - if (source < sourceEnd) { - UChar32 ch2 = static_cast(*source); - // If it's a low surrogate, convert to UChar32. - if (ch2 >= 0xDC00 && ch2 <= 0xDFFF) { - ch = ((ch - 0xD800) << 10) + (ch2 - 0xDC00) + 0x0010000; - ++source; - } else if (strict) { // it's an unpaired high surrogate - --source; // return to the illegal value itself - result = sourceIllegal; - break; - } - } else { // We don't have the 16 bits following the high surrogate. - --source; // return to the high surrogate - result = sourceExhausted; - break; - } - } else if (strict) { - // UTF-16 surrogate values are illegal in UTF-32 - if (ch >= 0xDC00 && ch <= 0xDFFF) { - --source; // return to the illegal value itself - result = sourceIllegal; - break; - } - } - // Figure out how many bytes the result will require - if (ch < (UChar32)0x80) { - bytesToWrite = 1; - } else if (ch < (UChar32)0x800) { - bytesToWrite = 2; - } else if (ch < (UChar32)0x10000) { - bytesToWrite = 3; - } else if (ch < (UChar32)0x110000) { - bytesToWrite = 4; - } else { - bytesToWrite = 3; - ch = replacementCharacter; +ConversionResult convertUTF16ToUTF8(const UChar** sourceStart, + const UChar* sourceEnd, + char** targetStart, + char* targetEnd, + bool strict) { + ConversionResult result = conversionOK; + const UChar* source = *sourceStart; + char* target = *targetStart; + while (source < sourceEnd) { + UChar32 ch; + unsigned short bytesToWrite = 0; + const UChar32 byteMask = 0xBF; + const UChar32 byteMark = 0x80; + const UChar* oldSource = + source; // In case we have to back up because of target overflow. + ch = static_cast(*source++); + // If we have a surrogate pair, convert to UChar32 first. + if (ch >= 0xD800 && ch <= 0xDBFF) { + // If the 16 bits following the high surrogate are in the source buffer... + if (source < sourceEnd) { + UChar32 ch2 = static_cast(*source); + // If it's a low surrogate, convert to UChar32. + if (ch2 >= 0xDC00 && ch2 <= 0xDFFF) { + ch = ((ch - 0xD800) << 10) + (ch2 - 0xDC00) + 0x0010000; + ++source; + } else if (strict) { // it's an unpaired high surrogate + --source; // return to the illegal value itself + result = sourceIllegal; + break; } + } else { // We don't have the 16 bits following the high surrogate. + --source; // return to the high surrogate + result = sourceExhausted; + break; + } + } else if (strict) { + // UTF-16 surrogate values are illegal in UTF-32 + if (ch >= 0xDC00 && ch <= 0xDFFF) { + --source; // return to the illegal value itself + result = sourceIllegal; + break; + } + } + // Figure out how many bytes the result will require + if (ch < (UChar32)0x80) { + bytesToWrite = 1; + } else if (ch < (UChar32)0x800) { + bytesToWrite = 2; + } else if (ch < (UChar32)0x10000) { + bytesToWrite = 3; + } else if (ch < (UChar32)0x110000) { + bytesToWrite = 4; + } else { + bytesToWrite = 3; + ch = replacementCharacter; + } - target += bytesToWrite; - if (target > targetEnd) { - source = oldSource; // Back up source pointer! - target -= bytesToWrite; - result = targetExhausted; - break; - } - switch (bytesToWrite) { // note: everything falls through. - case 4: *--target = (char)((ch | byteMark) & byteMask); ch >>= 6; - case 3: *--target = (char)((ch | byteMark) & byteMask); ch >>= 6; - case 2: *--target = (char)((ch | byteMark) & byteMask); ch >>= 6; - case 1: *--target = (char)(ch | firstByteMark[bytesToWrite]); - } - target += bytesToWrite; + target += bytesToWrite; + if (target > targetEnd) { + source = oldSource; // Back up source pointer! + target -= bytesToWrite; + result = targetExhausted; + break; } - *sourceStart = source; - *targetStart = target; - return result; + switch (bytesToWrite) { // note: everything falls through. + case 4: + *--target = (char)((ch | byteMark) & byteMask); + ch >>= 6; + case 3: + *--target = (char)((ch | byteMark) & byteMask); + ch >>= 6; + case 2: + *--target = (char)((ch | byteMark) & byteMask); + ch >>= 6; + case 1: + *--target = (char)(ch | firstByteMark[bytesToWrite]); + } + target += bytesToWrite; + } + *sourceStart = source; + *targetStart = target; + return result; } // This must be called with the length pre-determined by the first byte. // If presented with a length > 4, this returns false. The Unicode // definition of UTF-8 goes up to 4-byte sequences. -static bool isLegalUTF8(const unsigned char* source, int length) -{ - unsigned char a; - const unsigned char* srcptr = source + length; - switch (length) { - default: return false; - // Everything else falls through when "true"... - case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; - case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; - case 2: if ((a = (*--srcptr)) > 0xBF) return false; - - switch (*source) { - // no fall-through in this inner switch - case 0xE0: if (a < 0xA0) return false; break; - case 0xED: if (a > 0x9F) return false; break; - case 0xF0: if (a < 0x90) return false; break; - case 0xF4: if (a > 0x8F) return false; break; - default: if (a < 0x80) return false; - } +static bool isLegalUTF8(const unsigned char* source, int length) { + unsigned char a; + const unsigned char* srcptr = source + length; + switch (length) { + default: + return false; + // Everything else falls through when "true"... + case 4: + if ((a = (*--srcptr)) < 0x80 || a > 0xBF) + return false; + case 3: + if ((a = (*--srcptr)) < 0x80 || a > 0xBF) + return false; + case 2: + if ((a = (*--srcptr)) > 0xBF) + return false; - case 1: if (*source >= 0x80 && *source < 0xC2) return false; - } - if (*source > 0xF4) + switch (*source) { + // no fall-through in this inner switch + case 0xE0: + if (a < 0xA0) + return false; + break; + case 0xED: + if (a > 0x9F) + return false; + break; + case 0xF0: + if (a < 0x90) + return false; + break; + case 0xF4: + if (a > 0x8F) + return false; + break; + default: + if (a < 0x80) + return false; + } + + case 1: + if (*source >= 0x80 && *source < 0xC2) return false; - return true; + } + if (*source > 0xF4) + return false; + return true; } // Magic values subtracted from a buffer value during UTF8 conversion. // This table contains as many values as there might be trailing bytes // in a UTF-8 sequence. -static const UChar32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL, 0x03C82080UL, static_cast(0xFA082080UL), static_cast(0x82082080UL) }; - -static inline UChar32 readUTF8Sequence(const char*& sequence, unsigned length) -{ - UChar32 character = 0; - - // The cases all fall through. - switch (length) { - case 6: character += static_cast(*sequence++); character <<= 6; - case 5: character += static_cast(*sequence++); character <<= 6; - case 4: character += static_cast(*sequence++); character <<= 6; - case 3: character += static_cast(*sequence++); character <<= 6; - case 2: character += static_cast(*sequence++); character <<= 6; - case 1: character += static_cast(*sequence++); - } - - return character - offsetsFromUTF8[length - 1]; +static const UChar32 offsetsFromUTF8[6] = {0x00000000UL, + 0x00003080UL, + 0x000E2080UL, + 0x03C82080UL, + static_cast(0xFA082080UL), + static_cast(0x82082080UL)}; + +static inline UChar32 readUTF8Sequence(const char*& sequence, unsigned length) { + UChar32 character = 0; + + // The cases all fall through. + switch (length) { + case 6: + character += static_cast(*sequence++); + character <<= 6; + case 5: + character += static_cast(*sequence++); + character <<= 6; + case 4: + character += static_cast(*sequence++); + character <<= 6; + case 3: + character += static_cast(*sequence++); + character <<= 6; + case 2: + character += static_cast(*sequence++); + character <<= 6; + case 1: + character += static_cast(*sequence++); + } + + return character - offsetsFromUTF8[length - 1]; } -ConversionResult convertUTF8ToUTF16( - const char** sourceStart, const char* sourceEnd, - UChar** targetStart, UChar* targetEnd, bool* sourceAllASCII, bool strict) -{ - ConversionResult result = conversionOK; - const char* source = *sourceStart; - UChar* target = *targetStart; - UChar orAllData = 0; - while (source < sourceEnd) { - int utf8SequenceLength = inlineUTF8SequenceLength(*source); - if (sourceEnd - source < utf8SequenceLength) { - result = sourceExhausted; - break; - } - // Do this check whether lenient or strict - if (!isLegalUTF8(reinterpret_cast(source), utf8SequenceLength)) { - result = sourceIllegal; - break; - } +ConversionResult convertUTF8ToUTF16(const char** sourceStart, + const char* sourceEnd, + UChar** targetStart, + UChar* targetEnd, + bool* sourceAllASCII, + bool strict) { + ConversionResult result = conversionOK; + const char* source = *sourceStart; + UChar* target = *targetStart; + UChar orAllData = 0; + while (source < sourceEnd) { + int utf8SequenceLength = inlineUTF8SequenceLength(*source); + if (sourceEnd - source < utf8SequenceLength) { + result = sourceExhausted; + break; + } + // Do this check whether lenient or strict + if (!isLegalUTF8(reinterpret_cast(source), + utf8SequenceLength)) { + result = sourceIllegal; + break; + } - UChar32 character = readUTF8Sequence(source, utf8SequenceLength); + UChar32 character = readUTF8Sequence(source, utf8SequenceLength); - if (target >= targetEnd) { - source -= utf8SequenceLength; // Back up source pointer! - result = targetExhausted; - break; - } + if (target >= targetEnd) { + source -= utf8SequenceLength; // Back up source pointer! + result = targetExhausted; + break; + } - if (U_IS_BMP(character)) { - // UTF-16 surrogate values are illegal in UTF-32 - if (U_IS_SURROGATE(character)) { - if (strict) { - source -= utf8SequenceLength; // return to the illegal value itself - result = sourceIllegal; - break; - } else { - *target++ = replacementCharacter; - orAllData |= replacementCharacter; - } - } else { - *target++ = character; // normal case - orAllData |= character; - } - } else if (U_IS_SUPPLEMENTARY(character)) { - // target is a character in range 0xFFFF - 0x10FFFF - if (target + 1 >= targetEnd) { - source -= utf8SequenceLength; // Back up source pointer! - result = targetExhausted; - break; - } - *target++ = U16_LEAD(character); - *target++ = U16_TRAIL(character); - orAllData = 0xffff; + if (U_IS_BMP(character)) { + // UTF-16 surrogate values are illegal in UTF-32 + if (U_IS_SURROGATE(character)) { + if (strict) { + source -= utf8SequenceLength; // return to the illegal value itself + result = sourceIllegal; + break; } else { - if (strict) { - source -= utf8SequenceLength; // return to the start - result = sourceIllegal; - break; // Bail out; shouldn't continue - } else { - *target++ = replacementCharacter; - orAllData |= replacementCharacter; - } + *target++ = replacementCharacter; + orAllData |= replacementCharacter; } + } else { + *target++ = character; // normal case + orAllData |= character; + } + } else if (U_IS_SUPPLEMENTARY(character)) { + // target is a character in range 0xFFFF - 0x10FFFF + if (target + 1 >= targetEnd) { + source -= utf8SequenceLength; // Back up source pointer! + result = targetExhausted; + break; + } + *target++ = U16_LEAD(character); + *target++ = U16_TRAIL(character); + orAllData = 0xffff; + } else { + if (strict) { + source -= utf8SequenceLength; // return to the start + result = sourceIllegal; + break; // Bail out; shouldn't continue + } else { + *target++ = replacementCharacter; + orAllData |= replacementCharacter; + } } - *sourceStart = source; - *targetStart = target; + } + *sourceStart = source; + *targetStart = target; - if (sourceAllASCII) - *sourceAllASCII = !(orAllData & ~0x7f); + if (sourceAllASCII) + *sourceAllASCII = !(orAllData & ~0x7f); - return result; + return result; } -unsigned calculateStringHashAndLengthFromUTF8MaskingTop8Bits(const char* data, const char* dataEnd, unsigned& dataLength, unsigned& utf16Length) -{ - if (!data) - return 0; +unsigned calculateStringHashAndLengthFromUTF8MaskingTop8Bits( + const char* data, + const char* dataEnd, + unsigned& dataLength, + unsigned& utf16Length) { + if (!data) + return 0; - StringHasher stringHasher; - dataLength = 0; - utf16Length = 0; + StringHasher stringHasher; + dataLength = 0; + utf16Length = 0; - while (data < dataEnd || (!dataEnd && *data)) { - if (isASCII(*data)) { - stringHasher.addCharacter(*data++); - dataLength++; - utf16Length++; - continue; - } - - int utf8SequenceLength = inlineUTF8SequenceLengthNonASCII(*data); - dataLength += utf8SequenceLength; - - if (!dataEnd) { - for (int i = 1; i < utf8SequenceLength; ++i) { - if (!data[i]) - return 0; - } - } else if (dataEnd - data < utf8SequenceLength) - return 0; - - if (!isLegalUTF8(reinterpret_cast(data), utf8SequenceLength)) - return 0; - - UChar32 character = readUTF8Sequence(data, utf8SequenceLength); - ASSERT(!isASCII(character)); - - if (U_IS_BMP(character)) { - // UTF-16 surrogate values are illegal in UTF-32 - if (U_IS_SURROGATE(character)) - return 0; - stringHasher.addCharacter(static_cast(character)); // normal case - utf16Length++; - } else if (U_IS_SUPPLEMENTARY(character)) { - stringHasher.addCharacters(static_cast(U16_LEAD(character)), - static_cast(U16_TRAIL(character))); - utf16Length += 2; - } else - return 0; + while (data < dataEnd || (!dataEnd && *data)) { + if (isASCII(*data)) { + stringHasher.addCharacter(*data++); + dataLength++; + utf16Length++; + continue; } - return stringHasher.hashWithTop8BitsMasked(); -} + int utf8SequenceLength = inlineUTF8SequenceLengthNonASCII(*data); + dataLength += utf8SequenceLength; -template -ALWAYS_INLINE bool equalWithUTF8Internal(const CharType* a, const CharType* aEnd, const char* b, const char* bEnd) -{ - while (b < bEnd) { - if (isASCII(*b)) { - if (*a++ != *b++) - return false; - continue; - } + if (!dataEnd) { + for (int i = 1; i < utf8SequenceLength; ++i) { + if (!data[i]) + return 0; + } + } else if (dataEnd - data < utf8SequenceLength) + return 0; - int utf8SequenceLength = inlineUTF8SequenceLengthNonASCII(*b); + if (!isLegalUTF8(reinterpret_cast(data), + utf8SequenceLength)) + return 0; - if (bEnd - b < utf8SequenceLength) - return false; + UChar32 character = readUTF8Sequence(data, utf8SequenceLength); + ASSERT(!isASCII(character)); - if (!isLegalUTF8(reinterpret_cast(b), utf8SequenceLength)) - return 0; - - UChar32 character = readUTF8Sequence(b, utf8SequenceLength); - ASSERT(!isASCII(character)); - - if (U_IS_BMP(character)) { - // UTF-16 surrogate values are illegal in UTF-32 - if (U_IS_SURROGATE(character)) - return false; - if (*a++ != character) - return false; - } else if (U_IS_SUPPLEMENTARY(character)) { - if (*a++ != U16_LEAD(character)) - return false; - if (*a++ != U16_TRAIL(character)) - return false; - } else - return false; + if (U_IS_BMP(character)) { + // UTF-16 surrogate values are illegal in UTF-32 + if (U_IS_SURROGATE(character)) + return 0; + stringHasher.addCharacter(static_cast(character)); // normal case + utf16Length++; + } else if (U_IS_SUPPLEMENTARY(character)) { + stringHasher.addCharacters(static_cast(U16_LEAD(character)), + static_cast(U16_TRAIL(character))); + utf16Length += 2; + } else + return 0; + } + + return stringHasher.hashWithTop8BitsMasked(); +} + +template +ALWAYS_INLINE bool equalWithUTF8Internal(const CharType* a, + const CharType* aEnd, + const char* b, + const char* bEnd) { + while (b < bEnd) { + if (isASCII(*b)) { + if (*a++ != *b++) + return false; + continue; } - return a == aEnd; + int utf8SequenceLength = inlineUTF8SequenceLengthNonASCII(*b); + + if (bEnd - b < utf8SequenceLength) + return false; + + if (!isLegalUTF8(reinterpret_cast(b), + utf8SequenceLength)) + return 0; + + UChar32 character = readUTF8Sequence(b, utf8SequenceLength); + ASSERT(!isASCII(character)); + + if (U_IS_BMP(character)) { + // UTF-16 surrogate values are illegal in UTF-32 + if (U_IS_SURROGATE(character)) + return false; + if (*a++ != character) + return false; + } else if (U_IS_SUPPLEMENTARY(character)) { + if (*a++ != U16_LEAD(character)) + return false; + if (*a++ != U16_TRAIL(character)) + return false; + } else + return false; + } + + return a == aEnd; } -bool equalUTF16WithUTF8(const UChar* a, const UChar* aEnd, const char* b, const char* bEnd) -{ - return equalWithUTF8Internal(a, aEnd, b, bEnd); +bool equalUTF16WithUTF8(const UChar* a, + const UChar* aEnd, + const char* b, + const char* bEnd) { + return equalWithUTF8Internal(a, aEnd, b, bEnd); } -bool equalLatin1WithUTF8(const LChar* a, const LChar* aEnd, const char* b, const char* bEnd) -{ - return equalWithUTF8Internal(a, aEnd, b, bEnd); +bool equalLatin1WithUTF8(const LChar* a, + const LChar* aEnd, + const char* b, + const char* bEnd) { + return equalWithUTF8Internal(a, aEnd, b, bEnd); } -} // namespace Unicode -} // namespace WTF +} // namespace Unicode +} // namespace WTF diff --git a/sky/engine/wtf/unicode/UTF8.h b/sky/engine/wtf/unicode/UTF8.h index f9bff954d9aee..34df937d251da 100644 --- a/sky/engine/wtf/unicode/UTF8.h +++ b/sky/engine/wtf/unicode/UTF8.h @@ -32,55 +32,73 @@ namespace WTF { namespace Unicode { - // Given a first byte, gives the length of the UTF-8 sequence it begins. - // Returns 0 for bytes that are not legal starts of UTF-8 sequences. - // Only allows sequences of up to 4 bytes, since that works for all Unicode characters (U-00000000 to U-0010FFFF). - WTF_EXPORT int UTF8SequenceLength(char); +// Given a first byte, gives the length of the UTF-8 sequence it begins. +// Returns 0 for bytes that are not legal starts of UTF-8 sequences. +// Only allows sequences of up to 4 bytes, since that works for all Unicode +// characters (U-00000000 to U-0010FFFF). +WTF_EXPORT int UTF8SequenceLength(char); - // Takes a null-terminated C-style string with a UTF-8 sequence in it and converts it to a character. - // Only allows Unicode characters (U-00000000 to U-0010FFFF). - // Returns -1 if the sequence is not valid (including presence of extra bytes). - WTF_EXPORT int decodeUTF8Sequence(const char*); +// Takes a null-terminated C-style string with a UTF-8 sequence in it and +// converts it to a character. Only allows Unicode characters (U-00000000 to +// U-0010FFFF). Returns -1 if the sequence is not valid (including presence of +// extra bytes). +WTF_EXPORT int decodeUTF8Sequence(const char*); - typedef enum { - conversionOK, // conversion successful - sourceExhausted, // partial character in source, but hit end - targetExhausted, // insuff. room in target for conversion - sourceIllegal // source sequence is illegal/malformed - } ConversionResult; +typedef enum { + conversionOK, // conversion successful + sourceExhausted, // partial character in source, but hit end + targetExhausted, // insuff. room in target for conversion + sourceIllegal // source sequence is illegal/malformed +} ConversionResult; - // These conversion functions take a "strict" argument. When this - // flag is set to strict, both irregular sequences and isolated surrogates - // will cause an error. When the flag is set to lenient, both irregular - // sequences and isolated surrogates are converted. - // - // Whether the flag is strict or lenient, all illegal sequences will cause - // an error return. This includes sequences such as: , , - // or in UTF-8, and values above 0x10FFFF in UTF-32. Conformant code - // must check for illegal sequences. - // - // When the flag is set to lenient, characters over 0x10FFFF are converted - // to the replacement character; otherwise (when the flag is set to strict) - // they constitute an error. +// These conversion functions take a "strict" argument. When this +// flag is set to strict, both irregular sequences and isolated surrogates +// will cause an error. When the flag is set to lenient, both irregular +// sequences and isolated surrogates are converted. +// +// Whether the flag is strict or lenient, all illegal sequences will cause +// an error return. This includes sequences such as: , , +// or in UTF-8, and values above 0x10FFFF in UTF-32. Conformant code +// must check for illegal sequences. +// +// When the flag is set to lenient, characters over 0x10FFFF are converted +// to the replacement character; otherwise (when the flag is set to strict) +// they constitute an error. - WTF_EXPORT ConversionResult convertUTF8ToUTF16( - const char** sourceStart, const char* sourceEnd, - UChar** targetStart, UChar* targetEnd, bool* isSourceAllASCII = 0, bool strict = true); +WTF_EXPORT ConversionResult convertUTF8ToUTF16(const char** sourceStart, + const char* sourceEnd, + UChar** targetStart, + UChar* targetEnd, + bool* isSourceAllASCII = 0, + bool strict = true); - WTF_EXPORT ConversionResult convertLatin1ToUTF8( - const LChar** sourceStart, const LChar* sourceEnd, - char** targetStart, char* targetEnd); +WTF_EXPORT ConversionResult convertLatin1ToUTF8(const LChar** sourceStart, + const LChar* sourceEnd, + char** targetStart, + char* targetEnd); - WTF_EXPORT ConversionResult convertUTF16ToUTF8( - const UChar** sourceStart, const UChar* sourceEnd, - char** targetStart, char* targetEnd, bool strict = true); +WTF_EXPORT ConversionResult convertUTF16ToUTF8(const UChar** sourceStart, + const UChar* sourceEnd, + char** targetStart, + char* targetEnd, + bool strict = true); - WTF_EXPORT unsigned calculateStringHashAndLengthFromUTF8MaskingTop8Bits(const char* data, const char* dataEnd, unsigned& dataLength, unsigned& utf16Length); +WTF_EXPORT unsigned calculateStringHashAndLengthFromUTF8MaskingTop8Bits( + const char* data, + const char* dataEnd, + unsigned& dataLength, + unsigned& utf16Length); - WTF_EXPORT bool equalUTF16WithUTF8(const UChar* a, const UChar* aEnd, const char* b, const char* bEnd); - WTF_EXPORT bool equalLatin1WithUTF8(const LChar* a, const LChar* aEnd, const char* b, const char* bEnd); +WTF_EXPORT bool equalUTF16WithUTF8(const UChar* a, + const UChar* aEnd, + const char* b, + const char* bEnd); +WTF_EXPORT bool equalLatin1WithUTF8(const LChar* a, + const LChar* aEnd, + const char* b, + const char* bEnd); -} // namespace Unicode -} // namespace WTF +} // namespace Unicode +} // namespace WTF #endif // SKY_ENGINE_WTF_UNICODE_UTF8_H_ diff --git a/sky/engine/wtf/unicode/icu/CollatorICU.cpp b/sky/engine/wtf/unicode/icu/CollatorICU.cpp index b20dc8f0d2b56..6013680f2e4a6 100644 --- a/sky/engine/wtf/unicode/icu/CollatorICU.cpp +++ b/sky/engine/wtf/unicode/icu/CollatorICU.cpp @@ -39,91 +39,93 @@ namespace WTF { static UCollator* cachedCollator; -static Mutex& cachedCollatorMutex() -{ - AtomicallyInitializedStatic(Mutex&, mutex = *new Mutex); - return mutex; +static Mutex& cachedCollatorMutex() { + AtomicallyInitializedStatic(Mutex&, mutex = *new Mutex); + return mutex; } Collator::Collator(const char* locale) - : m_collator(0) - , m_locale(locale ? strdup(locale) : 0) - , m_lowerFirst(false) -{ -} + : m_collator(0), + m_locale(locale ? strdup(locale) : 0), + m_lowerFirst(false) {} -PassOwnPtr Collator::userDefault() -{ - return adoptPtr(new Collator(0)); +PassOwnPtr Collator::userDefault() { + return adoptPtr(new Collator(0)); } -Collator::~Collator() -{ - releaseCollator(); - free(m_locale); +Collator::~Collator() { + releaseCollator(); + free(m_locale); } -void Collator::setOrderLowerFirst(bool lowerFirst) -{ - m_lowerFirst = lowerFirst; +void Collator::setOrderLowerFirst(bool lowerFirst) { + m_lowerFirst = lowerFirst; } -Collator::Result Collator::collate(const UChar* lhs, size_t lhsLength, const UChar* rhs, size_t rhsLength) const -{ - if (!m_collator) - createCollator(); +Collator::Result Collator::collate(const UChar* lhs, + size_t lhsLength, + const UChar* rhs, + size_t rhsLength) const { + if (!m_collator) + createCollator(); - return static_cast(ucol_strcoll(m_collator, lhs, lhsLength, rhs, rhsLength)); + return static_cast( + ucol_strcoll(m_collator, lhs, lhsLength, rhs, rhsLength)); } -void Collator::createCollator() const -{ - ASSERT(!m_collator); - UErrorCode status = U_ZERO_ERROR; - - { - Locker lock(cachedCollatorMutex()); - if (cachedCollator) { - const char* cachedCollatorLocale = ucol_getLocaleByType(cachedCollator, ULOC_REQUESTED_LOCALE, &status); - ASSERT(U_SUCCESS(status)); - ASSERT(cachedCollatorLocale); - - UColAttributeValue cachedCollatorLowerFirst = ucol_getAttribute(cachedCollator, UCOL_CASE_FIRST, &status); - ASSERT(U_SUCCESS(status)); - - // FIXME: default locale is never matched, because ucol_getLocaleByType returns the actual one used, not 0. - if (m_locale && 0 == strcmp(cachedCollatorLocale, m_locale) - && ((UCOL_LOWER_FIRST == cachedCollatorLowerFirst && m_lowerFirst) || (UCOL_UPPER_FIRST == cachedCollatorLowerFirst && !m_lowerFirst))) { - m_collator = cachedCollator; - cachedCollator = 0; - return; - } - } - } - - m_collator = ucol_open(m_locale, &status); - if (U_FAILURE(status)) { - status = U_ZERO_ERROR; - m_collator = ucol_open("", &status); // Fallback to Unicode Collation Algorithm. +void Collator::createCollator() const { + ASSERT(!m_collator); + UErrorCode status = U_ZERO_ERROR; + + { + Locker lock(cachedCollatorMutex()); + if (cachedCollator) { + const char* cachedCollatorLocale = + ucol_getLocaleByType(cachedCollator, ULOC_REQUESTED_LOCALE, &status); + ASSERT(U_SUCCESS(status)); + ASSERT(cachedCollatorLocale); + + UColAttributeValue cachedCollatorLowerFirst = + ucol_getAttribute(cachedCollator, UCOL_CASE_FIRST, &status); + ASSERT(U_SUCCESS(status)); + + // FIXME: default locale is never matched, because ucol_getLocaleByType + // returns the actual one used, not 0. + if (m_locale && 0 == strcmp(cachedCollatorLocale, m_locale) && + ((UCOL_LOWER_FIRST == cachedCollatorLowerFirst && m_lowerFirst) || + (UCOL_UPPER_FIRST == cachedCollatorLowerFirst && !m_lowerFirst))) { + m_collator = cachedCollator; + cachedCollator = 0; + return; + } } - ASSERT(U_SUCCESS(status)); - - ucol_setAttribute(m_collator, UCOL_CASE_FIRST, m_lowerFirst ? UCOL_LOWER_FIRST : UCOL_UPPER_FIRST, &status); - ASSERT(U_SUCCESS(status)); - - ucol_setAttribute(m_collator, UCOL_NORMALIZATION_MODE, UCOL_ON, &status); - ASSERT(U_SUCCESS(status)); + } + + m_collator = ucol_open(m_locale, &status); + if (U_FAILURE(status)) { + status = U_ZERO_ERROR; + m_collator = + ucol_open("", &status); // Fallback to Unicode Collation Algorithm. + } + ASSERT(U_SUCCESS(status)); + + ucol_setAttribute(m_collator, UCOL_CASE_FIRST, + m_lowerFirst ? UCOL_LOWER_FIRST : UCOL_UPPER_FIRST, + &status); + ASSERT(U_SUCCESS(status)); + + ucol_setAttribute(m_collator, UCOL_NORMALIZATION_MODE, UCOL_ON, &status); + ASSERT(U_SUCCESS(status)); } -void Collator::releaseCollator() -{ - { - Locker lock(cachedCollatorMutex()); - if (cachedCollator) - ucol_close(cachedCollator); - cachedCollator = m_collator; - m_collator = 0; - } +void Collator::releaseCollator() { + { + Locker lock(cachedCollatorMutex()); + if (cachedCollator) + ucol_close(cachedCollator); + cachedCollator = m_collator; + m_collator = 0; + } } -} // namespace WTF +} // namespace WTF diff --git a/sky/engine/wtf/unicode/icu/UnicodeIcu.h b/sky/engine/wtf/unicode/icu/UnicodeIcu.h index 2a58a5a2df766..f8c329f5301f3 100644 --- a/sky/engine/wtf/unicode/icu/UnicodeIcu.h +++ b/sky/engine/wtf/unicode/icu/UnicodeIcu.h @@ -31,200 +31,196 @@ namespace WTF { namespace Unicode { enum Direction { - LeftToRight = U_LEFT_TO_RIGHT, - RightToLeft = U_RIGHT_TO_LEFT, - EuropeanNumber = U_EUROPEAN_NUMBER, - EuropeanNumberSeparator = U_EUROPEAN_NUMBER_SEPARATOR, - EuropeanNumberTerminator = U_EUROPEAN_NUMBER_TERMINATOR, - ArabicNumber = U_ARABIC_NUMBER, - CommonNumberSeparator = U_COMMON_NUMBER_SEPARATOR, - BlockSeparator = U_BLOCK_SEPARATOR, - SegmentSeparator = U_SEGMENT_SEPARATOR, - WhiteSpaceNeutral = U_WHITE_SPACE_NEUTRAL, - OtherNeutral = U_OTHER_NEUTRAL, - LeftToRightEmbedding = U_LEFT_TO_RIGHT_EMBEDDING, - LeftToRightOverride = U_LEFT_TO_RIGHT_OVERRIDE, - RightToLeftArabic = U_RIGHT_TO_LEFT_ARABIC, - RightToLeftEmbedding = U_RIGHT_TO_LEFT_EMBEDDING, - RightToLeftOverride = U_RIGHT_TO_LEFT_OVERRIDE, - PopDirectionalFormat = U_POP_DIRECTIONAL_FORMAT, - NonSpacingMark = U_DIR_NON_SPACING_MARK, - BoundaryNeutral = U_BOUNDARY_NEUTRAL + LeftToRight = U_LEFT_TO_RIGHT, + RightToLeft = U_RIGHT_TO_LEFT, + EuropeanNumber = U_EUROPEAN_NUMBER, + EuropeanNumberSeparator = U_EUROPEAN_NUMBER_SEPARATOR, + EuropeanNumberTerminator = U_EUROPEAN_NUMBER_TERMINATOR, + ArabicNumber = U_ARABIC_NUMBER, + CommonNumberSeparator = U_COMMON_NUMBER_SEPARATOR, + BlockSeparator = U_BLOCK_SEPARATOR, + SegmentSeparator = U_SEGMENT_SEPARATOR, + WhiteSpaceNeutral = U_WHITE_SPACE_NEUTRAL, + OtherNeutral = U_OTHER_NEUTRAL, + LeftToRightEmbedding = U_LEFT_TO_RIGHT_EMBEDDING, + LeftToRightOverride = U_LEFT_TO_RIGHT_OVERRIDE, + RightToLeftArabic = U_RIGHT_TO_LEFT_ARABIC, + RightToLeftEmbedding = U_RIGHT_TO_LEFT_EMBEDDING, + RightToLeftOverride = U_RIGHT_TO_LEFT_OVERRIDE, + PopDirectionalFormat = U_POP_DIRECTIONAL_FORMAT, + NonSpacingMark = U_DIR_NON_SPACING_MARK, + BoundaryNeutral = U_BOUNDARY_NEUTRAL }; enum DecompositionType { - DecompositionNone = U_DT_NONE, - DecompositionCanonical = U_DT_CANONICAL, - DecompositionCompat = U_DT_COMPAT, - DecompositionCircle = U_DT_CIRCLE, - DecompositionFinal = U_DT_FINAL, - DecompositionFont = U_DT_FONT, - DecompositionFraction = U_DT_FRACTION, - DecompositionInitial = U_DT_INITIAL, - DecompositionIsolated = U_DT_ISOLATED, - DecompositionMedial = U_DT_MEDIAL, - DecompositionNarrow = U_DT_NARROW, - DecompositionNoBreak = U_DT_NOBREAK, - DecompositionSmall = U_DT_SMALL, - DecompositionSquare = U_DT_SQUARE, - DecompositionSub = U_DT_SUB, - DecompositionSuper = U_DT_SUPER, - DecompositionVertical = U_DT_VERTICAL, - DecompositionWide = U_DT_WIDE, + DecompositionNone = U_DT_NONE, + DecompositionCanonical = U_DT_CANONICAL, + DecompositionCompat = U_DT_COMPAT, + DecompositionCircle = U_DT_CIRCLE, + DecompositionFinal = U_DT_FINAL, + DecompositionFont = U_DT_FONT, + DecompositionFraction = U_DT_FRACTION, + DecompositionInitial = U_DT_INITIAL, + DecompositionIsolated = U_DT_ISOLATED, + DecompositionMedial = U_DT_MEDIAL, + DecompositionNarrow = U_DT_NARROW, + DecompositionNoBreak = U_DT_NOBREAK, + DecompositionSmall = U_DT_SMALL, + DecompositionSquare = U_DT_SQUARE, + DecompositionSub = U_DT_SUB, + DecompositionSuper = U_DT_SUPER, + DecompositionVertical = U_DT_VERTICAL, + DecompositionWide = U_DT_WIDE, }; enum CharCategory { - NoCategory = 0, - Other_NotAssigned = U_MASK(U_GENERAL_OTHER_TYPES), - Letter_Uppercase = U_MASK(U_UPPERCASE_LETTER), - Letter_Lowercase = U_MASK(U_LOWERCASE_LETTER), - Letter_Titlecase = U_MASK(U_TITLECASE_LETTER), - Letter_Modifier = U_MASK(U_MODIFIER_LETTER), - Letter_Other = U_MASK(U_OTHER_LETTER), - - Mark_NonSpacing = U_MASK(U_NON_SPACING_MARK), - Mark_Enclosing = U_MASK(U_ENCLOSING_MARK), - Mark_SpacingCombining = U_MASK(U_COMBINING_SPACING_MARK), - - Number_DecimalDigit = U_MASK(U_DECIMAL_DIGIT_NUMBER), - Number_Letter = U_MASK(U_LETTER_NUMBER), - Number_Other = U_MASK(U_OTHER_NUMBER), - - Separator_Space = U_MASK(U_SPACE_SEPARATOR), - Separator_Line = U_MASK(U_LINE_SEPARATOR), - Separator_Paragraph = U_MASK(U_PARAGRAPH_SEPARATOR), - - Other_Control = U_MASK(U_CONTROL_CHAR), - Other_Format = U_MASK(U_FORMAT_CHAR), - Other_PrivateUse = U_MASK(U_PRIVATE_USE_CHAR), - Other_Surrogate = U_MASK(U_SURROGATE), - - Punctuation_Dash = U_MASK(U_DASH_PUNCTUATION), - Punctuation_Open = U_MASK(U_START_PUNCTUATION), - Punctuation_Close = U_MASK(U_END_PUNCTUATION), - Punctuation_Connector = U_MASK(U_CONNECTOR_PUNCTUATION), - Punctuation_Other = U_MASK(U_OTHER_PUNCTUATION), - - Symbol_Math = U_MASK(U_MATH_SYMBOL), - Symbol_Currency = U_MASK(U_CURRENCY_SYMBOL), - Symbol_Modifier = U_MASK(U_MODIFIER_SYMBOL), - Symbol_Other = U_MASK(U_OTHER_SYMBOL), - - Punctuation_InitialQuote = U_MASK(U_INITIAL_PUNCTUATION), - Punctuation_FinalQuote = U_MASK(U_FINAL_PUNCTUATION) + NoCategory = 0, + Other_NotAssigned = U_MASK(U_GENERAL_OTHER_TYPES), + Letter_Uppercase = U_MASK(U_UPPERCASE_LETTER), + Letter_Lowercase = U_MASK(U_LOWERCASE_LETTER), + Letter_Titlecase = U_MASK(U_TITLECASE_LETTER), + Letter_Modifier = U_MASK(U_MODIFIER_LETTER), + Letter_Other = U_MASK(U_OTHER_LETTER), + + Mark_NonSpacing = U_MASK(U_NON_SPACING_MARK), + Mark_Enclosing = U_MASK(U_ENCLOSING_MARK), + Mark_SpacingCombining = U_MASK(U_COMBINING_SPACING_MARK), + + Number_DecimalDigit = U_MASK(U_DECIMAL_DIGIT_NUMBER), + Number_Letter = U_MASK(U_LETTER_NUMBER), + Number_Other = U_MASK(U_OTHER_NUMBER), + + Separator_Space = U_MASK(U_SPACE_SEPARATOR), + Separator_Line = U_MASK(U_LINE_SEPARATOR), + Separator_Paragraph = U_MASK(U_PARAGRAPH_SEPARATOR), + + Other_Control = U_MASK(U_CONTROL_CHAR), + Other_Format = U_MASK(U_FORMAT_CHAR), + Other_PrivateUse = U_MASK(U_PRIVATE_USE_CHAR), + Other_Surrogate = U_MASK(U_SURROGATE), + + Punctuation_Dash = U_MASK(U_DASH_PUNCTUATION), + Punctuation_Open = U_MASK(U_START_PUNCTUATION), + Punctuation_Close = U_MASK(U_END_PUNCTUATION), + Punctuation_Connector = U_MASK(U_CONNECTOR_PUNCTUATION), + Punctuation_Other = U_MASK(U_OTHER_PUNCTUATION), + + Symbol_Math = U_MASK(U_MATH_SYMBOL), + Symbol_Currency = U_MASK(U_CURRENCY_SYMBOL), + Symbol_Modifier = U_MASK(U_MODIFIER_SYMBOL), + Symbol_Other = U_MASK(U_OTHER_SYMBOL), + + Punctuation_InitialQuote = U_MASK(U_INITIAL_PUNCTUATION), + Punctuation_FinalQuote = U_MASK(U_FINAL_PUNCTUATION) }; -inline UChar32 foldCase(UChar32 c) -{ - return u_foldCase(c, U_FOLD_CASE_DEFAULT); +inline UChar32 foldCase(UChar32 c) { + return u_foldCase(c, U_FOLD_CASE_DEFAULT); } -inline int foldCase(UChar* result, int resultLength, const UChar* src, int srcLength, bool* error) -{ - UErrorCode status = U_ZERO_ERROR; - int realLength = u_strFoldCase(result, resultLength, src, srcLength, U_FOLD_CASE_DEFAULT, &status); - *error = !U_SUCCESS(status); - return realLength; +inline int foldCase(UChar* result, + int resultLength, + const UChar* src, + int srcLength, + bool* error) { + UErrorCode status = U_ZERO_ERROR; + int realLength = u_strFoldCase(result, resultLength, src, srcLength, + U_FOLD_CASE_DEFAULT, &status); + *error = !U_SUCCESS(status); + return realLength; } -inline int toLower(UChar* result, int resultLength, const UChar* src, int srcLength, bool* error) -{ - UErrorCode status = U_ZERO_ERROR; - int realLength = u_strToLower(result, resultLength, src, srcLength, "", &status); - *error = !!U_FAILURE(status); - return realLength; +inline int toLower(UChar* result, + int resultLength, + const UChar* src, + int srcLength, + bool* error) { + UErrorCode status = U_ZERO_ERROR; + int realLength = + u_strToLower(result, resultLength, src, srcLength, "", &status); + *error = !!U_FAILURE(status); + return realLength; } -inline UChar32 toLower(UChar32 c) -{ - return u_tolower(c); +inline UChar32 toLower(UChar32 c) { + return u_tolower(c); } -inline UChar32 toUpper(UChar32 c) -{ - return u_toupper(c); +inline UChar32 toUpper(UChar32 c) { + return u_toupper(c); } -inline int toUpper(UChar* result, int resultLength, const UChar* src, int srcLength, bool* error) -{ - UErrorCode status = U_ZERO_ERROR; - int realLength = u_strToUpper(result, resultLength, src, srcLength, "", &status); - *error = !!U_FAILURE(status); - return realLength; +inline int toUpper(UChar* result, + int resultLength, + const UChar* src, + int srcLength, + bool* error) { + UErrorCode status = U_ZERO_ERROR; + int realLength = + u_strToUpper(result, resultLength, src, srcLength, "", &status); + *error = !!U_FAILURE(status); + return realLength; } -inline UChar32 toTitleCase(UChar32 c) -{ - return u_totitle(c); +inline UChar32 toTitleCase(UChar32 c) { + return u_totitle(c); } -inline bool isArabicChar(UChar32 c) -{ - return ublock_getCode(c) == UBLOCK_ARABIC; +inline bool isArabicChar(UChar32 c) { + return ublock_getCode(c) == UBLOCK_ARABIC; } -inline bool isAlphanumeric(UChar32 c) -{ - return u_isalnum(c); +inline bool isAlphanumeric(UChar32 c) { + return u_isalnum(c); } -inline bool isSeparatorSpace(UChar32 c) -{ - return u_charType(c) == U_SPACE_SEPARATOR; +inline bool isSeparatorSpace(UChar32 c) { + return u_charType(c) == U_SPACE_SEPARATOR; } -inline bool isPrintableChar(UChar32 c) -{ - return !!u_isprint(c); +inline bool isPrintableChar(UChar32 c) { + return !!u_isprint(c); } -inline bool isPunct(UChar32 c) -{ - return !!u_ispunct(c); +inline bool isPunct(UChar32 c) { + return !!u_ispunct(c); } -inline bool hasLineBreakingPropertyComplexContext(UChar32 c) -{ - return u_getIntPropertyValue(c, UCHAR_LINE_BREAK) == U_LB_COMPLEX_CONTEXT; +inline bool hasLineBreakingPropertyComplexContext(UChar32 c) { + return u_getIntPropertyValue(c, UCHAR_LINE_BREAK) == U_LB_COMPLEX_CONTEXT; } -inline UChar32 mirroredChar(UChar32 c) -{ - return u_charMirror(c); +inline UChar32 mirroredChar(UChar32 c) { + return u_charMirror(c); } -inline CharCategory category(UChar32 c) -{ - return static_cast(U_GET_GC_MASK(c)); +inline CharCategory category(UChar32 c) { + return static_cast(U_GET_GC_MASK(c)); } -inline Direction direction(UChar32 c) -{ - return static_cast(u_charDirection(c)); +inline Direction direction(UChar32 c) { + return static_cast(u_charDirection(c)); } -inline bool isLower(UChar32 c) -{ - return !!u_islower(c); +inline bool isLower(UChar32 c) { + return !!u_islower(c); } -inline uint8_t combiningClass(UChar32 c) -{ - return u_getCombiningClass(c); +inline uint8_t combiningClass(UChar32 c) { + return u_getCombiningClass(c); } -inline DecompositionType decompositionType(UChar32 c) -{ - return static_cast(u_getIntPropertyValue(c, UCHAR_DECOMPOSITION_TYPE)); +inline DecompositionType decompositionType(UChar32 c) { + return static_cast( + u_getIntPropertyValue(c, UCHAR_DECOMPOSITION_TYPE)); } -inline int umemcasecmp(const UChar* a, const UChar* b, int len) -{ - return u_memcasecmp(a, b, len, U_FOLD_CASE_DEFAULT); +inline int umemcasecmp(const UChar* a, const UChar* b, int len) { + return u_memcasecmp(a, b, len, U_FOLD_CASE_DEFAULT); } -} // namespace Unicode +} // namespace Unicode -} // namespace WTF +} // namespace WTF #endif // SKY_ENGINE_WTF_UNICODE_ICU_UNICODEICU_H_ diff --git a/sky/packages/sky_engine/LICENSE b/sky/packages/sky_engine/LICENSE index 9044e74286a5d..6f57bfd364128 100644 --- a/sky/packages/sky_engine/LICENSE +++ b/sky/packages/sky_engine/LICENSE @@ -2798,7 +2798,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- engine -Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. +Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. +All rights reserved. Copyright (C) 2013 Google Inc. All rights reserved. Redistribution and use in source and binary forms, with or without @@ -3138,7 +3139,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- engine -Copyright (C) 2003, 2006, 2008, 2009, 2010, 2012 Apple Inc. All rights reserved. +Copyright (C) 2003, 2006, 2008, 2009, 2010, 2012 Apple Inc. +All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions @@ -3370,7 +3372,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- engine -Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012 Apple Inc. All rights reserved. +Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012 Apple Inc. All +rights reserved. Copyright (C) 2005 Alexey Proskuryakov. Redistribution and use in source and binary forms, with or without @@ -4340,7 +4343,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- engine -Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights reserved. +Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. +All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions @@ -5404,7 +5408,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- engine -Copyright (C) 2008, 2009 Paul Pedriana . All rights reserved. +Copyright (C) 2008, 2009 Paul Pedriana . +All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions @@ -6121,16 +6126,16 @@ are met: notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY -EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- engine @@ -6299,16 +6304,17 @@ are met: notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY - EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. -------------------------------------------------------------------------------- engine @@ -6721,16 +6727,16 @@ are met: notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY -EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- engine @@ -6825,16 +6831,16 @@ are met: notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY -EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- engine @@ -7040,16 +7046,16 @@ are met: notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY -EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- engine @@ -7185,16 +7191,16 @@ are met: notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY -EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- engine @@ -7234,16 +7240,16 @@ are met: notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY -EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- engine @@ -7258,16 +7264,16 @@ are met: notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY -EXPRESS OR IMPLIED WARRANTIES,:tabnew INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES,:tabnew INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- engine @@ -7302,7 +7308,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. engine Copyright (c) 1991, 2000, 2001 by Lucent Technologies. -Copyright (C) 2002, 2005, 2006, 2007, 2008, 2010, 2012 Apple Inc. All rights reserved. +Copyright (C) 2002, 2005, 2006, 2007, 2008, 2010, 2012 Apple Inc. +All rights reserved. Permission to use, copy, modify, and distribute this software for any purpose without fee is hereby granted, provided that this entire notice @@ -7319,7 +7326,8 @@ engine Copyright (c) 2005, 2007, Google Inc. All rights reserved. -Copyright (C) 2005, 2006, 2007, 2008, 2009, 2011 Apple Inc. All rights reserved. +Copyright (C) 2005, 2006, 2007, 2008, 2009, 2011 Apple Inc. +All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -8012,36 +8020,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- engine -Copyright 2013 The Chromium Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------------- -engine - Copyright 2017 The Flutter Authors. All rights reserved. Redistribution and use in source and binary forms, with or without @@ -8556,6 +8534,37 @@ That's all there is to it! -------------------------------------------------------------------------------- engine garnet + +Copyright 2013 The Chromium Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +-------------------------------------------------------------------------------- +engine +garnet icu Copyright 2014 The Chromium Authors. All rights reserved. @@ -8607,6 +8616,39 @@ distribution. contributors may be used to endorse or promote products derived from this software without specific prior written permission. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +-------------------------------------------------------------------------------- +engine +garnet +icu +skia + +Copyright 2016 The Chromium Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR @@ -9189,38 +9231,6 @@ necessary. Here is a sample; alter the names: That's all there is to it! -------------------------------------------------------------------------------- -engine -icu -skia - -Copyright 2016 The Chromium Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------------- etc1 observatory_pub_packages skia diff --git a/synchronization/semaphore.h b/synchronization/semaphore.h index ca8c60309829e..bb35957e58c8b 100644 --- a/synchronization/semaphore.h +++ b/synchronization/semaphore.h @@ -7,9 +7,9 @@ #include +#include "lib/fxl/compiler_specific.h" #include "lib/fxl/macros.h" #include "lib/fxl/time/time_delta.h" -#include "lib/fxl/compiler_specific.h" namespace flutter { diff --git a/third_party/txt/src/txt/asset_font_manager.cc b/third_party/txt/src/txt/asset_font_manager.cc index 698016b5c04e4..aadf7e44ec269 100644 --- a/third_party/txt/src/txt/asset_font_manager.cc +++ b/third_party/txt/src/txt/asset_font_manager.cc @@ -14,8 +14,8 @@ * limitations under the License. */ -#include "lib/fxl/logging.h" #include "txt/asset_font_manager.h" +#include "lib/fxl/logging.h" namespace txt { diff --git a/third_party/txt/src/txt/directory_asset_data_provider.cc b/third_party/txt/src/txt/directory_asset_data_provider.cc index 3141fa7374c85..c82476978927e 100644 --- a/third_party/txt/src/txt/directory_asset_data_provider.cc +++ b/third_party/txt/src/txt/directory_asset_data_provider.cc @@ -2,12 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "txt/directory_asset_data_provider.h" #include #include #include "lib/fxl/logging.h" #include "third_party/skia/include/core/SkStream.h" #include "third_party/skia/include/core/SkTypeface.h" -#include "txt/directory_asset_data_provider.h" namespace txt { diff --git a/third_party/txt/src/txt/paragraph.cc b/third_party/txt/src/txt/paragraph.cc index 9b54aaceb19a7..b95e967eb3dcc 100644 --- a/third_party/txt/src/txt/paragraph.cc +++ b/third_party/txt/src/txt/paragraph.cc @@ -350,15 +350,14 @@ void Paragraph::Layout(double width, bool force) { (lines_ == max_lines - 1 || max_lines == std::numeric_limits::max())) { float ellipsis_width = layout.measureText( - reinterpret_cast(ellipsis.data()), - 0, ellipsis.length(), ellipsis.length(), bidiFlags, - font, minikin_paint, minikin_font_collection, nullptr); + reinterpret_cast(ellipsis.data()), 0, + ellipsis.length(), ellipsis.length(), bidiFlags, font, + minikin_paint, minikin_font_collection, nullptr); std::vector text_advances(text_count); float text_width = layout.measureText( - text.data() + layout_start, 0, text_count, text_count, - bidiFlags, font, minikin_paint, minikin_font_collection, - text_advances.data()); + text.data() + layout_start, 0, text_count, text_count, bidiFlags, + font, minikin_paint, minikin_font_collection, text_advances.data()); // Truncate characters from the text until the ellipsis fits. size_t truncate_count = 0; @@ -368,12 +367,13 @@ void Paragraph::Layout(double width, bool force) { truncate_count++; } - ellipsized_text.reserve(text_count - truncate_count + ellipsis.length()); + ellipsized_text.reserve(text_count - truncate_count + + ellipsis.length()); ellipsized_text.insert(ellipsized_text.begin(), text.begin() + layout_start, text.begin() + layout_end - truncate_count); - ellipsized_text.insert(ellipsized_text.end(), - ellipsis.begin(), ellipsis.end()); + ellipsized_text.insert(ellipsized_text.end(), ellipsis.begin(), + ellipsis.end()); text_ptr = ellipsized_text.data(); text_count = ellipsized_text.size(); @@ -388,8 +388,8 @@ void Paragraph::Layout(double width, bool force) { // However, this is not significant for reasonably sized paragraphs. It is // currently recommended to break up very long paragraphs (10k+ // characters) to ensure speedy layout. - layout.doLayout(text_ptr, 0, text_count, text_count, - bidiFlags, font, minikin_paint, minikin_font_collection); + layout.doLayout(text_ptr, 0, text_count, text_count, bidiFlags, font, + minikin_paint, minikin_font_collection); FillWhitespaceSet(layout_start, layout_end, minikin::getHbFontLocked(layout.getFont(0))); diff --git a/third_party/txt/tests/CmapCoverageTest.cpp b/third_party/txt/tests/CmapCoverageTest.cpp index 2d046a38afaeb..f32c76653dd8c 100644 --- a/third_party/txt/tests/CmapCoverageTest.cpp +++ b/third_party/txt/tests/CmapCoverageTest.cpp @@ -49,7 +49,8 @@ static std::vector buildCmapFormat4Table( uint16_t segmentCount = ranges.size() / 2 + 1 /* +1 for end marker */; const size_t numOfUint16 = - 8 /* format, length, languages, segCountX2, searchRange, entrySelector, rangeShift, pad */ + 8 /* format, length, languages, segCountX2, searchRange, entrySelector, + rangeShift, pad */ + segmentCount * 4 /* endCount, startCount, idRange, idRangeOffset */; const size_t finalLength = sizeof(uint16_t) * numOfUint16; diff --git a/third_party/txt/tests/GraphemeBreakTests.cpp b/third_party/txt/tests/GraphemeBreakTests.cpp index 8f42a02ecfc06..a31a0d90f6aca 100644 --- a/third_party/txt/tests/GraphemeBreakTests.cpp +++ b/third_party/txt/tests/GraphemeBreakTests.cpp @@ -145,7 +145,7 @@ TEST(GraphemeBreak, rules) { EXPECT_FALSE(IsBreak("'a' | U+0301")); // combining accent // TODO(jsimmons): re-enable this test when ICU has been updated in all // Flutter platforms. - //EXPECT_FALSE(IsBreak("'a' | U+200D")); // ZWJ + // EXPECT_FALSE(IsBreak("'a' | U+200D")); // ZWJ // Rule GB9a, x SpacingMark EXPECT_FALSE(IsBreak("U+0915 | U+093E")); // KA, AA (spacing mark) // Rule GB9b, Prepend x diff --git a/tools/licenses/lib/patterns.dart b/tools/licenses/lib/patterns.dart index 18fb4166627d7..1f6be57254990 100644 --- a/tools/licenses/lib/patterns.dart +++ b/tools/licenses/lib/patterns.dart @@ -102,7 +102,7 @@ final List copyrightStatementPatterns = [ new RegExp(r'[-_a-zA-Z0-9()]+ function provided freely by .+'), // TODO(ianh): file a bug on analyzer about what happens if you omit this comma new RegExp(r'^.+ optimized code \(C\) COPYRIGHT .+$', caseSensitive: false), new RegExp(r'^\(Royal Institute of Technology, Stockholm, Sweden\)\.$'), - new RegExp(r'^https?://[^ ]+$'), + new RegExp(r'^\(?https?://[^ ]+$\)?'), new RegExp(r'^The Original Code is Mozilla Communicator client code, released$'), new RegExp(r'^March 31, 1998.$'), // mozilla first release date @@ -1034,7 +1034,7 @@ final List csReferencesByUrl = . All rights reserved. +Copyright (C) 2008, 2009 Paul Pedriana . +All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions @@ -7719,7 +7724,8 @@ FILE: ../../../flutter/sky/engine/wtf/FastMalloc.cpp ---------------------------------------------------------------------------------------------------- Copyright (c) 2005, 2007, Google Inc. All rights reserved. -Copyright (C) 2005, 2006, 2007, 2008, 2009, 2011 Apple Inc. All rights reserved. +Copyright (C) 2005, 2006, 2007, 2008, 2009, 2011 Apple Inc. +All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -8330,7 +8336,8 @@ TYPE: LicenseType.unknown FILE: ../../../flutter/sky/engine/wtf/dtoa.cpp ---------------------------------------------------------------------------------------------------- Copyright (c) 1991, 2000, 2001 by Lucent Technologies. -Copyright (C) 2002, 2005, 2006, 2007, 2008, 2010, 2012 Apple Inc. All rights reserved. +Copyright (C) 2002, 2005, 2006, 2007, 2008, 2010, 2012 Apple Inc. +All rights reserved. Permission to use, copy, modify, and distribute this software for any purpose without fee is hereby granted, provided that this entire notice @@ -8470,7 +8477,8 @@ TYPE: LicenseType.bsd FILE: ../../../flutter/sky/engine/wtf/text/CString.cpp FILE: ../../../flutter/sky/engine/wtf/text/CString.h ---------------------------------------------------------------------------------------------------- -Copyright (C) 2003, 2006, 2008, 2009, 2010, 2012 Apple Inc. All rights reserved. +Copyright (C) 2003, 2006, 2008, 2009, 2010, 2012 Apple Inc. +All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions @@ -9135,16 +9143,16 @@ are met: notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY -EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== ==================================================================================================== @@ -9164,16 +9172,16 @@ are met: notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY -EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== ==================================================================================================== diff --git a/vulkan/vulkan_device.cc b/vulkan/vulkan_device.cc index 37f788a9f874b..b834c25a7e673 100644 --- a/vulkan/vulkan_device.cc +++ b/vulkan/vulkan_device.cc @@ -58,7 +58,7 @@ VulkanDevice::VulkanDevice(VulkanProcTable& p_vk, .pQueuePriorities = priorities, }; - const char *extensions[] = { + const char* extensions[] = { VK_KHR_SWAPCHAIN_EXTENSION_NAME, #if OS_FUCHSIA VK_GOOGLE_EXTERNAL_MEMORY_MAGMA_EXTENSION_NAME, diff --git a/vulkan/vulkan_handle.h b/vulkan/vulkan_handle.h index 327f4f3b2c12a..87f535e9b9132 100644 --- a/vulkan/vulkan_handle.h +++ b/vulkan/vulkan_handle.h @@ -57,7 +57,7 @@ class VulkanHandle { void Reset() { DisposeIfNecessary(); } -private: + private: Handle handle_; Disposer disposer_; diff --git a/vulkan/vulkan_image.cc b/vulkan/vulkan_image.cc index 239defb9b9371..2dfb2cdc83d1f 100644 --- a/vulkan/vulkan_image.cc +++ b/vulkan/vulkan_image.cc @@ -56,7 +56,7 @@ bool VulkanImage::InsertImageMemoryBarrier( nullptr, // buffer_memory_barriers 1, // image_memory_barrier_count &image_memory_barrier // image_memory_barriers - ); + ); if (success) { access_flags_ = dest_access_flags;